diff --git a/DEPS b/DEPS
index e1c1016..0f4c6079 100644
--- a/DEPS
+++ b/DEPS
@@ -175,7 +175,7 @@
   # luci-go CIPD package version.
   # Make sure the revision is uploaded by infra-packagers builder.
   # https://ci.chromium.org/p/infra-internal/g/infra-packagers/console
-  'luci_go': 'git_revision:19175e196dd495f6e092845f7d777cb5bf157b3e',
+  'luci_go': 'git_revision:e81c0c9c528d0a416922e4ccd958d0de59a64816',
 
   # This can be overridden, e.g. with custom_vars, to build clang from HEAD
   # instead of downloading the prebuilt pinned revision.
@@ -204,11 +204,11 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling Skia
   # and whatever else without interference from each other.
-  'skia_revision': '2558c468cc71b3cc8b671c6e55aad1d9ae8a8bba',
+  'skia_revision': 'dc20847579665223826ff5f2d344a0b566e3b4b5',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling V8
   # and whatever else without interference from each other.
-  'v8_revision': '4f0f4e2a3c5ad9dc7312774da5197e0179c30863',
+  'v8_revision': 'fc442e89638f38a476e53abca22aafde28c55aa5',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling swarming_client
   # and whatever else without interference from each other.
@@ -283,7 +283,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling devtools-frontend
   # and whatever else without interference from each other.
-  'devtools_frontend_revision': 'aa1d0c8587914c402d993da0bbda16e84d513fdf',
+  'devtools_frontend_revision': '3ac2a3b23c1b647bed70be845adaa1e058416a34',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling libprotobuf-mutator
   # and whatever else without interference from each other.
@@ -327,7 +327,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.
-  'quiche_revision': '91e0ce88f914bf9663dc6858c1a735c3d8829c47',
+  'quiche_revision': '5111790e59dc91b13e47fc7742b57ee29897b139',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling ios_webkit
   # and whatever else without interference from each other.
@@ -561,7 +561,7 @@
   },
 
   'src/ios/third_party/material_components_ios/src': {
-      'url': Var('chromium_git') + '/external/github.com/material-components/material-components-ios.git' + '@' + 'ee79f1c2b1ded032bf91dfcf01d82eb20c73fa31',
+      'url': Var('chromium_git') + '/external/github.com/material-components/material-components-ios.git' + '@' + 'bf652865ce9c4ec36498f5a38b5c948974062ecc',
       'condition': 'checkout_ios',
   },
 
@@ -929,7 +929,7 @@
   },
 
   'src/third_party/depot_tools':
-    Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + '36de4be91ef66852c49aa5fd9e0cc31d5ec05ae9',
+    Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + '2737963b46b333e8db51f433b56af53fc17cfdc8',
 
   'src/third_party/devtools-frontend/src':
     Var('chromium_git') + '/devtools/devtools-frontend' + '@' + Var('devtools_frontend_revision'),
@@ -1585,7 +1585,7 @@
       'packages': [
         {
           'package': 'skia/tools/goldctl/windows-amd64',
-          'version': 'wr5p_MyXcHVxA-eHznijCeFaqR2HUo02Hw70e0CWCIoC',
+          'version': '3sDKbIeT1dc4BYnE_uMfW71xbKa-b75KWLUwVEIJF-QC',
         },
       ],
       'dep_type': 'cipd',
@@ -1595,7 +1595,7 @@
       'packages': [
         {
           'package': 'skia/tools/goldctl/mac-amd64',
-          'version': 'TPtcrf_XH3mB9pdK4W3N0QPZunsqJ08T7U7fWPvMnRAC',
+          'version': 'Rbel_7KQuxv_-SIkB3oT43MBYgKlr3AUwFystGNHKJ8C',
         },
       ],
       'dep_type': 'cipd',
@@ -1609,7 +1609,7 @@
     Var('chromium_git') + '/v8/v8.git' + '@' +  Var('v8_revision'),
 
   'src-internal': {
-    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@f8d6ef784e7aaa03ad2b20707944b123881ec8ab',
+    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@9e64d40cc7c8207b7de94268052944ffb6422ab4',
     'condition': 'checkout_src_internal',
   },
 
diff --git a/android_webview/BUILD.gn b/android_webview/BUILD.gn
index 5ded435..0c115ad 100644
--- a/android_webview/BUILD.gn
+++ b/android_webview/BUILD.gn
@@ -489,7 +489,7 @@
     "java/src/org/chromium/android_webview/NullAwViewMethods.java",
     "java/src/org/chromium/android_webview/OverScrollGlow.java",
     "java/src/org/chromium/android_webview/PopupTouchHandleDrawable.java",
-    "java/src/org/chromium/android_webview/ScriptReference.java",
+    "java/src/org/chromium/android_webview/ScriptHandler.java",
     "java/src/org/chromium/android_webview/ScrollAccessibilityHelper.java",
     "java/src/org/chromium/android_webview/SslUtil.java",
     "java/src/org/chromium/android_webview/ViewPositionObserver.java",
diff --git a/android_webview/glue/java/src/com/android/webview/chromium/SharedWebViewChromium.java b/android_webview/glue/java/src/com/android/webview/chromium/SharedWebViewChromium.java
index 9815cf74..9f0a2a0e 100644
--- a/android_webview/glue/java/src/com/android/webview/chromium/SharedWebViewChromium.java
+++ b/android_webview/glue/java/src/com/android/webview/chromium/SharedWebViewChromium.java
@@ -9,7 +9,7 @@
 
 import org.chromium.android_webview.AwContents;
 import org.chromium.android_webview.AwRenderProcess;
-import org.chromium.android_webview.ScriptReference;
+import org.chromium.android_webview.ScriptHandler;
 import org.chromium.android_webview.WebMessageListener;
 import org.chromium.android_webview.WebViewChromiumRunQueue;
 import org.chromium.base.ThreadUtils;
@@ -137,7 +137,7 @@
         mAwContents.removeWebMessageListener(jsObjectName);
     }
 
-    public ScriptReference addDocumentStartJavaScript(
+    public ScriptHandler addDocumentStartJavaScript(
             final String script, final String[] allowedOriginRules) {
         if (checkNeedsPost()) {
             return mRunQueue.runOnUiThreadBlocking(
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 b3ae70a..fc0c259 100644
--- a/android_webview/java/src/org/chromium/android_webview/AwContents.java
+++ b/android_webview/java/src/org/chromium/android_webview/AwContents.java
@@ -2621,9 +2621,9 @@
      * Add a JavaScript snippet that will run after the document has been created, but before any
      * script in the document executes. Note that calling this method multiple times will add
      * multiple scripts. Added scripts will take effect from the next navigation. If want to remove
-     * previously set script, use the returned ScriptReference object to do so. Any JavaScript
+     * previously set script, use the returned ScriptHandler object to do so. Any JavaScript
      * objects injected by addWebMessageListener() or addJavascriptInterface() will be available to
-     * use in this script. Scripts can be removed using the ScriptReference object returned when
+     * use in this script. Scripts can be removed using the ScriptHandler object returned when
      * they were added. The DOM tree may not be ready at the moment that the script runs.
      *
      * If multiple scripts are added, they will be executed in the same order they were added.
@@ -2634,9 +2634,9 @@
      *
      * @throws IllegalArgumentException if one of the allowedOriginRules is invalid or one of
      *                                  jsObjectName and allowedOriginRules is {@code null}.
-     * @return A {@link ScriptReference} for removing the script.
+     * @return A {@link ScriptHandler} for removing the script.
      */
-    public ScriptReference addDocumentStartJavaScript(
+    public ScriptHandler addDocumentStartJavaScript(
             @NonNull String script, @NonNull String[] allowedOriginRules) {
         if (script == null) {
             throw new IllegalArgumentException("script shouldn't be null.");
@@ -2649,7 +2649,7 @@
             }
         }
 
-        return new ScriptReference(AwContents.this,
+        return new ScriptHandler(AwContents.this,
                 AwContentsJni.get().addDocumentStartJavaScript(
                         mNativeAwContents, AwContents.this, script, allowedOriginRules));
     }
diff --git a/android_webview/java/src/org/chromium/android_webview/ScriptReference.java b/android_webview/java/src/org/chromium/android_webview/ScriptHandler.java
similarity index 89%
rename from android_webview/java/src/org/chromium/android_webview/ScriptReference.java
rename to android_webview/java/src/org/chromium/android_webview/ScriptHandler.java
index 37ee88ac1..e34486d 100644
--- a/android_webview/java/src/org/chromium/android_webview/ScriptReference.java
+++ b/android_webview/java/src/org/chromium/android_webview/ScriptHandler.java
@@ -11,11 +11,11 @@
 /**
  * Used for Js Java interaction, to delete the document start JavaScript snippet.
  */
-public class ScriptReference {
+public class ScriptHandler {
     private WeakReference<AwContents> mAwContentsRef;
     private int mScriptId;
 
-    public ScriptReference(AwContents awContents, int scriptId) {
+    public ScriptHandler(AwContents awContents, int scriptId) {
         assert scriptId >= 0;
         mAwContentsRef = new WeakReference(awContents);
         mScriptId = scriptId;
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/JsJavaInteractionTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/JsJavaInteractionTest.java
index e40a52d..a250d23f 100644
--- a/android_webview/javatests/src/org/chromium/android_webview/test/JsJavaInteractionTest.java
+++ b/android_webview/javatests/src/org/chromium/android_webview/test/JsJavaInteractionTest.java
@@ -19,7 +19,7 @@
 
 import org.chromium.android_webview.AwContents;
 import org.chromium.android_webview.JsReplyProxy;
-import org.chromium.android_webview.ScriptReference;
+import org.chromium.android_webview.ScriptHandler;
 import org.chromium.android_webview.WebMessageListener;
 import org.chromium.android_webview.test.TestAwContentsClient.OnReceivedTitleHelper;
 import org.chromium.android_webview.test.util.CommonResources;
@@ -1194,13 +1194,13 @@
     @Feature({"AndroidWebView", "JsJavaInteraction"})
     public void testDocumentStartJavaScript_removeScript() throws Throwable {
         addWebMessageListenerOnUiThread(mAwContents, JS_OBJECT_NAME, new String[] {"*"}, mListener);
-        ScriptReference[] references = new ScriptReference[2];
+        ScriptHandler[] handlers = new ScriptHandler[2];
         for (int i = 0; i < 2; ++i) {
             final String script =
                     JS_OBJECT_NAME + ".postMessage('" + HELLO + Integer.toString(i) + "');";
             // Since we are matching both origins, the script will run in both iframe and main
             // frame, but it will send message in only iframe.
-            references[i] =
+            handlers[i] =
                     addDocumentStartJavaScriptOnUiThread(mAwContents, script, new String[] {"*"});
         }
 
@@ -1213,14 +1213,14 @@
             Assert.assertEquals(HELLO + Integer.toString(i), data.mMessage);
         }
 
-        TestThreadUtils.runOnUiThreadBlocking(() -> references[0].remove());
+        TestThreadUtils.runOnUiThreadBlocking(() -> handlers[0].remove());
         // Load the page again.
         loadUrlFromPath(HELLO_WORLD_HTML);
 
         TestWebMessageListener.Data data = mListener.waitForOnPostMessage();
         Assert.assertEquals(HELLO + "1", data.mMessage);
 
-        TestThreadUtils.runOnUiThreadBlocking(() -> references[1].remove());
+        TestThreadUtils.runOnUiThreadBlocking(() -> handlers[1].remove());
         // Load the page again.
         loadUrlFromPath(HELLO_WORLD_HTML);
 
@@ -1234,7 +1234,7 @@
         addWebMessageListenerOnUiThread(mAwContents, JS_OBJECT_NAME, new String[] {"*"}, mListener);
 
         final String script = JS_OBJECT_NAME + ".postMessage('" + HELLO + "');";
-        ScriptReference reference =
+        ScriptHandler handler =
                 addDocumentStartJavaScriptOnUiThread(mAwContents, script, new String[] {"*"});
 
         final String url = loadUrlFromPath(HELLO_WORLD_HTML);
@@ -1245,16 +1245,16 @@
         Assert.assertEquals(HELLO, data.mMessage);
 
         // Remove twice, the second time should take no effect.
-        TestThreadUtils.runOnUiThreadBlocking(() -> reference.remove());
-        TestThreadUtils.runOnUiThreadBlocking(() -> reference.remove());
+        TestThreadUtils.runOnUiThreadBlocking(() -> handler.remove());
+        TestThreadUtils.runOnUiThreadBlocking(() -> handler.remove());
         // Load the page again.
         loadUrlFromPath(HELLO_WORLD_HTML);
 
         Assert.assertTrue(mListener.hasNoMoreOnPostMessage());
 
         // Remove twice again, should have no effect.
-        TestThreadUtils.runOnUiThreadBlocking(() -> reference.remove());
-        TestThreadUtils.runOnUiThreadBlocking(() -> reference.remove());
+        TestThreadUtils.runOnUiThreadBlocking(() -> handler.remove());
+        TestThreadUtils.runOnUiThreadBlocking(() -> handler.remove());
         // Load the page again.
         loadUrlFromPath(HELLO_WORLD_HTML);
 
@@ -1317,7 +1317,7 @@
                 + "</body></html>";
     }
 
-    private static ScriptReference addDocumentStartJavaScriptOnUiThread(
+    private static ScriptHandler addDocumentStartJavaScriptOnUiThread(
             final AwContents awContents, final String script, final String[] allowedOriginRules) {
         return TestThreadUtils.runOnUiThreadBlockingNoException(
                 () -> awContents.addDocumentStartJavaScript(script, allowedOriginRules));
diff --git a/android_webview/support_library/BUILD.gn b/android_webview/support_library/BUILD.gn
index bfbe0bc..65cf647 100644
--- a/android_webview/support_library/BUILD.gn
+++ b/android_webview/support_library/BUILD.gn
@@ -11,7 +11,7 @@
     "java/src/org/chromium/support_lib_glue/SupportLibJsReplyProxyAdapter.java",
     "java/src/org/chromium/support_lib_glue/SupportLibProxyControllerAdapter.java",
     "java/src/org/chromium/support_lib_glue/SupportLibReflectionUtil.java",
-    "java/src/org/chromium/support_lib_glue/SupportLibScriptReferenceAdapter.java",
+    "java/src/org/chromium/support_lib_glue/SupportLibScriptHandlerAdapter.java",
     "java/src/org/chromium/support_lib_glue/SupportLibServiceWorkerClientAdapter.java",
     "java/src/org/chromium/support_lib_glue/SupportLibServiceWorkerControllerAdapter.java",
     "java/src/org/chromium/support_lib_glue/SupportLibServiceWorkerSettingsAdapter.java",
diff --git a/android_webview/support_library/boundary_interfaces/BUILD.gn b/android_webview/support_library/boundary_interfaces/BUILD.gn
index 3221dbc8..68b6ca52 100644
--- a/android_webview/support_library/boundary_interfaces/BUILD.gn
+++ b/android_webview/support_library/boundary_interfaces/BUILD.gn
@@ -12,6 +12,7 @@
     "src/org/chromium/support_lib_boundary/JsReplyProxyBoundaryInterface.java",
     "src/org/chromium/support_lib_boundary/ProxyControllerBoundaryInterface.java",
     "src/org/chromium/support_lib_boundary/SafeBrowsingResponseBoundaryInterface.java",
+    "src/org/chromium/support_lib_boundary/ScriptHandlerBoundaryInterface.java",
     "src/org/chromium/support_lib_boundary/ScriptReferenceBoundaryInterface.java",
     "src/org/chromium/support_lib_boundary/ServiceWorkerClientBoundaryInterface.java",
     "src/org/chromium/support_lib_boundary/ServiceWorkerControllerBoundaryInterface.java",
diff --git a/android_webview/support_library/boundary_interfaces/src/org/chromium/support_lib_boundary/ScriptHandlerBoundaryInterface.java b/android_webview/support_library/boundary_interfaces/src/org/chromium/support_lib_boundary/ScriptHandlerBoundaryInterface.java
new file mode 100644
index 0000000..4dcaa75
--- /dev/null
+++ b/android_webview/support_library/boundary_interfaces/src/org/chromium/support_lib_boundary/ScriptHandlerBoundaryInterface.java
@@ -0,0 +1,10 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.support_lib_boundary;
+
+/**
+ * Boundary interface for AwContents.addDocumentStartJavascript().
+ */
+public interface ScriptHandlerBoundaryInterface extends ScriptReferenceBoundaryInterface {}
diff --git a/android_webview/support_library/boundary_interfaces/src/org/chromium/support_lib_boundary/ScriptReferenceBoundaryInterface.java b/android_webview/support_library/boundary_interfaces/src/org/chromium/support_lib_boundary/ScriptReferenceBoundaryInterface.java
index 6358e00..aba822a 100644
--- a/android_webview/support_library/boundary_interfaces/src/org/chromium/support_lib_boundary/ScriptReferenceBoundaryInterface.java
+++ b/android_webview/support_library/boundary_interfaces/src/org/chromium/support_lib_boundary/ScriptReferenceBoundaryInterface.java
@@ -6,6 +6,9 @@
 
 /**
  * Boundary interface for AwContents.addDocumentStartJavascript().
+ *
+ * TODO(ctzsm): Delete this interface once we've updated the APKs on
+ * the AndroidX bots and move the remove method to ScriptHandlerBoundaryInterface.
  */
 public interface ScriptReferenceBoundaryInterface {
     void remove();
diff --git a/android_webview/support_library/boundary_interfaces/src/org/chromium/support_lib_boundary/WebViewProviderBoundaryInterface.java b/android_webview/support_library/boundary_interfaces/src/org/chromium/support_lib_boundary/WebViewProviderBoundaryInterface.java
index 843d387..d524b31 100644
--- a/android_webview/support_library/boundary_interfaces/src/org/chromium/support_lib_boundary/WebViewProviderBoundaryInterface.java
+++ b/android_webview/support_library/boundary_interfaces/src/org/chromium/support_lib_boundary/WebViewProviderBoundaryInterface.java
@@ -20,7 +20,7 @@
     void addWebMessageListener(String jsObjectName, String[] allowedOriginRules,
             /* WebMessageListener */ InvocationHandler listener);
     void removeWebMessageListener(String jsObjectName);
-    /* ScriptReference */ InvocationHandler addDocumentStartJavaScript(
+    /* ScriptHandler */ InvocationHandler addDocumentStartJavaScript(
             String script, String[] allowedOriginRules);
     WebViewClient getWebViewClient();
     WebChromeClient getWebChromeClient();
diff --git a/android_webview/support_library/boundary_interfaces/src/org/chromium/support_lib_boundary/util/Features.java b/android_webview/support_library/boundary_interfaces/src/org/chromium/support_lib_boundary/util/Features.java
index b2f55e1e..3038a4f 100644
--- a/android_webview/support_library/boundary_interfaces/src/org/chromium/support_lib_boundary/util/Features.java
+++ b/android_webview/support_library/boundary_interfaces/src/org/chromium/support_lib_boundary/util/Features.java
@@ -184,5 +184,5 @@
     public static final String SET_SUPPORT_LIBRARY_VERSION = "SET_SUPPORT_LIBRARY_VERSION";
 
     // WebViewCompat.addDocumentStartJavascript
-    public static final String DOCUMENT_START_SCRIPT = "DOCUMENT_START_SCRIPT:1";
+    public static final String DOCUMENT_START_SCRIPT = "DOCUMENT_START_SCRIPT";
 }
diff --git a/android_webview/support_library/java/src/org/chromium/support_lib_glue/SupportLibScriptHandlerAdapter.java b/android_webview/support_library/java/src/org/chromium/support_lib_glue/SupportLibScriptHandlerAdapter.java
new file mode 100644
index 0000000..20a6de31
--- /dev/null
+++ b/android_webview/support_library/java/src/org/chromium/support_lib_glue/SupportLibScriptHandlerAdapter.java
@@ -0,0 +1,28 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.support_lib_glue;
+
+import static org.chromium.support_lib_glue.SupportLibWebViewChromiumFactory.recordApiCall;
+
+import org.chromium.android_webview.ScriptHandler;
+import org.chromium.support_lib_boundary.ScriptHandlerBoundaryInterface;
+import org.chromium.support_lib_glue.SupportLibWebViewChromiumFactory.ApiCall;
+
+/**
+ * Adapter between ScriptHandlerBoundaryInterface and ScriptHandler.
+ */
+class SupportLibScriptHandlerAdapter implements ScriptHandlerBoundaryInterface {
+    private ScriptHandler mScriptHandler;
+
+    public SupportLibScriptHandlerAdapter(ScriptHandler scriptHandler) {
+        mScriptHandler = scriptHandler;
+    }
+
+    @Override
+    public void remove() {
+        recordApiCall(ApiCall.REMOVE_DOCUMENT_START_SCRIPT);
+        mScriptHandler.remove();
+    }
+}
diff --git a/android_webview/support_library/java/src/org/chromium/support_lib_glue/SupportLibScriptReferenceAdapter.java b/android_webview/support_library/java/src/org/chromium/support_lib_glue/SupportLibScriptReferenceAdapter.java
deleted file mode 100644
index 82b11f31..0000000
--- a/android_webview/support_library/java/src/org/chromium/support_lib_glue/SupportLibScriptReferenceAdapter.java
+++ /dev/null
@@ -1,28 +0,0 @@
-// Copyright 2020 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-package org.chromium.support_lib_glue;
-
-import static org.chromium.support_lib_glue.SupportLibWebViewChromiumFactory.recordApiCall;
-
-import org.chromium.android_webview.ScriptReference;
-import org.chromium.support_lib_boundary.ScriptReferenceBoundaryInterface;
-import org.chromium.support_lib_glue.SupportLibWebViewChromiumFactory.ApiCall;
-
-/**
- * Adapter between ScriptReferenceBoundaryInterface and ScriptReference.
- */
-class SupportLibScriptReferenceAdapter implements ScriptReferenceBoundaryInterface {
-    private ScriptReference mScriptReference;
-
-    public SupportLibScriptReferenceAdapter(ScriptReference scriptReference) {
-        mScriptReference = scriptReference;
-    }
-
-    @Override
-    public void remove() {
-        recordApiCall(ApiCall.REMOVE_DOCUMENT_START_SCRIPT);
-        mScriptReference.remove();
-    }
-}
diff --git a/android_webview/support_library/java/src/org/chromium/support_lib_glue/SupportLibWebViewChromium.java b/android_webview/support_library/java/src/org/chromium/support_lib_glue/SupportLibWebViewChromium.java
index 5d4b908..0b0dd257 100644
--- a/android_webview/support_library/java/src/org/chromium/support_lib_glue/SupportLibWebViewChromium.java
+++ b/android_webview/support_library/java/src/org/chromium/support_lib_glue/SupportLibWebViewChromium.java
@@ -89,11 +89,11 @@
     }
 
     @Override
-    public /* ScriptReference */ InvocationHandler addDocumentStartJavaScript(
+    public /* ScriptHandler */ InvocationHandler addDocumentStartJavaScript(
             final String script, final String[] allowedOriginRules) {
         recordApiCall(ApiCall.ADD_DOCUMENT_START_SCRIPT);
         return BoundaryInterfaceReflectionUtil.createInvocationHandlerFor(
-                new SupportLibScriptReferenceAdapter(
+                new SupportLibScriptHandlerAdapter(
                         mSharedWebViewChromium.addDocumentStartJavaScript(
                                 script, allowedOriginRules)));
     }
diff --git a/ash/app_list/views/app_list_item_view.cc b/ash/app_list/views/app_list_item_view.cc
index 5182f81..5ea1eaf 100644
--- a/ash/app_list/views/app_list_item_view.cc
+++ b/ash/app_list/views/app_list_item_view.cc
@@ -365,26 +365,16 @@
   switch (ui_state) {
     case UI_STATE_NORMAL:
       title_->SetVisible(true);
-      if (ui_state_ == UI_STATE_DRAGGING) {
+      if (ui_state_ == UI_STATE_DRAGGING)
         ScaleAppIcon(false);
-      } else if (ui_state_ == UI_STATE_CARDIFY) {
-        title_->SetFontList(GetAppListConfig().app_title_font());
-        ScaleIconImmediatly(1.0f);
-      }
       break;
     case UI_STATE_DRAGGING:
       title_->SetVisible(false);
-      if (ui_state_ == UI_STATE_NORMAL)
+      if (ui_state_ == UI_STATE_NORMAL && !in_cardified_grid_)
         ScaleAppIcon(true);
       break;
     case UI_STATE_DROPPING_IN_FOLDER:
       break;
-    case UI_STATE_CARDIFY:
-      gfx::FontList font_size = GetAppListConfig().app_title_font();
-      const int size_delta = font_size.GetFontSize() * (1 - kCardifyIconScale);
-      title_->SetFontList(font_size.DeriveWithSizeDelta(-size_delta));
-      ScaleIconImmediatly(kCardifyIconScale);
-      break;
   }
   ui_state_ = ui_state;
 
@@ -993,8 +983,18 @@
   SetUIState(UI_STATE_DRAGGING);
 }
 
-void AppListItemView::SetCardifyUIState() {
-  SetUIState(UI_STATE_CARDIFY);
+void AppListItemView::EnterCardifyState() {
+  in_cardified_grid_ = true;
+  gfx::FontList font_size = GetAppListConfig().app_title_font();
+  const int size_delta = font_size.GetFontSize() * (1 - kCardifyIconScale);
+  title_->SetFontList(font_size.DeriveWithSizeDelta(-size_delta));
+  ScaleIconImmediatly(kCardifyIconScale);
+}
+
+void AppListItemView::ExitCardifyState() {
+  title_->SetFontList(GetAppListConfig().app_title_font());
+  ScaleIconImmediatly(1.0f);
+  in_cardified_grid_ = false;
 }
 
 void AppListItemView::SetNormalUIState() {
diff --git a/ash/app_list/views/app_list_item_view.h b/ash/app_list/views/app_list_item_view.h
index 751dada..a2239b7 100644
--- a/ash/app_list/views/app_list_item_view.h
+++ b/ash/app_list/views/app_list_item_view.h
@@ -97,11 +97,13 @@
 
   // Sets UI state to dragging state.
   void SetDragUIState();
-  // Sets UI state to cardify state.
-  void SetCardifyUIState();
   // Sets UI state to normal state.
   void SetNormalUIState();
 
+  // Handles the icon's scaling and animation for a cardified grid.
+  void EnterCardifyState();
+  void ExitCardifyState();
+
   // Returns the icon bounds for with |target_bounds| as the bounds of this view
   // and given |icon_size| and the |icon_scale| if the icon was scaled from the
   // original display size.
@@ -157,7 +159,6 @@
     UI_STATE_NORMAL,              // Normal UI (icon + label)
     UI_STATE_DRAGGING,            // Dragging UI (scaled icon only)
     UI_STATE_DROPPING_IN_FOLDER,  // Folder dropping preview UI
-    UI_STATE_CARDIFY,             // Cardify UI (scaled icon + label)
   };
 
   // gfx::AnimationDelegate:
@@ -272,6 +273,9 @@
   // A11y alerts and a focus ring.
   bool focus_silently_ = false;
 
+  // Whether AppsGridView is in cardified state.
+  bool in_cardified_grid_ = false;
+
   // The animation that runs when dragged view enters or exits this view.
   std::unique_ptr<gfx::SlideAnimation> dragged_view_hover_animation_;
 
diff --git a/ash/app_list/views/apps_grid_view.cc b/ash/app_list/views/apps_grid_view.cc
index 02e49e5d..38d7633 100644
--- a/ash/app_list/views/apps_grid_view.cc
+++ b/ash/app_list/views/apps_grid_view.cc
@@ -883,7 +883,7 @@
   if (cardified_state_) {
     // Temporarily set to cardified UI State so it animates back to its position
     // smoothly with all other icons.
-    released_drag_view->SetCardifyUIState();
+    released_drag_view->EnterCardifyState();
     // Compensate drag_source_bounds for the translation of the items_container
     // during AnimateCardifiedState().
     gfx::Point start_position = items_container_->origin();
@@ -2349,9 +2349,9 @@
     current_bounds.Offset(translate_offset);
 
     if (cardified_state_)
-      entry_view->SetCardifyUIState();
+      entry_view->EnterCardifyState();
     else
-      entry_view->SetNormalUIState();
+      entry_view->ExitCardifyState();
 
     gfx::Rect target_bounds(view_model_.ideal_bounds(i));
     entry_view->SetBoundsRect(target_bounds);
diff --git a/ash/public/cpp/ash_features.cc b/ash/public/cpp/ash_features.cc
index e9ac0643..9cd8104 100644
--- a/ash/public/cpp/ash_features.cc
+++ b/ash/public/cpp/ash_features.cc
@@ -75,9 +75,6 @@
 const base::Feature kMediaSessionNotification{"MediaSessionNotification",
                                               base::FEATURE_ENABLED_BY_DEFAULT};
 
-const base::Feature kMovablePartialScreenshot{
-    "MovablePartialScreenshot", base::FEATURE_DISABLED_BY_DEFAULT};
-
 const base::Feature kNightLight{"NightLight", base::FEATURE_ENABLED_BY_DEFAULT};
 
 const base::Feature kNotificationExpansionAnimation{
@@ -305,10 +302,6 @@
   return base::FeatureList::IsEnabled(kDisplayAlignAssist);
 }
 
-bool IsMovablePartialScreenshotEnabled() {
-  return base::FeatureList::IsEnabled(kMovablePartialScreenshot);
-}
-
 bool IsNotificationsInContextMenuEnabled() {
   return base::FeatureList::IsEnabled(kNotificationsInContextMenu);
 }
diff --git a/ash/public/cpp/ash_features.h b/ash/public/cpp/ash_features.h
index a658bb41..a883736 100644
--- a/ash/public/cpp/ash_features.h
+++ b/ash/public/cpp/ash_features.h
@@ -270,8 +270,6 @@
 
 ASH_PUBLIC_EXPORT bool IsDisplayAlignmentAssistanceEnabled();
 
-ASH_PUBLIC_EXPORT bool IsMovablePartialScreenshotEnabled();
-
 ASH_PUBLIC_EXPORT bool IsNotificationsInContextMenuEnabled();
 
 ASH_PUBLIC_EXPORT bool IsTemporaryHoldingSpaceEnabled();
diff --git a/ash/shelf/shelf_layout_manager.cc b/ash/shelf/shelf_layout_manager.cc
index f3eb5b5..0399f03 100644
--- a/ash/shelf/shelf_layout_manager.cc
+++ b/ash/shelf/shelf_layout_manager.cc
@@ -473,6 +473,8 @@
 }
 
 void ShelfLayoutManager::LayoutShelf(bool animate) {
+  // Do not animate if the shelf container is animating.
+  animate &= !IsShelfContainerAnimating();
   // The ShelfWidget may be partially closed (no native widget) during shutdown
   // or before it's been fully initialized so skip layout.
   if (in_shutdown_ || !shelf_widget_->native_widget())
@@ -1088,7 +1090,11 @@
   const bool was_adding_user = state_.IsAddingSecondaryUser();
   const bool was_locked = state_.IsScreenLocked();
   state_.session_state = state;
-  MaybeUpdateShelfBackground(AnimationChangeType::ANIMATE);
+
+  // Animate shelf layout if the container is not animating.
+  bool animate = !IsShelfContainerAnimating();
+  MaybeUpdateShelfBackground(animate ? AnimationChangeType::ANIMATE
+                                     : AnimationChangeType::IMMEDIATE);
   HideContextualNudges();
   if (was_adding_user != state_.IsAddingSecondaryUser()) {
     UpdateShelfVisibilityAfterLoginUIChange();
@@ -1101,7 +1107,7 @@
     UpdateShelfVisibilityAfterLoginUIChange();
 
   CalculateTargetBoundsAndUpdateWorkArea();
-  UpdateBoundsAndOpacity(true /* animate */);
+  UpdateBoundsAndOpacity(animate);
   UpdateVisibilityState();
   UpdateContextualNudges();
 }
@@ -1940,7 +1946,7 @@
 
 void ShelfLayoutManager::UpdateShelfVisibilityAfterLoginUIChange() {
   UpdateVisibilityState();
-  LayoutShelf();
+  LayoutShelf(/*animate=*/false);
 }
 
 float ShelfLayoutManager::ComputeTargetOpacity(const State& state) const {
@@ -2711,4 +2717,18 @@
       FROM_HERE, visibility_update_for_tray_callback_.callback());
 }
 
+bool ShelfLayoutManager::IsShelfContainerAnimating() const {
+  // TODO(oshima): We're re-layouting during shelf construction. We probably
+  // should wait and then layout once after shelf is fully constructed.
+  if (!shelf_widget_ || !shelf_widget_->native_widget_private() ||
+      !shelf_widget_->GetNativeWindow()) {
+    return false;
+  }
+  return shelf_widget_->GetNativeWindow()
+      ->parent()
+      ->layer()
+      ->GetAnimator()
+      ->is_animating();
+}
+
 }  // namespace ash
diff --git a/ash/shelf/shelf_layout_manager.h b/ash/shelf/shelf_layout_manager.h
index ff25296..9eab692 100644
--- a/ash/shelf/shelf_layout_manager.h
+++ b/ash/shelf/shelf_layout_manager.h
@@ -522,6 +522,8 @@
   // Updates the visibility state because of the change on a status area tray.
   void UpdateVisibilityStateForTrayBubbleChange(bool bubble_shown);
 
+  bool IsShelfContainerAnimating() const;
+
   bool in_shutdown_ = false;
 
   // True if the last mouse event was a mouse drag.
diff --git a/ash/system/phonehub/phone_hub_metrics.cc b/ash/system/phonehub/phone_hub_metrics.cc
index fc726a9..cb9dbf2e 100644
--- a/ash/system/phonehub/phone_hub_metrics.cc
+++ b/ash/system/phonehub/phone_hub_metrics.cc
@@ -24,10 +24,10 @@
       return "PhoneHub.InterstitialScreenEvent.TetherConnectionPending";
     case Screen::kOnboardingExistingMultideviceUser:
       return "PhoneHub.InterstitialScreenEvent.Onboarding."
-             "ExistingMultideviceUser";
+             "ExistingMultideviceUser2";
     case Screen::kOnboardingNewMultideviceUser:
       return "PhoneHub.InterstitialScreenEvent.Onboarding."
-             "NewMultideviceUser";
+             "NewMultideviceUser2";
     case Screen::kOnboardingDismissPrompt:
       return "PhoneHub.InterstitialScreenEvent.OnboardingDismissPrompt";
     default:
@@ -41,6 +41,22 @@
 void LogInterstitialScreenEvent(Screen screen, InterstitialScreenEvent event) {
   base::UmaHistogramEnumeration(GetInterstitialScreenEventHistogramName(screen),
                                 event);
+
+  // NOTE(https://crbug.com/1187255): The new- and existing-user metrics were
+  // previously reversed. For continuity, we continue logging the old metrics in
+  // reverse. The new metrics
+  // "PhoneHub.InterstitialScreenEvent.Onboarding.NewMultideviceUser2" and
+  // "PhoneHub.InterstitialScreenEvent.Onboarding.ExistingMultideviceUser2" are
+  // logged correctly.
+  if (screen == Screen::kOnboardingExistingMultideviceUser) {
+    base::UmaHistogramEnumeration(
+        "PhoneHub.InterstitialScreenEvent.Onboarding.NewMultideviceUser",
+        event);
+  } else if (screen == Screen::kOnboardingNewMultideviceUser) {
+    base::UmaHistogramEnumeration(
+        "PhoneHub.InterstitialScreenEvent.Onboarding.ExistingMultideviceUser",
+        event);
+  }
 }
 
 void LogScreenOnBubbleOpen(Screen screen) {
diff --git a/ash/system/phonehub/phone_hub_ui_controller.cc b/ash/system/phonehub/phone_hub_ui_controller.cc
index 085ea36a5..1d7724e 100644
--- a/ash/system/phonehub/phone_hub_ui_controller.cc
+++ b/ash/system/phonehub/phone_hub_ui_controller.cc
@@ -188,11 +188,11 @@
       return UiState::kHidden;
 
     case FeatureStatus::kEligiblePhoneButNotSetUp:
-      return should_show_onboarding_ui ? UiState::kOnboardingWithPhone
+      return should_show_onboarding_ui ? UiState::kOnboardingWithoutPhone
                                        : UiState::kHidden;
 
     case FeatureStatus::kDisabled:
-      return should_show_onboarding_ui ? UiState::kOnboardingWithoutPhone
+      return should_show_onboarding_ui ? UiState::kOnboardingWithPhone
                                        : UiState::kHidden;
 
     case FeatureStatus::kUnavailableBluetoothOff:
diff --git a/ash/system/phonehub/phone_hub_ui_controller_unittest.cc b/ash/system/phonehub/phone_hub_ui_controller_unittest.cc
index f114860..2730a7a 100644
--- a/ash/system/phonehub/phone_hub_ui_controller_unittest.cc
+++ b/ash/system/phonehub/phone_hub_ui_controller_unittest.cc
@@ -115,7 +115,8 @@
 }
 
 TEST_F(PhoneHubUiControllerTest, ShowOnboardingUi_WithoutPhone) {
-  GetFeatureStatusProvider()->SetStatus(FeatureStatus::kDisabled);
+  GetFeatureStatusProvider()->SetStatus(
+      FeatureStatus::kEligiblePhoneButNotSetUp);
   EXPECT_TRUE(ui_state_changed_);
   ui_state_changed_ = false;
   GetOnboardingUiTracker()->SetShouldShowOnboardingUi(true);
@@ -129,8 +130,7 @@
 }
 
 TEST_F(PhoneHubUiControllerTest, ShowOnboardingUi_WithPhone) {
-  GetFeatureStatusProvider()->SetStatus(
-      FeatureStatus::kEligiblePhoneButNotSetUp);
+  GetFeatureStatusProvider()->SetStatus(FeatureStatus::kDisabled);
   EXPECT_TRUE(ui_state_changed_);
   ui_state_changed_ = false;
   GetOnboardingUiTracker()->SetShouldShowOnboardingUi(true);
diff --git a/ash/system/status_area_widget_delegate.cc b/ash/system/status_area_widget_delegate.cc
index a68e165c..1961e64 100644
--- a/ash/system/status_area_widget_delegate.cc
+++ b/ash/system/status_area_widget_delegate.cc
@@ -209,10 +209,12 @@
 }
 
 void StatusAreaWidgetDelegate::UpdateLayout(bool animate) {
-  if (animate)
+  if (animate) {
     StatusAreaWidgetDelegateAnimationSettings settings(layer());
-
-  Layout();
+    Layout();
+  } else {
+    Layout();
+  }
 }
 
 void StatusAreaWidgetDelegate::ChildPreferredSizeChanged(View* child) {
@@ -221,8 +223,17 @@
   if (new_size == current_size)
     return;
   // Need to re-layout the shelf when trays or items are added/removed.
-  StatusAreaWidgetDelegateAnimationSettings settings(layer());
-
+  // don't run uring login or unlock if the shelf container is animating.
+  std::unique_ptr<StatusAreaWidgetDelegateAnimationSettings> settings;
+  if (!shelf_->shelf_widget()
+           ->GetNativeWindow()
+           ->parent()
+           ->layer()
+           ->GetAnimator()
+           ->is_animating()) {
+    settings =
+        std::make_unique<StatusAreaWidgetDelegateAnimationSettings>(layer());
+  }
   shelf_->shelf_layout_manager()->LayoutShelf(/*animate=*/false);
 }
 
diff --git a/ash/utility/screenshot_controller.cc b/ash/utility/screenshot_controller.cc
index 0057387..c12738c 100644
--- a/ash/utility/screenshot_controller.cc
+++ b/ash/utility/screenshot_controller.cc
@@ -10,7 +10,6 @@
 #include "ash/accelerators/accelerator_controller_impl.h"
 #include "ash/display/mouse_cursor_event_filter.h"
 #include "ash/magnifier/magnifier_glass.h"
-#include "ash/public/cpp/ash_features.h"
 #include "ash/public/cpp/shell_window_ids.h"
 #include "ash/screenshot_delegate.h"
 #include "ash/shell.h"
@@ -21,7 +20,6 @@
 #include "ui/aura/client/screen_position_client.h"
 #include "ui/aura/window_targeter.h"
 #include "ui/base/cursor/mojom/cursor_type.mojom-shared.h"
-#include "ui/base/hit_test.h"
 #include "ui/compositor/paint_recorder.h"
 #include "ui/display/screen.h"
 #include "ui/events/event.h"
@@ -30,9 +28,6 @@
 #include "ui/events/pointer_details.h"
 #include "ui/events/types/event_type.h"
 #include "ui/gfx/canvas.h"
-#include "ui/gfx/color_palette.h"
-#include "ui/gfx/geometry/point_conversions.h"
-#include "ui/gfx/scoped_canvas.h"
 #include "ui/views/widget/widget.h"
 #include "ui/wm/core/accelerator_filter.h"
 #include "ui/wm/core/cursor_manager.h"
@@ -88,48 +83,6 @@
   return true;
 }
 
-// Returns the hit test component for |point| on |rect|.
-int GetHTComponentForRect(const gfx::Point& point,
-                          const gfx::Rect& rect,
-                          int border_size) {
-  gfx::Rect corner(rect.origin(), gfx::Size());
-  corner.Inset(-border_size, -border_size);
-  if (corner.Contains(point))
-    return HTTOPLEFT;
-
-  corner.Offset(rect.width(), 0);
-  if (corner.Contains(point))
-    return HTTOPRIGHT;
-
-  corner.Offset(0, rect.height());
-  if (corner.Contains(point))
-    return HTBOTTOMRIGHT;
-
-  corner.Offset(-rect.width(), 0);
-  if (corner.Contains(point))
-    return HTBOTTOMLEFT;
-
-  gfx::Rect horizontal_border(rect.origin(), gfx::Size(rect.width(), 0));
-  horizontal_border.Inset(0, -border_size);
-  if (horizontal_border.Contains(point))
-    return HTTOP;
-
-  horizontal_border.Offset(0, rect.height());
-  if (horizontal_border.Contains(point))
-    return HTBOTTOM;
-
-  gfx::Rect vertical_border(rect.origin(), gfx::Size(0, rect.height()));
-  vertical_border.Inset(-border_size, 0);
-  if (vertical_border.Contains(point))
-    return HTLEFT;
-
-  vertical_border.Offset(rect.width(), 0);
-  if (vertical_border.Contains(point))
-    return HTRIGHT;
-
-  return HTNOWHERE;
-}
-
 }  // namespace
 
 class ScreenshotController::ScreenshotLayer : public ui::LayerOwner,
@@ -352,266 +305,6 @@
   DISALLOW_COPY_AND_ASSIGN(ScreenshotLayer);
 };
 
-class ScreenshotController::MovableScreenshotLayer
-    : public ScreenshotLayer::ScreenshotLayer {
- public:
-  MovableScreenshotLayer(ScreenshotController* controller,
-                         ui::Layer* parent,
-                         bool immediate_overlay)
-      : ScreenshotLayer(controller, parent, immediate_overlay) {}
-  ~MovableScreenshotLayer() override = default;
-
- private:
-  static constexpr SkColor kBorderColor = gfx::kGoogleBlue300;
-  static constexpr int kBorderStrokePx = 2;
-  static constexpr int kBorderShadowPx = 1;
-  static constexpr SkColor kCursorColor = SK_ColorWHITE;
-  static constexpr SkColor kIconTextBorderColor =
-      SkColorSetA(gfx::kGoogleGrey900, 0x80);  // 0.5 grey900
-  static constexpr int kDragHandleSize = 8;
-
-  struct PointerInfo {
-    static PointerInfo ForEvent(const ui::Event& event) {
-      if (event.IsMouseEvent()) {
-        const auto& pointer_details = event.AsMouseEvent()->pointer_details();
-        return {pointer_details.pointer_type, pointer_details.id};
-      }
-
-      if (event.IsTouchEvent()) {
-        const auto& pointer_details = event.AsTouchEvent()->pointer_details();
-        return {pointer_details.pointer_type, pointer_details.id};
-      }
-
-      return PointerInfo();
-    }
-
-    bool operator==(const PointerInfo& other) const {
-      return type == other.type && id == other.id;
-    }
-    bool operator!=(const PointerInfo& other) const {
-      return !(*this == other);
-    }
-
-    ui::EventPointerType type = ui::EventPointerType::kUnknown;
-    ui::PointerId id = ui::kPointerIdUnknown;
-  };
-
-  void StartDrag(const ui::LocatedEvent& event) {
-    DCHECK(!drag_pointer_.has_value());
-
-    gfx::Rect start_region = region();
-
-    if (start_region.IsEmpty()) {
-      start_region = gfx::Rect(event.root_location(), gfx::Size());
-      start_hit_ = HTBOTTOMRIGHT;
-    } else {
-      start_hit_ = GetHTComponentForRect(event.root_location(), start_region,
-                                         kDragHandleSize);
-    }
-
-    if (start_hit_ == HTNOWHERE) {
-      // TODO(crbug.com/1076605): Complete via confirming bubble.
-      if (!region().IsEmpty())
-        controller()->CompletePartialScreenshot();
-      return;
-    }
-
-    drag_pointer_ = PointerInfo::ForEvent(event);
-    start_region_ = start_region;
-    MaybeChangeCursor(ui::mojom::CursorType::kNone);
-
-    ContinueDrag(event);
-  }
-
-  void ContinueDrag(const ui::LocatedEvent& event) {
-    if (!drag_pointer_.has_value() ||
-        drag_pointer_.value() != PointerInfo::ForEvent(event)) {
-      return;
-    }
-
-    set_cursor_location_in_root(event.root_location());
-
-    gfx::Point anchor = start_region_.origin();
-    gfx::Point ref = event.root_location();
-
-    if (start_hit_ == HTTOPLEFT) {
-      anchor = start_region_.bottom_right();
-    }
-    if (start_hit_ == HTTOPRIGHT) {
-      anchor = start_region_.bottom_left();
-    }
-    if (start_hit_ == HTBOTTOMLEFT) {
-      anchor = start_region_.top_right();
-    }
-    if (start_hit_ == HTTOP) {
-      anchor = start_region_.bottom_left();
-      ref.set_x(start_region_.right());
-    }
-    if (start_hit_ == HTBOTTOM) {
-      ref.set_x(start_region_.right());
-    }
-    if (start_hit_ == HTLEFT) {
-      anchor = start_region_.top_right();
-      ref.set_y(start_region_.bottom());
-    }
-    if (start_hit_ == HTRIGHT) {
-      ref.set_y(start_region_.bottom());
-    }
-
-    SetRegion(
-        gfx::Rect(std::min(anchor.x(), ref.x()), std::min(anchor.y(), ref.y()),
-                  ::abs(anchor.x() - ref.x()), ::abs(anchor.y() - ref.y())));
-
-    if (!magnifier_glass_) {
-      MagnifierGlass::Params params{2.0f, 64};
-      magnifier_glass_ = std::make_unique<MagnifierGlass>(std::move(params));
-    }
-    aura::Window* event_root =
-        static_cast<aura::Window*>(event.target())->GetRootWindow();
-    magnifier_glass_->ShowFor(event_root, event.root_location());
-
-    // TODO(crbug.com/1076605): Create/update a magnifier glass to allow
-    // better alignment for selected region.
-  }
-
-  void EndDrag(const ui::LocatedEvent& event) {
-    if (!drag_pointer_.has_value() ||
-        drag_pointer_.value() != PointerInfo::ForEvent(event)) {
-      return;
-    }
-
-    ContinueDrag(event);
-
-    drag_pointer_.reset();
-    start_region_ = gfx::Rect();
-    start_hit_ = HTNOWHERE;
-    magnifier_glass_->Close();
-
-    MaybeChangeCursor(ui::mojom::CursorType::kCross);
-  }
-
-  // ScreenshotLayer::ScreenshotLayer:
-  void OnPointerPressed(const ui::LocatedEvent& event) override {
-    if (drag_pointer_.has_value())
-      return;
-
-    StartDrag(event);
-  }
-
-  void OnPointerMoved(const ui::LocatedEvent& event) override {
-    // TODO(crbug.com/1076605): Change cursor when on resizing border.
-  }
-
-  void OnPointerDragged(const ui::LocatedEvent& event) override {
-    ContinueDrag(event);
-  }
-
-  void OnPointerReleased(const ui::LocatedEvent& event) override {
-    EndDrag(event);
-  }
-
-  void OnPaintLayer(const ui::PaintContext& context) override {
-    ui::PaintRecorder recorder(context, layer()->size());
-    auto* canvas = recorder.canvas();
-
-    // 0.6 grey900
-    const SkColor kOverlayColor = SkColorSetA(gfx::kGoogleGrey900, 0x99);
-    if (draw_inactive_overlay())
-      canvas->DrawColor(kOverlayColor);
-
-    if (!region().IsEmpty())
-      DrawRegion(canvas);
-
-    DrawPseudoCursor(canvas);
-  }
-
-  void DrawRegion(gfx::Canvas* canvas) {
-    gfx::ScopedCanvas scoped_canvas(canvas);
-    float scale = canvas->UndoDeviceScaleFactor();
-
-    gfx::RectF rect(region());
-    rect.Scale(scale);
-
-    gfx::Rect fill_rect(gfx::ToEnclosingRect(rect));
-    canvas->FillRect(fill_rect, SK_ColorBLACK, SkBlendMode::kClear);
-
-    // Add space of border stroke px + shadow so that the border and its shadow
-    // are drawn outside |fill_rect| and would not be captured as part of the
-    // screenshot.
-    rect = gfx::RectF(fill_rect);
-    rect.Inset(-kBorderStrokePx / 2 - kBorderShadowPx,
-               -kBorderStrokePx / 2 - kBorderShadowPx);
-
-    cc::PaintFlags flags;
-    flags.setAntiAlias(true);
-    flags.setStyle(cc::PaintFlags::kStroke_Style);
-
-    flags.setColor(kIconTextBorderColor);
-    flags.setStrokeWidth(SkIntToScalar(kBorderStrokePx + 2 * kBorderShadowPx));
-    canvas->DrawRect(rect, flags);
-
-    flags.setColor(kBorderColor);
-    flags.setStrokeWidth(SkIntToScalar(kBorderStrokePx));
-    canvas->DrawRect(rect, flags);
-
-    // TODO(crbug.com/1076605): Draw drag handles.
-  }
-
-  void DrawPseudoCursor(gfx::Canvas* canvas) {
-    if (!drag_pointer_.has_value())
-      return;
-
-    gfx::PointF pseudo_cursor_point(cursor_location_in_root());
-    gfx::Vector2dF width(kCursorSize / 2, 0);
-    gfx::Vector2dF height(0, kCursorSize / 2);
-
-    // If the cursor is above/before region, use negative offset to move
-    // towards outside of the region.
-    const int x_dir = pseudo_cursor_point.x() == region().x() ? -1 : 1;
-    const int y_dir = pseudo_cursor_point.y() == region().y() ? -1 : 1;
-
-    gfx::ScopedCanvas scoped_canvas(canvas);
-    float scale = canvas->UndoDeviceScaleFactor();
-
-    pseudo_cursor_point.Scale(scale);
-    pseudo_cursor_point = gfx::PointF(gfx::ToCeiledPoint(pseudo_cursor_point));
-    pseudo_cursor_point.Offset(x_dir * (kBorderStrokePx / 2 + kBorderShadowPx),
-                               y_dir * (kBorderStrokePx / 2 + kBorderShadowPx));
-
-    width.Scale(scale);
-    height.Scale(scale);
-
-    cc::PaintFlags flags;
-    flags.setAntiAlias(true);
-    flags.setStyle(cc::PaintFlags::kStroke_Style);
-    flags.setStrokeCap(cc::PaintFlags::kRound_Cap);
-
-    flags.setColor(kIconTextBorderColor);
-    flags.setStrokeWidth(SkIntToScalar(kBorderStrokePx + 2 * kBorderShadowPx));
-    canvas->DrawLine(pseudo_cursor_point - width, pseudo_cursor_point + width,
-                     flags);
-    canvas->DrawLine(pseudo_cursor_point - height, pseudo_cursor_point + height,
-                     flags);
-
-    flags.setColor(kCursorColor);
-    flags.setStrokeWidth(SkIntToScalar(kBorderStrokePx));
-    canvas->DrawLine(pseudo_cursor_point - width, pseudo_cursor_point + width,
-                     flags);
-    canvas->DrawLine(pseudo_cursor_point - height, pseudo_cursor_point + height,
-                     flags);
-
-    // TODO(crbug.com/1076605): Draw cursor coordinates.
-  }
-
-  base::Optional<PointerInfo> drag_pointer_;
-  gfx::Rect start_region_;
-  int start_hit_ = HTNOWHERE;
-
-  // Shows a magnifier glass centered at mouse/touch location when changing
-  // the screenshot region.
-  std::unique_ptr<MagnifierGlass> magnifier_glass_;
-};
-
 class ScreenshotController::ScopedCursorSetter {
  public:
   explicit ScopedCursorSetter(ui::mojom::CursorType cursor) {
@@ -720,17 +413,10 @@
   mode_ = PARTIAL;
   display::Screen::GetScreen()->AddObserver(this);
   for (aura::Window* root : Shell::GetAllRootWindows()) {
-    if (features::IsMovablePartialScreenshotEnabled()) {
-      layers_[root] = std::make_unique<MovableScreenshotLayer>(
-          this,
-          Shell::GetContainer(root, kShellWindowId_OverlayContainer)->layer(),
-          draw_overlay_immediately);
-    } else {
-      layers_[root] = std::make_unique<ScreenshotLayer>(
-          this,
-          Shell::GetContainer(root, kShellWindowId_OverlayContainer)->layer(),
-          draw_overlay_immediately);
-    }
+    layers_[root] = std::make_unique<ScreenshotLayer>(
+        this,
+        Shell::GetContainer(root, kShellWindowId_OverlayContainer)->layer(),
+        draw_overlay_immediately);
   }
 
   if (!pen_events_only_) {
diff --git a/ash/utility/screenshot_controller.h b/ash/utility/screenshot_controller.h
index 0f5409f1..3ba1019 100644
--- a/ash/utility/screenshot_controller.h
+++ b/ash/utility/screenshot_controller.h
@@ -87,7 +87,6 @@
 
   class ScopedCursorSetter;
   class ScreenshotLayer;
-  class MovableScreenshotLayer;
 
   // Starts, ends, cancels, or updates the region selection.
   void CompleteWindowScreenshot();
diff --git a/ash/utility/screenshot_controller_unittest.cc b/ash/utility/screenshot_controller_unittest.cc
index 10cb0c7..de7b7ee 100644
--- a/ash/utility/screenshot_controller_unittest.cc
+++ b/ash/utility/screenshot_controller_unittest.cc
@@ -8,7 +8,6 @@
 #include "ash/display/mirror_window_test_api.h"
 #include "ash/display/mouse_cursor_event_filter.h"
 #include "ash/display/window_tree_host_manager.h"
-#include "ash/public/cpp/ash_features.h"
 #include "ash/screenshot_delegate.h"
 #include "ash/shell.h"
 #include "ash/test/ash_test_base.h"
@@ -16,7 +15,6 @@
 #include "ash/test_screenshot_delegate.h"
 #include "ash/wm/window_util.h"
 #include "base/run_loop.h"
-#include "base/test/scoped_feature_list.h"
 #include "ui/aura/env.h"
 #include "ui/aura/window_event_dispatcher.h"
 #include "ui/base/cursor/cursor.h"
@@ -152,72 +150,6 @@
   EXPECT_FALSE(IsActive());
 }
 
-TEST_F(PartialScreenshotControllerTest, Movable) {
-  base::test::ScopedFeatureList scoped_feature_list;
-  scoped_feature_list.InitAndEnableFeature(features::kMovablePartialScreenshot);
-
-  TestScreenshotDelegate* test_delegate = GetScreenshotDelegate();
-  ui::test::EventGenerator generator(Shell::GetPrimaryRootWindow());
-
-  // Test data to exercise on an initial region of 10+20-50x50.
-  struct {
-    const gfx::Point drag_start;
-    const gfx::Point drag_end;
-    const gfx::Rect expected_region;
-  } kTestCases[] = {
-      // Drag from top-left. |drag_end| becomes the new origin.
-      {gfx::Point(10, 20), gfx::Point(20, 30), gfx::Rect(20, 30, 40, 40)},
-      // Drag from top-right. |drag_end| becomes the new top-right.
-      {gfx::Point(60, 20), gfx::Point(20, 30), gfx::Rect(10, 30, 10, 40)},
-      // Drag from bottom-left. |drag_end| becomes the new bottom-left.
-      {gfx::Point(10, 70), gfx::Point(20, 30), gfx::Rect(20, 20, 40, 10)},
-      // Drag from bottom-right. |drag_end| becomes the new bottom-right.
-      {gfx::Point(60, 70), gfx::Point(20, 30), gfx::Rect(10, 20, 10, 10)},
-
-      // Drag from top. |drag_end|'s y becomes new top.
-      {gfx::Point(30, 20), gfx::Point(20, 30), gfx::Rect(10, 30, 50, 40)},
-      // Drag from top. |drag_end|'s y becomes new bottom.
-      {gfx::Point(30, 20), gfx::Point(20, 80), gfx::Rect(10, 70, 50, 10)},
-
-      // Drag from left. |drag_end|'x becomes the new left.
-      {gfx::Point(10, 30), gfx::Point(20, 30), gfx::Rect(20, 20, 40, 50)},
-      // Drag from left. |drag_end|'x becomes the new right.
-      {gfx::Point(10, 30), gfx::Point(70, 30), gfx::Rect(60, 20, 10, 50)},
-
-      // Drag from right. |drag_end|'x becomes the new right.
-      {gfx::Point(60, 30), gfx::Point(20, 30), gfx::Rect(10, 20, 10, 50)},
-      // Drag from right. |drag_end|'x becomes the new left.
-      {gfx::Point(60, 30), gfx::Point(5, 30), gfx::Rect(5, 20, 5, 50)},
-
-      // Drag from bottom. |drag_end|'y becomes the new bottom.
-      {gfx::Point(30, 70), gfx::Point(20, 30), gfx::Rect(10, 20, 50, 10)},
-      // Drag from bottom. |drag_end|'y becomes the new top.
-      {gfx::Point(30, 70), gfx::Point(20, 10), gfx::Rect(10, 10, 50, 10)},
-  };
-  for (size_t i = 0; i < base::size(kTestCases); ++i) {
-    const auto& test = kTestCases[i];
-    SCOPED_TRACE(testing::Message()
-                 << ", i=" << i << "start=" << test.drag_start.ToString()
-                 << ", end=" << test.drag_end.ToString()
-                 << ", expect=" << test.expected_region.ToString());
-
-    StartPartialScreenshotSession();
-    generator.MoveMouseTo(10, 20);
-    generator.DragMouseBy(50, 50);
-
-    generator.MoveMouseTo(test.drag_start);
-    generator.DragMouseTo(test.drag_end);
-
-    // Complete partial screenshot selection by clicking outside the region.
-    generator.MoveMouseTo(0, 0);
-    generator.ClickLeftButton();
-
-    EXPECT_EQ(test.expected_region, test_delegate->last_rect());
-    EXPECT_EQ(static_cast<int>(i + 1),
-              test_delegate->handle_take_partial_screenshot_count());
-  }
-}
-
 TEST_F(PartialScreenshotControllerTest, BasicTouch) {
   StartPartialScreenshotSession();
   TestScreenshotDelegate* test_delegate = GetScreenshotDelegate();
diff --git a/base/BUILD.gn b/base/BUILD.gn
index 5288a27..43bd572 100644
--- a/base/BUILD.gn
+++ b/base/BUILD.gn
@@ -2240,6 +2240,8 @@
       "trace_event/memory_dump_scheduler.h",
       "trace_event/memory_infra_background_allowlist.cc",
       "trace_event/memory_infra_background_allowlist.h",
+      "trace_event/memory_pressure_level_proto.cc",
+      "trace_event/memory_pressure_level_proto.h",
       "trace_event/memory_usage_estimator.cc",
       "trace_event/memory_usage_estimator.h",
       "trace_event/optional_trace_event.h",
diff --git a/base/allocator/partition_allocator/pcscan.cc b/base/allocator/partition_allocator/pcscan.cc
index 43d20dc..5d533b0a 100644
--- a/base/allocator/partition_allocator/pcscan.cc
+++ b/base/allocator/partition_allocator/pcscan.cc
@@ -1259,10 +1259,13 @@
                   [](Root* root) { return root->IsQuarantineEnabled(); }));
 #endif
 
-  if (state_.exchange(State::kScheduled, std::memory_order_acq_rel) !=
-      State::kNotRunning) {
-    // Bail out if PCScan is already in progress.
-    return;
+  {
+    // If scanning is already in progress, bail out.
+    State expected = State::kNotRunning;
+    if (!state_.compare_exchange_strong(expected, State::kScheduled,
+                                        std::memory_order_acq_rel,
+                                        std::memory_order_relaxed))
+      return;
   }
 
   quarantine_data_.ResetAndAdvanceEpoch();
diff --git a/base/logging.cc b/base/logging.cc
index 3311c49..b4d44d9 100644
--- a/base/logging.cc
+++ b/base/logging.cc
@@ -794,6 +794,11 @@
         severity = FX_LOG_ERROR;
         break;
     }
+    // TODO(https://crbug.com/1188820): Integrate verbose levels with the switch
+    // statement.
+    if (severity_ <= LOGGING_VERBOSE) {
+      severity = FX_LOG_DEBUG;
+    }
 
     fx_logger_t* logger = fx_log_get_logger();
     if (logger) {
diff --git a/base/memory/memory_pressure_listener.cc b/base/memory/memory_pressure_listener.cc
index 391d4e8d..623ae26 100644
--- a/base/memory/memory_pressure_listener.cc
+++ b/base/memory/memory_pressure_listener.cc
@@ -7,6 +7,11 @@
 #include "base/observer_list_threadsafe.h"
 #include "base/threading/sequenced_task_runner_handle.h"
 #include "base/trace_event/base_tracing.h"
+#include "base/tracing_buildflags.h"
+
+#if BUILDFLAG(ENABLE_BASE_TRACING)
+#include "base/trace_event/memory_pressure_level_proto.h"  // no-presubmit-check
+#endif
 
 namespace base {
 
@@ -66,23 +71,6 @@
 
 }  // namespace
 
-#if BUILDFLAG(ENABLE_BASE_TRACING)
-// static
-perfetto::protos::pbzero::MemoryPressureLevel
-MemoryPressureListener::LevelAsTraceEnum(
-    MemoryPressureListener::MemoryPressureLevel memory_pressure_level) {
-  using ProtoLevel = perfetto::protos::pbzero::MemoryPressureLevel;
-  switch (memory_pressure_level) {
-    case MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE:
-      return ProtoLevel::MEMORY_PRESSURE_LEVEL_NONE;
-    case MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE:
-      return ProtoLevel::MEMORY_PRESSURE_LEVEL_MODERATE;
-    case MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL:
-      return ProtoLevel::MEMORY_PRESSURE_LEVEL_CRITICAL;
-  }
-}
-#endif
-
 MemoryPressureListener::MemoryPressureListener(
     const base::Location& creation_location,
     const MemoryPressureListener::MemoryPressureCallback& callback)
@@ -111,7 +99,8 @@
       [&](perfetto::EventContext ctx) {
         auto* event = ctx.event<perfetto::protos::pbzero::ChromeTrackEvent>();
         auto* data = event->set_chrome_memory_pressure_notification();
-        data->set_level(LevelAsTraceEnum(memory_pressure_level));
+        data->set_level(
+            trace_event::MemoryPressureLevelToTraceEnum(memory_pressure_level));
         data->set_creation_location_iid(
             base::trace_event::InternedSourceLocation::Get(
                 &ctx,
@@ -137,7 +126,8 @@
       [&](perfetto::EventContext ctx) {
         auto* event = ctx.event<perfetto::protos::pbzero::ChromeTrackEvent>();
         auto* data = event->set_chrome_memory_pressure_notification();
-        data->set_level(LevelAsTraceEnum(memory_pressure_level));
+        data->set_level(
+            trace_event::MemoryPressureLevelToTraceEnum(memory_pressure_level));
       });
   if (AreNotificationsSuppressed())
     return;
diff --git a/base/memory/memory_pressure_listener.h b/base/memory/memory_pressure_listener.h
index 7ae221d..802d4c7 100644
--- a/base/memory/memory_pressure_listener.h
+++ b/base/memory/memory_pressure_listener.h
@@ -16,10 +16,6 @@
 #include "base/macros.h"
 #include "base/tracing_buildflags.h"
 
-#if BUILDFLAG(ENABLE_BASE_TRACING)
-#include "base/tracing/protos/chrome_track_event.pbzero.h"  // nogncheck
-#endif
-
 namespace base {
 
 // To start listening, create a new instance, passing a callback to a
@@ -77,11 +73,6 @@
     kMaxValue = MEMORY_PRESSURE_LEVEL_CRITICAL,
   };
 
-#if BUILDFLAG(ENABLE_BASE_TRACING)
-  static perfetto::protos::pbzero::MemoryPressureLevel LevelAsTraceEnum(
-      MemoryPressureListener::MemoryPressureLevel memory_pressure_level);
-#endif
-
   using MemoryPressureCallback = RepeatingCallback<void(MemoryPressureLevel)>;
   using SyncMemoryPressureCallback =
       RepeatingCallback<void(MemoryPressureLevel)>;
diff --git a/base/trace_event/memory_pressure_level_proto.cc b/base/trace_event/memory_pressure_level_proto.cc
new file mode 100644
index 0000000..3b4a594a
--- /dev/null
+++ b/base/trace_event/memory_pressure_level_proto.cc
@@ -0,0 +1,31 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// MemoryPressure provides static APIs for handling memory pressure on
+// platforms that have such signals, such as Android and ChromeOS.
+// The app will try to discard buffers that aren't deemed essential (individual
+// modules will implement their own policy).
+
+#include "base/trace_event/memory_pressure_level_proto.h"
+
+#include "base/tracing/protos/chrome_track_event.pbzero.h"  // nogncheck
+
+namespace base {
+namespace trace_event {
+
+perfetto::protos::pbzero::MemoryPressureLevel MemoryPressureLevelToTraceEnum(
+    MemoryPressureListener::MemoryPressureLevel memory_pressure_level) {
+  using ProtoLevel = perfetto::protos::pbzero::MemoryPressureLevel;
+  switch (memory_pressure_level) {
+    case MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE:
+      return ProtoLevel::MEMORY_PRESSURE_LEVEL_NONE;
+    case MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE:
+      return ProtoLevel::MEMORY_PRESSURE_LEVEL_MODERATE;
+    case MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL:
+      return ProtoLevel::MEMORY_PRESSURE_LEVEL_CRITICAL;
+  }
+}
+
+}  // namespace trace_event
+}  // namespace base
diff --git a/base/trace_event/memory_pressure_level_proto.h b/base/trace_event/memory_pressure_level_proto.h
new file mode 100644
index 0000000..d60d43dc
--- /dev/null
+++ b/base/trace_event/memory_pressure_level_proto.h
@@ -0,0 +1,27 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// MemoryPressure provides static APIs for handling memory pressure on
+// platforms that have such signals, such as Android and ChromeOS.
+// The app will try to discard buffers that aren't deemed essential (individual
+// modules will implement their own policy).
+
+#ifndef BASE_TRACE_EVENT_MEMORY_PRESSURE_LEVEL_PROTO_H_
+#define BASE_TRACE_EVENT_MEMORY_PRESSURE_LEVEL_PROTO_H_
+
+#include "base/base_export.h"
+#include "base/memory/memory_pressure_listener.h"
+#include "base/tracing/protos/chrome_track_event.pbzero.h"
+
+namespace base {
+namespace trace_event {
+
+BASE_EXPORT perfetto::protos::pbzero::MemoryPressureLevel
+MemoryPressureLevelToTraceEnum(
+    MemoryPressureListener::MemoryPressureLevel memory_pressure_level);
+
+}
+}  // namespace base
+
+#endif  // BASE_TRACE_EVENT_MEMORY_PRESSURE_LEVEL_PROTO_H_
diff --git a/base/util/memory_pressure/multi_source_memory_pressure_monitor.cc b/base/util/memory_pressure/multi_source_memory_pressure_monitor.cc
index ce5d8c0..32cbc1e 100644
--- a/base/util/memory_pressure/multi_source_memory_pressure_monitor.cc
+++ b/base/util/memory_pressure/multi_source_memory_pressure_monitor.cc
@@ -10,8 +10,13 @@
 #include "base/metrics/histogram_macros.h"
 #include "base/time/time.h"
 #include "base/trace_event/base_tracing.h"
+#include "base/tracing_buildflags.h"
 #include "base/util/memory_pressure/system_memory_pressure_evaluator.h"
 
+#if BUILDFLAG(ENABLE_BASE_TRACING)
+#include "base/trace_event/memory_pressure_level_proto.h"  // no-presubmit-check
+#endif
+
 namespace util {
 
 MultiSourceMemoryPressureMonitor::MultiSourceMemoryPressureMonitor()
@@ -86,7 +91,8 @@
       [&](perfetto::EventContext ctx) {
         auto* event = ctx.event<perfetto::protos::pbzero::ChromeTrackEvent>();
         auto* data = event->set_chrome_memory_pressure_notification();
-        data->set_level(base::MemoryPressureListener::LevelAsTraceEnum(level));
+        data->set_level(
+            base::trace_event::MemoryPressureLevelToTraceEnum(level));
       });
 
   // Records the duration of the latest pressure session, there are 4
diff --git a/base/win/embedded_i18n/language_selector.h b/base/win/embedded_i18n/language_selector.h
index 84d1e9cd..c439edd 100644
--- a/base/win/embedded_i18n/language_selector.h
+++ b/base/win/embedded_i18n/language_selector.h
@@ -22,7 +22,7 @@
 namespace i18n {
 
 // Selects a language from a set of available translations based on the user's
-// preferred language list. An optional preferred langauge may be provided to
+// preferred language list. An optional preferred language may be provided to
 // override selection should a corresponding translation be available.
 class BASE_EXPORT LanguageSelector {
  public:
@@ -40,8 +40,8 @@
                    span<const LangToOffset> languages_to_offset);
 
   // Constructor for testing purposes.
-  // |candidates| is a list of all candiate languages that can be used to
-  // determine which langauge to use.
+  // |candidates| is a list of all candidate languages that can be used to
+  // determine which language to use.
   // |languages_to_offset_begin| and |languages_to_offset_end| point to a sorted
   // array of language identifiers (and their offsets) for which translations
   // are available.
diff --git a/build/android/test_runner.py b/build/android/test_runner.py
index b11ce23..ae7d1bcb 100755
--- a/build/android/test_runner.py
+++ b/build/android/test_runner.py
@@ -904,6 +904,27 @@
             global_tags=list(global_results_tags),
             indent=2)
 
+      test_class_to_file_name_dict = {}
+      # Test Location is only supported for instrumentation tests as it
+      # requires the size-info file.
+      if test_instance.TestType() == 'instrumentation':
+        test_class_to_file_name_dict = _CreateClassToFileNameDict(args.test_apk)
+
+      if result_sink_client:
+        for run in all_raw_results:
+          for results in run:
+            for r in results.GetAll():
+              # Matches chrome.page_info.PageInfoViewTest#testChromePage
+              match = re.search(r'^(.+\..+)#', r.GetName())
+              test_file_name = test_class_to_file_name_dict.get(
+                  match.group(1)) if match else None
+              # Some tests put in non utf-8 char as part of the test
+              # which breaks uploads, so need to decode and re-encode.
+              result_sink_client.Post(
+                  r.GetName(), r.GetType(), r.GetDuration(),
+                  r.GetLog().decode('utf-8', 'replace').encode('utf-8'),
+                  test_file_name)
+
   @contextlib.contextmanager
   def upload_logcats_file():
     try:
@@ -964,29 +985,11 @@
         for r in reversed(raw_results):
           iteration_results.AddTestRunResults(r)
         all_iteration_results.append(iteration_results)
-
-        test_class_to_file_name_dict = {}
-        # Test Location is only supported for instrumentation tests as it
-        # requires the size-info file.
-        if test_instance.TestType() == 'instrumentation':
-          test_class_to_file_name_dict = _CreateClassToFileNameDict(
-              args.test_apk)
-
         iteration_count += 1
-        for r in iteration_results.GetAll():
-          if result_sink_client:
-            # Matches chrome.page_info.PageInfoViewTest#testChromePage
-            match = re.search(r'^(.+\..+)#', r.GetName())
-            test_file_name = test_class_to_file_name_dict.get(
-                match.group(1)) if match else None
-            # Some tests put in non utf-8 char as part of the test
-            # which breaks uploads, so need to decode and re-encode.
-            result_sink_client.Post(
-                r.GetName(), r.GetType(), r.GetDuration(),
-                r.GetLog().decode('utf-8', 'replace').encode('utf-8'),
-                test_file_name)
 
+        for r in iteration_results.GetAll():
           result_counts[r.GetName()][r.GetType()] += 1
+
         report_results.LogFull(
             results=iteration_results,
             test_type=test_instance.TestType(),
diff --git a/chrome/android/features/start_surface/internal/java/src/org/chromium/chrome/features/start_surface/StartSurfaceMediator.java b/chrome/android/features/start_surface/internal/java/src/org/chromium/chrome/features/start_surface/StartSurfaceMediator.java
index f6d9c1a2..7cb2128 100644
--- a/chrome/android/features/start_surface/internal/java/src/org/chromium/chrome/features/start_surface/StartSurfaceMediator.java
+++ b/chrome/android/features/start_surface/internal/java/src/org/chromium/chrome/features/start_surface/StartSurfaceMediator.java
@@ -651,18 +651,21 @@
     public boolean onBackPressed() {
         boolean isOnHomepage = mStartSurfaceState == StartSurfaceState.SHOWN_HOMEPAGE;
 
+        // When the SecondaryTasksSurface is shown, the TabGridDialog is controlled by
+        // mSecondaryTasksSurfaceController, while the TabSelectionEditor dialog is controlled
+        // by mController. Therefore, we need to check both controllers whether any dialog is
+        // visible. If so, the corresponding controller will handle the back button.
+        // When the Start surface is shown, tapping "Group Tabs" from menu will also show the
+        // the TabSelectionEditor dialog. Therefore, we need to check both controllers as well.
+        if (mSecondaryTasksSurfaceController != null
+                && mSecondaryTasksSurfaceController.isDialogVisible()) {
+            return mSecondaryTasksSurfaceController.onBackPressed(isOnHomepage);
+        } else if (mController.isDialogVisible()) {
+            return mController.onBackPressed(isOnHomepage);
+        }
+
         if (mStartSurfaceState == StartSurfaceState.SHOWN_TABSWITCHER) {
-            // When the SecondaryTasksSurface is shown, the TabGridDialog is controlled by
-            // mSecondaryTasksSurfaceController, while the TabSelectionEditor dialog is controlled
-            // by mController. Therefore, we need to check both controllers whether any dialog is
-            // visible. If so, the corresponding controller will handle the back button.
-            if (mSecondaryTasksSurfaceController != null
-                    && mSecondaryTasksSurfaceController.isDialogVisible()) {
-                return mSecondaryTasksSurfaceController.onBackPressed(isOnHomepage);
-            } else if (mController.isDialogVisible()) {
-                return mController.onBackPressed(isOnHomepage);
-            } else if (mPreviousStartSurfaceState == StartSurfaceState.SHOWN_HOMEPAGE
-                    && !mIsIncognito) {
+            if (mPreviousStartSurfaceState == StartSurfaceState.SHOWN_HOMEPAGE && !mIsIncognito) {
                 // Secondary tasks surface is used as the main surface in incognito mode.
                 // If we reached Tab switcher from HomePage, and there isn't any dialog shown,
                 // updates the state, and ChromeTabbedActivity will handle the back button.
diff --git a/chrome/android/features/start_surface/internal/javatests/src/org/chromium/chrome/features/start_surface/StartSurfaceTest.java b/chrome/android/features/start_surface/internal/javatests/src/org/chromium/chrome/features/start_surface/StartSurfaceTest.java
index 964a755..9f4f5e7 100644
--- a/chrome/android/features/start_surface/internal/javatests/src/org/chromium/chrome/features/start_surface/StartSurfaceTest.java
+++ b/chrome/android/features/start_surface/internal/javatests/src/org/chromium/chrome/features/start_surface/StartSurfaceTest.java
@@ -57,6 +57,7 @@
 import android.widget.LinearLayout;
 import android.widget.TextView;
 
+import androidx.recyclerview.widget.RecyclerView;
 import androidx.test.espresso.Espresso;
 import androidx.test.espresso.UiController;
 import androidx.test.espresso.ViewAction;
@@ -1683,6 +1684,82 @@
     @Test
     @LargeTest
     @Feature({"StartSurface"})
+    @EnableFeatures(ChromeFeatureList.TAB_GROUPS_ANDROID)
+    // clang-format off
+    @CommandLineFlags.Add({BASE_PARAMS + "/single"})
+    public void testShow_SingleAsHomepage_BackButtonOnHomepageWithGroupTabsDialog()
+        throws ExecutionException {
+        // clang-format on
+        backButtonOnHomepageWithGroupTabsDialogImpl();
+    }
+
+    @Test
+    @LargeTest
+    @Feature({"StartSurface"})
+    @EnableFeatures(ChromeFeatureList.TAB_GROUPS_ANDROID)
+    // clang-format off
+    @CommandLineFlags.Add({BASE_PARAMS + "/single/show_last_active_tab_only/true"})
+    public void testShow_SingleAsHomepageV2_BackButtonOnHomepageWithGroupTabsDialog()
+        throws ExecutionException {
+        // clang-format on
+        backButtonOnHomepageWithGroupTabsDialogImpl();
+    }
+
+    private void backButtonOnHomepageWithGroupTabsDialogImpl() throws ExecutionException {
+        if (!mImmediateReturn) {
+            pressHomePageButton();
+        }
+
+        ChromeTabbedActivity cta = mActivityTestRule.getActivity();
+        CriteriaHelper.pollUiThread(
+                () -> cta.getLayoutManager() != null && cta.getLayoutManager().overviewVisible());
+        waitForTabModel();
+        TabUiTestHelper.verifyTabModelTabCount(cta, 1, 0);
+        onViewWaiting(withId(R.id.logo));
+
+        // Launches the first site in MV tiles to create the second tab for grouping.
+        LinearLayout tilesLayout =
+                cta.findViewById(org.chromium.chrome.tab_ui.R.id.mv_tiles_layout);
+        TestThreadUtils.runOnUiThreadBlocking(() -> tilesLayout.getChildAt(0).performClick());
+        CriteriaHelper.pollUiThread(() -> !cta.getLayoutManager().overviewVisible());
+        // Verifies a new Tab is created.
+        TabUiTestHelper.verifyTabModelTabCount(cta, 2, 0);
+
+        // When show_last_active_tab_only is enabled, we need to enter the tab switcher first to
+        // initialize the secondary task surface which shows the TabSelectionEditor dialog.
+        onViewWaiting(withId(org.chromium.chrome.tab_ui.R.id.tab_switcher_button));
+        TabUiTestHelper.enterTabSwitcher(cta);
+        waitForView(withId(R.id.secondary_tasks_surface_view));
+        List<Tab> tabs =
+                getTabsInCurrentTabModel(mActivityTestRule.getActivity().getCurrentTabModel());
+        TabSelectionEditorTestingRobot robot = new TabSelectionEditorTestingRobot();
+
+        // Enters the homepage, and shows the TabSelectionEditor dialog.
+        pressHomePageButton();
+        waitForView(withId(R.id.primary_tasks_surface_view));
+
+        StartSurfaceCoordinator startSurfaceCoordinator = getStartSurfaceFromUIThread();
+        TestThreadUtils.runOnUiThreadBlocking(
+                () -> startSurfaceCoordinator.showTabSelectionEditorForTesting(tabs));
+        robot.resultRobot.verifyTabSelectionEditorIsVisible()
+                .verifyToolbarActionButtonDisabled()
+                .verifyToolbarActionButtonWithResourceId(
+                        org.chromium.chrome.tab_ui.R.string.tab_selection_editor_group)
+                .verifyToolbarSelectionTextWithResourceId(
+                        org.chromium.chrome.tab_ui.R.string
+                                .tab_selection_editor_toolbar_select_tabs)
+                .verifyAdapterHasItemCount(tabs.size())
+                .verifyHasAtLeastNItemVisible(2);
+
+        // Verifies that tapping the back button will close the TabSelectionEditor.
+        pressBack();
+        robot.resultRobot.verifyTabSelectionEditorIsHidden();
+        onView(allOf(withId(R.id.primary_tasks_surface_view), isDisplayed()));
+    }
+
+    @Test
+    @LargeTest
+    @Feature({"StartSurface"})
     @DisableIf.Build(sdk_is_less_than = M, message = "https://crbug.com/1170553")
     @CommandLineFlags.Add({BASE_PARAMS + "/single/omnibox_focused_on_new_tab/true"})
     public void testOmnibox_FocusedOnNewTabInSingleSurface() {
@@ -1985,6 +2062,142 @@
                 .check(matches(withEffectiveVisibility(GONE)));
     }
 
+    @Test
+    @LargeTest
+    @Feature({"StartSurface"})
+    // clang-format off
+    @CommandLineFlags.Add({BASE_PARAMS + "/single/show_tabs_in_mru_order/true"})
+    public void testShow_SingleAsHomepage_ShowTabsInMRUOrder() throws ExecutionException {
+        // clang-format on
+        if (!mImmediateReturn) {
+            pressHomePageButton();
+        }
+
+        ChromeTabbedActivity cta = mActivityTestRule.getActivity();
+        CriteriaHelper.pollUiThread(
+                () -> cta.getLayoutManager() != null && cta.getLayoutManager().overviewVisible());
+        waitForTabModel();
+        TabUiTestHelper.verifyTabModelTabCount(cta, 1, 0);
+        onViewWaiting(withId(R.id.logo));
+        Tab tab1 = cta.getCurrentTabModel().getTabAt(0);
+
+        // Launches the first site in MV tiles.
+        LinearLayout tilesLayout =
+                cta.findViewById(org.chromium.chrome.tab_ui.R.id.mv_tiles_layout);
+        TestThreadUtils.runOnUiThreadBlocking(() -> tilesLayout.getChildAt(0).performClick());
+        CriteriaHelper.pollUiThread(() -> !cta.getLayoutManager().overviewVisible());
+        TabUiTestHelper.verifyTabModelTabCount(cta, 2, 0);
+        Tab tab2 = cta.getActivityTab();
+        // Verifies that the titles of the two Tabs are different.
+        TestThreadUtils.runOnUiThreadBlocking(
+                () -> { Assert.assertNotEquals(tab1.getTitle(), tab2.getTitle()); });
+
+        // Returns to the Start surface.
+        OverviewModeBehaviorWatcher overviewModeWatcher = new OverviewModeBehaviorWatcher(
+                mActivityTestRule.getActivity().getLayoutManager(), true, false);
+        pressHomePageButton();
+        overviewModeWatcher.waitForBehavior();
+        waitForView(allOf(
+                withParent(withId(org.chromium.chrome.tab_ui.R.id.carousel_tab_switcher_container)),
+                withId(org.chromium.chrome.tab_ui.R.id.tab_list_view)));
+
+        RecyclerView recyclerView = mActivityTestRule.getActivity().findViewById(
+                org.chromium.chrome.tab_ui.R.id.tab_list_view);
+        assertEquals(2, recyclerView.getChildCount());
+        // Verifies that the tabs are shown in MRU order: the first card in the carousel Tab
+        // switcher is the last created Tab by tapping the MV tile; the second card is the Tab
+        // created or restored in setup().
+        RecyclerView.ViewHolder firstViewHolder = recyclerView.findViewHolderForAdapterPosition(0);
+        TextView title1 =
+                firstViewHolder.itemView.findViewById(org.chromium.chrome.tab_ui.R.id.tab_title);
+        TestThreadUtils.runOnUiThreadBlocking(
+                () -> Assert.assertEquals(tab2.getTitle(), title1.getText()));
+
+        RecyclerView.ViewHolder secondViewHolder = recyclerView.findViewHolderForAdapterPosition(1);
+        TextView title2 =
+                secondViewHolder.itemView.findViewById(org.chromium.chrome.tab_ui.R.id.tab_title);
+        TestThreadUtils.runOnUiThreadBlocking(
+                () -> Assert.assertEquals(tab1.getTitle(), title2.getText()));
+    }
+
+    @Test
+    @LargeTest
+    @Feature({"StartSurface"})
+    // clang-format off
+    @CommandLineFlags.Add({BASE_PARAMS + "/single/show_tabs_in_mru_order/true"})
+    public void testShow_TabSwitcher_ShowTabsInMRUOrder() throws ExecutionException {
+        // clang-format on
+        tabSwitcher_ShowTabsInMRUOrderImpl();
+    }
+
+    @Test
+    @LargeTest
+    @Feature({"StartSurface"})
+    @EnableFeatures(ChromeFeatureList.TAB_GROUPS_ANDROID)
+    // clang-format off
+    @CommandLineFlags.Add({BASE_PARAMS +
+        "/single/show_tabs_in_mru_order/true/show_last_active_tab_only/true"})
+    public void testShowV2_TabSwitcher_ShowTabsInMRUOrder() throws ExecutionException {
+        // clang-format on
+        tabSwitcher_ShowTabsInMRUOrderImpl();
+    }
+
+    private void tabSwitcher_ShowTabsInMRUOrderImpl() throws ExecutionException {
+        if (!mImmediateReturn) {
+            pressHomePageButton();
+        }
+
+        ChromeTabbedActivity cta = mActivityTestRule.getActivity();
+        CriteriaHelper.pollUiThread(
+                () -> cta.getLayoutManager() != null && cta.getLayoutManager().overviewVisible());
+        waitForTabModel();
+        TabUiTestHelper.verifyTabModelTabCount(cta, 1, 0);
+        onViewWaiting(withId(R.id.logo));
+        Tab tab1 = cta.getCurrentTabModel().getTabAt(0);
+
+        // Launches the first site in MV tiles.
+        LinearLayout tilesLayout =
+                cta.findViewById(org.chromium.chrome.tab_ui.R.id.mv_tiles_layout);
+        TestThreadUtils.runOnUiThreadBlocking(() -> tilesLayout.getChildAt(0).performClick());
+        CriteriaHelper.pollUiThread(() -> !cta.getLayoutManager().overviewVisible());
+        TabUiTestHelper.verifyTabModelTabCount(cta, 2, 0);
+        Tab tab2 = cta.getActivityTab();
+
+        // Verifies that the titles of the two Tabs are different.
+        TestThreadUtils.runOnUiThreadBlocking(
+                () -> { Assert.assertNotEquals(tab1.getTitle(), tab2.getTitle()); });
+
+        if (isInstantReturn()) {
+            // TODO(crbug.com/1076274): fix toolbar to avoid wrongly focusing on the toolbar
+            // omnibox.
+            return;
+        }
+        // Enter the Tab switcher.
+        TabUiTestHelper.enterTabSwitcher(cta);
+        waitForView(allOf(withParent(withId(R.id.secondary_tasks_surface_view)),
+                withId(org.chromium.chrome.tab_ui.R.id.tab_list_view)));
+
+        ViewGroup secondaryTaskSurface =
+                mActivityTestRule.getActivity().findViewById(R.id.secondary_tasks_surface_view);
+        RecyclerView recyclerView =
+                secondaryTaskSurface.findViewById(org.chromium.chrome.tab_ui.R.id.tab_list_view);
+        assertEquals(2, recyclerView.getChildCount());
+        // Verifies that the tabs are shown in MRU order: the first card in the Tab switcher is the
+        // last created Tab by tapping the MV tile; the second card is the Tab created or restored
+        // in setup().
+        RecyclerView.ViewHolder firstViewHolder = recyclerView.findViewHolderForAdapterPosition(0);
+        TextView title1 =
+                firstViewHolder.itemView.findViewById(org.chromium.chrome.tab_ui.R.id.tab_title);
+        TestThreadUtils.runOnUiThreadBlocking(
+                () -> Assert.assertEquals(tab2.getTitle(), title1.getText()));
+
+        RecyclerView.ViewHolder secondViewHolder = recyclerView.findViewHolderForAdapterPosition(1);
+        TextView title2 =
+                secondViewHolder.itemView.findViewById(org.chromium.chrome.tab_ui.R.id.tab_title);
+        TestThreadUtils.runOnUiThreadBlocking(
+                () -> Assert.assertEquals(tab1.getTitle(), title2.getText()));
+    }
+
     private void scrollToolbar() {
         waitForTabModel();
         TabUiTestHelper.verifyTabModelTabCount(mActivityTestRule.getActivity(), 1, 0);
diff --git a/chrome/android/features/start_surface/public/java/src/org/chromium/chrome/features/start_surface/StartSurfaceConfiguration.java b/chrome/android/features/start_surface/public/java/src/org/chromium/chrome/features/start_surface/StartSurfaceConfiguration.java
index e9d6fa5d..20212e82 100644
--- a/chrome/android/features/start_surface/public/java/src/org/chromium/chrome/features/start_surface/StartSurfaceConfiguration.java
+++ b/chrome/android/features/start_surface/public/java/src/org/chromium/chrome/features/start_surface/StartSurfaceConfiguration.java
@@ -92,6 +92,11 @@
             new StringCachedFieldTrialParameter(
                     ChromeFeatureList.START_SURFACE_ANDROID, NEW_SURFACE_PARAM, "");
 
+    private static final String SHOW_TABS_IN_MRU_ORDER_PARAM = "show_tabs_in_mru_order";
+    public static final BooleanCachedFieldTrialParameter SHOW_TABS_IN_MRU_ORDER =
+            new BooleanCachedFieldTrialParameter(
+                    ChromeFeatureList.START_SURFACE_ANDROID, SHOW_TABS_IN_MRU_ORDER_PARAM, false);
+
     private static final String STARTUP_UMA_PREFIX = "Startup.Android.";
     private static final String INSTANT_START_SUBFIX = ".Instant";
     private static final String REGULAR_START_SUBFIX = ".NoInstant";
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherMediator.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherMediator.java
index 3aa50ae..c822ab28 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherMediator.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherMediator.java
@@ -730,13 +730,15 @@
 
     @Override
     public boolean onBackPressed(boolean isOnHomepage) {
-        if (!mContainerViewModel.get(IS_VISIBLE)) return false;
-
+        // The TabSelectionEditor dialog can be shown on the Start surface without showing the Grid
+        // Tab switcher, so skip the check of visibility of mContainerViewModel here.
         if (mTabSelectionEditorController != null
                 && mTabSelectionEditorController.handleBackPressed()) {
             return true;
         }
 
+        if (!mContainerViewModel.get(IS_VISIBLE)) return false;
+
         if (mTabGridDialogController != null && mTabGridDialogController.handleBackPressed()) {
             return true;
         }
@@ -810,7 +812,9 @@
      */
     static boolean isShowingTabsInMRUOrder() {
         String feature = StartSurfaceConfiguration.START_SURFACE_VARIATION.getValue();
-        return TextUtils.equals(feature, "twopanes");
+        return TextUtils.equals(feature, "twopanes")
+                || StartSurfaceConfiguration.SHOW_TABS_IN_MRU_ORDER.getValue()
+                && TextUtils.equals(feature, "single");
     }
 
     /**
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/DEPS b/chrome/android/java/src/org/chromium/chrome/browser/DEPS
index 0ab8300..e126ddae 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/DEPS
+++ b/chrome/android/java/src/org/chromium/chrome/browser/DEPS
@@ -205,9 +205,6 @@
   "AndroidPaymentApp\.java": [
     "+chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java",
   ],
-  "CardEditor\.java": [
-    "+chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java",
-  ],
   "ChromePaymentRequestFactory\.java": [
     "+chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java",
   ],
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/app/flags/ChromeCachedFlags.java b/chrome/android/java/src/org/chromium/chrome/browser/app/flags/ChromeCachedFlags.java
index cb7c439..fec9372 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/app/flags/ChromeCachedFlags.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/app/flags/ChromeCachedFlags.java
@@ -72,7 +72,6 @@
                 ChromeFeatureList.PAINT_PREVIEW_DEMO,
                 ChromeFeatureList.PAINT_PREVIEW_SHOW_ON_STARTUP,
                 ChromeFeatureList.PRIORITIZE_BOOTSTRAP_TASKS,
-                ChromeFeatureList.SHARE_BY_DEFAULT_IN_CCT,
                 ChromeFeatureList.START_SURFACE_ANDROID,
                 ChromeFeatureList.SWAP_PIXEL_FORMAT_TO_FIX_CONVERT_FROM_TRANSLUCENT,
                 ChromeFeatureList.TAB_GRID_LAYOUT_ANDROID,
@@ -106,6 +105,7 @@
                 StartSurfaceConfiguration.HOME_BUTTON_ON_GRID_TAB_SWITCHER,
                 StartSurfaceConfiguration.NEW_SURFACE_FROM_HOME_BUTTON,
                 StartSurfaceConfiguration.OMNIBOX_FOCUSED_ON_NEW_TAB,
+                StartSurfaceConfiguration.SHOW_TABS_IN_MRU_ORDER,
                 StartSurfaceConfiguration.START_SURFACE_EXCLUDE_MV_TILES,
                 StartSurfaceConfiguration.START_SURFACE_HIDE_INCOGNITO_SWITCH_NO_TAB,
                 StartSurfaceConfiguration.START_SURFACE_LAST_ACTIVE_TAB_ONLY,
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/app/tabmodel/TabModelOrchestrator.java b/chrome/android/java/src/org/chromium/chrome/browser/app/tabmodel/TabModelOrchestrator.java
index 4b11b6a..2f38382 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/app/tabmodel/TabModelOrchestrator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/app/tabmodel/TabModelOrchestrator.java
@@ -4,7 +4,6 @@
 
 package org.chromium.chrome.browser.app.tabmodel;
 
-import org.chromium.base.supplier.ObservableSupplierImpl;
 import org.chromium.chrome.browser.compositor.layouts.content.TabContentManager;
 import org.chromium.chrome.browser.tabmodel.TabModelSelectorImpl;
 import org.chromium.chrome.browser.tabmodel.TabPersistentStore;
@@ -133,15 +132,6 @@
     }
 
     protected void wireSelectorAndStore() {
-        // Supply TabModelSelectorImpl with TabPersistentStore.
-        //
-        // TODO(crbug.com/1138561): Remove this dependency by making TabModelSelectorImpl emit
-        // events and TabPersistentStore react to them as an observer.
-        ObservableSupplierImpl<TabPersistentStore> tabPersistentStoreSupplier =
-                new ObservableSupplierImpl<>();
-        tabPersistentStoreSupplier.set(mTabPersistentStore);
-        mTabModelSelector.setTabPersistentStoreSupplier(tabPersistentStoreSupplier);
-
         // Notify TabModelSelectorImpl when TabPersistentStore initializes tab state
         final TabPersistentStoreObserver persistentStoreObserver =
                 new TabPersistentStoreObserver() {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/browserservices/TrustedWebActivitySettingsLauncher.java b/chrome/android/java/src/org/chromium/chrome/browser/browserservices/TrustedWebActivitySettingsLauncher.java
index f7581c3..3bb3d21 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/browserservices/TrustedWebActivitySettingsLauncher.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/browserservices/TrustedWebActivitySettingsLauncher.java
@@ -53,14 +53,14 @@
      */
     public static void launchForWebApkPackageName(
             Context context, String packageName, String webApkUrl) {
+        // Handle the case when settings are selected but Chrome was not running.
+        ChromeWebApkHost.init();
         if (!WebApkValidator.canWebApkHandleUrl(context, packageName, webApkUrl)) {
             Log.d(TAG, "WebApk " + packageName + " can't handle url " + webApkUrl);
             return;
         }
         if (getApplicationUid(context, packageName) == null) return;
 
-        // Handle the case when settings are selected but Chrome was not running.
-        ChromeWebApkHost.init();
         openSingleWebsitePrefs(context, webApkUrl);
     }
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/crash/MinidumpUploadServiceImpl.java b/chrome/android/java/src/org/chromium/chrome/browser/crash/MinidumpUploadServiceImpl.java
index c0a97b2..3f7667c6 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/crash/MinidumpUploadServiceImpl.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/crash/MinidumpUploadServiceImpl.java
@@ -15,6 +15,8 @@
 import androidx.annotation.StringDef;
 import androidx.annotation.VisibleForTesting;
 
+import org.chromium.base.ApplicationState;
+import org.chromium.base.ApplicationStatus.ApplicationStateListener;
 import org.chromium.base.ContextUtils;
 import org.chromium.base.Log;
 import org.chromium.base.StreamUtil;
@@ -120,6 +122,13 @@
         MinidumpUploadJobService.scheduleUpload(builder);
     }
 
+    private ApplicationStateListener createApplicationStateListener() {
+        return newState -> {
+            SharedPreferencesManager.getInstance().writeInt(
+                    ChromePreferenceKeys.LAST_SESSION_APPLICATION_STATE, newState);
+        };
+    }
+
     /**
      * Stores the successes and failures from uploading crash to UMA,
      */
@@ -127,11 +136,24 @@
         sBrowserCrashMetricsInitialized.set(true);
 
         SharedPreferencesManager sharedPrefs = SharedPreferencesManager.getInstance();
-        int previous_pid = sharedPrefs.readInt(ChromePreferenceKeys.LAST_SESSION_BROWSER_PID);
+        int previousPid = sharedPrefs.readInt(ChromePreferenceKeys.LAST_SESSION_BROWSER_PID);
+        @ApplicationState
+        int applicationExitState =
+                sharedPrefs.readInt(ChromePreferenceKeys.LAST_SESSION_APPLICATION_STATE);
+        String umaSuffix;
+        if (applicationExitState == ApplicationState.HAS_RUNNING_ACTIVITIES
+                || applicationExitState == ApplicationState.HAS_PAUSED_ACTIVITIES) {
+            umaSuffix = "Foreground";
+        } else {
+            umaSuffix = "Background";
+        }
         sharedPrefs.writeInt(ChromePreferenceKeys.LAST_SESSION_BROWSER_PID, Process.myPid());
-        if (previous_pid != 0) {
-            ProcessExitReasonFromSystem.recordExitReasonToUma(
-                    previous_pid, "Stability.Android.SystemExitReason.Browser");
+        if (previousPid != 0) {
+            int reason = ProcessExitReasonFromSystem.getExitReason(previousPid);
+            ProcessExitReasonFromSystem.recordAsEnumHistogram(
+                    "Stability.Android.SystemExitReason.Browser", reason);
+            ProcessExitReasonFromSystem.recordAsEnumHistogram(
+                    "Stability.Android.SystemExitReason.Browser." + umaSuffix, reason);
         }
 
         for (String type : TYPES) {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabIntentDataProvider.java b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabIntentDataProvider.java
index 25f37616..d2f1f46 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabIntentDataProvider.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabIntentDataProvider.java
@@ -41,7 +41,6 @@
 import org.chromium.chrome.browser.browserservices.intents.BrowserServicesIntentDataProvider;
 import org.chromium.chrome.browser.browserservices.intents.CustomButtonParams;
 import org.chromium.chrome.browser.flags.ActivityType;
-import org.chromium.chrome.browser.flags.CachedFeatureFlags;
 import org.chromium.chrome.browser.flags.ChromeFeatureList;
 import org.chromium.chrome.browser.gsa.GSAState;
 import org.chromium.chrome.browser.version.ChromeVersionInfo;
@@ -467,9 +466,7 @@
         int shareState = IntentUtils.safeGetIntExtra(
                 intent, CustomTabsIntent.EXTRA_SHARE_STATE, CustomTabsIntent.SHARE_STATE_DEFAULT);
         if (shareState == CustomTabsIntent.SHARE_STATE_ON
-                || (shareState == CustomTabsIntent.SHARE_STATE_DEFAULT
-                        && CachedFeatureFlags.isEnabled(
-                                ChromeFeatureList.SHARE_BY_DEFAULT_IN_CCT))) {
+                || shareState == CustomTabsIntent.SHARE_STATE_DEFAULT) {
             if (mToolbarButtons.isEmpty()) {
                 mToolbarButtons.add(
                         CustomButtonParamsImpl.createShareButton(context, getToolbarColor()));
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadNotificationFactory.java b/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadNotificationFactory.java
index e2b4610..4ddf398 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadNotificationFactory.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadNotificationFactory.java
@@ -26,7 +26,6 @@
 import android.content.Intent;
 import android.os.Build;
 import android.os.Bundle;
-import android.text.TextUtils;
 
 import androidx.core.app.NotificationCompat;
 
@@ -41,13 +40,12 @@
 import org.chromium.components.browser_ui.notifications.NotificationMetadata;
 import org.chromium.components.browser_ui.notifications.NotificationWrapperBuilder;
 import org.chromium.components.browser_ui.notifications.PendingIntentProvider;
-import org.chromium.components.embedder_support.util.UrlUtilities;
+import org.chromium.components.browser_ui.util.DownloadUtils;
 import org.chromium.components.offline_items_collection.ContentId;
 import org.chromium.components.offline_items_collection.LegacyHelpers;
 import org.chromium.components.offline_items_collection.OfflineItem;
 import org.chromium.components.offline_items_collection.PendingState;
-import org.chromium.components.url_formatter.SchemeDisplay;
-import org.chromium.components.url_formatter.UrlFormatter;
+import org.chromium.url.GURL;
 
 /**
  * Creates and updates notifications related to downloads.
@@ -350,18 +348,11 @@
             setSubText(builder,
                     context.getResources().getString(
                             R.string.download_notification_incognito_subtext));
-        } else if (downloadUpdate.getShouldPromoteOrigin()
-                && !TextUtils.isEmpty(downloadUpdate.getOriginalUrl())) {
+        } else if (downloadUpdate.getShouldPromoteOrigin()) {
             // Always show the origin URL if available (for normal profiles).
-            String formattedUrl = UrlFormatter.formatUrlForSecurityDisplay(
-                    downloadUpdate.getOriginalUrl(), SchemeDisplay.OMIT_HTTP_AND_HTTPS);
-
-            if (formattedUrl.length() > MAX_ORIGIN_LENGTH) {
-                // The origin is too long. Strip down to eTLD+1.
-                formattedUrl = UrlUtilities.getDomainAndRegistry(
-                        downloadUpdate.getOriginalUrl(), false /* includePrivateRegistries */);
-            }
-            setSubText(builder, formattedUrl);
+            String formattedUrl = DownloadUtils.formatUrlForDisplayInNotification(
+                    new GURL(downloadUpdate.getOriginalUrl()));
+            if (formattedUrl != null) setSubText(builder, formattedUrl);
         }
 
         return builder.build();
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/infobar/TranslateCompactInfoBar.java b/chrome/android/java/src/org/chromium/chrome/browser/infobar/TranslateCompactInfoBar.java
index f63e5739..038b84b0 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/infobar/TranslateCompactInfoBar.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/infobar/TranslateCompactInfoBar.java
@@ -183,12 +183,12 @@
     @Override
     public void onPreferenceChange() {
         if (mNativeTranslateInfoBarPtr != 0) {
-            String[] currentContentLangauges =
+            String[] currentContentLanguages =
                     TranslateCompactInfoBarJni.get().getContentLanguagesCodes(
                             mNativeTranslateInfoBarPtr, TranslateCompactInfoBar.this);
-            mOptions.updateContentLanguages(currentContentLangauges);
+            mOptions.updateContentLanguages(currentContentLanguages);
             if (mLanguageMenuHelper != null) {
-                mLanguageMenuHelper.onContentLanguagesChanged(currentContentLangauges);
+                mLanguageMenuHelper.onContentLanguagesChanged(currentContentLanguages);
             }
         }
     }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/payments/CardEditor.java b/chrome/android/java/src/org/chromium/chrome/browser/payments/CardEditor.java
index 3c0b64e..04bf0d1 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/payments/CardEditor.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/payments/CardEditor.java
@@ -15,10 +15,10 @@
 
 import org.chromium.base.ApiCompatibilityUtils;
 import org.chromium.base.Callback;
+import org.chromium.base.supplier.Supplier;
 import org.chromium.base.task.AsyncTask;
 import org.chromium.base.task.BackgroundOnlyAsyncTask;
 import org.chromium.chrome.R;
-import org.chromium.chrome.browser.app.ChromeActivity;
 import org.chromium.chrome.browser.autofill.CreditCardScanner;
 import org.chromium.chrome.browser.autofill.PersonalDataManager;
 import org.chromium.chrome.browser.autofill.PersonalDataManager.AutofillProfile;
@@ -30,11 +30,14 @@
 import org.chromium.chrome.browser.autofill.prefeditor.EditorModel;
 import org.chromium.chrome.browser.autofill.settings.AutofillProfileBridge.DropdownKeyValue;
 import org.chromium.chrome.browser.preferences.ChromePreferenceKeys;
+import org.chromium.chrome.browser.tabmodel.TabModelSelector;
+import org.chromium.chrome.browser.tabmodel.TabModelSelectorSupplier;
 import org.chromium.components.payments.BasicCardUtils;
 import org.chromium.components.payments.MethodStrings;
 import org.chromium.components.payments.PaymentRequestService;
 import org.chromium.content_public.browser.WebContents;
 import org.chromium.payments.mojom.PaymentMethodData;
+import org.chromium.ui.base.WindowAndroid;
 
 import java.text.SimpleDateFormat;
 import java.util.ArrayList;
@@ -265,8 +268,19 @@
         };
         mCalendar.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
 
-        ChromeActivity activity = ChromeActivity.fromWebContents(mWebContents);
-        mIsIncognito = activity != null && activity.getCurrentTabModel().isIncognito();
+        mIsIncognito = resolveIncognitoState(mWebContents.getTopLevelNativeWindow());
+    }
+
+    /** Returns whether the user is currently incognito, defaults to false. */
+    private boolean resolveIncognitoState(@Nullable WindowAndroid windowAndroid) {
+        if (windowAndroid == null) return false;
+
+        Supplier<TabModelSelector> tabModelSelectorSupplier =
+                TabModelSelectorSupplier.from(windowAndroid);
+        assert tabModelSelectorSupplier != null;
+        if (!tabModelSelectorSupplier.hasValue()) return false;
+
+        return tabModelSelectorSupplier.get().isIncognitoSelected();
     }
 
     private boolean isCardNumberLengthMaximum(@Nullable CharSequence value) {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/IncognitoTabModelImplCreator.java b/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/IncognitoTabModelImplCreator.java
index a01082d..300e373c 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/IncognitoTabModelImplCreator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/IncognitoTabModelImplCreator.java
@@ -24,7 +24,6 @@
     private final TabCreator mIncognitoTabCreator;
     private final TabModelOrderController mOrderController;
     private final TabContentManager mTabContentManager;
-    private final TabPersistentStore mTabSaver;
     private final NextTabPolicySupplier mNextTabPolicySupplier;
     private final AsyncTabParamsManager mAsyncTabParamsManager;
     private final TabModelDelegate mModelDelegate;
@@ -46,7 +45,6 @@
      * @param incognitoTabCreator Creates incognito tabs.
      * @param orderController     Determines the order for inserting new Tabs.
      * @param tabContentManager   Manages the display content of the tab.
-     * @param tabSaver            Handler for saving tabs.
      * @param nextTabPolicySupplier Supplies the policy to pick a next tab if the current is closed
      * @param asyncTabParamsManager An {@link AsyncTabParamsManager} instance.
      * @param modelDelegate       Delegate to handle external dependencies and interactions.
@@ -54,14 +52,13 @@
     IncognitoTabModelImplCreator(@Nullable Supplier<WindowAndroid> windowAndroidSupplier,
             TabCreator regularTabCreator, TabCreator incognitoTabCreator,
             TabModelOrderController orderController, TabContentManager tabContentManager,
-            TabPersistentStore tabSaver, NextTabPolicySupplier nextTabPolicySupplier,
+            NextTabPolicySupplier nextTabPolicySupplier,
             AsyncTabParamsManager asyncTabParamsManager, TabModelDelegate modelDelegate) {
         mWindowAndroidSupplier = windowAndroidSupplier;
         mRegularTabCreator = regularTabCreator;
         mIncognitoTabCreator = incognitoTabCreator;
         mOrderController = orderController;
         mTabContentManager = tabContentManager;
-        mTabSaver = tabSaver;
         mNextTabPolicySupplier = nextTabPolicySupplier;
         mAsyncTabParamsManager = asyncTabParamsManager;
         mModelDelegate = modelDelegate;
@@ -89,7 +86,7 @@
     public TabModel createTabModel() {
         Profile otrProfile = getOTRProfile();
         return new TabModelImpl(otrProfile, false, mRegularTabCreator, mIncognitoTabCreator,
-                mOrderController, mTabContentManager, mTabSaver, mNextTabPolicySupplier,
+                mOrderController, mTabContentManager, mNextTabPolicySupplier,
                 mAsyncTabParamsManager, mModelDelegate, false);
     }
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/TabModelImpl.java b/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/TabModelImpl.java
index d978aa0..d887dcfe 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/TabModelImpl.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/TabModelImpl.java
@@ -59,7 +59,6 @@
     private final TabCreator mIncognitoTabCreator;
     private final TabModelOrderController mOrderController;
     private final TabContentManager mTabContentManager;
-    private final TabPersistentStore mTabSaver;
     private final TabModelDelegate mModelDelegate;
     private final ObserverList<TabModelObserver> mObservers;
     private final NextTabPolicySupplier mNextTabPolicySupplier;
@@ -90,7 +89,7 @@
     public TabModelImpl(@NonNull Profile profile, boolean isTabbedActivity,
             TabCreator regularTabCreator, TabCreator incognitoTabCreator,
             TabModelOrderController orderController, TabContentManager tabContentManager,
-            TabPersistentStore tabSaver, NextTabPolicySupplier nextTabPolicySupplier,
+            NextTabPolicySupplier nextTabPolicySupplier,
             AsyncTabParamsManager asyncTabParamsManager, TabModelDelegate modelDelegate,
             boolean supportUndo) {
         super(profile, isTabbedActivity);
@@ -98,7 +97,6 @@
         mIncognitoTabCreator = incognitoTabCreator;
         mOrderController = orderController;
         mTabContentManager = tabContentManager;
-        mTabSaver = tabSaver;
         mNextTabPolicySupplier = nextTabPolicySupplier;
         mAsyncTabParamsManager = asyncTabParamsManager;
         mModelDelegate = modelDelegate;
@@ -355,9 +353,6 @@
             }
         }
 
-        // Re-save the tab list now that it is being kept.
-        mTabSaver.saveTabListAsynchronously();
-
         for (TabModelObserver obs : mObservers) obs.tabClosureUndone(tab);
     }
 
@@ -449,7 +444,7 @@
 
     @Override
     public void closeAllTabs(boolean allowDelegation, boolean uponExit) {
-        mTabSaver.cancelLoadingTabs(isIncognito());
+        for (TabModelObserver obs : mObservers) obs.willCloseAllTabs(isIncognito());
 
         if (uponExit) {
             commitAllTabClosures();
@@ -662,11 +657,11 @@
      */
     private void finalizeTabClosure(Tab tab, boolean notifyTabClosureCommitted) {
         if (mTabContentManager != null) mTabContentManager.removeTabThumbnail(tab.getId());
-        mTabSaver.removeTabFromQueues(tab);
         PersistedTabData.onTabClose(tab);
 
         if (!isIncognito()) HistoricalTabSaver.createHistoricalTab(tab);
 
+        for (TabModelObserver obs : mObservers) obs.didCloseTab(tab);
         for (TabModelObserver obs : mObservers) obs.didCloseTab(tab.getId(), tab.isIncognito());
         if (notifyTabClosureCommitted) {
             for (TabModelObserver obs : mObservers) obs.tabClosureCommitted(tab);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/TabModelSelectorImpl.java b/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/TabModelSelectorImpl.java
index 66566ffdb..aa13c8d 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/TabModelSelectorImpl.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/TabModelSelectorImpl.java
@@ -44,10 +44,6 @@
 
     private final TabModelOrderController mOrderController;
 
-    // TODO(crbug.com/1138561): Remove the dependency from TabModelSelectorImpl to
-    // TabPersistentStore.
-    private Supplier<TabPersistentStore> mTabSaver;
-
     private final AsyncTabParamsManager mAsyncTabParamsManager;
 
     private NextTabPolicySupplier mNextTabPolicySupplier;
@@ -84,19 +80,6 @@
         mAsyncTabParamsManager = asyncTabParamsManager;
     }
 
-    /**
-     * TODO(crbug.com/1138561): Do not add more parameters here. This is temporary while the
-     * dependency from TabModelSelectorImpl to TabPersistentStore is removed.
-     *
-     * This must be called after the constructor; NPEs are expected, otherwise.
-     * A Supplier that supplies null can be passed in tests.
-     */
-    public void setTabPersistentStoreSupplier(
-            Supplier<TabPersistentStore> tabPersistentStoreSupplier) {
-        assert mTabSaver == null;
-        mTabSaver = tabPersistentStoreSupplier;
-    }
-
     @Override
     public void markTabStateInitialized() {
         super.markTabStateInitialized();
@@ -133,14 +116,14 @@
                 (ChromeTabCreator) getTabCreatorManager().getTabCreator(true);
         TabModelImpl normalModel = new TabModelImpl(Profile.getLastUsedRegularProfile(),
                 mIsTabbedActivityForSync, regularTabCreator, incognitoTabCreator, mOrderController,
-                mTabContentManager, mTabSaver.get(), mNextTabPolicySupplier, mAsyncTabParamsManager,
-                this, mIsUndoSupported);
+                mTabContentManager, mNextTabPolicySupplier, mAsyncTabParamsManager, this,
+                mIsUndoSupported);
         regularTabCreator.setTabModel(normalModel, mOrderController);
 
-        IncognitoTabModel incognitoModel = new IncognitoTabModelImpl(
-                new IncognitoTabModelImplCreator(mWindowAndroidSupplier, regularTabCreator,
-                        incognitoTabCreator, mOrderController, mTabContentManager, mTabSaver.get(),
-                        mNextTabPolicySupplier, mAsyncTabParamsManager, this));
+        IncognitoTabModel incognitoModel =
+                new IncognitoTabModelImpl(new IncognitoTabModelImplCreator(mWindowAndroidSupplier,
+                        regularTabCreator, incognitoTabCreator, mOrderController,
+                        mTabContentManager, mNextTabPolicySupplier, mAsyncTabParamsManager, this));
         incognitoTabCreator.setTabModel(incognitoModel, mOrderController);
         onNativeLibraryReadyInternal(tabContentProvider, normalModel, incognitoModel);
     }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/TabPersistentStore.java b/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/TabPersistentStore.java
index 34bec149..ebbb516 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/TabPersistentStore.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/TabPersistentStore.java
@@ -92,6 +92,7 @@
 
     /** Prevents two TabPersistentStores from saving the same file simultaneously. */
     private static final Object SAVE_LIST_LOCK = new Object();
+    private TabModelObserver mTabModelObserver;
 
     public void onNativeLibraryReady(TabContentManager tabContentManager) {
         setTabContentManager(tabContentManager);
@@ -126,6 +127,26 @@
                 addTabToSaveQueue(tab);
             }
         };
+
+        mTabModelObserver = new TabModelObserver() {
+            @Override
+            public void didCloseTab(Tab tab) {
+                removeTabFromQueues(tab);
+            }
+
+            @Override
+            public void willCloseAllTabs(boolean incognito) {
+                cancelLoadingTabs(incognito);
+            }
+
+            @Override
+            public void tabClosureUndone(Tab tab) {
+                saveTabListAsynchronously();
+            }
+        };
+
+        mTabModelSelector.getModel(false).addObserver(mTabModelObserver);
+        mTabModelSelector.getModel(true).addObserver(mTabModelObserver);
     }
 
     /**
@@ -847,6 +868,11 @@
 
     public void destroy() {
         mDestroyed = true;
+        if (mTabModelObserver != null) {
+            mTabModelSelector.getModel(false).removeObserver(mTabModelObserver);
+            mTabModelSelector.getModel(true).removeObserver(mTabModelObserver);
+            mTabModelObserver = null;
+        }
         mPersistencePolicy.destroy();
         if (mTabLoader != null) mTabLoader.cancel(true);
         mTabsToSave.clear();
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/browserservices/ManageTrustedWebActivityDataActivityTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/browserservices/ManageTrustedWebActivityDataActivityTest.java
index 99c5fdd..9bbab5ec 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/browserservices/ManageTrustedWebActivityDataActivityTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/browserservices/ManageTrustedWebActivityDataActivityTest.java
@@ -36,9 +36,8 @@
 public class ManageTrustedWebActivityDataActivityTest {
     private static final String SETTINGS_ACTIVITY_NAME =
             "org.chromium.chrome.browser.settings.SettingsActivity";
-    private static final String WEBAPK_TEST_URL = "https://www.example.com";
-    private static final String TEST_PACKAGE_NAME =
-            InstrumentationRegistry.getTargetContext().getPackageName();
+    private static final String WEBAPK_TEST_URL = "https://pwa-directory.appspot.com/";
+    private static final String TEST_PACKAGE_NAME = "org.chromium.webapk.test";
 
     @Test
     @MediumTest
@@ -74,7 +73,7 @@
         Intent intent = new Intent();
         intent.setAction(
                 "android.support.customtabs.action.ACTION_MANAGE_TRUSTED_WEB_ACTIVITY_DATA");
-        intent.setPackage(packageName);
+        intent.setPackage(InstrumentationRegistry.getTargetContext().getPackageName());
         intent.setData(uri);
         intent.putExtra(WebApkConstants.EXTRA_IS_WEBAPK, true);
         // The following flag is required because the test starts the intent outside of an activity.
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/CustomTabActivityTestRule.java b/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/CustomTabActivityTestRule.java
index e832ecf..7799a8b3 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/CustomTabActivityTestRule.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/CustomTabActivityTestRule.java
@@ -10,15 +10,11 @@
 
 import org.junit.Assert;
 
-import org.chromium.base.FeatureList;
 import org.chromium.base.test.util.ScalableTimeout;
-import org.chromium.chrome.browser.flags.ChromeFeatureList;
 import org.chromium.chrome.browser.tab.Tab;
 import org.chromium.chrome.browser.tab.TabTestUtils;
 import org.chromium.chrome.test.ChromeActivityTestRule;
 
-import java.util.Collections;
-
 /**
  * Custom ActivityTestRule for all instrumentation tests that require a {@link CustomTabActivity}.
  */
@@ -46,10 +42,6 @@
 
     @Override
     public void launchActivity(@NonNull Intent intent) {
-        if (!FeatureList.hasTestFeatures()) {
-            FeatureList.setTestFeatures(
-                    Collections.singletonMap(ChromeFeatureList.SHARE_BY_DEFAULT_IN_CCT, true));
-        }
         putCustomTabIdInIntent(intent);
         super.launchActivity(intent);
     }
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/firstrun/FirstRunIntegrationTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/firstrun/FirstRunIntegrationTest.java
index af348f0..932e722 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/firstrun/FirstRunIntegrationTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/firstrun/FirstRunIntegrationTest.java
@@ -44,7 +44,6 @@
 import org.chromium.chrome.browser.customtabs.CustomTabActivity;
 import org.chromium.chrome.browser.customtabs.CustomTabsTestUtils;
 import org.chromium.chrome.browser.document.ChromeLauncherActivity;
-import org.chromium.chrome.browser.flags.ChromeFeatureList;
 import org.chromium.chrome.browser.locale.DefaultSearchEngineDialogHelperUtils;
 import org.chromium.chrome.browser.locale.LocaleManager;
 import org.chromium.chrome.browser.locale.LocaleManager.SearchEnginePromoType;
@@ -68,7 +67,6 @@
  * Integration test suite for the first run experience.
  */
 @RunWith(ChromeJUnit4ClassRunner.class)
-@Features.EnableFeatures(ChromeFeatureList.SHARE_BY_DEFAULT_IN_CCT)
 public class FirstRunIntegrationTest {
     private static final long DEFERRED_START_UP_POLL_TIME = 10000L;
     @Rule
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/media/MediaLauncherActivityTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/media/MediaLauncherActivityTest.java
index 1cb1eab..0b81ef8d 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/media/MediaLauncherActivityTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/media/MediaLauncherActivityTest.java
@@ -27,7 +27,6 @@
 import org.chromium.base.test.util.CriteriaHelper;
 import org.chromium.base.test.util.UrlUtils;
 import org.chromium.chrome.browser.customtabs.CustomTabActivity;
-import org.chromium.chrome.browser.flags.ChromeFeatureList;
 import org.chromium.chrome.browser.flags.ChromeSwitches;
 import org.chromium.chrome.browser.tab.Tab;
 import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
@@ -45,7 +44,6 @@
  */
 @RunWith(ChromeJUnit4ClassRunner.class)
 @CommandLineFlags.Add({ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE})
-@Features.EnableFeatures(ChromeFeatureList.SHARE_BY_DEFAULT_IN_CCT)
 public class MediaLauncherActivityTest {
     @Rule
     public MultiActivityTestRule mTestRule = new MultiActivityTestRule();
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/reengagement/ReengagementNotificationControllerIntegrationTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/reengagement/ReengagementNotificationControllerIntegrationTest.java
index 5a9fb236..309ace7 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/reengagement/ReengagementNotificationControllerIntegrationTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/reengagement/ReengagementNotificationControllerIntegrationTest.java
@@ -378,7 +378,6 @@
         // as expected with test values.
         features.put(ChromeFeatureList.SEARCH_ENGINE_PROMO_EXISTING_DEVICE, false);
         features.put(ChromeFeatureList.OMNIBOX_SEARCH_ENGINE_LOGO, false);
-        features.put(ChromeFeatureList.SHARE_BY_DEFAULT_IN_CCT, true);
         features.put(ChromeFeatureList.VOICE_SEARCH_AUDIO_CAPTURE_POLICY, false);
         FeatureList.setTestFeatures(features);
     }
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/tabmodel/TabModelSelectorObserverTestRule.java b/chrome/android/javatests/src/org/chromium/chrome/browser/tabmodel/TabModelSelectorObserverTestRule.java
index a49f1686..b655bedf 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/tabmodel/TabModelSelectorObserverTestRule.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/tabmodel/TabModelSelectorObserverTestRule.java
@@ -73,9 +73,6 @@
         TabContentManager tabContentManager = new TabContentManager(
                 InstrumentationRegistry.getTargetContext(), null, false, mSelector::getTabById);
         tabContentManager.initWithNative();
-        TabPersistencePolicy persistencePolicy = new TabbedModeTabPersistencePolicy(0, false);
-        TabPersistentStore tabPersistentStore =
-                new TabPersistentStore(persistencePolicy, mSelector, null);
         NextTabPolicySupplier nextTabPolicySupplier = () -> NextTabPolicy.HIERARCHICAL;
         AsyncTabParamsManager asyncTabParamsManager = AsyncTabParamsManagerSingleton.getInstance();
 
@@ -115,13 +112,12 @@
         };
 
         mNormalTabModel = new TabModelSelectorTestTabModel(Profile.getLastUsedRegularProfile(),
-                orderController, tabContentManager, tabPersistentStore, nextTabPolicySupplier,
-                asyncTabParamsManager, delegate);
+                orderController, tabContentManager, nextTabPolicySupplier, asyncTabParamsManager,
+                delegate);
 
         mIncognitoTabModel = new TabModelSelectorTestIncognitoTabModel(
                 Profile.getLastUsedRegularProfile().getPrimaryOTRProfile(), orderController,
-                tabContentManager, tabPersistentStore, nextTabPolicySupplier, asyncTabParamsManager,
-                delegate);
+                tabContentManager, nextTabPolicySupplier, asyncTabParamsManager, delegate);
 
         mSelector.initialize(mNormalTabModel, mIncognitoTabModel);
     }
@@ -134,11 +130,10 @@
 
         public TabModelSelectorTestTabModel(Profile profile,
                 TabModelOrderController orderController, TabContentManager tabContentManager,
-                TabPersistentStore tabPersistentStore, NextTabPolicySupplier nextTabPolicySupplier,
+                NextTabPolicySupplier nextTabPolicySupplier,
                 AsyncTabParamsManager asyncTabParamsManager, TabModelDelegate modelDelegate) {
             super(profile, false, null, null, orderController, tabContentManager,
-                    tabPersistentStore, nextTabPolicySupplier, asyncTabParamsManager, modelDelegate,
-                    false);
+                    nextTabPolicySupplier, asyncTabParamsManager, modelDelegate, false);
         }
 
         @Override
@@ -165,11 +160,10 @@
             extends TabModelSelectorTestTabModel implements IncognitoTabModel {
         public TabModelSelectorTestIncognitoTabModel(Profile profile,
                 TabModelOrderController orderController, TabContentManager tabContentManager,
-                TabPersistentStore tabPersistentStore, NextTabPolicySupplier nextTabPolicySupplier,
+                NextTabPolicySupplier nextTabPolicySupplier,
                 AsyncTabParamsManager asyncTabParamsManager, TabModelDelegate modelDelegate) {
             super(Profile.getLastUsedRegularProfile().getPrimaryOTRProfile(), orderController,
-                    tabContentManager, tabPersistentStore, nextTabPolicySupplier,
-                    asyncTabParamsManager, modelDelegate);
+                    tabContentManager, nextTabPolicySupplier, asyncTabParamsManager, modelDelegate);
         }
 
         @Override
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/tabmodel/TabPersistentStoreTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/tabmodel/TabPersistentStoreTest.java
index 7ee7be6..164506d 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/tabmodel/TabPersistentStoreTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/tabmodel/TabPersistentStoreTest.java
@@ -152,7 +152,7 @@
                     return new TabModelImpl(Profile.getLastUsedRegularProfile(), false,
                             getTabCreatorManager().getTabCreator(false),
                             getTabCreatorManager().getTabCreator(true), mTabModelOrderController,
-                            null, mTabPersistentStore, nextTabPolicySupplier,
+                            null, nextTabPolicySupplier,
                             AsyncTabParamsManagerSingleton.getInstance(), TestTabModelSelector.this,
                             true);
                 }
@@ -162,7 +162,7 @@
                     new IncognitoTabModelImpl(new IncognitoTabModelImplCreator(null,
                             getTabCreatorManager().getTabCreator(false),
                             getTabCreatorManager().getTabCreator(true), mTabModelOrderController,
-                            null, mTabPersistentStore, nextTabPolicySupplier,
+                            null, nextTabPolicySupplier,
                             AsyncTabParamsManagerSingleton.getInstance(), this));
             initialize(regularTabModel, incognitoTabModel);
         }
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/customtabs/CustomTabIntentDataProviderTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/customtabs/CustomTabIntentDataProviderTest.java
index 00068f1d..dcbd5c0 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/customtabs/CustomTabIntentDataProviderTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/customtabs/CustomTabIntentDataProviderTest.java
@@ -36,7 +36,6 @@
 import org.chromium.base.test.BaseRobolectricTestRunner;
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.document.ChromeLauncherActivity;
-import org.chromium.chrome.browser.flags.ChromeFeatureList;
 import org.chromium.chrome.test.util.browser.Features;
 import org.chromium.device.mojom.ScreenOrientationLockType;
 
@@ -46,7 +45,6 @@
 /** Tests for {@link CustomTabIntentDataProvider}. */
 @RunWith(BaseRobolectricTestRunner.class)
 @Config(manifest = Config.NONE)
-@Features.EnableFeatures({ChromeFeatureList.SHARE_BY_DEFAULT_IN_CCT})
 public class CustomTabIntentDataProviderTest {
     @Rule
     public TestRule mProcessor = new Features.JUnitProcessor();
@@ -203,22 +201,6 @@
     }
 
     @Test
-    @Features.DisableFeatures({ChromeFeatureList.SHARE_BY_DEFAULT_IN_CCT})
-    public void shareStateDefaultFlagDisabled_hasShareItemInMenu() {
-        Context context = ApplicationProvider.getApplicationContext();
-        Intent intent = new Intent()
-                                .putExtra(CustomTabsIntent.EXTRA_SHARE_STATE,
-                                        CustomTabsIntent.SHARE_STATE_DEFAULT)
-                                .putExtra(CustomTabsIntent.EXTRA_DEFAULT_SHARE_MENU_ITEM, true);
-
-        CustomTabIntentDataProvider dataProvider =
-                new CustomTabIntentDataProvider(intent, context, COLOR_SCHEME_LIGHT);
-
-        assertTrue(dataProvider.getCustomButtonsOnToolbar().isEmpty());
-        assertTrue(dataProvider.shouldShowShareMenuItem());
-    }
-
-    @Test
     public void shareStateOff_noShareItems() {
         Context context = ApplicationProvider.getApplicationContext();
         Intent intent = new Intent().putExtra(
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/tabmodel/TabModelSelectorImplTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/tabmodel/TabModelSelectorImplTest.java
index c2d7144..9b86933 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/tabmodel/TabModelSelectorImplTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/tabmodel/TabModelSelectorImplTest.java
@@ -60,7 +60,6 @@
                 mMockTabModelFilterFactory, mNextTabPolicySupplier, realAsyncTabParamsManager,
                 /*supportUndo=*/false,
                 /*isTabbedActivity=*/false, /*startIncognito=*/false);
-        mTabModelSelector.setTabPersistentStoreSupplier(() -> null);
         mTabCreatorManager.initialize(mTabModelSelector);
         mTabModelSelector.onNativeLibraryReadyInternal(mMockTabContentManager,
                 new MockTabModel(false, null), new MockTabModel(true, null));
diff --git a/chrome/android/profiles/newest.txt b/chrome/android/profiles/newest.txt
index 28d8a3a..8afb8ae 100644
--- a/chrome/android/profiles/newest.txt
+++ b/chrome/android/profiles/newest.txt
@@ -1 +1 @@
-chromeos-chrome-amd64-91.0.4448.0_rc-r1-merged.afdo.bz2
+chromeos-chrome-amd64-91.0.4449.0_rc-r1-merged.afdo.bz2
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn
index 8685553..9db2d22 100644
--- a/chrome/browser/BUILD.gn
+++ b/chrome/browser/BUILD.gn
@@ -953,6 +953,8 @@
     "optimization_guide/optimization_guide_keyed_service_factory.h",
     "optimization_guide/optimization_guide_navigation_data.cc",
     "optimization_guide/optimization_guide_navigation_data.h",
+    "optimization_guide/optimization_guide_tab_url_provider.cc",
+    "optimization_guide/optimization_guide_tab_url_provider.h",
     "optimization_guide/optimization_guide_top_host_provider.cc",
     "optimization_guide/optimization_guide_top_host_provider.h",
     "optimization_guide/optimization_guide_web_contents_observer.cc",
@@ -1542,8 +1544,6 @@
     "sharesheet/sharesheet_types.h",
     "sharing/ack_message_handler.cc",
     "sharing/ack_message_handler.h",
-    "sharing/click_to_call/feature.cc",
-    "sharing/click_to_call/feature.h",
     "sharing/features.cc",
     "sharing/features.h",
     "sharing/ping_message_handler.cc",
@@ -3049,6 +3049,8 @@
       "offline_pages/prefetch/notifications/prefetch_notification_service_factory.h",
       "optimization_guide/android/optimization_guide_bridge.cc",
       "optimization_guide/android/optimization_guide_bridge.h",
+      "optimization_guide/android/optimization_guide_tab_url_provider_android.cc",
+      "optimization_guide/android/optimization_guide_tab_url_provider_android.h",
       "page_load_metrics/observers/android_page_load_metrics_observer.cc",
       "page_load_metrics/observers/android_page_load_metrics_observer.h",
       "password_check/android/password_check_bridge.cc",
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc
index a65db7c..bf106f3 100644
--- a/chrome/browser/about_flags.cc
+++ b/chrome/browser/about_flags.cc
@@ -49,7 +49,6 @@
 #include "chrome/browser/prefetch/prefetch_proxy/prefetch_proxy_params.h"
 #include "chrome/browser/prefetch/search_prefetch/field_trial_settings.h"
 #include "chrome/browser/resource_coordinator/tab_manager_features.h"
-#include "chrome/browser/sharing/click_to_call/feature.h"
 #include "chrome/browser/sharing/features.h"
 #include "chrome/browser/sharing/shared_clipboard/feature_flags.h"
 #include "chrome/browser/sharing/sms/sms_flags.h"
@@ -1811,7 +1810,8 @@
     {"omnibox_focused_on_new_tab", "true"},
     {"home_button_on_grid_tab_switcher", "true"},
     {"new_home_surface_from_home_button", "hide_tab_switcher_only"},
-    {"hide_switch_when_no_incognito_tabs", "true"}};
+    {"hide_switch_when_no_incognito_tabs", "true"},
+    {"show_tabs_in_mru_order", "true"}};
 
 const FeatureEntry::FeatureParam kStartSurfaceAndroid_SingleSurface_V2[] = {
     {"start_surface_variation", "single"},
@@ -1824,7 +1824,8 @@
      {"show_last_active_tab_only", "true"},
      {"omnibox_focused_on_new_tab", "true"},
      {"home_button_on_grid_tab_switcher", "true"},
-     {"new_home_surface_from_home_button", "hide_tab_switcher_only"}};
+     {"new_home_surface_from_home_button", "hide_tab_switcher_only"},
+     {"show_tabs_in_mru_order", "true"}};
 
 const FeatureEntry::FeatureParam
     kStartSurfaceAndroid_SingleSurfaceWithoutMvTiles[] = {
@@ -3372,9 +3373,6 @@
     {"chrome-sharing-hub-v1-5", flag_descriptions::kChromeSharingHubV15Name,
      flag_descriptions::kChromeSharingHubV15Description, kOsAndroid,
      FEATURE_VALUE_TYPE(chrome::android::kChromeSharingHubV15)},
-    {"share-by-default-in-cct", flag_descriptions::kShareByDefaultInCCTName,
-     flag_descriptions::kShareByDefaultInCCTDescription, kOsAndroid,
-     FEATURE_VALUE_TYPE(chrome::android::kShareByDefaultInCCT)},
 #endif  // OS_ANDROID
     {"in-product-help-demo-mode-choice",
      flag_descriptions::kInProductHelpDemoModeChoiceName,
@@ -3573,6 +3571,11 @@
      flag_descriptions::kDesktopPWAsRunOnOsLoginDescription,
      kOsWin | kOsLinux | kOsMac,
      FEATURE_VALUE_TYPE(features::kDesktopPWAsRunOnOsLogin)},
+    {"enable-desktop-pwas-protocol-handling",
+     flag_descriptions::kDesktopPWAsProtocolHandlingName,
+     flag_descriptions::kDesktopPWAsProtocolHandlingDescription,
+     kOsWin | kOsLinux | kOsMac,
+     FEATURE_VALUE_TYPE(blink::features::kWebAppEnableProtocolHandlers)},
     {"enable-desktop-pwas-url-handling",
      flag_descriptions::kDesktopPWAsUrlHandlingName,
      flag_descriptions::kDesktopPWAsUrlHandlingDescription,
@@ -5379,12 +5382,6 @@
      FEATURE_VALUE_TYPE(chromeos::assistant::features::kAssistantTimersV2)},
 #endif  // BUILDFLAG(IS_CHROMEOS_ASH)
 
-#if BUILDFLAG(ENABLE_CLICK_TO_CALL)
-    {"click-to-call-ui", flag_descriptions::kClickToCallUIName,
-     flag_descriptions::kClickToCallUIDescription, kOsDesktop,
-     FEATURE_VALUE_TYPE(kClickToCallUI)},
-#endif  // BUILDFLAG(ENABLE_CLICK_TO_CALL)
-
 #if defined(OS_WIN) || defined(OS_MAC) || defined(OS_LINUX) || \
     defined(OS_CHROMEOS)
     {"remote-copy-receiver", flag_descriptions::kRemoteCopyReceiverName,
@@ -5815,10 +5812,6 @@
      kOsWin | kOsLinux | kOsCrOS | kOsAndroid,
      FEATURE_VALUE_TYPE(features::kFormControlsRefresh)},
 
-    {"color-picker-eye-dropper", flag_descriptions::kColorPickerEyeDropperName,
-     flag_descriptions::kColorPickerEyeDropperDescription, kOsWin | kOsMac,
-     FEATURE_VALUE_TYPE(features::kEyeDropper)},
-
 #if BUILDFLAG(IS_CHROMEOS_ASH)
     {"auto-screen-brightness", flag_descriptions::kAutoScreenBrightnessName,
      flag_descriptions::kAutoScreenBrightnessDescription, kOsCrOS,
@@ -6383,7 +6376,7 @@
      flag_descriptions::kPaintPreviewStartupDescription, kOsAndroid,
      FEATURE_WITH_PARAMS_VALUE_TYPE(paint_preview::kPaintPreviewShowOnStartup,
                                     kPaintPreviewStartupVariations,
-                                    "PaintPreviewStartup")},
+                                    "PaintPreviewShowOnStartup")},
 #endif  // ENABLE_PAINT_PREVIEW && defined(OS_ANDROID)
 
 #if defined(OS_ANDROID)
@@ -6593,11 +6586,6 @@
     {"enable-palm-suppression", flag_descriptions::kEnablePalmSuppressionName,
      flag_descriptions::kEnablePalmSuppressionDescription, kOsCrOS,
      FEATURE_VALUE_TYPE(ui::kEnablePalmSuppression)},
-
-    {"movable-partial-screenshot-region",
-     flag_descriptions::kMovablePartialScreenshotName,
-     flag_descriptions::kMovablePartialScreenshotDescription, kOsCrOS,
-     FEATURE_VALUE_TYPE(ash::features::kMovablePartialScreenshot)},
 #endif  // BUILDFLAG(IS_CHROMEOS_ASH)
 
     {"enable-experimental-cookie-features",
diff --git a/chrome/browser/ash/login/quick_unlock/pin_storage_cryptohome.cc b/chrome/browser/ash/login/quick_unlock/pin_storage_cryptohome.cc
index 45e9b171..09bf839 100644
--- a/chrome/browser/ash/login/quick_unlock/pin_storage_cryptohome.cc
+++ b/chrome/browser/ash/login/quick_unlock/pin_storage_cryptohome.cc
@@ -244,6 +244,7 @@
   if (return_code != cryptohome::MOUNT_ERROR_NONE) {
     can_authenticate_cache_[account_id] = false;
     std::move(callback).Run(false);
+    return;
   }
 
   const std::vector<cryptohome::KeyDefinition>& key_definitions =
diff --git a/chrome/browser/chromeos/external_protocol_dialog.cc b/chrome/browser/chromeos/external_protocol_dialog.cc
index 9b093db40..cd644df 100644
--- a/chrome/browser/chromeos/external_protocol_dialog.cc
+++ b/chrome/browser/chromeos/external_protocol_dialog.cc
@@ -8,7 +8,6 @@
 #include "chrome/browser/chromeos/arc/intent_helper/arc_external_protocol_dialog.h"
 #include "chrome/browser/chromeos/guest_os/guest_os_external_protocol_handler.h"
 #include "chrome/browser/external_protocol/external_protocol_handler.h"
-#include "chrome/browser/sharing/click_to_call/feature.h"
 #include "chrome/browser/tab_contents/tab_util.h"
 #include "chrome/browser/ui/browser_dialogs.h"
 #include "chrome/browser/ui/views/external_protocol_dialog.h"
@@ -117,12 +116,9 @@
 ExternalProtocolNoHandlersDialog::~ExternalProtocolNoHandlersDialog() = default;
 
 std::u16string ExternalProtocolNoHandlersDialog::GetWindowTitle() const {
-  // If click to call feature is available, we display a message to the user on
-  // how to use the feature.
-  // TODO(crbug.com/1007995) - This is a hotfix for M78 and we plan to use our
-  // own dialog with more information in the future.
-  if (scheme_ == url::kTelScheme &&
-      base::FeatureList::IsEnabled(kClickToCallUI)) {
+  // If this dialog is shown for a tel link, we display a message to the user on
+  // how to use the Click to Call feature.
+  if (scheme_ == url::kTelScheme) {
     return l10n_util::GetStringUTF16(
         IDS_BROWSER_SHARING_CLICK_TO_CALL_DIALOG_HELP_TEXT_NO_DEVICES);
   }
diff --git a/chrome/browser/enterprise/connectors/file_system/box_api_call_flow.cc b/chrome/browser/enterprise/connectors/file_system/box_api_call_flow.cc
index e29a293..308886c 100644
--- a/chrome/browser/enterprise/connectors/file_system/box_api_call_flow.cc
+++ b/chrome/browser/enterprise/connectors/file_system/box_api_call_flow.cc
@@ -6,7 +6,9 @@
 
 #include <string>
 
+#include "base/base64.h"
 #include "base/files/file_util.h"
+#include "base/hash/sha1.h"
 #include "base/json/json_writer.h"
 #include "base/task/post_task.h"
 #include "base/values.h"
@@ -441,7 +443,7 @@
 // https://developer.box.com/reference/post-files-upload-sessions/
 
 BoxCreateUploadSessionApiCallFlow::BoxCreateUploadSessionApiCallFlow(
-    Callback callback,
+    TaskCallback callback,
     const std::string& folder_id,
     const size_t file_size,
     const std::string& file_name)
@@ -524,13 +526,123 @@
 }
 
 ////////////////////////////////////////////////////////////////////////////////
+// ChunkedUpload: PartFileUpload
+////////////////////////////////////////////////////////////////////////////////
+// BoxApiCallFlow interface.
+// API reference:
+// https://developer.box.com/reference/put-files-upload-sessions-id/
+
+BoxPartFileUploadApiCallFlow::BoxPartFileUploadApiCallFlow(
+    TaskCallback callback,
+    const std::string& upload_endpoint,
+    const std::string& file_part_content,
+    const size_t byte_from,
+    const size_t byte_to,
+    const size_t byte_total)
+    : callback_(std::move(callback)),
+      upload_endpoint_(upload_endpoint),
+      part_content_(file_part_content),
+      content_range_(base::StringPrintf("bytes %zu-%zu/%zu",
+                                        byte_from,
+                                        byte_to,
+                                        byte_total)),
+      sha_digest_(CreateFileDigest(file_part_content)) {
+  DCHECK(upload_endpoint_.is_valid());
+}
+
+BoxPartFileUploadApiCallFlow::~BoxPartFileUploadApiCallFlow() = default;
+
+std::string BoxPartFileUploadApiCallFlow::CreateFileDigest(
+    const std::string& content) {
+  // Box API requires the digest to be SHA1 and Base64 encoded.
+  std::string sha_encoded;
+  base::Base64Encode(base::SHA1HashString(content), &sha_encoded);
+
+  std::string sha_digest("sha=");
+  sha_digest.append(sha_encoded);
+  sha_digest.append("=");
+  return sha_digest;
+}
+
+GURL BoxPartFileUploadApiCallFlow::CreateApiCallUrl() {
+  return upload_endpoint_;
+}
+
+net::HttpRequestHeaders BoxPartFileUploadApiCallFlow::CreateApiCallHeaders() {
+  net::HttpRequestHeaders headers;
+  headers.SetHeader("content-range", content_range_);
+  headers.SetHeader("digest", sha_digest_);
+  return headers;
+}
+
+std::string BoxPartFileUploadApiCallFlow::CreateApiCallBody() {
+  return part_content_;
+}
+// TODO read the file in parts? #include "net/base/upload_file_element_reader.h"
+
+std::string BoxPartFileUploadApiCallFlow::CreateApiCallBodyContentType() {
+  return "application/octet-stream";
+}
+
+std::string BoxPartFileUploadApiCallFlow::GetRequestTypeForBody(
+    const std::string& body) {
+  CHECK(!body.empty());
+  return "PUT";
+}
+
+bool BoxPartFileUploadApiCallFlow::IsExpectedSuccessCode(int code) const {
+  return code == net::HTTP_OK;
+}
+
+void BoxPartFileUploadApiCallFlow::ProcessApiCallSuccess(
+    const network::mojom::URLResponseHead* head,
+    std::unique_ptr<std::string> body) {
+  DCHECK(body);
+  data_decoder::DataDecoder::ParseJsonIsolated(
+      *body, base::BindOnce(&BoxPartFileUploadApiCallFlow::OnJsonParsed,
+                            factory_.GetWeakPtr()));
+}
+
+void BoxPartFileUploadApiCallFlow::ProcessApiCallFailure(
+    int net_error,
+    const network::mojom::URLResponseHead* head,
+    std::unique_ptr<std::string> body) {
+  auto response_code = head->headers->response_code();
+  DVLOG(1) << "[BoxApiCallFlow] PartFileUplaod failed. Error code "
+           << response_code;
+  std::move(callback_).Run(false, response_code, base::Value());
+}
+
+void BoxPartFileUploadApiCallFlow::OnJsonParsed(
+    data_decoder::DataDecoder::ValueOrError result) {
+  if (!result.value) {
+    DVLOG(1) << "[BoxApiCallFlow] PartFileUplaod OnJsonParsed Error: "
+             << (result.error ? result.error->data()
+                              : "<no error info available>");
+    std::move(callback_).Run(false, net::HTTP_OK, base::Value());
+    return;
+  }
+
+  base::Value* part = result.value ? result.value->FindPath("part") : nullptr;
+  if (!part) {
+    const char* msg =
+        result.value ? "<no part>"
+                     : (result.error ? result.error->data() : "<no error>");
+    DVLOG(1) << "BoxPartFileUploadApiCallFlow::OnJsonParsed: " << msg;
+    std::move(callback_).Run(false, net::HTTP_OK, base::Value());
+    return;
+  }
+  std::move(callback_).Run(true, net::HTTP_OK, std::move(*part));
+}
+
+////////////////////////////////////////////////////////////////////////////////
 // ChunkedUpload: CommitUploadSession
 ////////////////////////////////////////////////////////////////////////////////
 // BoxApiCallFlow interface.
 // API reference:
 // https://developer.box.com/reference/post-files-upload-sessions-id-commit/
 BoxCommitUploadSessionApiCallFlow::BoxCommitUploadSessionApiCallFlow(
-    Callback callback,
+    TaskCallback callback,
     const std::string& commit_endpoint,
     const base::Value& parts,
     const std::string digest)
diff --git a/chrome/browser/enterprise/connectors/file_system/box_api_call_flow.h b/chrome/browser/enterprise/connectors/file_system/box_api_call_flow.h
index f21f1754..59ef4aa8 100644
--- a/chrome/browser/enterprise/connectors/file_system/box_api_call_flow.h
+++ b/chrome/browser/enterprise/connectors/file_system/box_api_call_flow.h
@@ -17,6 +17,9 @@
 // be implemented by subclasses.
 class BoxApiCallFlow : public OAuth2ApiCallFlow {
  public:
+  // Callback args are: whether request returned success, and
+  // net::HttpStatusCode.
+  using TaskCallback = base::OnceCallback<void(bool, int)>;
   BoxApiCallFlow();
   ~BoxApiCallFlow() override;
 
@@ -33,9 +36,11 @@
   static const size_t kWholeFileUploadMaxSize;
 };
 
-// Helper for finding the downloads folder in box.
+// Helper for finding the downloads folder in Box.
 class BoxFindUpstreamFolderApiCallFlow : public BoxApiCallFlow {
  public:
+  // Additional callback arg is: folder_id for the downloads folder found in
+  // Box.
   using TaskCallback = base::OnceCallback<void(bool, int, const std::string&)>;
   explicit BoxFindUpstreamFolderApiCallFlow(TaskCallback callback);
   ~BoxFindUpstreamFolderApiCallFlow() override;
@@ -61,6 +66,8 @@
 // Helper for creating an upstream downloads folder in box.
 class BoxCreateUpstreamFolderApiCallFlow : public BoxApiCallFlow {
  public:
+  // Additional callback arg is: folder_id for the downloads folder created in
+  // Box.
   using TaskCallback = base::OnceCallback<void(bool, int, const std::string&)>;
   explicit BoxCreateUpstreamFolderApiCallFlow(TaskCallback callback);
   ~BoxCreateUpstreamFolderApiCallFlow() override;
@@ -89,7 +96,6 @@
 // downloads folder in box.
 class BoxWholeFileUploadApiCallFlow : public BoxApiCallFlow {
  public:
-  using TaskCallback = base::OnceCallback<void(bool, int)>;
   BoxWholeFileUploadApiCallFlow(TaskCallback callback,
                                 const std::string& folder_id,
                                 const base::FilePath& target_file_name,
@@ -157,8 +163,10 @@
 // in Box.
 class BoxCreateUploadSessionApiCallFlow : public BoxApiCallFlow {
  public:
-  using Callback = base::OnceCallback<void(bool, int, base::Value)>;
-  BoxCreateUploadSessionApiCallFlow(Callback callback,
+  // Additional callback arg is: session endpoints provided in API request
+  // response.
+  using TaskCallback = base::OnceCallback<void(bool, int, base::Value)>;
+  BoxCreateUploadSessionApiCallFlow(TaskCallback callback,
                                     const std::string& folder_id,
                                     const size_t file_size,
                                     const std::string& file_name);
@@ -178,19 +186,65 @@
  private:
   void OnJsonParsed(data_decoder::DataDecoder::ValueOrError result);
 
-  Callback callback_;
+  TaskCallback callback_;
   const std::string folder_id_;
   const size_t file_size_;
   const std::string file_name_;
   base::WeakPtrFactory<BoxCreateUploadSessionApiCallFlow> factory_{this};
 };
 
+// Helper for uploading a part of the file to Box.
+class BoxPartFileUploadApiCallFlow : public BoxApiCallFlow {
+ public:
+  // Additional callback arg is: uploaded file part info in API request response
+  // that needs to be attached in CommitUploadSession request.
+  // Callback invoked when the file part upload completes. The bool argument is
+  // true if the upload succeeded and false otherwise. The int argument
+  // represents the final HTTP status code of the request. The Value holds a
+  // JSON part object as returned by the Box Upload Part API, which is valid
+  // only on success.
+  using TaskCallback = base::OnceCallback<void(bool, int, base::Value)>;
+  BoxPartFileUploadApiCallFlow(TaskCallback callback,
+                               const std::string& upload_endpoint,
+                               const std::string& file_part_content,
+                               const size_t byte_from,
+                               const size_t byte_to,
+                               const size_t byte_total);
+  ~BoxPartFileUploadApiCallFlow() override;
+
+  // Helper method.
+  static std::string CreateFileDigest(const std::string& content);
+
+ protected:
+  // BoxApiCallFlow interface.
+  GURL CreateApiCallUrl() override;
+  net::HttpRequestHeaders CreateApiCallHeaders() override;
+  std::string CreateApiCallBody() override;
+  std::string CreateApiCallBodyContentType() override;
+  std::string GetRequestTypeForBody(const std::string& body) override;
+  bool IsExpectedSuccessCode(int code) const override;
+  void ProcessApiCallSuccess(const network::mojom::URLResponseHead* head,
+                             std::unique_ptr<std::string> body) override;
+  void ProcessApiCallFailure(int net_error,
+                             const network::mojom::URLResponseHead* head,
+                             std::unique_ptr<std::string> body) override;
+
+ private:
+  void OnJsonParsed(data_decoder::DataDecoder::ValueOrError result);
+
+  TaskCallback callback_;
+  const GURL upload_endpoint_;
+  const std::string& part_content_;
+  const std::string content_range_;
+  const std::string sha_digest_;
+  base::WeakPtrFactory<BoxPartFileUploadApiCallFlow> factory_{this};
+};
+
 // Helper for committing an upload session once all the parts are uploaded
 // successfully.
 class BoxCommitUploadSessionApiCallFlow : public BoxApiCallFlow {
  public:
-  using Callback = base::OnceCallback<void(bool, int)>;
-  BoxCommitUploadSessionApiCallFlow(Callback callback,
+  BoxCommitUploadSessionApiCallFlow(TaskCallback callback,
                                     const std::string& commit_endpoint,
                                     const base::Value& parts,
                                     const std::string digest);
@@ -209,7 +263,7 @@
                              std::unique_ptr<std::string> body) override;
 
  private:
-  Callback callback_;
+  TaskCallback callback_;
   const std::string commit_endpoint_;
   const base::Value upload_session_parts_;
   const std::string sha_digest_;
diff --git a/chrome/browser/enterprise/connectors/file_system/box_api_call_flow_unittest.cc b/chrome/browser/enterprise/connectors/file_system/box_api_call_flow_unittest.cc
index 6d6cbc7..a7dbb1d 100644
--- a/chrome/browser/enterprise/connectors/file_system/box_api_call_flow_unittest.cc
+++ b/chrome/browser/enterprise/connectors/file_system/box_api_call_flow_unittest.cc
@@ -8,6 +8,7 @@
 
 #include <memory>
 
+#include "base/base64.h"
 #include "base/bind.h"
 #include "base/files/file_util.h"
 #include "base/files/scoped_temp_dir.h"
@@ -633,6 +634,146 @@
 }
 
 ////////////////////////////////////////////////////////////////////////////////
+// PartFileUpload
+////////////////////////////////////////////////////////////////////////////////
+
+class BoxPartFileUploadApiCallFlowForTest
+    : public BoxPartFileUploadApiCallFlow {
+ public:
+  using BoxPartFileUploadApiCallFlow::BoxPartFileUploadApiCallFlow;
+  using BoxPartFileUploadApiCallFlow::CreateApiCallBody;
+  using BoxPartFileUploadApiCallFlow::CreateApiCallBodyContentType;
+  using BoxPartFileUploadApiCallFlow::CreateApiCallHeaders;
+  using BoxPartFileUploadApiCallFlow::CreateApiCallUrl;
+  using BoxPartFileUploadApiCallFlow::CreateFileDigest;
+  using BoxPartFileUploadApiCallFlow::GetRequestTypeForBody;
+  using BoxPartFileUploadApiCallFlow::IsExpectedSuccessCode;
+  using BoxPartFileUploadApiCallFlow::ProcessApiCallFailure;
+  using BoxPartFileUploadApiCallFlow::ProcessApiCallSuccess;
+};
+
+class BoxPartFileUploadApiCallFlowTest
+    : public BoxApiCallFlowTest<BoxPartFileUploadApiCallFlowForTest> {
+ protected:
+  BoxPartFileUploadApiCallFlowTest()
+      : file_content_("random file part content"),
+        expected_sha_(
+            BoxPartFileUploadApiCallFlow::CreateFileDigest(file_content_)),
+        expected_content_range_(base::StringPrintf("bytes 0-%zu/%zu",
+                                                   file_content_.size(),
+                                                   file_content_.size())) {}
+
+  void SetUp() override {
+    flow_ = std::make_unique<BoxPartFileUploadApiCallFlowForTest>(
+        base::BindOnce(&BoxPartFileUploadApiCallFlowTest::OnResponse,
+                       factory_.GetWeakPtr()),
+        upload_endpoint_, file_content_, 0, file_content_.size(),
+        file_content_.size());
+  }
+
+  void OnResponse(bool success, int response_code, base::Value) {
+    processed_success_ = success;
+    response_code_ = response_code;
+    if (quit_closure_)
+      std::move(quit_closure_).Run();
+  }
+
+  const std::string upload_endpoint_ = "https://provider.com/upload/id=12345";
+  const std::string file_content_;
+  const std::string expected_sha_;
+  const std::string expected_content_range_;
+
+  base::test::SingleThreadTaskEnvironment task_environment_;
+  data_decoder::test::InProcessDataDecoder decoder_;
+  base::OnceClosure quit_closure_;
+  base::WeakPtrFactory<BoxPartFileUploadApiCallFlowTest> factory_{this};
+};
+
+TEST_F(BoxPartFileUploadApiCallFlowTest, CreateApiCallUrl) {
+  ASSERT_EQ(flow_->CreateApiCallUrl(), upload_endpoint_);
+}
+
+TEST_F(BoxPartFileUploadApiCallFlowTest, CreateApiCallHeaders) {
+  net::HttpRequestHeaders headers = flow_->CreateApiCallHeaders();
+  std::string digest;
+  headers.GetHeader("digest", &digest);
+  base::Base64Decode(digest, &digest);
+  EXPECT_EQ(digest, expected_sha_);
+
+  std::string content_range;
+  headers.GetHeader("content-range", &content_range);
+  EXPECT_EQ(content_range, expected_content_range_) << headers.ToString();
+}
+
+TEST_F(BoxPartFileUploadApiCallFlowTest, CreateApiCallBody) {
+  std::string body = flow_->CreateApiCallBody();
+  ASSERT_EQ(body, file_content_);
+}
+
+TEST_F(BoxPartFileUploadApiCallFlowTest, CreateApiCallBodyContentType) {
+  std::string type = flow_->CreateApiCallBodyContentType();
+  ASSERT_EQ(type, "application/octet-stream");
+}
+
+TEST_F(BoxPartFileUploadApiCallFlowTest, GetRequestTypeForBody) {
+  std::string type = flow_->GetRequestTypeForBody(file_content_);
+  ASSERT_EQ(type, "PUT");
+}
+
+TEST_F(BoxPartFileUploadApiCallFlowTest, IsExpectedSuccessCode) {
+  ASSERT_TRUE(flow_->IsExpectedSuccessCode(200));
+  ASSERT_FALSE(flow_->IsExpectedSuccessCode(201));
+  ASSERT_FALSE(flow_->IsExpectedSuccessCode(202));
+  ASSERT_FALSE(flow_->IsExpectedSuccessCode(400));
+  ASSERT_FALSE(flow_->IsExpectedSuccessCode(404));
+  ASSERT_FALSE(flow_->IsExpectedSuccessCode(409));
+  ASSERT_FALSE(flow_->IsExpectedSuccessCode(412));
+}
+
+TEST_F(BoxPartFileUploadApiCallFlowTest, ProcessApiCallSuccess) {
+  auto http_head = network::CreateURLResponseHead(net::HTTP_OK);
+  std::string body(R"({
+    "part": {
+    "offset": 16777216,
+    "part_id": "6F2D3486",
+    "sha1": "134b65991ed521fcfe4724b7d814ab8ded5185dc",
+    "size": 3222784
+  }
+  })");
+  flow_->ProcessApiCallSuccess(http_head.get(),
+                               std::make_unique<std::string>(body));
+  base::RunLoop().RunUntilIdle();
+  ASSERT_TRUE(processed_success_);
+  ASSERT_EQ(response_code_, net::HTTP_OK);
+}
+
+TEST_F(BoxPartFileUploadApiCallFlowTest, ProcessApiCallSuccess_ParseError) {
+  auto http_head = network::CreateURLResponseHead(net::HTTP_OK);
+  flow_->ProcessApiCallSuccess(http_head.get(),
+                               std::make_unique<std::string>());
+  base::RunLoop().RunUntilIdle();
+  ASSERT_FALSE(processed_success_);
+  ASSERT_EQ(response_code_, net::HTTP_OK);
+}
+
+TEST_F(BoxPartFileUploadApiCallFlowTest, ProcessApiCallSuccess_EmptyResponse) {
+  auto http_head = network::CreateURLResponseHead(net::HTTP_OK);
+  flow_->ProcessApiCallSuccess(
+      http_head.get(), std::make_unique<std::string>(kEmptyResponseBody));
+  base::RunLoop().RunUntilIdle();
+  ASSERT_FALSE(processed_success_);
+  ASSERT_EQ(response_code_, net::HTTP_OK);
+}
+
+TEST_F(BoxPartFileUploadApiCallFlowTest, ProcessApiCallFailure) {
+  auto http_head = network::CreateURLResponseHead(net::HTTP_CONFLICT);
+  flow_->ProcessApiCallFailure(net::OK, http_head.get(), {});
+  base::RunLoop().RunUntilIdle();
+  ASSERT_FALSE(processed_success_);
+  ASSERT_EQ(response_code_, net::HTTP_CONFLICT);
+}
+
+////////////////////////////////////////////////////////////////////////////////
 // CommitUploadSession
 ////////////////////////////////////////////////////////////////////////////////
 
diff --git a/chrome/browser/enterprise/connectors/file_system/box_api_call_test_helper.cc b/chrome/browser/enterprise/connectors/file_system/box_api_call_test_helper.cc
index 543d94b..012bf7d 100644
--- a/chrome/browser/enterprise/connectors/file_system/box_api_call_test_helper.cc
+++ b/chrome/browser/enterprise/connectors/file_system/box_api_call_test_helper.cc
@@ -10,6 +10,8 @@
 const char kFileSystemBoxCreateFolderUrl[] = "https://api.box.com/2.0/folders";
 const char kFileSystemBoxWholeFileUploadUrl[] =
     "https://upload.box.com/api/2.0/files/content";
+
+const char kEmptyResponseBody[] = R"({})";
 const char kFileSystemBoxFindFolderResponseBody[] = R"({
     "entries": [
       {
diff --git a/chrome/browser/enterprise/connectors/file_system/box_api_call_test_helper.h b/chrome/browser/enterprise/connectors/file_system/box_api_call_test_helper.h
index 545d33a..39a4083 100644
--- a/chrome/browser/enterprise/connectors/file_system/box_api_call_test_helper.h
+++ b/chrome/browser/enterprise/connectors/file_system/box_api_call_test_helper.h
@@ -12,9 +12,10 @@
 extern const char kFileSystemBoxCreateFolderUrl[];
 extern const char kFileSystemBoxWholeFileUploadUrl[];
 
-// Expected responses for calls to Box endpoints; used for
-// network::TestURLLoaderFactory.
+// Expected responses for calls to Box endpoints.
 
+// Empty response body.
+extern const char kEmptyResponseBody[];
 // Expected response from kFileSystemBoxFindFolderUrl.
 extern const char kFileSystemBoxFindFolderResponseBody[];
 // Expected folder id extracted from kFileSystemBoxFindFolderResponseBody.
diff --git a/chrome/browser/flag-metadata.json b/chrome/browser/flag-metadata.json
index acc80217..b72095a0 100644
--- a/chrome/browser/flag-metadata.json
+++ b/chrome/browser/flag-metadata.json
@@ -116,7 +116,7 @@
   },
   {
     "name": "android-detailed-language-settings",
-    "owners": [ "perrier", "chrome-langauge@google.com" ],
+    "owners": [ "perrier", "chrome-language@google.com" ],
     "expiry_milestone": 91
   },
   {
@@ -595,11 +595,6 @@
     "expiry_milestone": 78
   },
   {
-    "name": "click-to-call-ui",
-    "owners": [ "//chrome/browser/sharing/OWNERS" ],
-    "expiry_milestone": 90
-  },
-  {
     "name": "client-storage-access-context-auditing",
     "owners": [
       "sauski",
@@ -619,11 +614,6 @@
     "expiry_milestone": 80
   },
   {
-    "name": "color-picker-eye-dropper",
-    "owners": [ "iopopesc@microsoft.com", "masonfreed" ],
-    "expiry_milestone": 90
-  },
-  {
     "name": "color-provider-redirection",
     "owners": [ "//ui/color/OWNERS" ],
     "expiry_milestone": 88
@@ -964,7 +954,7 @@
   },
   {
     "name": "desktop-restructured-language-settings",
-    "owners": [ "joshsantana", "chrome-langauge@google.com"],
+    "owners": [ "joshsantana", "chrome-language@google.com"],
     "expiry_milestone": 93
   },
   {
@@ -999,8 +989,8 @@
   },
   {
     "name": "diagnostics-app",
-    "owners": [ "baileyberro", "jimmyxgong", "zentaro"],
-    "expiry_milestone": 90
+    "owners": [ "//chromeos/components/diagnostics_ui/OWNERS" ],
+    "expiry_milestone": 92
   },
   {
     "name": "dice-web-signin-interception",
@@ -1643,6 +1633,11 @@
     "expiry_milestone": 93
   },
   {
+    "name": "enable-desktop-pwas-protocol-handling",
+    "owners": [ "samtan@microsoft.com", "desktop-pwas-team@google.com" ],
+    "expiry_milestone": 95
+  },
+  {
     "name": "enable-desktop-pwas-remove-status-bar",
     "owners": [ "dmurph@chromium.org", "desktop-pwas-team@google.com" ],
     "expiry_milestone": 91
@@ -1799,7 +1794,7 @@
   {
     "name": "enable-generic-sensor-extra-classes",
     "owners": [ "reillyg@chromium.org", "raphael.kubo.da.costa@intel.com" ],
-    "expiry_milestone": 90
+    "expiry_milestone": 94
   },
   {
     "name": "enable-google-srp-isolated-prerender-nsp",
@@ -3585,11 +3580,6 @@
     "expiry_milestone": 84
   },
   {
-    "name": "movable-partial-screenshot-region",
-    "owners": [ "xiyuan", "chromeos-wmp-eng" ],
-    "expiry_milestone": 90
-  },
-  {
     "name": "nearby-sharing",
     "owners": [ "vecore@google.com", "cros-system-services@google.com", "cross-device-team@google.com" ],
     "expiry_milestone": 95
@@ -4665,11 +4655,6 @@
     "expiry_milestone": 92
   },
   {
-    "name": "share-by-default-in-cct",
-    "owners": [ "sophey", "chrome-sharing-eng@google.com" ],
-    "expiry_milestone": 90
-  },
-  {
     "name": "shared-clipboard-ui",
     "owners": [ "//chrome/browser/sharing/OWNERS" ],
     "expiry_milestone": 90
diff --git a/chrome/browser/flag_descriptions.cc b/chrome/browser/flag_descriptions.cc
index 417e19b..208018d 100644
--- a/chrome/browser/flag_descriptions.cc
+++ b/chrome/browser/flag_descriptions.cc
@@ -736,6 +736,11 @@
     "user logs in. Launching a PWA while the browser is not running is known "
     "to cause a failure to restore sessions. See https://crbug.com/938759.";
 
+const char kDesktopPWAsProtocolHandlingName[] = "Desktop PWA Protocol handling";
+const char kDesktopPWAsProtocolHandlingDescription[] =
+    "Enable web app manifests to declare protocol handling behavior."
+    "See: https://crbug.com/1019239.";
+
 const char kDesktopPWAsUrlHandlingName[] = "Desktop PWA URL handling";
 const char kDesktopPWAsUrlHandlingDescription[] =
     "Enable web app manifests to declare URL handling behavior. Prototype "
@@ -1210,11 +1215,6 @@
 const char kFormControlsRefreshDescription[] =
     "If enabled, HTML forms elements will be rendered using an updated style.";
 
-const char kColorPickerEyeDropperName[] = "Color Picker Eye Dropper Support";
-const char kColorPickerEyeDropperDescription[] =
-    "If enabled, the color picker will contain an eye dropper control that "
-    "can be used to pick colors.";
-
 const char kGlobalMediaControlsName[] = "Global Media Controls";
 const char kGlobalMediaControlsDescription[] =
     "Enables the Global Media Controls UI in the toolbar.";
@@ -2124,10 +2124,6 @@
 const char kSidePanelName[] = "Side panel";
 const char kSidePanelDescription[] = "Host some content in a side panel.";
 
-const char kShareByDefaultInCCTName[] = "Enable sharing by default in CCT.";
-const char kShareByDefaultInCCTDescription[] =
-    "Enables a sharing option by default in Chrome Custom Tabs.";
-
 const char kSharedClipboardUIName[] =
     "Enable shared clipboard feature signals to be handled";
 const char kSharedClipboardUIDescription[] =
@@ -4071,11 +4067,6 @@
 const char kDriveFsBidirectionalNativeMessagingDescription[] =
     "Enable enhanced native messaging host to communicate with DriveFS.";
 
-const char kMovablePartialScreenshotName[] =
-    "Movable/resizable partial screenshot region";
-const char kMovablePartialScreenshotDescription[] =
-    "Allow partial screenshot region to be moved/resized via mouse/touch.";
-
 const char kEnableAppDataSearchName[] = "Enable app data search in launcher";
 const char kEnableAppDataSearchDescription[] =
     "Allow launcher search to access data available through Firebase App "
@@ -5044,14 +5035,6 @@
     "rather than crashing. If enabled, DCHECKs will crash the calling process.";
 #endif  // defined(DCHECK_IS_CONFIGURABLE)
 
-#if BUILDFLAG(ENABLE_CLICK_TO_CALL)
-const char kClickToCallUIName[] =
-    "Enable click to call feature signals to be handled on desktop";
-const char kClickToCallUIDescription[] =
-    "Enables click to call feature signals to be handled on desktop by showing "
-    "a list of user's available devices with telephony functionality.";
-#endif  // BUILDFLAG(ENABLE_CLICK_TO_CALL)
-
 #if BUILDFLAG(ENABLE_DICE_SUPPORT)
 const char kDiceWebSigninInterceptionName[] = "Dice Web-Signin Interception";
 const char kDiceWebSigninInterceptionDescription[] =
diff --git a/chrome/browser/flag_descriptions.h b/chrome/browser/flag_descriptions.h
index 7957510..26360f2 100644
--- a/chrome/browser/flag_descriptions.h
+++ b/chrome/browser/flag_descriptions.h
@@ -459,6 +459,9 @@
 extern const char kDesktopPWAsRunOnOsLoginName[];
 extern const char kDesktopPWAsRunOnOsLoginDescription[];
 
+extern const char kDesktopPWAsProtocolHandlingName[];
+extern const char kDesktopPWAsProtocolHandlingDescription[];
+
 extern const char kDesktopPWAsUrlHandlingName[];
 extern const char kDesktopPWAsUrlHandlingDescription[];
 
@@ -727,9 +730,6 @@
 extern const char kFormControlsRefreshName[];
 extern const char kFormControlsRefreshDescription[];
 
-extern const char kColorPickerEyeDropperName[];
-extern const char kColorPickerEyeDropperDescription[];
-
 extern const char kGlobalMediaControlsName[];
 extern const char kGlobalMediaControlsDescription[];
 
@@ -1248,9 +1248,6 @@
 extern const char kSidePanelName[];
 extern const char kSidePanelDescription[];
 
-extern const char kShareByDefaultInCCTName[];
-extern const char kShareByDefaultInCCTDescription[];
-
 extern const char kSharedClipboardUIName[];
 extern const char kSharedClipboardUIDescription[];
 
@@ -2367,9 +2364,6 @@
 extern const char kDriveFsBidirectionalNativeMessagingName[];
 extern const char kDriveFsBidirectionalNativeMessagingDescription[];
 
-extern const char kMovablePartialScreenshotName[];
-extern const char kMovablePartialScreenshotDescription[];
-
 extern const char kEnableAdvancedPpdAttributesName[];
 extern const char kEnableAdvancedPpdAttributesDescription[];
 
@@ -2974,11 +2968,6 @@
 extern const char kDcheckIsFatalDescription[];
 #endif  // defined(DCHECK_IS_CONFIGURABLE)
 
-#if BUILDFLAG(ENABLE_CLICK_TO_CALL)
-extern const char kClickToCallUIName[];
-extern const char kClickToCallUIDescription[];
-#endif  // BUILDFLAG(ENABLE_CLICK_TO_CALL)
-
 #if BUILDFLAG(ENABLE_DICE_SUPPORT)
 extern const char kDiceWebSigninInterceptionName[];
 extern const char kDiceWebSigninInterceptionDescription[];
diff --git a/chrome/browser/flags/android/chrome_feature_list.cc b/chrome/browser/flags/android/chrome_feature_list.cc
index 58aff9f..46b6240f 100644
--- a/chrome/browser/flags/android/chrome_feature_list.cc
+++ b/chrome/browser/flags/android/chrome_feature_list.cc
@@ -217,7 +217,6 @@
     &kServiceManagerForBackgroundPrefetch,
     &kServiceManagerForDownload,
     &kShareButtonInTopToolbar,
-    &kShareByDefaultInCCT,
     &kSharedClipboardUI,
     &kShoppingAssist,
     &kSpannableInlineAutocomplete,
@@ -608,9 +607,6 @@
 const base::Feature kShareButtonInTopToolbar{"ShareButtonInTopToolbar",
                                              base::FEATURE_DISABLED_BY_DEFAULT};
 
-const base::Feature kShareByDefaultInCCT{"ShareByDefaultInCCT",
-                                         base::FEATURE_ENABLED_BY_DEFAULT};
-
 const base::Feature kShoppingAssist{"ShoppingAssist",
                                     base::FEATURE_DISABLED_BY_DEFAULT};
 
diff --git a/chrome/browser/flags/android/chrome_feature_list.h b/chrome/browser/flags/android/chrome_feature_list.h
index 2db7380..5c24425e 100644
--- a/chrome/browser/flags/android/chrome_feature_list.h
+++ b/chrome/browser/flags/android/chrome_feature_list.h
@@ -109,7 +109,6 @@
 extern const base::Feature kServiceManagerForBackgroundPrefetch;
 extern const base::Feature kServiceManagerForDownload;
 extern const base::Feature kShareButtonInTopToolbar;
-extern const base::Feature kShareByDefaultInCCT;
 extern const base::Feature kShoppingAssist;
 extern const base::Feature kSpannableInlineAutocomplete;
 extern const base::Feature kSpecialLocaleWrapper;
diff --git a/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/CachedFeatureFlags.java b/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/CachedFeatureFlags.java
index 4c6752b..db71456 100644
--- a/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/CachedFeatureFlags.java
+++ b/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/CachedFeatureFlags.java
@@ -60,7 +60,6 @@
             put(ChromeFeatureList.EARLY_LIBRARY_LOAD, false);
             put(ChromeFeatureList.PRIORITIZE_BOOTSTRAP_TASKS, true);
             put(ChromeFeatureList.IMMERSIVE_UI_MODE, false);
-            put(ChromeFeatureList.SHARE_BY_DEFAULT_IN_CCT, true);
             put(ChromeFeatureList.SWAP_PIXEL_FORMAT_TO_FIX_CONVERT_FROM_TRANSLUCENT, true);
             put(ChromeFeatureList.START_SURFACE_ANDROID, false);
             put(ChromeFeatureList.PAINT_PREVIEW_DEMO, false);
diff --git a/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/ChromeFeatureList.java b/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/ChromeFeatureList.java
index f114d9d4..460b32c5 100644
--- a/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/ChromeFeatureList.java
+++ b/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/ChromeFeatureList.java
@@ -428,7 +428,6 @@
             "ServiceManagerForBackgroundPrefetch";
     public static final String SERVICE_MANAGER_FOR_DOWNLOAD = "ServiceManagerForDownload";
     public static final String SHARE_BUTTON_IN_TOP_TOOLBAR = "ShareButtonInTopToolbar";
-    public static final String SHARE_BY_DEFAULT_IN_CCT = "ShareByDefaultInCCT";
     public static final String SHARED_CLIPBOARD_UI = "SharedClipboardUI";
     public static final String SHOW_TRUSTED_PUBLISHER_URL = "ShowTrustedPublisherURL";
     public static final String SMART_SUGGESTION_FOR_LARGE_DOWNLOADS =
diff --git a/chrome/browser/history/history_service_factory.cc b/chrome/browser/history/history_service_factory.cc
index 8ae55f1..4acc96a 100644
--- a/chrome/browser/history/history_service_factory.cc
+++ b/chrome/browser/history/history_service_factory.cc
@@ -8,7 +8,6 @@
 #include "chrome/browser/history/chrome_history_client.h"
 #include "chrome/browser/profiles/incognito_helpers.h"
 #include "chrome/browser/profiles/profile.h"
-#include "chrome/common/pref_names.h"
 #include "components/bookmarks/browser/bookmark_model.h"
 #include "components/history/content/browser/content_visit_delegate.h"
 #include "components/history/content/browser/history_database_helper.h"
diff --git a/chrome/browser/language/android/java/res/xml/languages_detailed_preferences.xml b/chrome/browser/language/android/java/res/xml/languages_detailed_preferences.xml
index 82983e2..02154c9 100644
--- a/chrome/browser/language/android/java/res/xml/languages_detailed_preferences.xml
+++ b/chrome/browser/language/android/java/res/xml/languages_detailed_preferences.xml
@@ -17,7 +17,7 @@
     </PreferenceCategory>
 
     <PreferenceCategory
-      android:key="content_langauges_section"
+      android:key="content_languages_section"
       android:title="@string/languages_content_title"
       android:order="2"
       app:allowDividerAbove="true">
diff --git a/chrome/browser/media/webrtc/webrtc_event_log_manager_unittest.cc b/chrome/browser/media/webrtc/webrtc_event_log_manager_unittest.cc
index 1e10791..96cc0f3 100644
--- a/chrome/browser/media/webrtc/webrtc_event_log_manager_unittest.cc
+++ b/chrome/browser/media/webrtc/webrtc_event_log_manager_unittest.cc
@@ -2274,7 +2274,9 @@
   ExpectRemoteFileContents(*file_path, std::string());
 }
 
-TEST_F(WebRtcEventLogManagerTest, RemoteLogFileCreatedInCorrectDirectory) {
+// TODO(crbug.com/1185008): Fix this flaky test.
+TEST_F(WebRtcEventLogManagerTest,
+       DISABLED_RemoteLogFileCreatedInCorrectDirectory) {
   // Set up separate browser contexts; each one will get one log.
   constexpr size_t kLogsNum = 3;
   std::unique_ptr<TestingProfile> browser_contexts[kLogsNum];
diff --git a/chrome/browser/media_galleries/chromeos/mtp_device_task_helper.cc b/chrome/browser/media_galleries/chromeos/mtp_device_task_helper.cc
index e644dc6..a2bd9ca 100644
--- a/chrome/browser/media_galleries/chromeos/mtp_device_task_helper.cc
+++ b/chrome/browser/media_galleries/chromeos/mtp_device_task_helper.cc
@@ -435,10 +435,10 @@
   uint32_t bytes_to_read =
       std::min(base::checked_cast<uint32_t>(request.buf_len),
                base::saturated_cast<uint32_t>(file_info.size - request.offset));
-
+  auto file_id = request.file_id;
+  auto offset = base::checked_cast<uint32_t>(request.offset);
   GetMediaTransferProtocolManager()->ReadFileChunk(
-      device_handle_, request.file_id,
-      base::checked_cast<uint32_t>(request.offset), bytes_to_read,
+      device_handle_, file_id, offset, bytes_to_read,
       base::BindOnce(&MTPDeviceTaskHelper::OnDidReadBytes,
                      weak_ptr_factory_.GetWeakPtr(), std::move(request),
                      file_info));
diff --git a/chrome/browser/optimization_guide/android/DEPS b/chrome/browser/optimization_guide/android/DEPS
index 0d019e1..5bbfd42 100644
--- a/chrome/browser/optimization_guide/android/DEPS
+++ b/chrome/browser/optimization_guide/android/DEPS
@@ -1,3 +1,4 @@
 include_rules = [
   "+content/public/android/java",
+  "+content/public/test",
 ]
diff --git a/chrome/browser/optimization_guide/android/optimization_guide_bridge_unittest.cc b/chrome/browser/optimization_guide/android/optimization_guide_bridge_unittest.cc
index b1be12f1..c50edb82 100644
--- a/chrome/browser/optimization_guide/android/optimization_guide_bridge_unittest.cc
+++ b/chrome/browser/optimization_guide/android/optimization_guide_bridge_unittest.cc
@@ -38,6 +38,7 @@
                                       pref_service,
                                       /*hint_store=*/nullptr,
                                       /*top_host_provider=*/nullptr,
+                                      /*tab_url_provider=*/nullptr,
                                       /*url_loader_factory=*/nullptr) {}
   ~MockOptimizationGuideHintsManager() override = default;
   MOCK_METHOD4(CanApplyOptimizationAsync,
diff --git a/chrome/browser/optimization_guide/android/optimization_guide_tab_url_provider_android.cc b/chrome/browser/optimization_guide/android/optimization_guide_tab_url_provider_android.cc
new file mode 100644
index 0000000..b5b50ff
--- /dev/null
+++ b/chrome/browser/optimization_guide/android/optimization_guide_tab_url_provider_android.cc
@@ -0,0 +1,42 @@
+// Copyright 2021 The Chromium 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/optimization_guide/android/optimization_guide_tab_url_provider_android.h"
+
+#include "chrome/browser/android/tab_android.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/ui/android/tab_model/tab_model.h"
+#include "chrome/browser/ui/android/tab_model/tab_model_list.h"
+#include "content/public/browser/web_contents.h"
+
+namespace optimization_guide {
+namespace android {
+
+OptimizationGuideTabUrlProviderAndroid::OptimizationGuideTabUrlProviderAndroid(
+    Profile* profile)
+    : OptimizationGuideTabUrlProvider(profile) {}
+OptimizationGuideTabUrlProviderAndroid::
+    ~OptimizationGuideTabUrlProviderAndroid() = default;
+
+const std::vector<content::WebContents*>
+OptimizationGuideTabUrlProviderAndroid::GetAllWebContentsForProfile(
+    Profile* profile) {
+  std::vector<content::WebContents*> web_contents_list;
+  for (auto tab_model = TabModelList::begin(); tab_model != TabModelList::end();
+       ++tab_model) {
+    if ((*tab_model)->GetProfile() != profile)
+      continue;
+
+    int tab_count = (*tab_model)->GetTabCount();
+    for (int i = 0; i < tab_count; i++) {
+      content::WebContents* web_contents = (*tab_model)->GetWebContentsAt(i);
+      if (web_contents)
+        web_contents_list.push_back(web_contents);
+    }
+  }
+  return web_contents_list;
+}
+
+}  // namespace android
+}  // namespace optimization_guide
diff --git a/chrome/browser/optimization_guide/android/optimization_guide_tab_url_provider_android.h b/chrome/browser/optimization_guide/android/optimization_guide_tab_url_provider_android.h
new file mode 100644
index 0000000..7c9cc87
--- /dev/null
+++ b/chrome/browser/optimization_guide/android/optimization_guide_tab_url_provider_android.h
@@ -0,0 +1,30 @@
+// Copyright 2021 The Chromium 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_OPTIMIZATION_GUIDE_ANDROID_OPTIMIZATION_GUIDE_TAB_URL_PROVIDER_ANDROID_H_
+#define CHROME_BROWSER_OPTIMIZATION_GUIDE_ANDROID_OPTIMIZATION_GUIDE_TAB_URL_PROVIDER_ANDROID_H_
+
+#include "chrome/browser/optimization_guide/optimization_guide_tab_url_provider.h"
+
+namespace optimization_guide {
+namespace android {
+
+// Implementation of OptimizationGuideTabUrlProvider that gets URLs from Android
+// browser windows.
+class OptimizationGuideTabUrlProviderAndroid
+    : public OptimizationGuideTabUrlProvider {
+ public:
+  explicit OptimizationGuideTabUrlProviderAndroid(Profile* profile);
+  ~OptimizationGuideTabUrlProviderAndroid() override;
+
+ private:
+  // OptimizationGuideTabUrlProvider:
+  const std::vector<content::WebContents*> GetAllWebContentsForProfile(
+      Profile* profile) override;
+};
+
+}  // namespace android
+}  // namespace optimization_guide
+
+#endif  // CHROME_BROWSER_OPTIMIZATION_GUIDE_ANDROID_OPTIMIZATION_GUIDE_TAB_URL_PROVIDER_ANDROID_H_
diff --git a/chrome/browser/optimization_guide/android/optimization_guide_tab_url_provider_android_unittest.cc b/chrome/browser/optimization_guide/android/optimization_guide_tab_url_provider_android_unittest.cc
new file mode 100644
index 0000000..5a4d9ac
--- /dev/null
+++ b/chrome/browser/optimization_guide/android/optimization_guide_tab_url_provider_android_unittest.cc
@@ -0,0 +1,140 @@
+// Copyright 2021 The Chromium 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/optimization_guide/android/optimization_guide_tab_url_provider_android.h"
+
+#include "chrome/browser/android/tab_android.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/ui/android/tab_model/tab_model.h"
+#include "chrome/browser/ui/android/tab_model/tab_model_list.h"
+#include "chrome/test/base/chrome_render_view_host_test_harness.h"
+#include "content/public/test/web_contents_tester.h"
+#include "testing/gmock/include/gmock/gmock.h"
+
+namespace optimization_guide {
+namespace android {
+namespace {
+
+using ::testing::ElementsAre;
+
+// FakeTabModel that can be used for testing Android tab behavior.
+class FakeTabModel : public TabModel {
+ public:
+  explicit FakeTabModel(
+      Profile* profile,
+      const std::vector<content::WebContents*>& web_contents_list)
+      : TabModel(profile, /*is_tabbed_activity=*/false),
+        web_contents_list_(web_contents_list) {}
+
+  int GetTabCount() const override {
+    return static_cast<int>(web_contents_list_.size());
+  }
+  int GetActiveIndex() const override { return 0; }
+  content::WebContents* GetWebContentsAt(int index) const override {
+    if (index < static_cast<int>(web_contents_list_.size()))
+      return web_contents_list_[index];
+    return nullptr;
+  }
+
+  base::android::ScopedJavaLocalRef<jobject> GetJavaObject() const override {
+    return nullptr;
+  }
+  void CreateTab(TabAndroid* parent,
+                 content::WebContents* web_contents) override {}
+  void HandlePopupNavigation(TabAndroid* parent,
+                             NavigateParams* params) override {}
+  content::WebContents* CreateNewTabForDevTools(const GURL& url) override {
+    return nullptr;
+  }
+  bool IsSessionRestoreInProgress() const override { return false; }
+  bool IsActiveModel() const override { return false; }
+  TabAndroid* GetTabAt(int index) const override { return nullptr; }
+  void SetActiveIndex(int index) override {}
+  void CloseTabAt(int index) override {}
+  void AddObserver(TabModelObserver* observer) override {}
+  void RemoveObserver(TabModelObserver* observer) override {}
+
+ private:
+  std::vector<content::WebContents*> web_contents_list_;
+};
+
+class OptimizationGuideTabUrlProviderAndroidTest
+    : public ChromeRenderViewHostTestHarness {
+ public:
+  OptimizationGuideTabUrlProviderAndroidTest() = default;
+  ~OptimizationGuideTabUrlProviderAndroidTest() override = default;
+
+  void SetUp() override {
+    ChromeRenderViewHostTestHarness::SetUp();
+    tab_url_provider_ =
+        std::make_unique<OptimizationGuideTabUrlProviderAndroid>(profile());
+  }
+
+  void TearDown() override {
+    tab_url_provider_.reset();
+    ChromeRenderViewHostTestHarness::TearDown();
+  }
+
+  OptimizationGuideTabUrlProviderAndroid* tab_url_provider() const {
+    return tab_url_provider_.get();
+  }
+
+ private:
+  std::unique_ptr<OptimizationGuideTabUrlProviderAndroid> tab_url_provider_;
+};
+
+TEST_F(OptimizationGuideTabUrlProviderAndroidTest,
+       GetUrlsOfActiveTabsNoOpenTabs) {
+  std::vector<GURL> urls =
+      tab_url_provider()->GetUrlsOfActiveTabs(base::TimeDelta::FromDays(90));
+  EXPECT_TRUE(urls.empty());
+}
+
+TEST_F(OptimizationGuideTabUrlProviderAndroidTest,
+       GetUrlsOfActiveTabsFiltersOutTabs) {
+  std::unique_ptr<content::WebContents> web_contents =
+      content::WebContentsTester::CreateTestWebContents(
+          browser_context(), content::SiteInstance::Create(browser_context()));
+  content::WebContentsTester* web_contents_tester =
+      content::WebContentsTester::For(web_contents.get());
+  web_contents_tester->SetLastCommittedURL(GURL("https://example.com/a"));
+  std::unique_ptr<content::WebContents> web_contents2 =
+      content::WebContentsTester::CreateTestWebContents(
+          browser_context(), content::SiteInstance::Create(browser_context()));
+  content::WebContentsTester* web_contents_tester2 =
+      content::WebContentsTester::For(web_contents2.get());
+  web_contents_tester2->SetLastCommittedURL(GURL("https://example.com/b"));
+  web_contents_tester2->SetLastActiveTime(base::TimeTicks::Now() -
+                                          base::TimeDelta::FromDays(2));
+  std::unique_ptr<content::WebContents> stale_web_contents =
+      content::WebContentsTester::CreateTestWebContents(
+          browser_context(), content::SiteInstance::Create(browser_context()));
+  content::WebContentsTester* stale_web_contents_tester =
+      content::WebContentsTester::For(stale_web_contents.get());
+  stale_web_contents_tester->SetLastActiveTime(base::TimeTicks::Now() -
+                                               base::TimeDelta::FromDays(100));
+  stale_web_contents_tester->SetLastCommittedURL(GURL("https://stale.com"));
+  FakeTabModel tab_model(profile(), {web_contents.get(), web_contents2.get(),
+                                     stale_web_contents.get()});
+  TabModelList::AddTabModel(&tab_model);
+
+  std::unique_ptr<content::WebContents> otr_web_contents =
+      content::WebContentsTester::CreateTestWebContents(
+          browser_context(), content::SiteInstance::Create(browser_context()));
+  content::WebContentsTester* otr_web_contents_tester =
+      content::WebContentsTester::For(otr_web_contents.get());
+  otr_web_contents_tester->SetLastCommittedURL(GURL("https://incognito.com"));
+  FakeTabModel otr_tab_model(profile()->GetPrimaryOTRProfile(),
+                             {otr_web_contents.get()});
+  TabModelList::AddTabModel(&otr_tab_model);
+
+  std::vector<GURL> urls =
+      tab_url_provider()->GetUrlsOfActiveTabs(base::TimeDelta::FromDays(90));
+  EXPECT_THAT(urls, ElementsAre(GURL("https://example.com/a"),
+                                GURL("https://example.com/b")));
+}
+
+}  // namespace
+}  // namespace android
+}  // namespace optimization_guide
diff --git a/chrome/browser/optimization_guide/optimization_guide_hints_manager.cc b/chrome/browser/optimization_guide/optimization_guide_hints_manager.cc
index ed431f2..72353c7 100644
--- a/chrome/browser/optimization_guide/optimization_guide_hints_manager.cc
+++ b/chrome/browser/optimization_guide/optimization_guide_hints_manager.cc
@@ -44,6 +44,7 @@
 #include "components/optimization_guide/core/optimization_guide_util.h"
 #include "components/optimization_guide/core/optimization_hints_component_update_listener.h"
 #include "components/optimization_guide/core/optimization_metadata.h"
+#include "components/optimization_guide/core/tab_url_provider.h"
 #include "components/optimization_guide/core/top_host_provider.h"
 #include "components/optimization_guide/proto/models.pb.h"
 #include "components/prefs/pref_service.h"
@@ -64,11 +65,6 @@
 // startup will have a newer version than it.
 constexpr char kManualConfigComponentVersion[] = "0.0.0";
 
-// Delay until successfully fetched hints should be updated by requesting from
-// the remote Optimization Guide Service.
-constexpr base::TimeDelta kUpdateFetchedHintsDelay =
-    base::TimeDelta::FromHours(24);
-
 // Provides a random time delta in seconds between |kFetchRandomMinDelay| and
 // |kFetchRandomMaxDelay|.
 base::TimeDelta RandomFetchDelay() {
@@ -255,6 +251,7 @@
     PrefService* pref_service,
     optimization_guide::OptimizationGuideStore* hint_store,
     optimization_guide::TopHostProvider* top_host_provider,
+    optimization_guide::TabUrlProvider* tab_url_provider,
     scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory)
     : background_task_runner_(base::ThreadPool::CreateSequencedTaskRunner(
           {base::MayBlock(), base::TaskPriority::BEST_EFFORT})),
@@ -276,6 +273,7 @@
           optimization_guide::features::
               ExternalAppPackageNamesApprovedForFetch()),
       top_host_provider_(top_host_provider),
+      tab_url_provider_(tab_url_provider),
       clock_(base::DefaultClock::GetInstance()) {
   g_browser_process->network_quality_tracker()
       ->AddEffectiveConnectionTypeObserver(this);
@@ -596,7 +594,7 @@
       optimization_guide::kComponentHintsUpdatedResultHistogramString,
       hints_updated);
 
-  MaybeScheduleTopHostsHintsFetch();
+  MaybeScheduleActiveTabsHintsFetch();
 
   MaybeRunUpdateClosure(std::move(update_closure));
 }
@@ -619,56 +617,81 @@
   clock_ = clock;
 }
 
-void OptimizationGuideHintsManager::MaybeScheduleTopHostsHintsFetch() {
-  if (!top_host_provider_ ||
-      !optimization_guide::IsUserPermittedToFetchFromRemoteOptimizationGuide(
+void OptimizationGuideHintsManager::MaybeScheduleActiveTabsHintsFetch() {
+  if (!optimization_guide::IsUserPermittedToFetchFromRemoteOptimizationGuide(
           profile_->IsOffTheRecord(), pref_service_)) {
     return;
   }
 
-  if (!optimization_guide::features::ShouldBatchUpdateHintsForTopHosts())
-    return;
-
   if (optimization_guide::switches::ShouldOverrideFetchHintsTimer()) {
     SetLastHintsFetchAttemptTime(clock_->Now());
-    FetchTopHostsHints();
-  } else if (!top_hosts_hints_fetch_timer_.IsRunning()) {
+    FetchHintsForActiveTabs();
+  } else if (!active_tabs_hints_fetch_timer_.IsRunning()) {
     // Only Schedule this is the time is not already running.
-    ScheduleTopHostsHintsFetch();
+    ScheduleActiveTabsHintsFetch();
   }
 }
 
-void OptimizationGuideHintsManager::ScheduleTopHostsHintsFetch() {
-  DCHECK(!top_hosts_hints_fetch_timer_.IsRunning());
+void OptimizationGuideHintsManager::ScheduleActiveTabsHintsFetch() {
+  DCHECK(!active_tabs_hints_fetch_timer_.IsRunning());
 
-  const base::TimeDelta time_until_update_time =
-      hint_cache_->GetFetchedHintsUpdateTime() - clock_->Now();
-  base::TimeDelta fetcher_delay;
-  if (time_until_update_time <= base::TimeDelta()) {
+  const base::TimeDelta active_tabs_refresh_duration =
+      optimization_guide::features::GetActiveTabsFetchRefreshDuration();
+  const base::TimeDelta time_since_last_fetch =
+      clock_->Now() - GetLastHintsFetchAttemptTime();
+  if (time_since_last_fetch >= active_tabs_refresh_duration) {
     // Fetched hints in the store should be updated and an attempt has not
-    // been made in last |kUpdateFetchedHintsDelay|.
+    // been made in last
+    // |optimization_guide::features::GetActiveTabsFetchRefreshDuration()|.
     SetLastHintsFetchAttemptTime(clock_->Now());
-    top_hosts_hints_fetch_timer_.Start(
+    active_tabs_hints_fetch_timer_.Start(
         FROM_HERE, RandomFetchDelay(), this,
-        &OptimizationGuideHintsManager::FetchTopHostsHints);
+        &OptimizationGuideHintsManager::FetchHintsForActiveTabs);
   } else {
     // If the fetched hints in the store are still up-to-date, set a timer
     // for when the hints need to be updated.
-    fetcher_delay = time_until_update_time;
-    top_hosts_hints_fetch_timer_.Start(
+    base::TimeDelta fetcher_delay =
+        active_tabs_refresh_duration - time_since_last_fetch;
+    active_tabs_hints_fetch_timer_.Start(
         FROM_HERE, fetcher_delay, this,
-        &OptimizationGuideHintsManager::ScheduleTopHostsHintsFetch);
+        &OptimizationGuideHintsManager::ScheduleActiveTabsHintsFetch);
   }
 }
 
-void OptimizationGuideHintsManager::FetchTopHostsHints() {
-  DCHECK(top_host_provider_);
+const std::vector<GURL>
+OptimizationGuideHintsManager::GetActiveTabURLsToRefresh() {
+  if (!tab_url_provider_)
+    return {};
+
+  std::vector<GURL> active_tab_urls = tab_url_provider_->GetUrlsOfActiveTabs(
+      optimization_guide::features::GetActiveTabsStalenessTolerance());
+
+  std::set<GURL> urls_to_refresh;
+  for (const auto& url : active_tab_urls) {
+    if (!hint_cache_->HasURLKeyedEntryForURL(url))
+      urls_to_refresh.insert(url);
+  }
+  return std::vector<GURL>(urls_to_refresh.begin(), urls_to_refresh.end());
+}
+
+void OptimizationGuideHintsManager::FetchHintsForActiveTabs() {
+  active_tabs_hints_fetch_timer_.Stop();
+  active_tabs_hints_fetch_timer_.Start(
+      FROM_HERE,
+      optimization_guide::features::GetActiveTabsFetchRefreshDuration(), this,
+      &OptimizationGuideHintsManager::ScheduleActiveTabsHintsFetch);
 
   if (!HasOptimizationTypeToFetchFor())
     return;
 
-  std::vector<std::string> top_hosts = top_host_provider_->GetTopHosts();
-  if (top_hosts.empty())
+  std::vector<std::string> top_hosts;
+  if (top_host_provider_)
+    top_hosts = top_host_provider_->GetTopHosts();
+
+  const std::vector<GURL> active_tab_urls_to_refresh =
+      GetActiveTabURLsToRefresh();
+
+  if (top_hosts.empty() && active_tab_urls_to_refresh.empty())
     return;
 
   if (!batch_update_hints_fetcher_) {
@@ -676,28 +699,47 @@
     batch_update_hints_fetcher_ = hints_fetcher_factory_->BuildInstance();
   }
 
+  // Add hosts of active tabs to list of hosts to fetch for. Since we are mainly
+  // fetching for updated information on tabs, add those to the front of the
+  // list.
+  base::flat_set<std::string> top_hosts_set =
+      base::flat_set<std::string>(top_hosts.begin(), top_hosts.end());
+  for (const auto& url : active_tab_urls_to_refresh) {
+    if (!url.has_host() ||
+        top_hosts_set.find(url.host()) == top_hosts_set.end()) {
+      continue;
+    }
+    if (!hint_cache_->HasHint(url.host())) {
+      top_hosts_set.insert(url.host());
+      top_hosts.insert(top_hosts.begin(), url.host());
+    }
+  }
+
   batch_update_hints_fetcher_->FetchOptimizationGuideServiceHints(
-      top_hosts, std::vector<GURL>{}, registered_optimization_types_,
+      top_hosts, active_tab_urls_to_refresh, registered_optimization_types_,
       optimization_guide::proto::CONTEXT_BATCH_UPDATE,
       base::BindOnce(
-          &OptimizationGuideHintsManager::OnTopHostsHintsFetched,
-          ui_weak_ptr_factory_.GetWeakPtr(),
-          base::flat_set<std::string>(top_hosts.begin(), top_hosts.end())));
+          &OptimizationGuideHintsManager::OnHintsForActiveTabsFetched,
+          ui_weak_ptr_factory_.GetWeakPtr(), top_hosts_set,
+          base::flat_set<GURL>(active_tab_urls_to_refresh.begin(),
+                               active_tab_urls_to_refresh.end())));
 }
 
-void OptimizationGuideHintsManager::OnTopHostsHintsFetched(
+void OptimizationGuideHintsManager::OnHintsForActiveTabsFetched(
     const base::flat_set<std::string>& hosts_fetched,
+    const base::flat_set<GURL>& urls_fetched,
     base::Optional<std::unique_ptr<optimization_guide::proto::GetHintsResponse>>
         get_hints_response) {
   if (!get_hints_response)
     return;
 
   hint_cache_->UpdateFetchedHints(
-      std::move(*get_hints_response), clock_->Now() + kUpdateFetchedHintsDelay,
-      hosts_fetched,
-      /*urls_fetched=*/{},
+      std::move(*get_hints_response),
+      clock_->Now() +
+          optimization_guide::features::GetActiveTabsFetchRefreshDuration(),
+      hosts_fetched, urls_fetched,
       base::BindOnce(
-          &OptimizationGuideHintsManager::OnFetchedTopHostsHintsStored,
+          &OptimizationGuideHintsManager::OnFetchedActiveTabsHintsStored,
           ui_weak_ptr_factory_.GetWeakPtr()));
 }
 
@@ -717,7 +759,9 @@
   }
 
   hint_cache_->UpdateFetchedHints(
-      std::move(*get_hints_response), clock_->Now() + kUpdateFetchedHintsDelay,
+      std::move(*get_hints_response),
+      clock_->Now() +
+          optimization_guide::features::GetActiveTabsFetchRefreshDuration(),
       page_navigation_hosts_requested, page_navigation_urls_requested,
       base::BindOnce(
           &OptimizationGuideHintsManager::OnFetchedPageNavigationHintsStored,
@@ -725,7 +769,7 @@
           navigation_url, page_navigation_hosts_requested));
 }
 
-void OptimizationGuideHintsManager::OnFetchedTopHostsHintsStored() {
+void OptimizationGuideHintsManager::OnFetchedActiveTabsHintsStored() {
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
   LOCAL_HISTOGRAM_BOOLEAN("OptimizationGuide.FetchedHints.Stored", true);
 
@@ -738,11 +782,6 @@
   }
 
   hint_cache_->PurgeExpiredFetchedHints();
-
-  top_hosts_hints_fetch_timer_.Stop();
-  top_hosts_hints_fetch_timer_.Start(
-      FROM_HERE, hint_cache_->GetFetchedHintsUpdateTime() - clock_->Now(), this,
-      &OptimizationGuideHintsManager::ScheduleTopHostsHintsFetch);
 }
 
 void OptimizationGuideHintsManager::OnFetchedPageNavigationHintsStored(
diff --git a/chrome/browser/optimization_guide/optimization_guide_hints_manager.h b/chrome/browser/optimization_guide/optimization_guide_hints_manager.h
index 332e92a..e9f40c4 100644
--- a/chrome/browser/optimization_guide/optimization_guide_hints_manager.h
+++ b/chrome/browser/optimization_guide/optimization_guide_hints_manager.h
@@ -48,6 +48,7 @@
 enum class OptimizationTargetDecision;
 enum class OptimizationTypeDecision;
 class StoreUpdateData;
+class TabUrlProvider;
 class TopHostProvider;
 }  // namespace optimization_guide
 
@@ -65,6 +66,7 @@
       PrefService* pref_service,
       optimization_guide::OptimizationGuideStore* hint_store,
       optimization_guide::TopHostProvider* top_host_provider,
+      optimization_guide::TabUrlProvider* tab_url_provider,
       scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory);
 
   ~OptimizationGuideHintsManager() override;
@@ -251,24 +253,29 @@
   void OnComponentHintsUpdated(base::OnceClosure update_closure,
                                bool hints_updated);
 
-  // Method to decide whether to fetch new hints for user's top sites and
-  // proceeds to schedule the fetch.
-  void MaybeScheduleTopHostsHintsFetch();
+  // Returns the URLs that are currently in the active tab model that do not
+  // have a hint available in |hint_cache_|.
+  const std::vector<GURL> GetActiveTabURLsToRefresh();
 
-  // Schedules |hints_fetch_timer_| to fire based on:
-  // 1. The update time for the fetched hints in the store and
-  // 2. The last time a fetch attempt was made.
-  void ScheduleTopHostsHintsFetch();
+  // Method to decide whether to fetch new hints for tab URLs and proceeds to
+  // schedule the fetch if so.
+  void MaybeScheduleActiveTabsHintsFetch();
+
+  // Schedules |active_tabs_hints_fetch_timer_| to fire based on the last time a
+  // fetch attempt was made.
+  void ScheduleActiveTabsHintsFetch();
 
   // Called to make a request to fetch hints from the remote Optimization Guide
-  // Service. Used to fetch hints for origins frequently visited by the user.
-  void FetchTopHostsHints();
+  // Service. Used to fetch hints for origins frequently visited by the user and
+  // URLs open in the active tab model.
+  void FetchHintsForActiveTabs();
 
-  // Called when the hints for the top hosts have been fetched from the remote
+  // Called when the hints for active tabs have been fetched from the remote
   // Optimization Guide Service and are ready for parsing. This is used when
   // fetching hints in batch mode.
-  void OnTopHostsHintsFetched(
+  void OnHintsForActiveTabsFetched(
       const base::flat_set<std::string>& hosts_fetched,
+      const base::flat_set<GURL>& urls_fetched,
       base::Optional<
           std::unique_ptr<optimization_guide::proto::GetHintsResponse>>
           get_hints_response);
@@ -291,7 +298,7 @@
 
   // Called when the fetched hints have been stored in |hint_cache| and are
   // ready to be used. This is used when hints were fetched in batch mode.
-  void OnFetchedTopHostsHintsStored();
+  void OnFetchedActiveTabsHintsStored();
 
   // Called when the fetched hints have been stored in |hint_cache| and are
   // ready to be used. This is used when hints were fetched in real-time.
@@ -456,9 +463,12 @@
   // The top host provider that can be queried. Not owned.
   optimization_guide::TopHostProvider* top_host_provider_ = nullptr;
 
+  // The tab URL provider that can be queried. Not owned.
+  optimization_guide::TabUrlProvider* tab_url_provider_ = nullptr;
+
   // The timer used to schedule fetching hints from the remote Optimization
   // Guide Service.
-  base::OneShotTimer top_hosts_hints_fetch_timer_;
+  base::OneShotTimer active_tabs_hints_fetch_timer_;
 
   // The clock used to schedule fetching from the remote Optimization Guide
   // Service.
diff --git a/chrome/browser/optimization_guide/optimization_guide_hints_manager_unittest.cc b/chrome/browser/optimization_guide/optimization_guide_hints_manager_unittest.cc
index 5247e4c..754c0f7e 100644
--- a/chrome/browser/optimization_guide/optimization_guide_hints_manager_unittest.cc
+++ b/chrome/browser/optimization_guide/optimization_guide_hints_manager_unittest.cc
@@ -15,6 +15,7 @@
 #include "base/test/metrics/histogram_tester.h"
 #include "base/test/scoped_feature_list.h"
 #include "chrome/browser/optimization_guide/optimization_guide_navigation_data.h"
+#include "chrome/browser/optimization_guide/optimization_guide_tab_url_provider.h"
 #include "chrome/browser/optimization_guide/optimization_guide_web_contents_observer.h"
 #include "chrome/test/base/testing_profile.h"
 #include "components/data_reduction_proxy/core/common/data_reduction_proxy_pref_names.h"
@@ -48,10 +49,8 @@
 
 namespace {
 
-// Retry delay is 16 minutes to allow for kFetchRetryDelaySecs +
-// kFetchRandomMaxDelaySecs to pass.
-constexpr int kTestFetchRetryDelaySecs = 60 * 16;
-constexpr int kUpdateFetchHintsTimeSecs = 24 * 60 * 60;  // 24 hours.
+// Allows for default hour to pass + random delay between 30 and 60 seconds.
+constexpr int kUpdateFetchHintsTimeSecs = 61 * 60;  // 1 hours and 1 minutes.
 
 const int kDefaultHostBloomFilterNumHashFunctions = 7;
 const int kDefaultHostBloomFilterNumBits = 511;
@@ -133,7 +132,7 @@
   std::vector<std::string> GetTopHosts() override {
     num_top_hosts_called_++;
 
-      return top_hosts_;
+    return top_hosts_;
   }
 
   int get_num_top_hosts_called() const { return num_top_hosts_called_; }
@@ -143,6 +142,24 @@
   int num_top_hosts_called_ = 0;
 };
 
+// A mock class implementation of TabUrlProvider.
+class FakeTabUrlProvider : public optimization_guide::TabUrlProvider {
+ public:
+  const std::vector<GURL> GetUrlsOfActiveTabs(
+      const base::TimeDelta& duration_since_last_shown) override {
+    num_urls_called_++;
+    return urls_;
+  }
+
+  void SetUrls(const std::vector<GURL>& urls) { urls_ = urls; }
+
+  int get_num_urls_called() const { return num_urls_called_; }
+
+ private:
+  std::vector<GURL> urls_;
+  int num_urls_called_ = 0;
+};
+
 enum class HintsFetcherEndState {
   kFetchFailed = 0,
   kFetchSuccessWithHostHints = 1,
@@ -282,9 +299,11 @@
         db_provider_.get(), temp_dir(),
         task_environment_.GetMainThreadTaskRunner());
 
+    tab_url_provider_ = std::make_unique<FakeTabUrlProvider>();
+
     hints_manager_ = std::make_unique<OptimizationGuideHintsManager>(
         &testing_profile_, pref_service_.get(), hint_store_.get(),
-        top_host_provider, url_loader_factory_);
+        top_host_provider, tab_url_provider_.get(), url_loader_factory_);
     hints_manager_->SetClockForTesting(task_environment_.GetMockClock());
 
     // Run until hint cache is initialized and the OptimizationGuideHintsManager
@@ -295,6 +314,7 @@
   void ResetHintsManager() {
     hints_manager_->Shutdown();
     hints_manager_.reset();
+    tab_url_provider_.reset();
     hint_store_.reset();
     pref_service_.reset();
     RunUntilIdle();
@@ -399,6 +419,10 @@
 
   TestingPrefServiceSimple* pref_service() const { return pref_service_.get(); }
 
+  FakeTabUrlProvider* tab_url_provider() const {
+    return tab_url_provider_.get();
+  }
+
   void RunUntilIdle() {
     task_environment_.RunUntilIdle();
     base::RunLoop().RunUntilIdle();
@@ -421,6 +445,7 @@
   TestingProfile testing_profile_;
   std::unique_ptr<content::TestWebContentsFactory> web_contents_factory_;
   std::unique_ptr<optimization_guide::OptimizationGuideStore> hint_store_;
+  std::unique_ptr<FakeTabUrlProvider> tab_url_provider_;
   std::unique_ptr<OptimizationGuideHintsManager> hints_manager_;
   std::unique_ptr<TestingPrefServiceSimple> pref_service_;
   scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory_;
@@ -1869,7 +1894,7 @@
   InitializeWithDefaultConfig("1.0.0");
 
   // Force timer to expire and schedule a hints fetch.
-  MoveClockForwardBy(base::TimeDelta::FromSeconds(kTestFetchRetryDelaySecs));
+  MoveClockForwardBy(base::TimeDelta::FromSeconds(kUpdateFetchHintsTimeSecs));
   EXPECT_EQ(0, top_host_provider->get_num_top_hosts_called());
   // Hints fetcher should not even be created.
   EXPECT_FALSE(batch_update_hints_fetcher());
@@ -1963,25 +1988,7 @@
   InitializeWithDefaultConfig("1.0.0");
 
   // Force timer to expire and schedule a hints fetch.
-  MoveClockForwardBy(base::TimeDelta::FromSeconds(kTestFetchRetryDelaySecs));
-  // Hints fetcher should not even be created.
-  EXPECT_FALSE(batch_update_hints_fetcher());
-}
-
-TEST_F(OptimizationGuideHintsManagerFetchingTest,
-       HintsFetchNotAllowedIfFeatureIsEnabledButTopHostProviderIsNotProvided) {
-  base::CommandLine::ForCurrentProcess()->AppendSwitch(
-      optimization_guide::switches::kDisableCheckingUserPermissionsForTesting);
-  CreateHintsManager(/*top_host_provider=*/nullptr);
-  hints_manager()->RegisterOptimizationTypes(
-      {optimization_guide::proto::DEFER_ALL_SCRIPT});
-  hints_manager()->SetHintsFetcherFactoryForTesting(
-      BuildTestHintsFetcherFactory(
-          {HintsFetcherEndState::kFetchSuccessWithHostHints}));
-  InitializeWithDefaultConfig("1.0.0");
-
-  // Force timer to expire and schedule a hints fetch.
-  MoveClockForwardBy(base::TimeDelta::FromSeconds(kTestFetchRetryDelaySecs));
+  MoveClockForwardBy(base::TimeDelta::FromSeconds(kUpdateFetchHintsTimeSecs));
   // Hints fetcher should not even be created.
   EXPECT_FALSE(batch_update_hints_fetcher());
 }
@@ -2002,7 +2009,7 @@
   InitializeWithDefaultConfig("1.0.0");
 
   // Force timer to expire and schedule a hints fetch but the fetch is not made.
-  MoveClockForwardBy(base::TimeDelta::FromSeconds(kTestFetchRetryDelaySecs));
+  MoveClockForwardBy(base::TimeDelta::FromSeconds(kUpdateFetchHintsTimeSecs));
   EXPECT_EQ(0, top_host_provider->get_num_top_hosts_called());
   // Hints fetcher should not be created.
   EXPECT_FALSE(batch_update_hints_fetcher());
@@ -2034,15 +2041,15 @@
       BuildTestHintsFetcherFactory(
           {HintsFetcherEndState::kFetchSuccessWithHostHints}));
 
-  // Force timer to expire and schedule a hints fetch but the fetch is not made.
-  MoveClockForwardBy(base::TimeDelta::FromSeconds(kTestFetchRetryDelaySecs));
+  // Force timer to expire after random delay and schedule a hints fetch.
+  MoveClockForwardBy(base::TimeDelta::FromSeconds(60 * 2));
   EXPECT_EQ(0, top_host_provider->get_num_top_hosts_called());
   // Hints fetcher should not be created.
   EXPECT_FALSE(batch_update_hints_fetcher());
 }
 
 TEST_F(OptimizationGuideHintsManagerFetchingTest,
-       HintsFetcherEnabledNoHostsToFetch) {
+       HintsFetcherEnabledNoHostsOrUrlsToFetch) {
   base::CommandLine::ForCurrentProcess()->AppendSwitch(
       optimization_guide::switches::kDisableCheckingUserPermissionsForTesting);
   std::unique_ptr<FakeTopHostProvider> top_host_provider =
@@ -2056,43 +2063,54 @@
           {HintsFetcherEndState::kFetchSuccessWithHostHints}));
   InitializeWithDefaultConfig("1.0.0");
 
-  // Force timer to expire and schedule a hints fetch.
-  MoveClockForwardBy(base::TimeDelta::FromSeconds(kTestFetchRetryDelaySecs));
+  // Force timer to expire after random delay and schedule a hints fetch.
+  MoveClockForwardBy(base::TimeDelta::FromSeconds(60 * 2));
   EXPECT_EQ(1, top_host_provider->get_num_top_hosts_called());
+  EXPECT_EQ(1, tab_url_provider()->get_num_urls_called());
   // Hints fetcher should not be even created.
   EXPECT_FALSE(batch_update_hints_fetcher());
+
+  // Move it forward again to make sure timer is scheduled.
+  MoveClockForwardBy(base::TimeDelta::FromSeconds(kUpdateFetchHintsTimeSecs));
+  EXPECT_EQ(2, top_host_provider->get_num_top_hosts_called());
+  EXPECT_EQ(2, tab_url_provider()->get_num_urls_called());
+  // Still no hosts or URLs, so hints fetcher should still not be even created.
+  EXPECT_FALSE(batch_update_hints_fetcher());
 }
 
 TEST_F(OptimizationGuideHintsManagerFetchingTest,
-       HintsFetcherEnabledWithHostsNoHintsInResponse) {
+       HintsFetcherEnabledNoHostsButHasUrlsToFetch) {
   base::CommandLine::ForCurrentProcess()->AppendSwitch(
       optimization_guide::switches::kDisableCheckingUserPermissionsForTesting);
   std::unique_ptr<FakeTopHostProvider> top_host_provider =
-      std::make_unique<FakeTopHostProvider>(
-          std::vector<std::string>({"example1.com", "example2.com"}));
+      std::make_unique<FakeTopHostProvider>(std::vector<std::string>({}));
   CreateHintsManager(top_host_provider.get());
+
   hints_manager()->RegisterOptimizationTypes(
       {optimization_guide::proto::DEFER_ALL_SCRIPT});
   hints_manager()->SetHintsFetcherFactoryForTesting(
       BuildTestHintsFetcherFactory(
-          {HintsFetcherEndState::kFetchSuccessWithNoHints}));
+          {HintsFetcherEndState::kFetchSuccessWithHostHints}));
   InitializeWithDefaultConfig("1.0.0");
 
-  // Force timer to expire and schedule a hints fetch.
-  MoveClockForwardBy(base::TimeDelta::FromSeconds(kTestFetchRetryDelaySecs));
+  tab_url_provider()->SetUrls({GURL("https://a.com"), GURL("https://b.com")});
+
+  // Force timer to expire after random delay and schedule a hints fetch that
+  // succeeds.
+  MoveClockForwardBy(base::TimeDelta::FromSeconds(60 * 2));
   EXPECT_EQ(1, top_host_provider->get_num_top_hosts_called());
+  EXPECT_EQ(1, tab_url_provider()->get_num_urls_called());
   EXPECT_EQ(1, batch_update_hints_fetcher()->num_fetches_requested());
 
-  // Check that hints should not be fetched again after the delay for a hints
-  // fetch attempt with no hints.
-  MoveClockForwardBy(base::TimeDelta::FromSeconds(kTestFetchRetryDelaySecs));
-  // This should be called exactly once, confirming that hints are not fetched
-  // again after |kTestFetchRetryDelaySecs|.
-  EXPECT_EQ(1, top_host_provider->get_num_top_hosts_called());
+  // Move it forward again to make sure timer is scheduled.
+  MoveClockForwardBy(base::TimeDelta::FromSeconds(kUpdateFetchHintsTimeSecs));
+  EXPECT_EQ(2, top_host_provider->get_num_top_hosts_called());
+  EXPECT_EQ(2, tab_url_provider()->get_num_urls_called());
+  // Urls didn't change and we have all URLs cached in store.
   EXPECT_EQ(1, batch_update_hints_fetcher()->num_fetches_requested());
 }
 
-TEST_F(OptimizationGuideHintsManagerFetchingTest, HintsFetcherTimerRetryDelay) {
+TEST_F(OptimizationGuideHintsManagerFetchingTest, HintsFetcherTimerFetch) {
   base::CommandLine::ForCurrentProcess()->AppendSwitch(
       optimization_guide::switches::kDisableCheckingUserPermissionsForTesting);
   std::unique_ptr<FakeTopHostProvider> top_host_provider =
@@ -2103,46 +2121,16 @@
   hints_manager()->RegisterOptimizationTypes(
       {optimization_guide::proto::DEFER_ALL_SCRIPT});
   hints_manager()->SetHintsFetcherFactoryForTesting(
-      BuildTestHintsFetcherFactory({HintsFetcherEndState::kFetchFailed}));
-  InitializeWithDefaultConfig("1.0.0");
-
-  // Force timer to expire and schedule a hints fetch - first time.
-  MoveClockForwardBy(base::TimeDelta::FromSeconds(kTestFetchRetryDelaySecs));
-  EXPECT_EQ(1, top_host_provider->get_num_top_hosts_called());
-  EXPECT_EQ(1, batch_update_hints_fetcher()->num_fetches_requested());
-
-  // Force speculative timer to expire after fetch fails.
-  MoveClockForwardBy(base::TimeDelta::FromSeconds(kTestFetchRetryDelaySecs));
-  EXPECT_EQ(1, top_host_provider->get_num_top_hosts_called());
-  EXPECT_EQ(1, batch_update_hints_fetcher()->num_fetches_requested());
-}
-
-TEST_F(OptimizationGuideHintsManagerFetchingTest,
-       HintsFetcherTimerFetchSucceeds) {
-  base::CommandLine::ForCurrentProcess()->AppendSwitch(
-      optimization_guide::switches::kDisableCheckingUserPermissionsForTesting);
-  std::unique_ptr<FakeTopHostProvider> top_host_provider =
-      std::make_unique<FakeTopHostProvider>(
-          std::vector<std::string>({"example1.com", "example2.com"}));
-
-  // Force hints fetch scheduling.
-  CreateHintsManager(top_host_provider.get());
-  hints_manager()->RegisterOptimizationTypes(
-      {optimization_guide::proto::DEFER_ALL_SCRIPT});
-  hints_manager()->SetHintsFetcherFactoryForTesting(
       BuildTestHintsFetcherFactory(
           {HintsFetcherEndState::kFetchSuccessWithHostHints}));
   InitializeWithDefaultConfig("1.0.0");
 
-  // Force timer to expire and schedule a hints fetch that succeeds.
-  MoveClockForwardBy(base::TimeDelta::FromSeconds(kTestFetchRetryDelaySecs));
+  // Force timer to expire after random delay and schedule a hints fetch that
+  // succeeds.
+  MoveClockForwardBy(base::TimeDelta::FromSeconds(60 * 2));
   EXPECT_EQ(1, batch_update_hints_fetcher()->num_fetches_requested());
 
-  // TODO(mcrouse): Make sure timer is triggered by metadata entry,
-  // |hint_cache| control needed.
-  MoveClockForwardBy(base::TimeDelta::FromSeconds(kTestFetchRetryDelaySecs));
-  EXPECT_EQ(1, batch_update_hints_fetcher()->num_fetches_requested());
-
+  // Move it forward again to make sure timer is scheduled.
   MoveClockForwardBy(base::TimeDelta::FromSeconds(kUpdateFetchHintsTimeSecs));
   EXPECT_EQ(2, batch_update_hints_fetcher()->num_fetches_requested());
 }
@@ -3690,39 +3678,3 @@
   EXPECT_EQ(optimization_guide::OptimizationTypeDecision::kNoHintAvailable,
             optimization_type_decision);
 }
-
-class OptimizationGuideHintsManagerFetchingNoBatchUpdateTest
-    : public OptimizationGuideHintsManagerTest {
- public:
-  OptimizationGuideHintsManagerFetchingNoBatchUpdateTest() {
-    scoped_list_.InitAndEnableFeatureWithParameters(
-        optimization_guide::features::kRemoteOptimizationGuideFetching,
-        {{"batch_update_hints_for_top_hosts", "false"}});
-  }
-
- private:
-  base::test::ScopedFeatureList scoped_list_;
-};
-
-TEST_F(OptimizationGuideHintsManagerFetchingNoBatchUpdateTest,
-       BatchUpdateHintsFetchNotScheduledIfNotAllowed) {
-  base::CommandLine::ForCurrentProcess()->AppendSwitch(
-      optimization_guide::switches::kDisableCheckingUserPermissionsForTesting);
-  std::unique_ptr<FakeTopHostProvider> top_host_provider =
-      std::make_unique<FakeTopHostProvider>(
-          std::vector<std::string>({"example1.com", "example2.com"}));
-
-  // Force hints fetch scheduling.
-  CreateHintsManager(top_host_provider.get());
-  hints_manager()->RegisterOptimizationTypes(
-      {optimization_guide::proto::DEFER_ALL_SCRIPT});
-  hints_manager()->SetHintsFetcherFactoryForTesting(
-      BuildTestHintsFetcherFactory(
-          {HintsFetcherEndState::kFetchSuccessWithHostHints}));
-  InitializeWithDefaultConfig("1.0.0");
-
-  // Force timer to expire and schedule a hints fetch.
-  MoveClockForwardBy(base::TimeDelta::FromSeconds(kTestFetchRetryDelaySecs));
-  // Hints fetcher should not even be created.
-  EXPECT_FALSE(batch_update_hints_fetcher());
-}
diff --git a/chrome/browser/optimization_guide/optimization_guide_keyed_service.cc b/chrome/browser/optimization_guide/optimization_guide_keyed_service.cc
index ee74ba8..ba06325 100644
--- a/chrome/browser/optimization_guide/optimization_guide_keyed_service.cc
+++ b/chrome/browser/optimization_guide/optimization_guide_keyed_service.cc
@@ -12,6 +12,7 @@
 #include "base/optional.h"
 #include "base/sequenced_task_runner.h"
 #include "base/task/task_traits.h"
+#include "build/build_config.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/metrics/chrome_metrics_service_accessor.h"
 #include "chrome/browser/optimization_guide/optimization_guide_hints_manager.h"
@@ -27,6 +28,7 @@
 #include "components/optimization_guide/core/optimization_guide_features.h"
 #include "components/optimization_guide/core/optimization_guide_store.h"
 #include "components/optimization_guide/core/optimization_guide_util.h"
+#include "components/optimization_guide/core/tab_url_provider.h"
 #include "components/optimization_guide/core/top_host_provider.h"
 #include "components/optimization_guide/proto/models.pb.h"
 #include "content/public/browser/browser_context.h"
@@ -35,6 +37,12 @@
 #include "content/public/browser/storage_partition.h"
 #include "url/gurl.h"
 
+#if defined(OS_ANDROID)
+#include "chrome/browser/optimization_guide/android/optimization_guide_tab_url_provider_android.h"
+#else
+#include "chrome/browser/optimization_guide/optimization_guide_tab_url_provider.h"
+#endif
+
 namespace {
 
 // Returns the top host provider to be used with this keyed service. Can return
@@ -147,6 +155,15 @@
         "SyntheticOptimizationGuideRemoteFetching",
         optimization_guide_fetching_enabled ? "Enabled" : "Disabled");
 
+#if defined(OS_ANDROID)
+    tab_url_provider_ = std::make_unique<
+        optimization_guide::android::OptimizationGuideTabUrlProviderAndroid>(
+        profile);
+#else
+    tab_url_provider_ =
+        std::make_unique<OptimizationGuideTabUrlProvider>(profile);
+#endif
+
     hint_store_ =
         optimization_guide::features::ShouldPersistHintsToDisk()
             ? std::make_unique<optimization_guide::OptimizationGuideStore>(
@@ -172,7 +189,7 @@
 
   hints_manager_ = std::make_unique<OptimizationGuideHintsManager>(
       profile, profile->GetPrefs(), hint_store, top_host_provider_.get(),
-      url_loader_factory);
+      tab_url_provider_.get(), url_loader_factory);
   prediction_manager_ = std::make_unique<optimization_guide::PredictionManager>(
       prediction_model_and_features_store, top_host_provider_.get(),
       url_loader_factory, profile->GetPrefs(), profile);
diff --git a/chrome/browser/optimization_guide/optimization_guide_keyed_service.h b/chrome/browser/optimization_guide/optimization_guide_keyed_service.h
index 9fb5ad0c..1f10045 100644
--- a/chrome/browser/optimization_guide/optimization_guide_keyed_service.h
+++ b/chrome/browser/optimization_guide/optimization_guide_keyed_service.h
@@ -33,6 +33,7 @@
 class PredictionManager;
 class PredictionManagerBrowserTestBase;
 class PredictionModelDownloadClient;
+class TabUrlProvider;
 class TopHostProvider;
 }  // namespace optimization_guide
 
@@ -169,6 +170,10 @@
   // behavior.
   std::unique_ptr<optimization_guide::TopHostProvider> top_host_provider_;
 
+  // The tab URL provider to use for fetching information for the user's active
+  // tabs. Will be null if the user is off the record.
+  std::unique_ptr<optimization_guide::TabUrlProvider> tab_url_provider_;
+
   DISALLOW_COPY_AND_ASSIGN(OptimizationGuideKeyedService);
 };
 
diff --git a/chrome/browser/optimization_guide/optimization_guide_tab_url_provider.cc b/chrome/browser/optimization_guide/optimization_guide_tab_url_provider.cc
new file mode 100644
index 0000000..9bf5d46
--- /dev/null
+++ b/chrome/browser/optimization_guide/optimization_guide_tab_url_provider.cc
@@ -0,0 +1,85 @@
+// Copyright 2021 The Chromium 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/optimization_guide/optimization_guide_tab_url_provider.h"
+
+#include "build/build_config.h"
+#include "chrome/browser/profiles/profile.h"
+#include "content/public/browser/web_contents.h"
+#include "url/gurl.h"
+
+#if !defined(OS_ANDROID)
+#include "chrome/browser/ui/browser.h"
+#include "chrome/browser/ui/browser_list.h"
+#include "chrome/browser/ui/tabs/tab_strip_model.h"
+#endif
+
+namespace {
+
+// Returns whether the web contents has been shown in the last 90 days.
+bool IsWebContentsCandidateForRefresh(
+    content::WebContents* web_contents,
+    const base::TimeDelta& duration_since_last_shown) {
+  return web_contents &&
+         (base::TimeTicks::Now() - web_contents->GetLastActiveTime()) <
+             duration_since_last_shown;
+}
+
+}  // namespace
+
+OptimizationGuideTabUrlProvider::OptimizationGuideTabUrlProvider(
+    Profile* profile)
+    : profile_(profile) {}
+OptimizationGuideTabUrlProvider::~OptimizationGuideTabUrlProvider() = default;
+
+const std::vector<GURL> OptimizationGuideTabUrlProvider::GetUrlsOfActiveTabs(
+    const base::TimeDelta& duration_since_last_shown) {
+  const std::vector<content::WebContents*> web_contents_list =
+      GetAllWebContentsForProfile(profile_);
+  std::vector<std::pair<GURL, base::TimeTicks>> urls_and_active_time;
+  urls_and_active_time.reserve(web_contents_list.size());
+  for (content::WebContents* web_contents : web_contents_list) {
+    if (IsWebContentsCandidateForRefresh(web_contents,
+                                         duration_since_last_shown)) {
+      urls_and_active_time.emplace_back(
+          std::make_pair(web_contents->GetLastCommittedURL(),
+                         web_contents->GetLastActiveTime()));
+    }
+  }
+  std::sort(urls_and_active_time.begin(), urls_and_active_time.end(),
+            [](const std::pair<GURL, base::TimeTicks>& a,
+               const std::pair<GURL, base::TimeTicks>& b) {
+              return a.second > b.second;
+            });
+
+  std::vector<GURL> urls;
+  urls.reserve(urls_and_active_time.size());
+  for (const auto& url_and_active_time : urls_and_active_time) {
+    urls.emplace_back(url_and_active_time.first);
+  }
+  return urls;
+}
+
+const std::vector<content::WebContents*>
+OptimizationGuideTabUrlProvider::GetAllWebContentsForProfile(Profile* profile) {
+#if defined(OS_ANDROID)
+  NOTREACHED();
+  return {};
+#else
+  std::vector<content::WebContents*> web_contents_list;
+  for (Browser* browser : *BrowserList::GetInstance()) {
+    if (!browser || browser->profile() != profile)
+      continue;
+
+    TabStripModel* tab_strip_model = browser->tab_strip_model();
+    int tab_count = tab_strip_model->count();
+    for (int i = 0; i < tab_count; i++) {
+      content::WebContents* web_contents = tab_strip_model->GetWebContentsAt(i);
+      if (web_contents)
+        web_contents_list.push_back(web_contents);
+    }
+  }
+  return web_contents_list;
+#endif
+}
diff --git a/chrome/browser/optimization_guide/optimization_guide_tab_url_provider.h b/chrome/browser/optimization_guide/optimization_guide_tab_url_provider.h
new file mode 100644
index 0000000..4fe322b
--- /dev/null
+++ b/chrome/browser/optimization_guide/optimization_guide_tab_url_provider.h
@@ -0,0 +1,41 @@
+// Copyright 2021 The Chromium 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_OPTIMIZATION_GUIDE_OPTIMIZATION_GUIDE_TAB_URL_PROVIDER_H_
+#define CHROME_BROWSER_OPTIMIZATION_GUIDE_OPTIMIZATION_GUIDE_TAB_URL_PROVIDER_H_
+
+#include "components/optimization_guide/core/tab_url_provider.h"
+
+class Profile;
+
+namespace content {
+class WebContents;
+}  // namespace content
+
+// An implementation of optimization_guide::TabUrlProvider that provides tab
+// URLs that are currently shown for the profile.
+class OptimizationGuideTabUrlProvider
+    : public optimization_guide::TabUrlProvider {
+ public:
+  explicit OptimizationGuideTabUrlProvider(Profile* profile);
+  ~OptimizationGuideTabUrlProvider() override;
+
+  OptimizationGuideTabUrlProvider(
+      const OptimizationGuideTabUrlProvider& other) = delete;
+  OptimizationGuideTabUrlProvider& operator=(
+      const OptimizationGuideTabUrlProvider&) = delete;
+
+  // optimization_guide::TabUrlProvider:
+  const std::vector<GURL> GetUrlsOfActiveTabs(
+      const base::TimeDelta& duration_since_last_shown) override;
+
+ private:
+  // Returns all web contents shown across all browser windows for |profile|.
+  virtual const std::vector<content::WebContents*> GetAllWebContentsForProfile(
+      Profile* profile);
+
+  Profile* profile_;
+};
+
+#endif  // CHROME_BROWSER_OPTIMIZATION_GUIDE_OPTIMIZATION_GUIDE_TAB_URL_PROVIDER_H_
diff --git a/chrome/browser/optimization_guide/optimization_guide_tab_url_provider_unittest.cc b/chrome/browser/optimization_guide/optimization_guide_tab_url_provider_unittest.cc
new file mode 100644
index 0000000..dff940f
--- /dev/null
+++ b/chrome/browser/optimization_guide/optimization_guide_tab_url_provider_unittest.cc
@@ -0,0 +1,62 @@
+// Copyright 2021 The Chromium 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/optimization_guide/optimization_guide_tab_url_provider.h"
+
+#include "chrome/test/base/browser_with_test_window_test.h"
+#include "testing/gmock/include/gmock/gmock.h"
+
+using testing::ElementsAre;
+
+class OptimizationGuideTabUrlProviderTest : public BrowserWithTestWindowTest {
+ public:
+  OptimizationGuideTabUrlProviderTest() = default;
+  ~OptimizationGuideTabUrlProviderTest() override = default;
+
+  void SetUp() override {
+    BrowserWithTestWindowTest::SetUp();
+    otr_browser_window_ = CreateBrowserWindow();
+    otr_browser_ =
+        CreateBrowser(profile()->GetPrimaryOTRProfile(), browser()->type(),
+                      /*hosted_app=*/false, otr_browser_window_.get());
+    tab_url_provider_ =
+        std::make_unique<OptimizationGuideTabUrlProvider>(profile());
+  }
+
+  void TearDown() override {
+    // Also destroy |otr_browser_| before the profile. browser()'s destruction
+    // is handled in BrowserWithTestWindowTest::TearDown().
+    otr_browser_->tab_strip_model()->CloseAllTabs();
+    otr_browser_.reset();
+    BrowserWithTestWindowTest::TearDown();
+  }
+
+  Browser* otr_browser() const { return otr_browser_.get(); }
+
+  OptimizationGuideTabUrlProvider* tab_url_provider() const {
+    return tab_url_provider_.get();
+  }
+
+ private:
+  std::unique_ptr<BrowserWindow> otr_browser_window_;
+  std::unique_ptr<Browser> otr_browser_;
+  std::unique_ptr<OptimizationGuideTabUrlProvider> tab_url_provider_;
+};
+
+TEST_F(OptimizationGuideTabUrlProviderTest, GetUrlsNoOpenTabs) {
+  std::vector<GURL> urls =
+      tab_url_provider()->GetUrlsOfActiveTabs(base::TimeDelta::FromDays(90));
+  EXPECT_TRUE(urls.empty());
+}
+
+TEST_F(OptimizationGuideTabUrlProviderTest, GetUrlsFiltersOutIncognitoTabs) {
+  AddTab(otr_browser(), GURL("https://otrshouldskip.com"));
+  AddTab(browser(), GURL("https://example.com"));
+  AddTab(browser(), GURL("https://example2.com"));
+
+  std::vector<GURL> urls =
+      tab_url_provider()->GetUrlsOfActiveTabs(base::TimeDelta::FromDays(90));
+  EXPECT_THAT(urls, ElementsAre(GURL("https://example2.com"),
+                                GURL("https://example.com")));
+}
diff --git a/chrome/browser/optimization_guide/page_text_observer_browsertest.cc b/chrome/browser/optimization_guide/page_text_observer_browsertest.cc
index d534efbf..3a55928 100644
--- a/chrome/browser/optimization_guide/page_text_observer_browsertest.cc
+++ b/chrome/browser/optimization_guide/page_text_observer_browsertest.cc
@@ -9,6 +9,8 @@
 #include "base/bind.h"
 #include "base/callback.h"
 #include "base/optional.h"
+#include "base/strings/strcat.h"
+#include "base/strings/string_util.h"
 #include "base/strings/utf_string_conversions.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/browser.h"
@@ -25,9 +27,20 @@
 #include "net/test/embedded_test_server/embedded_test_server.h"
 #include "net/test/embedded_test_server/http_request.h"
 #include "net/test/embedded_test_server/http_response.h"
+#include "testing/gmock/include/gmock/gmock.h"
 
 namespace optimization_guide {
 
+namespace {
+
+FrameTextDumpResult MakeFrameDump(mojom::TextDumpEvent event,
+                                  content::GlobalFrameRoutingId rfh_id,
+                                  bool amp_frame,
+                                  const std::u16string& contents) {
+  return FrameTextDumpResult::Initialize(event, rfh_id, amp_frame)
+      .CompleteWithContents(contents);
+}
+
 class TestConsumer : public PageTextObserver::Consumer {
  public:
   TestConsumer() = default;
@@ -36,16 +49,18 @@
   void Reset() { was_called_ = false; }
 
   void PopulateRequest(uint32_t max_size,
-                       const std::set<mojom::TextDumpEvent>& events) {
+                       const std::set<mojom::TextDumpEvent>& events,
+                       bool request_amp = false) {
     request_ = std::make_unique<PageTextObserver::ConsumerTextDumpRequest>();
     request_->max_size = max_size;
     request_->events = events;
-    request_->callback = base::BindRepeating(&TestConsumer::OnGotTextDump,
-                                             base::Unretained(this));
+    request_->callback =
+        base::BindOnce(&TestConsumer::OnGotTextDump, base::Unretained(this));
+    request_->dump_amp_subframes = request_amp;
   }
 
   void WaitForPageText() {
-    if (text_) {
+    if (result_) {
       return;
     }
 
@@ -56,7 +71,9 @@
 
   bool was_called() const { return was_called_; }
 
-  const base::Optional<std::u16string>& text() const { return text_; }
+  base::Optional<PageTextDumpResult> result() {
+    return base::OptionalFromPtr(result_.get());
+  }
 
   // PageTextObserver::Consumer:
   std::unique_ptr<PageTextObserver::ConsumerTextDumpRequest>
@@ -66,22 +83,22 @@
   }
 
  private:
-  void OnGotTextDump(const std::u16string& text) {
-    text_ = text;
+  void OnGotTextDump(const PageTextDumpResult& result) {
+    result_ = std::make_unique<PageTextDumpResult>(result);
     if (on_page_text_closure_) {
       std::move(on_page_text_closure_).Run();
     }
   }
 
   bool was_called_ = false;
-
   std::unique_ptr<PageTextObserver::ConsumerTextDumpRequest> request_;
 
   base::OnceClosure on_page_text_closure_;
-
-  base::Optional<std::u16string> text_;
+  std::unique_ptr<PageTextDumpResult> result_;
 };
 
+}  // namespace
+
 // This tests code in
 // //components/optimization_guide/content/browser/page_text_observer.h, but
 // this test is in //chrome because the components browsertests do not fully
@@ -133,6 +150,21 @@
       return resp;
     }
 
+    // This script is onLoad-blocking in the HTML, but is intentionally slow.
+    // This provides important time between first layout and finish load for
+    // tests that need it.
+    if (request.GetURL().path() == "/slow-add-world-text.js") {
+      std::unique_ptr<net::test_server::DelayedHttpResponse> resp =
+          std::make_unique<net::test_server::DelayedHttpResponse>(
+              base::TimeDelta::FromMilliseconds(500));
+      resp->set_code(net::HTTP_OK);
+      resp->set_content_type("application/javascript");
+      resp->set_content(
+          "var p = document.createElement('p'); p.innerHTML = 'world'; "
+          "document.body.appendChild(p); ");
+      return resp;
+    }
+
     if (request.GetURL().path() == "/dynamic.html") {
       std::unique_ptr<net::test_server::BasicHttpResponse> resp =
           std::make_unique<net::test_server::BasicHttpResponse>();
@@ -146,7 +178,7 @@
   }
 };
 
-IN_PROC_BROWSER_TEST_F(PageTextObserverBrowserTest, SimpleCase) {
+IN_PROC_BROWSER_TEST_F(PageTextObserverBrowserTest, SimpleCaseNoSubframes) {
   PageTextObserver::CreateForWebContents(web_contents());
   ASSERT_TRUE(observer());
 
@@ -160,8 +192,14 @@
   ASSERT_TRUE(consumer.was_called());
 
   consumer.WaitForPageText();
-  ASSERT_TRUE(consumer.text());
-  EXPECT_EQ(base::ASCIIToUTF16("hello"), *consumer.text());
+  ASSERT_TRUE(consumer.result());
+  EXPECT_THAT(consumer.result()->frame_results(),
+              ::testing::UnorderedElementsAreArray({
+                  MakeFrameDump(
+                      mojom::TextDumpEvent::kFirstLayout,
+                      web_contents()->GetMainFrame()->GetGlobalFrameRoutingId(),
+                      /*amp_frame=*/false, base::ASCIIToUTF16("hello")),
+              }));
 }
 
 IN_PROC_BROWSER_TEST_F(PageTextObserverBrowserTest, FirstLayoutAndOnLoad) {
@@ -189,14 +227,99 @@
   first_layout_consumer.WaitForPageText();
   on_load_consumer.WaitForPageText();
 
-  ASSERT_TRUE(first_layout_consumer.text());
-  // This check can be a bit flaky on some platforms. Check that "hello" is
-  // present, since the text captured may already be "hello world".
-  EXPECT_TRUE(base::Contains(*first_layout_consumer.text(),
-                             base::ASCIIToUTF16("hello")));
+  ASSERT_TRUE(first_layout_consumer.result());
+  EXPECT_THAT(
+      first_layout_consumer.result()->frame_results(),
+      ::testing::UnorderedElementsAreArray({
+          MakeFrameDump(
+              mojom::TextDumpEvent::kFirstLayout,
+              web_contents()->GetMainFrame()->GetGlobalFrameRoutingId(),
+              /*amp_frame=*/false, base::ASCIIToUTF16("hello")),
+          MakeFrameDump(
+              mojom::TextDumpEvent::kFinishedLoad,
+              web_contents()->GetMainFrame()->GetGlobalFrameRoutingId(),
+              /*amp_frame=*/false, base::ASCIIToUTF16("hello\n\nworld")),
+      }));
 
-  ASSERT_TRUE(on_load_consumer.text());
-  EXPECT_EQ(base::ASCIIToUTF16("hello\n\nworld"), *on_load_consumer.text());
+  EXPECT_EQ(first_layout_consumer.result(), on_load_consumer.result());
+}
+
+IN_PROC_BROWSER_TEST_F(PageTextObserverBrowserTest, OOPIFAMPSubframe) {
+  PageTextObserver::CreateForWebContents(web_contents());
+  ASSERT_TRUE(observer());
+
+  TestConsumer consumer;
+  observer()->AddConsumer(&consumer);
+  consumer.PopulateRequest(
+      /*max_size=*/1024,
+      /*events=*/{mojom::TextDumpEvent::kFirstLayout},
+      /*request_amp=*/true);
+
+  GURL url(embedded_test_server()->GetURL("a.com", "/dynamic.html"));
+  dynamic_response_body_ = base::StringPrintf(
+      "<html><body>"
+      "<script type=\"text/javascript\" src=\"/slow-first-layout.js\"></script>"
+      "<p>mainframe</p>"
+      "<iframe name=\"amp\" src=\"%s\"></iframe>"
+      "</body></html>",
+      embedded_test_server()->GetURL("b.com", "/amp.html").spec().c_str());
+  ui_test_utils::NavigateToURL(browser(), url);
+  ASSERT_TRUE(consumer.was_called());
+
+  consumer.WaitForPageText();
+
+  content::GlobalFrameRoutingId amp_frame_id;
+  for (auto* rfh : web_contents()->GetMainFrame()->GetFramesInSubtree()) {
+    if (rfh->GetFrameName() == "amp") {
+      amp_frame_id = rfh->GetGlobalFrameRoutingId();
+      break;
+    }
+  }
+
+  ASSERT_TRUE(consumer.result());
+  EXPECT_THAT(
+      consumer.result()->frame_results(),
+      ::testing::UnorderedElementsAreArray({
+          MakeFrameDump(
+              mojom::TextDumpEvent::kFirstLayout,
+              web_contents()->GetMainFrame()->GetGlobalFrameRoutingId(),
+              /*amp_frame=*/false, base::ASCIIToUTF16("mainframe")),
+          MakeFrameDump(mojom::TextDumpEvent::kFinishedLoad, amp_frame_id,
+                        /*amp_frame=*/true, base::ASCIIToUTF16("AMP")),
+      }));
+}
+
+IN_PROC_BROWSER_TEST_F(PageTextObserverBrowserTest, OOPIFNotAmpSubframe) {
+  PageTextObserver::CreateForWebContents(web_contents());
+  ASSERT_TRUE(observer());
+
+  TestConsumer consumer;
+  observer()->AddConsumer(&consumer);
+  consumer.PopulateRequest(
+      /*max_size=*/1024,
+      /*events=*/{mojom::TextDumpEvent::kFirstLayout},
+      /*request_amp=*/true);
+
+  GURL url(embedded_test_server()->GetURL("a.com", "/dynamic.html"));
+  dynamic_response_body_ = base::StringPrintf(
+      "<html><body>"
+      "<script type=\"text/javascript\" src=\"/slow-first-layout.js\"></script>"
+      "<p>mainframe</p>"
+      "<iframe src=\"%s\"></iframe>"
+      "</body></html>",
+      embedded_test_server()->GetURL("b.com", "/hello.html").spec().c_str());
+  ui_test_utils::NavigateToURL(browser(), url);
+  ASSERT_TRUE(consumer.was_called());
+
+  consumer.WaitForPageText();
+  ASSERT_TRUE(consumer.result());
+  EXPECT_THAT(consumer.result()->frame_results(),
+              ::testing::UnorderedElementsAreArray({
+                  MakeFrameDump(
+                      mojom::TextDumpEvent::kFirstLayout,
+                      web_contents()->GetMainFrame()->GetGlobalFrameRoutingId(),
+                      /*amp_frame=*/false, base::ASCIIToUTF16("mainframe")),
+              }));
 }
 
 class PageTextObserverSingleProcessBrowserTest
@@ -224,17 +347,58 @@
   GURL url(embedded_test_server()->GetURL("a.com", "/dynamic.html"));
   dynamic_response_body_ = base::StringPrintf(
       "<html><body>"
-      "<p>foo</p>"
+      "<script type=\"text/javascript\" src=\"/slow-first-layout.js\"></script>"
+      "<p>mainframe</p>"
       "<iframe src=\"%s\"></iframe>"
       "</body></html>",
       embedded_test_server()->GetURL("a.com", "/hello.html").spec().c_str());
-
   ui_test_utils::NavigateToURL(browser(), url);
   ASSERT_TRUE(consumer.was_called());
 
   consumer.WaitForPageText();
-  ASSERT_TRUE(consumer.text());
-  EXPECT_EQ(base::ASCIIToUTF16("foo\n\nhello"), *consumer.text());
+  ASSERT_TRUE(consumer.result());
+  EXPECT_THAT(
+      consumer.result()->frame_results(),
+      ::testing::UnorderedElementsAreArray({
+          MakeFrameDump(
+              mojom::TextDumpEvent::kFinishedLoad,
+              web_contents()->GetMainFrame()->GetGlobalFrameRoutingId(),
+              /*amp_frame=*/false, base::ASCIIToUTF16("mainframe\n\nhello")),
+      }));
+}
+
+IN_PROC_BROWSER_TEST_F(PageTextObserverSingleProcessBrowserTest,
+                       SameProcessAMPSubframe) {
+  PageTextObserver::CreateForWebContents(web_contents());
+  ASSERT_TRUE(observer());
+
+  TestConsumer consumer;
+  observer()->AddConsumer(&consumer);
+  consumer.PopulateRequest(
+      /*max_size=*/1024,
+      /*events=*/{mojom::TextDumpEvent::kFirstLayout},
+      /*request_amp=*/true);
+
+  GURL url(embedded_test_server()->GetURL("a.com", "/dynamic.html"));
+  dynamic_response_body_ = base::StringPrintf(
+      "<html><body>"
+      "<script type=\"text/javascript\" src=\"/slow-first-layout.js\"></script>"
+      "<p>mainframe</p>"
+      "<iframe src=\"%s\"></iframe>"
+      "</body></html>",
+      embedded_test_server()->GetURL("a.com", "/amp.html").spec().c_str());
+  ui_test_utils::NavigateToURL(browser(), url);
+  ASSERT_TRUE(consumer.was_called());
+
+  consumer.WaitForPageText();
+  ASSERT_TRUE(consumer.result());
+  EXPECT_THAT(consumer.result()->frame_results(),
+              ::testing::UnorderedElementsAreArray({
+                  MakeFrameDump(
+                      mojom::TextDumpEvent::kFirstLayout,
+                      web_contents()->GetMainFrame()->GetGlobalFrameRoutingId(),
+                      /*amp_frame=*/false, base::ASCIIToUTF16("mainframe")),
+              }));
 }
 
 }  // namespace optimization_guide
diff --git a/chrome/browser/policy/extension_policy_browsertest.cc b/chrome/browser/policy/extension_policy_browsertest.cc
index f84930c..cfee4aa9 100644
--- a/chrome/browser/policy/extension_policy_browsertest.cc
+++ b/chrome/browser/policy/extension_policy_browsertest.cc
@@ -5,10 +5,12 @@
 #include <memory>
 
 #include "base/files/file_util.h"
+#include "base/files/scoped_temp_dir.h"
 #include "base/run_loop.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/test/bind.h"
 #include "base/test/metrics/histogram_tester.h"
+#include "base/threading/thread_restrictions.h"
 #include "build/build_config.h"
 #include "build/chromeos_buildflags.h"
 #include "chrome/browser/background/background_contents_service.h"
@@ -17,6 +19,7 @@
 #include "chrome/browser/extensions/chrome_extension_test_notification_observer.h"
 #include "chrome/browser/extensions/component_loader.h"
 #include "chrome/browser/extensions/crx_installer.h"
+#include "chrome/browser/extensions/extension_browsertest.h"
 #include "chrome/browser/extensions/extension_management_constants.h"
 #include "chrome/browser/extensions/extension_management_test_util.h"
 #include "chrome/browser/extensions/extension_service.h"
@@ -40,6 +43,7 @@
 #include "chrome/common/extensions/extension_test_util.h"
 #include "chrome/common/extensions/manifest_handlers/app_launch_info.h"
 #include "chrome/test/base/ui_test_utils.h"
+#include "components/policy/core/browser/browser_policy_connector.h"
 #include "components/policy/policy_constants.h"
 #include "components/version_info/channel.h"
 #include "components/webapps/browser/installable/installable_metrics.h"
@@ -108,6 +112,9 @@
 const char kGoodCrxId[] = "ldnnhddmnhbkjipkidpdiheffobcpfmf";
 const char kSimpleWithIconCrxId[] = "dehdlahnlebladnfleagmjdapdjdcnlp";
 const char kHostedAppCrxId[] = "kbmnembihfiondgfjekmnmcbddelicoi";
+// Different versions of this extension Id at
+// {DIR_TEST_DATA}/extensions/pinning/ are used in extension pinning tests.
+const char kPinnedExtensionCrxId[] = "fdlpamochgodkfemfnickdlkabcfmbln";
 
 const char kGoodCrxVersion[] = "1.0.0.1";
 
@@ -921,6 +928,9 @@
 
   MOCK_METHOD1(ExtensionStageChanged,
                void(extensions::InstallStageTracker::Stage));
+  MOCK_METHOD2(OnExtensionInstallationFailed,
+               void(const extensions::ExtensionId&,
+                    extensions::InstallStageTracker::FailureReason));
 
   void OnExtensionDataChangedForTesting(
       const extensions::ExtensionId& id,
@@ -1347,6 +1357,410 @@
   EXPECT_EQ(installed_version.CompareTo(base::Version("1.0.0.1")), 0);
 }
 
+class ExtensionPinningTest : public extensions::ExtensionBrowserTest {
+ public:
+  ExtensionPinningTest() = default;
+  ~ExtensionPinningTest() override = default;
+
+ protected:
+  // Sets the ExtensionSettings policy so that extension |id| will be
+  // force-installed from update URL pointing to test server's file
+  // |update_url_suffix|. It also sets |override_update_url| flag as true for
+  // the |id|.
+  void SetExtensionSettingsPolicy(const std::string& update_url_suffix,
+                                  const std::string& id) {
+#if defined(OS_WIN)
+    // Unless enterprise managed, policy handler only allows extensions from the
+    // Chrome Webstore to be force installed. Mark enterprise managed for
+    // windows.
+    base::win::ScopedDomainStateForTesting scoped_domain(true);
+#endif
+
+    ASSERT_TRUE(embedded_test_server()->Started());
+    GURL update_url = embedded_test_server()->GetURL(update_url_suffix);
+
+    PolicyMap policies;
+    base::Value dict(base::Value::Type::DICTIONARY),
+        key_dict(base::Value::Type::DICTIONARY);
+    key_dict.SetStringKey(extensions::schema_constants::kInstallationMode,
+                          extensions::schema_constants::kForceInstalled);
+    key_dict.SetStringKey(extensions::schema_constants::kUpdateUrl,
+                          update_url.spec());
+    key_dict.SetBoolKey(extensions::schema_constants::kOverrideUpdateUrl, true);
+    dict.SetKey(id, std::move(key_dict));
+    policies.Set(key::kExtensionSettings, POLICY_LEVEL_MANDATORY,
+                 POLICY_SCOPE_USER, POLICY_SOURCE_CLOUD, std::move(dict),
+                 nullptr);
+    provider_.UpdateChromePolicy(policies);
+  }
+
+  // Triggers extension update process and waits for either extension
+  // installation success if |expected_install_success| is true or else
+  // extension installation failure.
+  const extensions::Extension* TriggerExtensionUpdate(
+      const std::string& id,
+      bool expected_install_success) {
+    extensions::ExtensionRegistry* registry = extension_registry();
+    if (registry->GetExtensionById(
+            id, extensions::ExtensionRegistry::EVERYTHING) == nullptr) {
+      return nullptr;
+    }
+
+    extensions::ExtensionService* service = extension_service();
+    extensions::ExtensionUpdater* updater = service->updater();
+    extensions::ExtensionUpdater::CheckParams params;
+    params.install_immediately = true;
+
+    if (expected_install_success) {
+      extensions::TestExtensionRegistryObserver update_observer(
+          extension_registry());
+      updater->CheckNow(std::move(params));
+      update_observer.WaitForExtensionWillBeInstalled();
+    } else {
+      base::RunLoop run_loop;
+      MockedInstallationCollectorObserver collector_observer(
+          browser()->profile());
+      extensions::InstallStageTracker* install_stage_tracker =
+          extensions::InstallStageTracker::Get(browser()->profile());
+      install_stage_tracker->AddObserver(&collector_observer);
+
+      // We expect install failure only due to no update for the extension.
+      EXPECT_CALL(
+          collector_observer,
+          OnExtensionInstallationFailed(
+              testing::_,
+              extensions::InstallStageTracker::FailureReason::NO_UPDATE))
+          .WillOnce(testing::Invoke([&]() { run_loop.Quit(); }));
+      updater->CheckNow(std::move(params));
+      run_loop.Run();
+    }
+
+    return registry->enabled_extensions().GetByID(id);
+  }
+
+  void SetUpInProcessBrowserTestFixture() override {
+    ExtensionBrowserTest::SetUpInProcessBrowserTestFixture();
+
+    EXPECT_CALL(provider_, IsInitializationComplete(testing::_))
+        .WillRepeatedly(testing::Return(true));
+    EXPECT_CALL(provider_, IsFirstPolicyLoadComplete(testing::_))
+        .WillRepeatedly(testing::Return(true));
+    BrowserPolicyConnector::SetPolicyProviderForTesting(&provider_);
+  }
+
+  std::string GetUpdateManifestBody(const std::string& id,
+                                    const std::string& crx_name,
+                                    const std::string& version) {
+    // "example.com" is a  placeholder that gets substituted with the test
+    // server address at runtime.
+    std::string crx_path = "http://example.com/" + crx_name;
+    return "<?xml version='1.0' encoding='UTF-8'?>"
+           "<gupdate xmlns='http://www.google.com/update2/response' "
+           "protocol='2.0'>"
+           " <app appid='" +
+           id +
+           "'>"
+           "  <updatecheck status='ok' codebase='" +
+           crx_path + "' version='" + version +
+           "' />"
+           " </app>"
+           "</gupdate>";
+  }
+
+  std::string GetUpdateManifestHeader() {
+    return "HTTP/1.1 200 OK\nContent-Type: application/json; "
+           "charset=utf-8\n";
+  }
+
+  base::FilePath PackLocalExtension(const std::string& relative_dir_path,
+                                    const std::string& relative_pem_path,
+                                    const base::FilePath& crx_path) {
+    base::FilePath base_path;
+    GetTestDataDirectory(&base_path);
+    auto extension_path =
+        base_path.Append(kTestExtensionsDir).AppendASCII(relative_dir_path);
+    base::FilePath pem_path =
+        base_path.Append(kTestExtensionsDir).AppendASCII(relative_pem_path);
+    return PackExtensionWithOptions(extension_path, crx_path, pem_path,
+                                    base::FilePath());
+  }
+
+ private:
+  MockConfigurationPolicyProvider provider_;
+};
+
+// Extension without update_url in manifest gets updated through update_url in
+// policy.
+IN_PROC_BROWSER_TEST_F(ExtensionPinningTest,
+                       UpdateExtensionWithNoUrlInManifest) {
+  base::ScopedAllowBlockingForTesting allow_blocking;
+  base::ScopedTempDir scoped_temp_dir;
+  EXPECT_TRUE(scoped_temp_dir.CreateUniqueTempDir());
+
+  base::FilePath install_crx_path =
+      scoped_temp_dir.GetPath().AppendASCII("v1.crx");
+  ASSERT_EQ(
+      PackLocalExtension("pinning/no_update_url/v1",
+                         "pinning/no_update_url/key.pem", install_crx_path),
+      install_crx_path);
+
+  // Intercept requests to install the extension and return false for any
+  // unexpected request.
+  ExtensionRequestInterceptor interceptor;
+  ASSERT_TRUE(embedded_test_server()->Start());
+  // TODO(b/172205862): Find a way to move these requests out of the test.
+  interceptor.set_interceptor_hook(base::BindLambdaForTesting(
+      [&](content::URLLoaderInterceptor::RequestParams* params) {
+        if (params->url_request.url.path() == "/update_manifest_v1.xml") {
+          content::URLLoaderInterceptor::WriteResponse(
+              GetUpdateManifestHeader(),
+              GetUpdateManifestBody(kPinnedExtensionCrxId, "v1.crx",
+                                    /*version=*/"1"),
+              params->client.get());
+          return true;
+        }
+
+        if (params->url_request.url.path() == "/v1.crx") {
+          content::URLLoaderInterceptor::WriteResponse(install_crx_path,
+                                                       params->client.get());
+          return true;
+        }
+        return false;
+      }));
+
+  extensions::TestExtensionRegistryObserver observer(extension_registry(),
+                                                     kPinnedExtensionCrxId);
+  SetExtensionSettingsPolicy("/update_manifest_v1.xml", kPinnedExtensionCrxId);
+  auto installed_extension = observer.WaitForExtensionWillBeInstalled();
+  ASSERT_TRUE(installed_extension);
+  EXPECT_EQ(installed_extension->version().CompareTo(base::Version("1")), 0);
+
+  base::FilePath updated_crx_path =
+      scoped_temp_dir.GetPath().AppendASCII("v2.crx");
+  ASSERT_EQ(
+      PackLocalExtension("pinning/no_update_url/v2",
+                         "pinning/no_update_url/key.pem", updated_crx_path),
+      updated_crx_path);
+
+  // Override the interceptor hook to only accept new requests for updated
+  // extension.
+  interceptor.set_interceptor_hook(base::BindLambdaForTesting(
+      [&](content::URLLoaderInterceptor::RequestParams* params) {
+        if (params->url_request.url.path() == "/update_manifest_v2.xml") {
+          content::URLLoaderInterceptor::WriteResponse(
+              GetUpdateManifestHeader(),
+              GetUpdateManifestBody(kPinnedExtensionCrxId, "v2.crx",
+                                    /*version=*/"2"),
+              params->client.get());
+          return true;
+        }
+
+        if (params->url_request.url.path() == "/v2.crx") {
+          content::URLLoaderInterceptor::WriteResponse(updated_crx_path,
+                                                       params->client.get());
+          return true;
+        }
+        return false;
+      }));
+  // Change |update_url| to point to version 2 of the extension.
+  SetExtensionSettingsPolicy("/update_manifest_v2.xml", kPinnedExtensionCrxId);
+
+  // Extension is updated from |update_url| in the policy.
+  const extensions::Extension* updated_extension = TriggerExtensionUpdate(
+      kPinnedExtensionCrxId, /*expected_install_success=*/true);
+  ASSERT_TRUE(updated_extension);
+  EXPECT_EQ(updated_extension->version().CompareTo(base::Version("2")), 0);
+}
+
+// Extension with one update_url in manifest gets updated through another
+// update_url in policy.
+IN_PROC_BROWSER_TEST_F(ExtensionPinningTest, UpdateExtensionWithUrlInManifest) {
+  base::ScopedAllowBlockingForTesting allow_blocking;
+  base::ScopedTempDir scoped_temp_dir;
+  EXPECT_TRUE(scoped_temp_dir.CreateUniqueTempDir());
+
+  base::FilePath install_crx_path =
+      scoped_temp_dir.GetPath().AppendASCII("v1.crx");
+  ASSERT_EQ(PackLocalExtension("pinning/update_url/v1",
+                               "pinning/update_url/key.pem", install_crx_path),
+            install_crx_path);
+
+  // Intercept requests to update the extension and return false for any
+  // unexpected request.
+  ExtensionRequestInterceptor interceptor;
+  ASSERT_TRUE(embedded_test_server()->Start());
+  // TODO(b/172205862): Find a way to move these requests out of the test.
+  interceptor.set_interceptor_hook(base::BindLambdaForTesting(
+      [&](content::URLLoaderInterceptor::RequestParams* params) {
+        if (params->url_request.url.path() == "/update_manifest_v1.xml") {
+          content::URLLoaderInterceptor::WriteResponse(
+              GetUpdateManifestHeader(),
+              GetUpdateManifestBody(kPinnedExtensionCrxId, "v1.crx",
+                                    /*version=*/"1"),
+              params->client.get());
+          return true;
+        }
+
+        if (params->url_request.url.path() == "/v1.crx") {
+          content::URLLoaderInterceptor::WriteResponse(install_crx_path,
+                                                       params->client.get());
+          return true;
+        }
+        return false;
+      }));
+
+  extensions::TestExtensionRegistryObserver observer(extension_registry(),
+                                                     kPinnedExtensionCrxId);
+  SetExtensionSettingsPolicy("/update_manifest_v1.xml", kPinnedExtensionCrxId);
+  auto installed_extension = observer.WaitForExtensionWillBeInstalled();
+  ASSERT_TRUE(installed_extension);
+  EXPECT_EQ(installed_extension->version().CompareTo(base::Version("1")), 0);
+
+  base::FilePath updated_crx_path =
+      scoped_temp_dir.GetPath().AppendASCII("v2.crx");
+  ASSERT_EQ(PackLocalExtension("pinning/update_url/v2",
+                               "pinning/update_url/key.pem", updated_crx_path),
+            updated_crx_path);
+
+  // Override the interceptor hook to only accept new requests for updated
+  // extension.
+  interceptor.set_interceptor_hook(base::BindLambdaForTesting(
+      [&](content::URLLoaderInterceptor::RequestParams* params) {
+        if (params->url_request.url.path() == "/update_manifest_v2.xml") {
+          content::URLLoaderInterceptor::WriteResponse(
+              GetUpdateManifestHeader(),
+              GetUpdateManifestBody(kPinnedExtensionCrxId, "v2.crx",
+                                    /*version=*/"2"),
+              params->client.get());
+          return true;
+        }
+
+        if (params->url_request.url.path() == "/v2.crx") {
+          content::URLLoaderInterceptor::WriteResponse(updated_crx_path,
+                                                       params->client.get());
+          return true;
+        }
+        return false;
+      }));
+  // Change |update_url| to point to version 2 of the extension.
+  SetExtensionSettingsPolicy("/update_manifest_v2.xml", kPinnedExtensionCrxId);
+
+  // Extension is updated from |update_url| in the policy.
+  const extensions::Extension* updated_extension = TriggerExtensionUpdate(
+      kPinnedExtensionCrxId, /*expected_install_success=*/true);
+  ASSERT_TRUE(updated_extension);
+  EXPECT_EQ(updated_extension->version().CompareTo(base::Version("2")), 0);
+}
+
+// Extension with one update_url in manifest is not updated through it.
+IN_PROC_BROWSER_TEST_F(ExtensionPinningTest, IgnoreUpdateUrlInManifest) {
+  base::ScopedAllowBlockingForTesting allow_blocking;
+  base::ScopedTempDir scoped_temp_dir;
+  EXPECT_TRUE(scoped_temp_dir.CreateUniqueTempDir());
+
+  base::FilePath install_crx_path =
+      scoped_temp_dir.GetPath().AppendASCII("v1.crx");
+  ASSERT_EQ(PackLocalExtension("pinning/update_url/v1",
+                               "pinning/update_url/key.pem", install_crx_path),
+            install_crx_path);
+
+  // Intercept requests to update the extension and return false for any
+  // unexpected request.
+  ExtensionRequestInterceptor interceptor;
+  ASSERT_TRUE(embedded_test_server()->Start());
+  // TODO(b/172205862): Find a way to move these requests out of the test.
+  interceptor.set_interceptor_hook(base::BindLambdaForTesting(
+      [&](content::URLLoaderInterceptor::RequestParams* params) {
+        if (params->url_request.url.path() == "/update_manifest_v1.xml") {
+          content::URLLoaderInterceptor::WriteResponse(
+              GetUpdateManifestHeader(),
+              GetUpdateManifestBody(kPinnedExtensionCrxId, "v1.crx",
+                                    /*version=*/"1"),
+              params->client.get());
+          return true;
+        }
+
+        if (params->url_request.url.path() == "/v1.crx") {
+          content::URLLoaderInterceptor::WriteResponse(install_crx_path,
+                                                       params->client.get());
+          return true;
+        }
+        return false;
+      }));
+
+  extensions::TestExtensionRegistryObserver observer(extension_registry(),
+                                                     kPinnedExtensionCrxId);
+  SetExtensionSettingsPolicy("/update_manifest_v1.xml", kPinnedExtensionCrxId);
+  auto installed_extension = observer.WaitForExtensionWillBeInstalled();
+  ASSERT_TRUE(installed_extension);
+  EXPECT_EQ(installed_extension->version().CompareTo(base::Version("1")), 0);
+
+  // Extension is not updated from |update_url| in extension manifest. The
+  // installation fails due to no updates as |update_url| in the
+  // ExtensionSettings policy is used for updates which points to the installed
+  // version.
+  const extensions::Extension* updated_extension = TriggerExtensionUpdate(
+      kPinnedExtensionCrxId, /*expected_install_success=*/false);
+  ASSERT_TRUE(updated_extension);
+  EXPECT_EQ(updated_extension->version().CompareTo(base::Version("1")), 0);
+}
+
+// Self hosted extension from the Chrome Web Store is not updated through the
+// update_url in it's manifest even though a new version is available on the
+// Store.
+IN_PROC_BROWSER_TEST_F(ExtensionPinningTest,
+                       SelfHostedCWSExtensionNotUpdatedFromStore) {
+  // Sample Google calendar extension from Chrome Web Store, for which we have
+  // an old CRX.
+  const char kGoogleCalendarCrxId[] = "gmbgaklkmjakoegficnlkhebmhkjfich";
+  extensions::ExtensionRegistry* registry = extension_registry();
+  ASSERT_FALSE(registry->GetExtensionById(
+      kGoogleCalendarCrxId, extensions::ExtensionRegistry::EVERYTHING));
+
+  // Intercept requests to update the extension and return false for any
+  // unexpected request.
+  ExtensionRequestInterceptor interceptor;
+  ASSERT_TRUE(embedded_test_server()->Start());
+  // TODO(b/172205862): Find a way to move these requests out of the test.
+  interceptor.set_interceptor_hook(base::BindLambdaForTesting(
+      [&](content::URLLoaderInterceptor::RequestParams* params) {
+        if (params->url_request.url.path() == "/update_manifest.xml") {
+          content::URLLoaderInterceptor::WriteResponse(
+              GetUpdateManifestHeader(),
+              GetUpdateManifestBody(kGoogleCalendarCrxId, "calendar.crx",
+                                    "3.1.0"),
+              params->client.get());
+          return true;
+        }
+
+        if (params->url_request.url.path() == "/calendar.crx") {
+          content::URLLoaderInterceptor::WriteResponse(
+              "chrome/test/data/extensions/pinning/calendar/"
+              "gmbgaklkmjakoegficnlkhebmhkjfich-google-calendar-3.1.0.crx",
+              params->client.get());
+          return true;
+        }
+        return false;
+      }));
+
+  extensions::TestExtensionRegistryObserver observer(extension_registry(),
+                                                     kGoogleCalendarCrxId);
+  SetExtensionSettingsPolicy("/update_manifest.xml", kGoogleCalendarCrxId);
+  auto installed_extension = observer.WaitForExtensionWillBeInstalled();
+  ASSERT_TRUE(installed_extension);
+  EXPECT_EQ(installed_extension->version().CompareTo(base::Version("3.1.0")),
+            0);
+
+  // Extension is not updated from |update_url| in extension manifest. The
+  // installation fails due to no updates as |update_url| in the
+  // ExtensionSettings policy is used for updates which points to the installed
+  // version.
+  const extensions::Extension* updated_extension = TriggerExtensionUpdate(
+      kGoogleCalendarCrxId, /*expected_install_success=*/false);
+  ASSERT_TRUE(updated_extension);
+  EXPECT_EQ(updated_extension->version().CompareTo(base::Version("3.1.0")), 0);
+}
+
 // Verifies that if multiple <app> tags for an extension are specified in the
 // update manifest, the first valid tag is selected for crx download. This
 // test helps to notify of any change in this behaviour as there might be
diff --git a/chrome/browser/preferences/android/java/src/org/chromium/chrome/browser/preferences/ChromePreferenceKeys.java b/chrome/browser/preferences/android/java/src/org/chromium/chrome/browser/preferences/ChromePreferenceKeys.java
index 54e15d5..fc79582ff4 100644
--- a/chrome/browser/preferences/android/java/src/org/chromium/chrome/browser/preferences/ChromePreferenceKeys.java
+++ b/chrome/browser/preferences/android/java/src/org/chromium/chrome/browser/preferences/ChromePreferenceKeys.java
@@ -486,6 +486,14 @@
     public static final String LAST_SESSION_BROWSER_PID =
             "Chrome.CrashReporting.LastSessionBrowserPid";
 
+    /**
+     * The application state last recorded by browser in previous session, updated when crash
+     * reporting is initialized and when current application state changes henceforth. If read after
+     * crash reporting is initialized, then the value would hold current session state.
+     */
+    public static final String LAST_SESSION_APPLICATION_STATE =
+            "Chrome.CrashReporting.LastSessionApplicationState";
+
     public static final String LOCALE_MANAGER_AUTO_SWITCH = "LocaleManager_PREF_AUTO_SWITCH";
     public static final String LOCALE_MANAGER_PROMO_SHOWN = "LocaleManager_PREF_PROMO_SHOWN";
     public static final String LOCALE_MANAGER_SEARCH_ENGINE_PROMO_SHOW_STATE =
@@ -911,6 +919,7 @@
                 IMAGE_DESCRIPTIONS_DONT_ASK_AGAIN,
                 ISOLATED_SPLITS_DEX_COMPILE_VERSION,
                 LAST_SESSION_BROWSER_PID,
+                LAST_SESSION_APPLICATION_STATE,
                 OFFLINE_INDICATOR_V2_WALL_TIME_SHOWN_MS,
                 OFFLINE_INDICATOR_V2_LAST_UPDATE_WALL_TIME_MS,
                 OFFLINE_INDICATOR_V2_TIME_IN_FOREGROUND_MS,
diff --git a/chrome/browser/renderer_context_menu/mock_render_view_context_menu.cc b/chrome/browser/renderer_context_menu/mock_render_view_context_menu.cc
index 9a6802b..36584ea 100644
--- a/chrome/browser/renderer_context_menu/mock_render_view_context_menu.cc
+++ b/chrome/browser/renderer_context_menu/mock_render_view_context_menu.cc
@@ -4,6 +4,8 @@
 
 #include "chrome/browser/renderer_context_menu/mock_render_view_context_menu.h"
 
+#include <algorithm>
+
 #include "chrome/app/chrome_command_ids.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/grit/generated_resources.h"
@@ -156,9 +158,6 @@
       return;
     }
   }
-
-  FAIL() << "Menu observer is trying to change a menu item it doesn't own."
-         << " command_id: " << command_id;
 }
 
 void MockRenderViewContextMenu::UpdateMenuIcon(int command_id,
@@ -174,10 +173,45 @@
          << " command_id: " << command_id;
 }
 
-void MockRenderViewContextMenu::RemoveMenuItem(int command_id) {}
+void MockRenderViewContextMenu::RemoveMenuItem(int command_id) {
+  auto old_end = items_.end();
+  auto new_end = std::remove_if(
+      items_.begin(), old_end,
+      [command_id](const auto& item) { return item.command_id == command_id; });
+
+  if (new_end == old_end) {
+    FAIL() << "Menu observer is trying to remove a menu item it doesn't own."
+           << " command_id: " << command_id;
+    return;
+  }
+
+  items_.erase(new_end, old_end);
+}
 
 void MockRenderViewContextMenu::RemoveAdjacentSeparators() {}
 
+void MockRenderViewContextMenu::RemoveSeparatorBeforeMenuItem(int command_id) {
+  auto iter = std::find_if(
+      items_.begin(), items_.end(),
+      [command_id](const auto& item) { return item.command_id == command_id; });
+
+  if (iter == items_.end()) {
+    FAIL() << "Menu observer is trying to remove a separator before a "
+              "non-existent item."
+           << " command_id: " << command_id;
+    return;
+  }
+
+  if (iter == items_.begin()) {
+    FAIL() << "Menu observer is trying to remove a separator before a "
+              "the first menu item."
+           << " command_id: " << command_id;
+    return;
+  }
+
+  items_.erase(iter - 1);
+}
+
 void MockRenderViewContextMenu::AddSpellCheckServiceItem(bool is_checked) {
   AddCheckItem(
       IDC_CONTENT_CONTEXT_SPELLING_TOGGLE,
diff --git a/chrome/browser/renderer_context_menu/mock_render_view_context_menu.h b/chrome/browser/renderer_context_menu/mock_render_view_context_menu.h
index 31ea3c1..c3019fd1 100644
--- a/chrome/browser/renderer_context_menu/mock_render_view_context_menu.h
+++ b/chrome/browser/renderer_context_menu/mock_render_view_context_menu.h
@@ -71,6 +71,7 @@
   void UpdateMenuIcon(int command_id, const ui::ImageModel& icon) override;
   void RemoveMenuItem(int command_id) override;
   void RemoveAdjacentSeparators() override;
+  void RemoveSeparatorBeforeMenuItem(int command_id) override;
   void AddSpellCheckServiceItem(bool is_checked) override;
   void AddAccessibilityLabelsServiceItem(bool is_checked) override;
   content::RenderViewHost* GetRenderViewHost() const override;
diff --git a/chrome/browser/renderer_context_menu/spelling_menu_observer.cc b/chrome/browser/renderer_context_menu/spelling_menu_observer.cc
index 0c26cba..5e17490 100644
--- a/chrome/browser/renderer_context_menu/spelling_menu_observer.cc
+++ b/chrome/browser/renderer_context_menu/spelling_menu_observer.cc
@@ -6,6 +6,7 @@
 
 #include <utility>
 
+#include "base/barrier_closure.h"
 #include "base/bind.h"
 #include "base/command_line.h"
 #include "base/i18n/case_conversion.h"
@@ -62,7 +63,7 @@
 void SpellingMenuObserver::InitMenu(const content::ContextMenuParams& params) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
   DCHECK(!params.misspelled_word.empty() ||
-      params.dictionary_suggestions.empty());
+         params.dictionary_suggestions.empty());
 
   // Exit if we are not in an editable element because we add a menu item only
   // for editable elements.
@@ -74,100 +75,141 @@
   if (params.misspelled_word.empty())
     return;
 
+  // Note that for Windows, suggestions_ will initially only contain those
+  // suggestions obtained using Hunspell.
   suggestions_ = params.dictionary_suggestions;
   misspelled_word_ = params.misspelled_word;
 
-  bool use_suggestions = SpellingServiceClient::IsAvailable(
+  use_remote_suggestions_ = SpellingServiceClient::IsAvailable(
       browser_context, SpellingServiceClient::SUGGEST);
 
-  if (!suggestions_.empty() || use_suggestions)
-    proxy_->AddSeparator();
-
-  // Append Dictionary spell check suggestions.
-  int length = std::min(kMaxSpellingSuggestions,
-                        static_cast<int>(params.dictionary_suggestions.size()));
-  for (int i = 0; i < length &&
-       IDC_SPELLCHECK_SUGGESTION_0 + i <= IDC_SPELLCHECK_SUGGESTION_LAST;
-       ++i) {
-    proxy_->AddMenuItem(IDC_SPELLCHECK_SUGGESTION_0 + static_cast<int>(i),
-                        params.dictionary_suggestions[i]);
-  }
-
-  // The service types |SpellingServiceClient::SPELLCHECK| and
-  // |SpellingServiceClient::SUGGEST| are mutually exclusive. Only one is
-  // available at at time.
-  //
-  // When |SpellingServiceClient::SPELLCHECK| is available, the contextual
-  // suggestions from |SpellingServiceClient| are already stored in
-  // |params.dictionary_suggestions|.  |SpellingMenuObserver| places these
-  // suggestions in the slots |IDC_SPELLCHECK_SUGGESTION_[0-LAST]|. If
-  // |SpellingMenuObserver| queried |SpellingServiceClient| again, then quality
-  // of suggestions would be reduced by lack of context around the misspelled
-  // word.
-  //
-  // When |SpellingServiceClient::SUGGEST| is available,
-  // |params.dictionary_suggestions| contains suggestions only from Hunspell
-  // dictionary. |SpellingMenuObserver| queries |SpellingServiceClient| with the
-  // misspelled word without the surrounding context. Spellcheck suggestions
-  // from |SpellingServiceClient::SUGGEST| are not available until
-  // |SpellingServiceClient| responds to the query. While |SpellingMenuObserver|
-  // waits for |SpellingServiceClient|, it shows a placeholder text "Loading
-  // suggestion..." in the |IDC_CONTENT_CONTEXT_SPELLING_SUGGESTION| slot. After
-  // |SpellingServiceClient| responds to the query, |SpellingMenuObserver|
-  // replaces the placeholder text with either the spelling suggestion or the
-  // message "No more suggestions from Google." The "No more suggestions"
-  // message is there when |SpellingServiceClient| returned the same suggestion
-  // as Hunspell.
-  if (use_suggestions) {
-    // Append a placeholder item for the suggestion from the Spelling service
-    // and send a request to the service if we can retrieve suggestions from it.
-    // Also, see if we can use the spelling service to get an ideal suggestion.
-    // Otherwise, we'll fall back to the set of suggestions.  Initialize
-    // variables used in OnTextCheckComplete(). We copy the input text to the
-    // result text so we can replace its misspelled regions with suggestions.
-    succeeded_ = false;
-    result_ = params.misspelled_word;
-
-    // Add a placeholder item. This item will be updated when we receive a
-    // response from the Spelling service. (We do not have to disable this
-    // item now since Chrome will call IsCommandIdEnabled() and disable it.)
-    loading_message_ =
-        l10n_util::GetStringUTF16(IDS_CONTENT_CONTEXT_SPELLING_CHECKING);
-    proxy_->AddMenuItem(IDC_CONTENT_CONTEXT_SPELLING_SUGGESTION,
-                        loading_message_);
-    // Invoke a JSON-RPC call to the Spelling service in the background so we
-    // can update the placeholder item when we receive its response. It also
-    // starts the animation timer so we can show animation until we receive
-    // it.
-    bool result = client_->RequestTextCheck(
-        browser_context, SpellingServiceClient::SUGGEST, params.misspelled_word,
-        base::BindOnce(&SpellingMenuObserver::OnTextCheckComplete,
-                       base::Unretained(this), SpellingServiceClient::SUGGEST));
-    if (result) {
-      loading_frame_ = 0;
-      animation_timer_.Start(FROM_HERE, base::TimeDelta::FromSeconds(1),
-          this, &SpellingMenuObserver::OnAnimationTimerExpired);
-    }
-  }
-
-  if (!params.dictionary_suggestions.empty()) {
-    // |spellcheck_service| can be null when the suggested word is
-    // provided by Web SpellCheck API.
+#if defined(OS_WIN) && BUILDFLAG(USE_BROWSER_SPELLCHECKER)
+  use_platform_suggestions_ = spellcheck::UseBrowserSpellChecker();
+  if (use_platform_suggestions_) {
+    // Need to asynchronously retrieve suggestions from the platform
+    // spellchecker, which requires the SpellcheckService.
     SpellcheckService* spellcheck_service =
         SpellcheckServiceFactory::GetForContext(browser_context);
-    if (spellcheck_service && spellcheck_service->GetMetrics())
-      spellcheck_service->GetMetrics()->RecordSuggestionStats(1);
+    if (!spellcheck_service)
+      return;
+
+    // If there are no local suggestions or the spellcheck cloud service isn't
+    // used, this separator will be removed later.
     proxy_->AddSeparator();
+
+    // Append placeholders for maximum number of suggestions. Note that all but
+    // the first placeholder will be made hidden in OnContextMenuShown override.
+    // It can't be done here because UpdateMenuItem can only be called after
+    // ToolkitDelegateViews is initialized, which happens after
+    // RenderViewContextMenu::InitMenu.
+    for (int i = 0;
+         i < kMaxSpellingSuggestions &&
+         IDC_SPELLCHECK_SUGGESTION_0 + i <= IDC_SPELLCHECK_SUGGESTION_LAST;
+         ++i) {
+      proxy_->AddMenuItem(IDC_SPELLCHECK_SUGGESTION_0 + i,
+                          /*title=*/std::u16string());
+    }
+
+    // Completion barrier for local (and possibly remote) retrieval of
+    // suggestions. Remote suggestion cannot be displayed until local
+    // suggestions have been retrieved, so that duplicates can be accounted for.
+    completion_barrier_ = base::BarrierClosure(
+        use_remote_suggestions_ ? 2 : 1,
+        base::BindOnce(&SpellingMenuObserver::OnGetSuggestionsComplete,
+                       weak_ptr_factory_.GetWeakPtr()));
+
+    // Asynchronously retrieve suggestions from the platform spellchecker.
+    spellcheck_platform::GetPerLanguageSuggestions(
+        spellcheck_service->platform_spell_checker(), misspelled_word_,
+        base::BindOnce(&SpellingMenuObserver::OnGetPlatformSuggestionsComplete,
+                       weak_ptr_factory_.GetWeakPtr()));
+
+    if (use_remote_suggestions_) {
+      // Asynchronously retrieve remote suggestions in parallel.
+      GetRemoteSuggestions();
+    } else {
+      // Animate first suggestion placeholder while retrieving suggestions.
+      loading_message_ =
+          l10n_util::GetStringUTF16(IDS_CONTENT_CONTEXT_SPELLING_CHECKING);
+      loading_frame_ = 0;
+      animation_timer_.Start(
+          FROM_HERE, base::TimeDelta::FromSeconds(1),
+          base::BindRepeating(&SpellingMenuObserver::OnAnimationTimerExpired,
+                              weak_ptr_factory_.GetWeakPtr(),
+                              IDC_SPELLCHECK_SUGGESTION_0));
+    }
+
+    // If there are no suggestions, this separator between suggestions and "Add
+    // to dictionary" will be removed later.
+    proxy_->AddSeparator();
+  } else {
+#endif  // defined(OS_WIN) && BUILDFLAG(USE_BROWSER_SPELLCHECKER)
+    if (!suggestions_.empty() || use_remote_suggestions_)
+      proxy_->AddSeparator();
+
+    // Append Dictionary spell check suggestions.
+    int length =
+        std::min(kMaxSpellingSuggestions,
+                 static_cast<int>(params.dictionary_suggestions.size()));
+    for (int i = 0; i < length && IDC_SPELLCHECK_SUGGESTION_0 + i <=
+                                      IDC_SPELLCHECK_SUGGESTION_LAST;
+         ++i) {
+      proxy_->AddMenuItem(IDC_SPELLCHECK_SUGGESTION_0 + i,
+                          params.dictionary_suggestions[i]);
+    }
+
+    if (use_remote_suggestions_)
+      GetRemoteSuggestions();
+
+    if (!params.dictionary_suggestions.empty()) {
+      // |spellcheck_service| can be null when the suggested word is
+      // provided by Web SpellCheck API.
+      SpellcheckService* spellcheck_service =
+          SpellcheckServiceFactory::GetForContext(browser_context);
+      if (spellcheck_service && spellcheck_service->GetMetrics())
+        spellcheck_service->GetMetrics()->RecordSuggestionStats(1);
+      proxy_->AddSeparator();
+    }
+#if defined(OS_WIN) && BUILDFLAG(USE_BROWSER_SPELLCHECKER)
   }
+#endif  // defined(OS_WIN) && BUILDFLAG(USE_BROWSER_SPELLCHECKER)
 
   // If word is misspelled, give option for "Add to dictionary" and, if
   // multilingual spellchecking is not enabled, a check item "Ask Google for
   // suggestions".
-  proxy_->AddMenuItem(IDC_SPELLCHECK_ADD_TO_DICTIONARY,
+  proxy_->AddMenuItem(
+      IDC_SPELLCHECK_ADD_TO_DICTIONARY,
       l10n_util::GetStringUTF16(IDS_CONTENT_CONTEXT_ADD_TO_DICTIONARY));
   proxy_->AddSpellCheckServiceItem(integrate_spelling_service_.GetValue());
 }
 
+#if defined(OS_WIN) && BUILDFLAG(USE_BROWSER_SPELLCHECKER)
+void SpellingMenuObserver::OnContextMenuShown(
+    const content::ContextMenuParams& /*params*/,
+    const gfx::Rect& /*bounds_in_screen*/) {
+  if (!use_platform_suggestions_)
+    return;
+
+  // Disable the first place holder but keep it visible for animation if not
+  // retrieving remote suggestions. Note that UpdateMenuItem does nothing if the
+  // command_id is not found, e.g. if there is an early exit from InitMenu.
+  proxy_->UpdateMenuItem(IDC_SPELLCHECK_SUGGESTION_0,
+                         /*enabled=*/false,
+                         /*hidden=*/(use_remote_suggestions_ ? true : false),
+                         loading_message_);
+
+  // Disable and hide the rest of the placeholders until suggestions obtained.
+  for (int i = 1;
+       i < kMaxSpellingSuggestions &&
+       IDC_SPELLCHECK_SUGGESTION_0 + i <= IDC_SPELLCHECK_SUGGESTION_LAST;
+       ++i) {
+    proxy_->UpdateMenuItem(IDC_SPELLCHECK_SUGGESTION_0 + i,
+                           /*enabled=*/false, /*hidden=*/true,
+                           /*title=*/std::u16string());
+  }
+}
+#endif  // defined(OS_WIN) && BUILDFLAG(USE_BROWSER_SPELLCHECKER)
+
 bool SpellingMenuObserver::IsCommandIdSupported(int command_id) {
   if (command_id >= IDC_SPELLCHECK_SUGGESTION_0 &&
       command_id <= IDC_SPELLCHECK_SUGGESTION_LAST)
@@ -313,13 +355,172 @@
   }
 }
 
-void SpellingMenuObserver::OnTextCheckComplete(
-    SpellingServiceClient::ServiceType type,
-    bool success,
-    const std::u16string& text,
-    const std::vector<SpellCheckResult>& results) {
+#if defined(OS_WIN) && BUILDFLAG(USE_BROWSER_SPELLCHECKER)
+void SpellingMenuObserver::OnGetPlatformSuggestionsComplete(
+    const spellcheck::PerLanguageSuggestions&
+        platform_per_language_suggestions) {
+  // Prioritize the platform results, since presumably the first user language
+  // will have a Windows language pack installed. Treat the Hunspell suggestions
+  // as if a single language.
+  spellcheck::PerLanguageSuggestions per_language_suggestions =
+      platform_per_language_suggestions;
+  per_language_suggestions.push_back(suggestions_);
+
+  std::vector<std::u16string> combined_suggestions;
+  spellcheck::FillSuggestions(per_language_suggestions, &combined_suggestions);
+  // suggestions_ will now include those from both the platform spellchecker and
+  // Hunspell.
+  suggestions_ = combined_suggestions;
+
+  // The menu can be updated with local suggestions without waiting for the
+  // request for remote suggestions to complete.
+  if (suggestions_.empty() && !use_remote_suggestions_)
+    proxy_->RemoveSeparatorBeforeMenuItem(IDC_SPELLCHECK_SUGGESTION_0);
+
+  // Update spell check suggestions displayed on the menu.
+  int length =
+      std::min(kMaxSpellingSuggestions, static_cast<int>(suggestions_.size()));
+
+  for (int i = 0; i < length && IDC_SPELLCHECK_SUGGESTION_0 + i <=
+                                    IDC_SPELLCHECK_SUGGESTION_LAST;
+       ++i) {
+    proxy_->UpdateMenuItem(IDC_SPELLCHECK_SUGGESTION_0 + i,
+                           /*enabled=*/true, /*hidden=*/false, suggestions_[i]);
+  }
+
+  for (int i = suggestions_.size(); i < kMaxSpellingSuggestions; ++i) {
+    // There were fewer suggestions returned than placeholder slots, remove the
+    // empty menu items.
+    proxy_->RemoveMenuItem(IDC_SPELLCHECK_SUGGESTION_0 + i);
+  }
+
+  if (suggestions_.empty()) {
+    proxy_->RemoveSeparatorBeforeMenuItem(IDC_SPELLCHECK_ADD_TO_DICTIONARY);
+  } else {
+    // |spellcheck_service| can be null when the suggested word is
+    // provided by Web SpellCheck API.
+    SpellcheckService* spellcheck_service =
+        SpellcheckServiceFactory::GetForContext(proxy_->GetBrowserContext());
+    if (spellcheck_service && spellcheck_service->GetMetrics())
+      spellcheck_service->GetMetrics()->RecordSuggestionStats(1);
+  }
+
+  completion_barrier_.Run();
+}
+
+void SpellingMenuObserver::OnGetSuggestionsComplete() {
   animation_timer_.Stop();
 
+  if (use_remote_suggestions_) {
+    // Update remote suggestion too using cached values from
+    // OnGetRemoteSuggestionsComplete.
+    UpdateRemoteSuggestion(remote_service_type_, succeeded_, remote_results_);
+  }
+
+  FireSuggestionsCompleteCallbackIfNeededForTesting();
+}
+
+void SpellingMenuObserver::RegisterSuggestionsCompleteCallbackForTesting(
+    base::OnceClosure callback) {
+  suggestions_complete_callback_for_testing_ = std::move(callback);
+}
+#endif  // defined(OS_WIN) && BUILDFLAG(USE_BROWSER_SPELLCHECKER)
+
+void SpellingMenuObserver::GetRemoteSuggestions() {
+  // The service types |SpellingServiceClient::SPELLCHECK| and
+  // |SpellingServiceClient::SUGGEST| are mutually exclusive. Only one is
+  // available at a time.
+  //
+  // When |SpellingServiceClient::SPELLCHECK| is available, the contextual
+  // suggestions from |SpellingServiceClient| are already stored in
+  // |params.dictionary_suggestions|.  |SpellingMenuObserver| places these
+  // suggestions in the slots |IDC_SPELLCHECK_SUGGESTION_[0-LAST]|. If
+  // |SpellingMenuObserver| queried |SpellingServiceClient| again, then
+  // quality of suggestions would be reduced by lack of context around the
+  // misspelled word.
+  //
+  // When |SpellingServiceClient::SUGGEST| is available,
+  // |params.dictionary_suggestions| contains suggestions only from Hunspell
+  // dictionary. |SpellingMenuObserver| queries |SpellingServiceClient| with
+  // the misspelled word without the surrounding context. Spellcheck
+  // suggestions from |SpellingServiceClient::SUGGEST| are not available until
+  // |SpellingServiceClient| responds to the query. While
+  // |SpellingMenuObserver| waits for |SpellingServiceClient|, it shows a
+  // placeholder text "Loading suggestion..." in the
+  // |IDC_CONTENT_CONTEXT_SPELLING_SUGGESTION| slot. After
+  // |SpellingServiceClient| responds to the query, |SpellingMenuObserver|
+  // replaces the placeholder text with either the spelling suggestion or the
+  // message "No more suggestions from Google." The "No more suggestions"
+  // message is there when |SpellingServiceClient| returned the same
+  // suggestion as Hunspell.
+  //
+  // Append a placeholder item for the suggestion from the Spelling service
+  // and send a request to the service if we can retrieve suggestions from it.
+  // Also, see if we can use the spelling service to get an ideal suggestion.
+  // Otherwise, we'll fall back to the set of suggestions.  Initialize
+  // variables used in OnTextCheckComplete(). We copy the input text to the
+  // result text so we can replace its misspelled regions with suggestions.
+  succeeded_ = false;
+  result_ = misspelled_word_;
+
+  // Add a placeholder item. This item will be updated when we receive a
+  // response from the Spelling service. (We do not have to disable this
+  // item now since Chrome will call IsCommandIdEnabled() and disable it.)
+  loading_message_ =
+      l10n_util::GetStringUTF16(IDS_CONTENT_CONTEXT_SPELLING_CHECKING);
+  proxy_->AddMenuItem(IDC_CONTENT_CONTEXT_SPELLING_SUGGESTION,
+                      loading_message_);
+
+  // Invoke a JSON-RPC call to the Spelling service in the background so we
+  // can update the placeholder item when we receive its response. It also
+  // starts the animation timer so we can show animation until we receive
+  // it.
+  content::BrowserContext* browser_context = proxy_->GetBrowserContext();
+  if (!browser_context)
+    return;
+  bool result = client_->RequestTextCheck(
+      browser_context, SpellingServiceClient::SUGGEST, misspelled_word_,
+      base::BindOnce(&SpellingMenuObserver::OnGetRemoteSuggestionsComplete,
+                     weak_ptr_factory_.GetWeakPtr(),
+                     SpellingServiceClient::SUGGEST));
+  if (result) {
+    loading_frame_ = 0;
+    animation_timer_.Start(
+        FROM_HERE, base::TimeDelta::FromSeconds(1),
+        base::BindRepeating(&SpellingMenuObserver::OnAnimationTimerExpired,
+                            weak_ptr_factory_.GetWeakPtr(),
+                            IDC_CONTENT_CONTEXT_SPELLING_SUGGESTION));
+  }
+}
+
+void SpellingMenuObserver::OnGetRemoteSuggestionsComplete(
+    SpellingServiceClient::ServiceType type,
+    bool success,
+    const std::u16string& /*text*/,
+    const std::vector<SpellCheckResult>& results) {
+#if defined(OS_WIN) && BUILDFLAG(USE_BROWSER_SPELLCHECKER)
+  if (use_platform_suggestions_) {
+    // Cache results since we need the parallel retrieval of local suggestions
+    // to also complete in order to proceed.
+    remote_service_type_ = type;
+    succeeded_ = success;
+    // Parameter |text| is unused.
+    remote_results_ = results;
+
+    completion_barrier_.Run();
+  } else {
+#endif  // defined(OS_WIN) && BUILDFLAG(USE_BROWSER_SPELLCHECKER)
+    animation_timer_.Stop();
+    UpdateRemoteSuggestion(type, success, results);
+#if defined(OS_WIN) && BUILDFLAG(USE_BROWSER_SPELLCHECKER)
+  }
+#endif  // defined(OS_WIN) && BUILDFLAG(USE_BROWSER_SPELLCHECKER)
+}
+
+void SpellingMenuObserver::UpdateRemoteSuggestion(
+    SpellingServiceClient::ServiceType type,
+    bool success,
+    const std::vector<SpellCheckResult>& results) {
   // Scan the text-check results and replace the misspelled regions with
   // suggested words. If the replaced text is included in the suggestion list
   // provided by the local spellchecker, we show a "No suggestions from Google"
@@ -355,7 +556,14 @@
   }
 }
 
-void SpellingMenuObserver::OnAnimationTimerExpired() {
+#if defined(OS_WIN) && BUILDFLAG(USE_BROWSER_SPELLCHECKER)
+void SpellingMenuObserver::FireSuggestionsCompleteCallbackIfNeededForTesting() {
+  if (suggestions_complete_callback_for_testing_)
+    std::move(suggestions_complete_callback_for_testing_).Run();
+}
+#endif  // defined(OS_WIN) && BUILDFLAG(USE_BROWSER_SPELLCHECKER)
+
+void SpellingMenuObserver::OnAnimationTimerExpired(int command_id) {
   // Append '.' characters to the end of "Checking".
   loading_frame_ = (loading_frame_ + 1) & 3;
   std::u16string loading_message =
@@ -363,6 +571,6 @@
 
   // Update the menu item with the text. We disable this item to prevent users
   // from selecting it.
-  proxy_->UpdateMenuItem(IDC_CONTENT_CONTEXT_SPELLING_SUGGESTION, false, false,
-                         loading_message);
+  proxy_->UpdateMenuItem(command_id,
+                         /*enabled=*/false, /*hidden=*/false, loading_message);
 }
diff --git a/chrome/browser/renderer_context_menu/spelling_menu_observer.h b/chrome/browser/renderer_context_menu/spelling_menu_observer.h
index af62f242..471ab4b 100644
--- a/chrome/browser/renderer_context_menu/spelling_menu_observer.h
+++ b/chrome/browser/renderer_context_menu/spelling_menu_observer.h
@@ -14,10 +14,14 @@
 
 #include "base/compiler_specific.h"
 #include "base/macros.h"
+#include "base/memory/weak_ptr.h"
 #include "base/timer/timer.h"
+#include "build/build_config.h"
 #include "components/prefs/pref_member.h"
 #include "components/renderer_context_menu/render_view_context_menu_observer.h"
 #include "components/spellcheck/browser/spelling_service_client.h"
+#include "components/spellcheck/common/spellcheck_common.h"
+#include "components/spellcheck/spellcheck_buildflags.h"
 
 class RenderViewContextMenuProxy;
 struct SpellCheckResult;
@@ -48,22 +52,61 @@
 
   // RenderViewContextMenuObserver implementation.
   void InitMenu(const content::ContextMenuParams& params) override;
+#if defined(OS_WIN) && BUILDFLAG(USE_BROWSER_SPELLCHECKER)
+  void OnContextMenuShown(const content::ContextMenuParams& params,
+                          const gfx::Rect& bounds_in_screen) override;
+#endif  // defined(OS_WIN) && BUILDFLAG(USE_BROWSER_SPELLCHECKER)
   bool IsCommandIdSupported(int command_id) override;
   bool IsCommandIdChecked(int command_id) override;
   bool IsCommandIdEnabled(int command_id) override;
   void ExecuteCommand(int command_id) override;
 
+#if defined(OS_WIN) && BUILDFLAG(USE_BROWSER_SPELLCHECKER)
+  // A callback function called when the platform spellchecker finishes getting
+  // suggestions for a misspelled word.
+  void OnGetPlatformSuggestionsComplete(
+      const spellcheck::PerLanguageSuggestions&
+          platform_per_language_suggestions);
+
+  // A callback for the BarrierClosure, fired when the platform (and possibly
+  // remote) retrieval of suggestions completes.
+  void OnGetSuggestionsComplete();
+
+  // Registers a callback for testing when local (and possibly remote) retrieval
+  // of suggestions has completed. This allows tests to wait in a run loop
+  // before confirming the presence of expected menu items.
+  void RegisterSuggestionsCompleteCallbackForTesting(
+      base::OnceClosure callback);
+#endif  // defined(OS_WIN) && BUILDFLAG(USE_BROWSER_SPELLCHECKER)
+
   // A callback function called when the Spelling service finishes checking a
   // misspelled word.
-  void OnTextCheckComplete(SpellingServiceClient::ServiceType type,
-                           bool success,
-                           const std::u16string& text,
-                           const std::vector<SpellCheckResult>& results);
+  void OnGetRemoteSuggestionsComplete(
+      SpellingServiceClient::ServiceType type,
+      bool success,
+      const std::u16string& text,
+      const std::vector<SpellCheckResult>& results);
 
  private:
+  // Method that starts the asynchronous retrieval of suggestions from the
+  // remote enhanced spellcheck service.
+  void GetRemoteSuggestions();
+
+  // Updates the presented suggestion from the Spelling service.
+  void UpdateRemoteSuggestion(SpellingServiceClient::ServiceType type,
+                              bool success,
+                              const std::vector<SpellCheckResult>& results);
+
+#if defined(OS_WIN) && BUILDFLAG(USE_BROWSER_SPELLCHECKER)
+  // Fires a callback for testing when local (and possibly remote) retrieval of
+  // suggestions has completed. This allows tests to wait in a run loop before
+  // confirming the presence of expected menu items.
+  void FireSuggestionsCompleteCallbackIfNeededForTesting();
+#endif  // defined(OS_WIN) && BUILDFLAG(USE_BROWSER_SPELLCHECKER)
+
   // The callback function for base::RepeatingTimer. This function updates the
-  // "loading..." animation in the context-menu item.
-  void OnAnimationTimerExpired();
+  // "loading..." animation in the context-menu item for the given command_id.
+  void OnAnimationTimerExpired(int command_id);
 
   // The interface to add a context-menu item and update it. This class uses
   // this interface to avoid accesing context-menu items directly.
@@ -112,6 +155,30 @@
   // Flag indicating whether automatic spelling correction is enabled.
   BooleanPrefMember autocorrect_spelling_;
 
+  // Flag indicating that suggestions from the remote spelling service may be
+  // available.
+  bool use_remote_suggestions_ = false;
+
+#if defined(OS_WIN) && BUILDFLAG(USE_BROWSER_SPELLCHECKER)
+  // Flag indicating that suggestions should be retrieved asynchronously from
+  // the platform spellchecker (effectively just Windows versions > Win7).
+  bool use_platform_suggestions_ = false;
+
+  // Barrier closure for completion of both remote and local check.
+  base::RepeatingClosure completion_barrier_;
+
+  // Used for caching remote results in case async platform suggestion retrieval
+  // has not completed.
+  SpellingServiceClient::ServiceType remote_service_type_ =
+      SpellingServiceClient::SUGGEST;
+  std::vector<SpellCheckResult> remote_results_;
+
+  // Callback registered using RegisterSuggestionsCompleteCallbackForTesting.
+  base::OnceClosure suggestions_complete_callback_for_testing_;
+#endif  // defined(OS_WIN) && BUILDFLAG(USE_BROWSER_SPELLCHECKER)
+
+  base::WeakPtrFactory<SpellingMenuObserver> weak_ptr_factory_{this};
+
   DISALLOW_COPY_AND_ASSIGN(SpellingMenuObserver);
 };
 
diff --git a/chrome/browser/renderer_context_menu/spelling_menu_observer_browsertest.cc b/chrome/browser/renderer_context_menu/spelling_menu_observer_browsertest.cc
index 6d9c0ba8..9fcf05b 100644
--- a/chrome/browser/renderer_context_menu/spelling_menu_observer_browsertest.cc
+++ b/chrome/browser/renderer_context_menu/spelling_menu_observer_browsertest.cc
@@ -6,13 +6,19 @@
 
 #include "base/macros.h"
 #include "base/strings/utf_string_conversions.h"
+#include "base/test/scoped_feature_list.h"
 #include "base/values.h"
+#include "build/build_config.h"
 #include "chrome/app/chrome_command_ids.h"
 #include "chrome/browser/renderer_context_menu/mock_render_view_context_menu.h"
+#include "chrome/browser/spellchecker/spellcheck_factory.h"
+#include "chrome/browser/spellchecker/spellcheck_service.h"
 #include "chrome/test/base/in_process_browser_test.h"
 #include "components/prefs/pref_service.h"
 #include "components/spellcheck/browser/pref_names.h"
 #include "components/spellcheck/browser/spelling_service_client.h"
+#include "components/spellcheck/common/spellcheck_features.h"
+#include "components/spellcheck/spellcheck_buildflags.h"
 #include "content/public/browser/context_menu_params.h"
 #include "content/public/test/browser_test.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -25,7 +31,68 @@
  public:
   SpellingMenuObserverTest();
 
-  void SetUpOnMainThread() override { Reset(false); }
+  void SetUpOnMainThread() override {
+    Reset(false);
+
+#if defined(OS_WIN) && BUILDFLAG(USE_BROWSER_SPELLCHECKER)
+    // Windows versions that don't support platform
+    // spellchecker fallback to Hunspell.
+    if (!spellcheck::WindowsVersionSupportsSpellchecker())
+      return;
+
+    base::ListValue dictionary;
+    dictionary.AppendString("en-US");
+    menu()->GetPrefs()->Set(spellcheck::prefs::kSpellCheckDictionaries,
+                            dictionary);
+    // Use SetTestingFactoryAndUse to force creation and initialization of
+    // SpellcheckService using the TestingProfile browser context.
+    SpellcheckServiceFactory::GetInstance()->SetTestingFactoryAndUse(
+        menu()->GetBrowserContext(),
+        base::BindRepeating(&SpellingMenuObserverTest::BuildSpellcheckService,
+                            base::Unretained(this)));
+#endif  // defined(OS_WIN) && BUILDFLAG(USE_BROWSER_SPELLCHECKER)
+  }
+
+#if defined(OS_WIN) && BUILDFLAG(USE_BROWSER_SPELLCHECKER)
+  std::unique_ptr<KeyedService> BuildSpellcheckService(
+      content::BrowserContext* context) {
+    auto spellcheck_service = std::make_unique<SpellcheckService>(context);
+
+    // Call SetLanguage to assure that the platform spellchecker is initialized.
+    spellcheck_platform::SetLanguage(
+        spellcheck_service->platform_spell_checker(), "en-US",
+        base::BindOnce(&SpellingMenuObserverTest::OnSetLanguageComplete,
+                       base::Unretained(this)));
+
+    RunUntilCallbackReceived();
+
+    return spellcheck_service;
+  }
+
+  void OnSetLanguageComplete(bool result) {
+    ASSERT_TRUE(result);
+    callback_received_ = true;
+    if (quit_)
+      std::move(quit_).Run();
+  }
+
+  void OnSuggestionsComplete() {
+    callback_received_ = true;
+    if (quit_)
+      std::move(quit_).Run();
+  }
+
+  void RunUntilCallbackReceived() {
+    if (callback_received_)
+      return;
+    base::RunLoop run_loop;
+    quit_ = run_loop.QuitClosure();
+    run_loop.Run();
+
+    // Reset status.
+    callback_received_ = false;
+  }
+#endif  // defined(OS_WIN) && BUILDFLAG(USE_BROWSER_SPELLCHECKER)
 
   void TearDownOnMainThread() override {
     observer_.reset();
@@ -46,7 +113,29 @@
     params.dictionary_suggestions.clear();
     if (suggestion)
       params.dictionary_suggestions.push_back(base::ASCIIToUTF16(suggestion));
+
+#if defined(OS_WIN) && BUILDFLAG(USE_BROWSER_SPELLCHECKER)
+    // Expect early return if word is spelled correctly.
+    if (params.misspelled_word.empty())
+      callback_received_ = true;
+
+    if (spellcheck::WindowsVersionSupportsSpellchecker()) {
+      observer_->RegisterSuggestionsCompleteCallbackForTesting(
+          base::BindOnce(&SpellingMenuObserverTest::OnSuggestionsComplete,
+                         base::Unretained(this)));
+    }
+#endif  // defined(OS_WIN) && BUILDFLAG(USE_BROWSER_SPELLCHECKER)
+
     observer_->InitMenu(params);
+
+    // Windows behavior needs this to be called as well to update placeholder
+    // menu items. Doesn't hurt for non-Windows platforms either.
+    observer_->OnContextMenuShown(params, gfx::Rect());
+
+#if defined(OS_WIN) && BUILDFLAG(USE_BROWSER_SPELLCHECKER)
+    if (spellcheck::WindowsVersionSupportsSpellchecker())
+      RunUntilCallbackReceived();
+#endif  // defined(OS_WIN) && BUILDFLAG(USE_BROWSER_SPELLCHECKER)
   }
 
   void ForceSuggestMode() {
@@ -70,14 +159,32 @@
  private:
   std::unique_ptr<SpellingMenuObserver> observer_;
   std::unique_ptr<MockRenderViewContextMenu> menu_;
+
+#if defined(OS_WIN) && BUILDFLAG(USE_BROWSER_SPELLCHECKER)
+  // Quits the RunLoop on receiving callbacks.
+  base::OnceClosure quit_;
+
+  // Flag used for early exit from RunLoop if callback already received.
+  bool callback_received_ = false;
+
+  base::test::ScopedFeatureList feature_list_;
+#endif  // defined(OS_WIN) && BUILDFLAG(USE_BROWSER_SPELLCHECKER)
+
   DISALLOW_COPY_AND_ASSIGN(SpellingMenuObserverTest);
 };
 
+#if defined(OS_WIN) && BUILDFLAG(USE_BROWSER_SPELLCHECKER)
 SpellingMenuObserverTest::SpellingMenuObserverTest() {
+  feature_list_.InitWithFeatures(
+      /*enabled_features=*/{spellcheck::kWinUseBrowserSpellChecker,
+                            spellcheck::kWinRetrieveSuggestionsOnlyOnDemand},
+      /*disabled_features=*/{spellcheck::kWinDelaySpellcheckServiceInit});
 }
+#else
+SpellingMenuObserverTest::SpellingMenuObserverTest() = default;
+#endif  // defined(OS_WIN) && BUILDFLAG(USE_BROWSER_SPELLCHECKER)
 
-SpellingMenuObserverTest::~SpellingMenuObserverTest() {
-}
+SpellingMenuObserverTest::~SpellingMenuObserverTest() = default;
 
 }  // namespace
 
@@ -90,7 +197,8 @@
 // Tests that right-clicking a misspelled word adds two items:
 // "Add to dictionary", "Use enhanced spell check".
 IN_PROC_BROWSER_TEST_F(SpellingMenuObserverTest, InitMenuWithMisspelledWord) {
-  InitMenu("wiimode", nullptr);
+  // Pick word that Windows platform spellcheck has no suggestions for.
+  InitMenu("missssspelling", nullptr);
   EXPECT_EQ(2U, menu()->GetMenuSize());
 
   // Read all the context-menu items added by this test and verify they are
@@ -110,9 +218,160 @@
   menu()->GetMenuItem(2, &item);
 }
 
+#if defined(OS_WIN) && BUILDFLAG(USE_BROWSER_SPELLCHECKER)
+// Tests that right-clicking a misspelled word that is identified as misspelled
+// by both Hunspell and Windows platform combines their suggestions.
+IN_PROC_BROWSER_TEST_F(SpellingMenuObserverTest,
+                       WinInitMenuWithMisspelledWordCombined) {
+  // Test invalid for Windows versions that don't support platform spellchecker.
+  if (!spellcheck::WindowsVersionSupportsSpellchecker())
+    return;
+
+  InitMenu("mispelled", "misspelling");
+  EXPECT_EQ(6U, menu()->GetMenuSize());
+
+  // Read all the context-menu items added by this test and verify they are
+  // expected ones.
+  MockRenderViewContextMenu::MockMenuItem item;
+  // First separator.
+  menu()->GetMenuItem(0, &item);
+  EXPECT_EQ(-1, item.command_id);
+  EXPECT_FALSE(item.enabled);
+  EXPECT_FALSE(item.hidden);
+  // First suggestion.
+  menu()->GetMenuItem(1, &item);
+  EXPECT_EQ(IDC_SPELLCHECK_SUGGESTION_0, item.command_id);
+  EXPECT_TRUE(item.enabled);
+  EXPECT_FALSE(item.hidden);
+  EXPECT_EQ(base::ASCIIToUTF16("misspelled"), item.title);
+  // Second suggestion.
+  menu()->GetMenuItem(2, &item);
+  EXPECT_EQ(IDC_SPELLCHECK_SUGGESTION_0 + 1, item.command_id);
+  EXPECT_TRUE(item.enabled);
+  EXPECT_FALSE(item.hidden);
+  EXPECT_EQ(base::ASCIIToUTF16("misspelling"), item.title);
+  // Second separator.
+  menu()->GetMenuItem(3, &item);
+  EXPECT_EQ(-1, item.command_id);
+  EXPECT_FALSE(item.enabled);
+  EXPECT_FALSE(item.hidden);
+  // Add to dictionary.
+  menu()->GetMenuItem(4, &item);
+  EXPECT_EQ(IDC_SPELLCHECK_ADD_TO_DICTIONARY, item.command_id);
+  EXPECT_TRUE(item.enabled);
+  EXPECT_FALSE(item.hidden);
+  // Enhanced spellcheck toggle.
+  menu()->GetMenuItem(5, &item);
+  EXPECT_EQ(IDC_CONTENT_CONTEXT_SPELLING_TOGGLE, item.command_id);
+  EXPECT_TRUE(item.enabled);
+  EXPECT_FALSE(item.checked);
+  EXPECT_FALSE(item.hidden);
+}
+
+// Tests that right-clicking a misspelled word that is identified as misspelled
+// by both Hunspell and Windows platform with the same suggestion leads to a
+// single suggestion.
+IN_PROC_BROWSER_TEST_F(SpellingMenuObserverTest,
+                       WinInitMenuWithMisspelledWordNoDuplicateSuggestions) {
+  // Test invalid for Windows versions that don't support platform spellchecker.
+  if (!spellcheck::WindowsVersionSupportsSpellchecker())
+    return;
+
+  InitMenu("mispelled", "misspelled");
+  EXPECT_EQ(5U, menu()->GetMenuSize());
+
+  // Read all the context-menu items added by this test and verify they are
+  // expected ones.
+  MockRenderViewContextMenu::MockMenuItem item;
+  // First separator.
+  menu()->GetMenuItem(0, &item);
+  EXPECT_EQ(-1, item.command_id);
+  EXPECT_FALSE(item.enabled);
+  EXPECT_FALSE(item.hidden);
+  // First and only suggestion.
+  menu()->GetMenuItem(1, &item);
+  EXPECT_EQ(IDC_SPELLCHECK_SUGGESTION_0, item.command_id);
+  EXPECT_TRUE(item.enabled);
+  EXPECT_FALSE(item.hidden);
+  EXPECT_EQ(base::ASCIIToUTF16("misspelled"), item.title);
+  // Second separator.
+  menu()->GetMenuItem(2, &item);
+  EXPECT_EQ(-1, item.command_id);
+  EXPECT_FALSE(item.enabled);
+  EXPECT_FALSE(item.hidden);
+  // Add to dictionary.
+  menu()->GetMenuItem(3, &item);
+  EXPECT_EQ(IDC_SPELLCHECK_ADD_TO_DICTIONARY, item.command_id);
+  EXPECT_TRUE(item.enabled);
+  EXPECT_FALSE(item.hidden);
+  // Enhanced spellcheck toggle.
+  menu()->GetMenuItem(4, &item);
+  EXPECT_EQ(IDC_CONTENT_CONTEXT_SPELLING_TOGGLE, item.command_id);
+  EXPECT_TRUE(item.enabled);
+  EXPECT_FALSE(item.checked);
+  EXPECT_FALSE(item.hidden);
+}
+
+// Tests that right-clicking a misspelled word that is identified as misspelled
+// by both Hunspell and Windows platform that has > 3 suggestions only displays
+// 3 suggestions.
+IN_PROC_BROWSER_TEST_F(SpellingMenuObserverTest,
+                       WinInitMenuWithMisspelledWordMaxSuggestions) {
+  // Test invalid for Windows versions that don't support platform spellchecker.
+  if (!spellcheck::WindowsVersionSupportsSpellchecker())
+    return;
+
+  InitMenu("wtree", "wee");
+  EXPECT_EQ(7U, menu()->GetMenuSize());
+
+  // Read all the context-menu items added by this test and verify they are
+  // expected ones.
+  MockRenderViewContextMenu::MockMenuItem item;
+  // First separator.
+  menu()->GetMenuItem(0, &item);
+  EXPECT_EQ(-1, item.command_id);
+  EXPECT_FALSE(item.enabled);
+  EXPECT_FALSE(item.hidden);
+  // First suggestion.
+  menu()->GetMenuItem(1, &item);
+  EXPECT_EQ(IDC_SPELLCHECK_SUGGESTION_0, item.command_id);
+  EXPECT_TRUE(item.enabled);
+  EXPECT_FALSE(item.hidden);
+  EXPECT_EQ(base::ASCIIToUTF16("tree"), item.title);
+  // Second suggestion.
+  menu()->GetMenuItem(2, &item);
+  EXPECT_EQ(IDC_SPELLCHECK_SUGGESTION_0 + 1, item.command_id);
+  EXPECT_TRUE(item.enabled);
+  EXPECT_FALSE(item.hidden);
+  EXPECT_EQ(base::ASCIIToUTF16("wee"), item.title);
+  // Third suggestion.
+  menu()->GetMenuItem(3, &item);
+  EXPECT_EQ(IDC_SPELLCHECK_SUGGESTION_0 + 2, item.command_id);
+  EXPECT_TRUE(item.enabled);
+  EXPECT_FALSE(item.hidden);
+  EXPECT_EQ(base::ASCIIToUTF16("were"), item.title);
+  // Second separator.
+  menu()->GetMenuItem(4, &item);
+  EXPECT_EQ(-1, item.command_id);
+  EXPECT_FALSE(item.enabled);
+  EXPECT_FALSE(item.hidden);
+  // Add to dictionary.
+  menu()->GetMenuItem(5, &item);
+  EXPECT_EQ(IDC_SPELLCHECK_ADD_TO_DICTIONARY, item.command_id);
+  EXPECT_TRUE(item.enabled);
+  EXPECT_FALSE(item.hidden);
+  // Enhanced spellcheck toggle.
+  menu()->GetMenuItem(6, &item);
+  EXPECT_EQ(IDC_CONTENT_CONTEXT_SPELLING_TOGGLE, item.command_id);
+  EXPECT_TRUE(item.enabled);
+  EXPECT_FALSE(item.checked);
+  EXPECT_FALSE(item.hidden);
+}
+#endif  // defined(OS_WIN) && BUILDFLAG(USE_BROWSER_SPELLCHECKER)
+
 // Tests that right-clicking a correct word when we enable spelling-service
 // integration to verify an item "Use enhanced spell check" is checked. Even
-// though this meanu itself does not add this item, its sub-menu adds the item
+// though this menu itself does not add this item, its sub-menu adds the item
 // and calls SpellingMenuObserver::IsChecked() to check it.
 IN_PROC_BROWSER_TEST_F(SpellingMenuObserverTest,
                        EnableSpellingServiceWithCorrectWord) {
@@ -135,7 +394,8 @@
   menu()->GetPrefs()->Set(spellcheck::prefs::kSpellCheckDictionaries,
                           dictionary);
 
-  InitMenu("wiimode", nullptr);
+  // Pick word that Windows platform spellcheck has no suggestions for.
+  InitMenu("missssspelling", nullptr);
   EXPECT_EQ(2U, menu()->GetMenuSize());
 
   // To avoid duplicates, this test reads only the "Use enhanced spell check"
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/options/options.css b/chrome/browser/resources/chromeos/accessibility/chromevox/options/options.css
index 7638100..30f0ce4 100644
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/options/options.css
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/options/options.css
@@ -65,6 +65,11 @@
     width: 40px;
 }
 
+#developerLog {
+  padding: 0;
+  width: 686px;
+}
+
 .option .developerLog {
   height: 60px;
   padding-inline-end: 0;
@@ -78,6 +83,7 @@
 
 .option .developer-option-icon-button {
   height: 60px;
+  padding: 0 20px;
 }
 
 .option input[type='checkbox']:checked {
diff --git a/chrome/browser/resources/chromeos/accessibility/select_to_speak/BUILD.gn b/chrome/browser/resources/chromeos/accessibility/select_to_speak/BUILD.gn
index f3ba67a97..4e7d0010 100644
--- a/chrome/browser/resources/chromeos/accessibility/select_to_speak/BUILD.gn
+++ b/chrome/browser/resources/chromeos/accessibility/select_to_speak/BUILD.gn
@@ -108,7 +108,7 @@
     "pipe.jpg",
   ]
   extra_js_files = [
-    "//chrome/test/data/webui/settings/fake_settings_private.js",
+    "fake_settings_private.js",
     "//chrome/test/data/webui/fake_chrome_event.js",
   ]
 
diff --git a/chrome/browser/resources/chromeos/accessibility/select_to_speak/fake_settings_private.js b/chrome/browser/resources/chromeos/accessibility/select_to_speak/fake_settings_private.js
new file mode 100644
index 0000000..9000116
--- /dev/null
+++ b/chrome/browser/resources/chromeos/accessibility/select_to_speak/fake_settings_private.js
@@ -0,0 +1,154 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+/**
+ * @fileoverview Fake implementation of chrome.settingsPrivate for testing.
+ * Note: This file is deprecated and is no longer maintained by the browser
+ * settings/WebUI team. For the updated version (JS modules only), see
+ * chrome/test/data/webui/settings/fake_settings_private.js.
+ */
+cr.define('settings', function() {
+  /**
+   * @typedef {Array<{key: string,
+   *               type: chrome.settingsPrivate.PrefType,
+   *               values: !Array<*>}>}
+   */
+  let FakeSettingsPrivatePref;
+
+  /**
+   * Creates a deep copy of the object.
+   * @param {*} obj
+   * @return {*}
+   */
+  function deepCopy(obj) {
+    return JSON.parse(JSON.stringify(obj));
+  }
+
+  /**
+   * Fake of chrome.settingsPrivate API. Use by setting
+   * CrSettingsPrefs.deferInitialization to true, then passing a
+   * FakeSettingsPrivate to settings-prefs#initialize().
+   * @implements {SettingsPrivate}
+   */
+  class FakeSettingsPrivate {
+    /** @param {Array<!settings.FakeSettingsPrivatePref>=} opt_initialPrefs */
+    constructor(opt_initialPrefs) {
+      this.disallowSetPref_ = false;
+      this.failNextSetPref_ = false;
+
+      this.prefs = {};
+
+      if (!opt_initialPrefs) {
+        return;
+      }
+      for (const pref of opt_initialPrefs) {
+        this.addPref_(pref.type, pref.key, pref.value);
+      }
+
+      // chrome.settingsPrivate override.
+      this.onPrefsChanged = /** @type {!ChromeEvent} */ (new FakeChromeEvent());
+    }
+
+    // chrome.settingsPrivate overrides.
+    getAllPrefs(callback) {
+      // Send a copy of prefs to keep our internal state private.
+      const prefs = [];
+      for (const key in this.prefs) {
+        prefs.push(deepCopy(this.prefs[key]));
+      }
+
+      // Run the callback asynchronously to test that the prefs aren't actually
+      // used before they become available.
+      setTimeout(callback.bind(null, prefs));
+    }
+
+    setPref(key, value, pageId, callback) {
+      const pref = this.prefs[key];
+      assertNotEquals(undefined, pref);
+      assertEquals(typeof value, typeof pref.value);
+      assertEquals(Array.isArray(value), Array.isArray(pref.value));
+
+      if (this.failNextSetPref_) {
+        callback(false);
+        this.failNextSetPref_ = false;
+        return;
+      }
+      assertNotEquals(true, this.disallowSetPref_);
+
+      const changed = JSON.stringify(pref.value) !== JSON.stringify(value);
+      pref.value = deepCopy(value);
+      callback(true);
+
+      // Like chrome.settingsPrivate, send a notification when prefs change.
+      if (changed) {
+        this.sendPrefChanges([{key, value}]);
+      }
+    }
+
+    getPref(key, callback) {
+      const pref = this.prefs[key];
+      assertNotEquals(undefined, pref);
+      callback(
+          /** @type {!chrome.settingsPrivate.PrefObject} */ (deepCopy(pref)));
+    }
+
+    // Functions used by tests.
+
+    /** Instructs the API to return a failure when setPref is next called. */
+    failNextSetPref() {
+      this.failNextSetPref_ = true;
+    }
+
+    /** Instructs the API to assert (fail the test) if setPref is called. */
+    disallowSetPref() {
+      this.disallowSetPref_ = true;
+    }
+
+    allowSetPref() {
+      this.disallowSetPref_ = false;
+    }
+
+    /**
+     * Notifies the listeners of pref changes.
+     * @param {!Array<{key: string, value: *}>} changes
+     */
+    sendPrefChanges(changes) {
+      const prefs = [];
+      for (const change of changes) {
+        const pref = this.prefs[change.key];
+        assertNotEquals(undefined, pref);
+        pref.value = change.value;
+        prefs.push(deepCopy(pref));
+      }
+      /** @type {FakeChromeEvent} */ (this.onPrefsChanged).callListeners(prefs);
+    }
+
+    /** @override */
+    getDefaultZoom() {}
+
+    /** @override */
+    setDefaultZoom() {}
+
+    // Private methods for use by the fake API.
+
+    /**
+     * @param {!chrome.settingsPrivate.PrefType} type
+     * @param {string} key
+     * @param {*} value
+     * @private
+     */
+    addPref_(type, key, value) {
+      this.prefs[key] = {
+        type,
+        key,
+        value,
+      };
+    }
+  }
+
+  return {
+    FakeSettingsPrivate,
+    FakeSettingsPrivatePref,
+  };
+});
diff --git a/chrome/browser/resources/chromeos/accessibility/select_to_speak/select_to_speak_prefs_test.js b/chrome/browser/resources/chromeos/accessibility/select_to_speak/select_to_speak_prefs_test.js
index cbe3ad9..ef75ddd1 100644
--- a/chrome/browser/resources/chromeos/accessibility/select_to_speak/select_to_speak_prefs_test.js
+++ b/chrome/browser/resources/chromeos/accessibility/select_to_speak/select_to_speak_prefs_test.js
@@ -5,8 +5,7 @@
 GEN_INCLUDE(['select_to_speak_e2e_test_base.js']);
 GEN_INCLUDE(['../../../../../../ui/webui/resources/js/cr.js']);
 GEN_INCLUDE(['../../../../../test/data/webui/fake_chrome_event.js']);
-GEN_INCLUDE(
-    ['../../../../../test/data/webui/settings/fake_settings_private.js']);
+GEN_INCLUDE(['fake_settings_private.js']);
 GEN_INCLUDE(['mock_storage.js']);
 
 /**
diff --git a/chrome/browser/resources/chromeos/login/debug/debug.js b/chrome/browser/resources/chromeos/login/debug/debug.js
index 13e799f..f3eb319 100644
--- a/chrome/browser/resources/chromeos/login/debug/debug.js
+++ b/chrome/browser/resources/chromeos/login/debug/debug.js
@@ -943,6 +943,9 @@
             optInDefaultState: true,
             legalFooterVisibility: false,
           },
+          trigger: (screen) => {
+            screen.updateA11ySettingsButtonVisibility(false);
+          },
         },
         {
           id: 'NoOptionToSubscribe',
@@ -951,6 +954,9 @@
             optInDefaultState: false,
             legalFooterVisibility: false,
           },
+          trigger: (screen) => {
+            screen.updateA11ySettingsButtonVisibility(false);
+          },
         },
         {
           id: 'WithLegalFooter',
@@ -959,6 +965,20 @@
             optInDefaultState: true,
             legalFooterVisibility: true,
           },
+          trigger: (screen) => {
+            screen.updateA11ySettingsButtonVisibility(false);
+          },
+        },
+        {
+          id: 'WithAceessibilityButton',
+          data: {
+            optInVisibility: true,
+            optInDefaultState: true,
+            legalFooterVisibility: true,
+          },
+          trigger: (screen) => {
+            screen.updateA11ySettingsButtonVisibility(true);
+          },
         },
       ],
     },
diff --git a/chrome/browser/resources/chromeos/login/enterprise_enrollment.html b/chrome/browser/resources/chromeos/login/enterprise_enrollment.html
index 92415ea..2ed45c93 100644
--- a/chrome/browser/resources/chromeos/login/enterprise_enrollment.html
+++ b/chrome/browser/resources/chromeos/login/enterprise_enrollment.html
@@ -9,7 +9,7 @@
 
 <link rel="import" href="/components/common_styles.html">
 <link rel="import" href="/components/hd_iron_icon.html">
-<link rel="import" href="/components/oobe_dialog.html">
+<link rel="import" href="/components/oobe_adaptive_dialog.html">
 <link rel="import" href="/components/oobe_i18n_behavior.html">
 <link rel="import" href="/components/login_screen_behavior.html">
 <link rel="import" href="/components/multi_step_behavior.html">
@@ -32,9 +32,24 @@
         vertical-align: top;
       }
 
+      :host-context(html[new-layout]) #contentEnrolling {
+        display: none;
+      }
+
+      :host-context(html:not([new-layout])) #subtitleEnrolling {
+        display: none;
+      }
+
       #attributePromptMessage {
         color: black;
       }
+
+      :host-context(html:not(html[new-layout])) .illustration {
+        display: none;
+      }
+      :host-context(html[new-layout]) .illustration-old {
+        display: none;
+      }
     </style>
     <!-- OAUTH GAIA DIALOG -->
     <gaia-dialog id="step-signin" class="flex" for-step="signin"
@@ -47,19 +62,24 @@
     </gaia-dialog>
 
     <!-- ENROLLMENT IN PROGRESS (SPINNER) DIALOG -->
-    <oobe-dialog id="step-working" for-step="working"
-        title-key="oauthEnrollScreenTitle">
-      <hd-iron-icon slot="oobe-icon"
+    <oobe-adaptive-dialog id="step-working" for-step="working">
+      <hd-iron-icon slot="icon"
           icon1x="oobe-32:enterprise" icon2x="oobe-64:enterprise">
       </hd-iron-icon>
+      <h1 slot="title">
+        [[i18nDynamic(locale, 'oauthEnrollScreenTitle')]]
+      </h1>
+      <p slot="subtitle" id="subtitleEnrolling">
+        [[i18nDynamic(locale, 'oauthEnrollWorking')]]
+      </p>
       <paper-progress slot="progress" indeterminate>
       </paper-progress>
-      <div slot="footer" class="flex layout vertical" role="alert">
-        <div class="step-message">
+      <div slot="content" class="flex layout vertical" role="alert">
+        <div class="step-message" id="contentEnrolling">
           [[i18nDynamic(locale, 'oauthEnrollWorking')]]
         </div>
       </div>
-    </oobe-dialog>
+    </oobe-adaptive-dialog>
 
     <offline-ad-login-element id="step-ad-join" is-domain-join
         for-step="ad-join"
@@ -78,14 +98,14 @@
             AttributePrompt: [ 'Done' ]
             ActiveDirectoryJoin: [ 'Try Again' ]
     -->
-    <oobe-dialog id="step-error" has-buttons role="alert"
+    <oobe-adaptive-dialog id="step-error" role="alert"
         for-step="error, attribute-prompt-error, active-directory-join-error">
-      <hd-iron-icon slot="oobe-icon"
+      <hd-iron-icon slot="icon"
                     icon1x="oobe-32:warning"
                     icon2x="oobe-64:warning"></hd-iron-icon>
       <h1 slot="title">[[i18nDynamic(locale, 'oauthEnrollErrorTitle')]]</h1>
       <div id="errorMsg" slot="subtitle">[[errorText_]]</div>
-      <div slot="footer"
+      <div slot="content"
           class="flex layout vertical center center-justified">
         <img srcset="images/1x/error.svg 1x,
                      images/2x/error.svg 2x"
@@ -135,41 +155,53 @@
         </div>
 
       </div>
-    </oobe-dialog>
+    </oobe-adaptive-dialog>
 
     <!-- SUCCESS DIALOG -->
-    <oobe-dialog has-buttons id="step-success" for-step="success" role="alert"
-        title-key="oauthEnrollSuccessTitle" footer-shrinkable>
-      <hd-iron-icon slot="oobe-icon"
+    <oobe-adaptive-dialog id="step-success" for-step="success" role="alert"
+        footer-shrinkable>
+      <hd-iron-icon slot="icon"
           icon1x="oobe-32:enterprise" icon2x="oobe-64:enterprise">
       </hd-iron-icon>
+      <h1 slot="title">
+        [[i18nDynamic(locale, 'oauthEnrollSuccessTitle')]]
+      </h1>
       <!-- Hide the subtitle if the domain could not be determined. -->
       <div hidden="[[isEmpty_(domainManager_)]]" class="self-start"
           slot="subtitle">
         <div>[[successText_(locale, deviceName_, domainManager_)]]</div>
       </div>
-      <div slot="footer" class="flex layout vertical center end-justified">
+      <div slot="content" class="flex layout vertical center center-justified">
         <img srcset="images/enrollment_success_illustration_1x.png 1x,
-                images/enrollment_success_illustration_2x.png 2x"
+                     images/enrollment_success_illustration_2x.png 2x"
             alt$="[[i18nDynamic(locale,
                 'enrollmentSuccessIllustrationTitle')]]"
-            class="oobe-illustration">
+            class="oobe-illustration illustration-old">
+        <img src="images/enrollment_complete.svg"
+            alt$="[[i18nDynamic(locale,
+                  'enrollmentSuccessIllustrationTitle')]]"
+            class="oobe-illustration illustration">
       </div>
-      <div slot="bottom-buttons" class="layout horizontal end-justified">
+      <div slot="bottom-buttons">
         <oobe-text-button inverse id="successDoneButton"
             text-key="oauthEnrollDone" on-click="onEnrollmentFinished_"
             class="focus-on-show"></oobe-text-button>
       </div>
-    </oobe-dialog>
+    </oobe-adaptive-dialog>
 
     <!-- ATTRIBUTE PROMPT DIALOG -->
-    <oobe-dialog id="step-attribute-prompt" for-step="attribute-prompt"
-        has-buttons title-key="oauthEnrollScreenTitle"
-        subtitle-key="oauthEnrollDeviceInformation">
-      <hd-iron-icon slot="oobe-icon"
+    <oobe-adaptive-dialog id="step-attribute-prompt"
+        for-step="attribute-prompt">
+      <hd-iron-icon slot="icon"
           icon1x="oobe-32:enterprise" icon2x="oobe-64:enterprise">
       </hd-iron-icon>
-      <div slot="footer" class="flex layout vertical">
+      <h1 slot="title">
+        [[i18nDynamic(locale, 'oauthEnrollScreenTitle')]]
+      </h1>
+      <p slot="subtitle">
+        [[i18nDynamic(locale, 'oauthEnrollDeviceInformation')]]
+      </p>
+      <div slot="content" class="flex layout vertical">
         <div class="step-message">
           <span id="attributePromptMessage">
             [[i18nDynamic(locale, 'oauthEnrollAttributeExplanation')]]
@@ -189,7 +221,7 @@
           </cr-input>
         </div>
       </div>
-      <div slot="bottom-buttons" class="layout horizontal end-justified">
+      <div slot="bottom-buttons">
         <oobe-text-button id="attributesSkip"
             text-key="oauthEnrollSkip" on-click="skipAttributes_">
         </oobe-text-button>
@@ -197,6 +229,6 @@
         <oobe-next-button id="attributesSubmit"
             on-click="submitAttributes_"></oobe-next-button>
       </div>
-    </oobe-dialog>
+    </oobe-adaptive-dialog>
   </template>
 </dom-module>
diff --git a/chrome/browser/resources/chromeos/login/images/enrollment_complete.svg b/chrome/browser/resources/chromeos/login/images/enrollment_complete.svg
new file mode 100644
index 0000000..b81833bd
--- /dev/null
+++ b/chrome/browser/resources/chromeos/login/images/enrollment_complete.svg
@@ -0,0 +1 @@
+<svg width="520" height="320" viewBox="0 0 520 320" fill="none" xmlns="http://www.w3.org/2000/svg"><path fill="#fff" d="M0 0h520v320H0z"/><g clip-path="url(#clip0)"><path fill="#fff" d="M0 31.733h520v256.533H0z"/><path d="M456.855 110.011h-68.519v127.772h68.519V110.011z" stroke="#E6E7EA" stroke-width="1.733" stroke-linejoin="round"/><path d="M399.629 120.887h8.996a3.54 3.54 0 0 1 3.362 2.41 3.555 3.555 0 0 0 3.363 2.409h13.381a3.539 3.539 0 0 0 3.051-1.733l.789-1.344a3.563 3.563 0 0 1 3.059-1.733h9.923a3.552 3.552 0 0 1 3.554 3.545v14.256a3.556 3.556 0 0 1-3.554 3.545H399.62a3.546 3.546 0 0 1-3.545-3.545v-14.23a3.546 3.546 0 0 1 3.554-3.58v0zm0 27.283h8.996a3.546 3.546 0 0 1 3.363 2.418v0a3.548 3.548 0 0 0 3.362 2.409h13.382a3.557 3.557 0 0 0 3.05-1.733l.789-1.343a3.568 3.568 0 0 1 3.059-1.734h9.924a3.567 3.567 0 0 1 3.553 3.554v14.239a3.554 3.554 0 0 1-3.553 3.545H399.62a3.54 3.54 0 0 1-3.282-2.187 3.535 3.535 0 0 1-.271-1.358v-14.257a3.55 3.55 0 0 1 3.562-3.553zm0 27.291h8.996a3.541 3.541 0 0 1 3.362 2.41v0a3.547 3.547 0 0 0 3.363 2.409h13.381a3.555 3.555 0 0 0 3.051-1.733l.789-1.344a3.553 3.553 0 0 1 3.059-1.733h9.923a3.552 3.552 0 0 1 3.554 3.545v14.265a3.558 3.558 0 0 1-3.554 3.545H399.62a3.546 3.546 0 0 1-3.545-3.545v-14.274a3.557 3.557 0 0 1 1.041-2.51 3.547 3.547 0 0 1 2.513-1.035z" stroke="#E6E7EA" stroke-width="1.733" stroke-linejoin="round"/><path d="M193.596 234.906c.216-4.333 1.638-30.741 3.648-44.512 1.864-12.792 8.104-29.571 20.454-35.039 22.334-9.898 46.921 4.558 68.016 12.592" stroke="#4285F4" stroke-width="1.733" stroke-linecap="round" stroke-linejoin="round"/><path d="M399.629 202.753h8.996a3.547 3.547 0 0 1 3.362 2.409v0a3.555 3.555 0 0 0 3.363 2.409h13.381a3.54 3.54 0 0 0 3.051-1.733l.789-1.335a3.553 3.553 0 0 1 3.059-1.733h9.923a3.552 3.552 0 0 1 3.554 3.545v14.256a3.563 3.563 0 0 1-3.554 3.554H399.62a3.556 3.556 0 0 1-3.545-3.554v-14.274a3.535 3.535 0 0 1 1.041-2.509 3.541 3.541 0 0 1 2.513-1.035z" stroke="#E6E7EA" stroke-width="1.733" stroke-linejoin="round"/><path d="M430.95 131.565h-16.718v2.929h16.718v-2.929zm0 27.282h-16.718v2.93h16.718v-2.93zm0 27.292h-16.718v2.929h16.718v-2.929zm0 27.82h-16.718v2.929h16.718v-2.929z" fill="#4285F4"/><path d="M388.336 237.792v21.493h3.467l1.222-21.493m63.83 0v21.493h-3.467l-1.222-21.493" stroke="#E6E7EA" stroke-width="1.733" stroke-linejoin="round"/><path d="M78.789 259.32h386.724" stroke="#D2E3FC" stroke-width="1.733" stroke-linecap="round" stroke-linejoin="round"/><path d="M73.987 94.341L50.84 96.075a1.396 1.396 0 0 0-1.05 2.175l13 19.197a1.401 1.401 0 0 0 1.256.608 1.401 1.401 0 0 0 1.154-.782l10.149-20.938a1.395 1.395 0 0 0-1.36-1.994z" stroke="#00AC47" stroke-width="1.733" stroke-linecap="round" stroke-linejoin="round"/><path d="M82.793 176.519a21.063 21.063 0 0 1 4.61-1.266l4.949-1.352a17.185 17.185 0 0 0 10.4-8.042l2.886-5.044 1.265-2.21.113.069c.363-.492.763-.955 1.196-1.387a14.47 14.47 0 0 1 10.202-4.289 14.465 14.465 0 0 1 10.247 4.181 14.464 14.464 0 0 1 3.218 15.741 14.46 14.46 0 0 1-3.11 4.708c-.434.429-.894.832-1.378 1.205l.061.121-2.193 1.283-5.009 2.929a17.152 17.152 0 0 0-7.93 10.452l-1.309 4.966a20.563 20.563 0 0 1-1.213 4.628 20.657 20.657 0 0 1-14.957 12.645 20.657 20.657 0 0 1-24.012-14.173 20.652 20.652 0 0 1 11.99-25.174l-.026.009z" fill="#4285F4"/><path d="M97.465 130.767a7.055 7.055 0 1 0 0-14.11 7.055 7.055 0 0 0 0 14.11z" fill="#EA4335"/><path d="M123.747 138.519l-2.431 24.31a1.525 1.525 0 0 0 1.366 1.669l24.086 2.409a1.526 1.526 0 0 0 1.669-1.366l2.431-24.311a1.525 1.525 0 0 0-1.366-1.669l-24.086-2.408a1.524 1.524 0 0 0-1.669 1.366z" stroke="#D2E3FC" stroke-width="1.733" stroke-linecap="round" stroke-linejoin="round"/><path d="M327.514 188.193H287.82c-19.825 0-35.897 16.071-35.897 35.897 0 19.826 16.072 35.897 35.897 35.897h39.694c19.825 0 35.897-16.071 35.897-35.897 0-19.826-16.072-35.897-35.897-35.897z" fill="#34A853"/><path d="M299.71 155.944c6.171 13.667 7.956 32.587-8.554 39.312-6.257 2.548-13.372 2.921-20.002 1.846-12.229-1.985-23.253-8.771-34.39-13.815l-3.198 51.619h-39.97m-.035.633s-.624 11.327-.936 13.944a7.006 7.006 0 0 1-.866 2.6 4.624 4.624 0 0 1-2.869 2.011 7.425 7.425 0 0 0-2.964 1.153 7.8 7.8 0 0 0-2.236 4.064h16.319a14.97 14.97 0 0 0 13.148-7.8l1.56-2.851v10.66h7.678s1.075-10.4 2.444-23.721" stroke="#4285F4" stroke-width="1.733" stroke-linecap="round" stroke-linejoin="round"/><path d="M245.822 223.31l6.066 23.053-7.176 2.713-3.77-9.975-.45 3.224a14.996 14.996 0 0 1-3.126 7.288 14.993 14.993 0 0 1-6.408 4.672l-8.389 2.929" stroke="#4285F4" stroke-width="1.733" stroke-linecap="round" stroke-linejoin="round"/><path d="M280.263 88.933c1.846 4.81 7.739 8.164 13.52 8.104l-.72-3.38-1.577-2.748-3.259-2.435-3.406-.97-4.558 1.43z" fill="#4285F4"/><path d="M284.63 105.591a8.953 8.953 0 1 0 0-17.906 8.953 8.953 0 0 0 0 17.906zm-3.535 4.558l.658-4.558m9.152 4.688l-1.135-6.309" stroke="#4285F4" stroke-width="1.733" stroke-linecap="round" stroke-linejoin="round"/><path d="M294.883 89.358a6.678 6.678 0 0 1 6.392-4.257 6.676 6.676 0 0 1 6.166 4.578c.072-.525.203-1.04.39-1.534a6.667 6.667 0 0 1 8.667-3.796 2.402 2.402 0 0 0 3.155-1.482c.06-.174.13-.338.199-.512a6.747 6.747 0 0 1 7.523-3.822 6.642 6.642 0 0 1 1.967.763 2.296 2.296 0 0 0 3.302-1.283l1.612-4.68a5.045 5.045 0 0 1 8.45-1.794 5.032 5.032 0 0 1 1.166 4.821 5.031 5.031 0 0 1-3.532 3.482l-6.067 1.647a2.305 2.305 0 0 0-1.672 2.6 6.673 6.673 0 0 1-9.022 7.202h-.044a2.364 2.364 0 0 0-3.111 1.421l-.121.312a6.676 6.676 0 0 1-12.558-.312 6.495 6.495 0 0 1-.39 1.534 6.688 6.688 0 0 1-6.106 4.436 6.68 6.68 0 0 1-6.34-9.29l-.026-.034z" fill="#4285F4"/><path d="M278.304 164.767c3.597-2.34 5.599-6.388 6.648-10.4a248.581 248.581 0 0 0 5.598-27.491m-32.283 11.917c-6.994-6.292-17.151-14.378-24.041-15.02a18.209 18.209 0 0 0-8.736 1.638 29.729 29.729 0 0 0-12.359 9.915c-.633.867-1.127 2.305-.199 2.877.71.442 1.612-.095 2.27-.615l9.534-7.471a17.323 17.323 0 0 0-4.862 6.067 6.69 6.69 0 0 0-.798 4.455c.382 1.49 1.907 2.764 3.398 2.392" stroke="#4285F4" stroke-width="1.733" stroke-linecap="round" stroke-linejoin="round"/><path d="M299.711 155.944s.641-9.88.866-15.184c.382-9.663 2.158-19.24-4.194-26.598-5.296-6.127-14.3-6.405-20.289-1.014-7.8 7.055-9.161 20.887-10.799 32.301a194.504 194.504 0 0 0-7.02-6.656M236.6 187.031c3.467 13.962 9.429 36.998 9.429 36.998s-7.427 2.496-12.029 4.16m-18.702-77.436c-10.851 0-20.428 8.164-23.002 16.986-1.976 6.76-.979 11.718.408 18.616 1.135 5.625 1.958 10.123 3.085 15.739" stroke="#4285F4" stroke-width="1.733" stroke-linecap="round" stroke-linejoin="round"/><path d="M229.667 114.639c2.4-8.572 1.984-19.604-4.533-25.957-6.933-6.734-16.527-7.8-24.163-6.067-9.377 2.184-16.293 8.615-23.764 14.309-6.517 4.983-14.17 9.62-22.698 9.109" stroke="#D2E3FC" stroke-width="1.733" stroke-linecap="round" stroke-linejoin="round"/><path d="M246.012 150.129l11.44-11.215m-17.082 4.472l10.4-10.582M247 153.578l-10.158-15.626m62.869 17.992H284.44m15.271-5.659h-13.589" stroke="#4285F4" stroke-width="1.733" stroke-linecap="round" stroke-linejoin="round"/><path d="M201.881 115.774l5.72 34.979h33.228" stroke="#053975" stroke-width="3.467" stroke-miterlimit="10"/><mask id="a" maskUnits="userSpaceOnUse" x="251" y="188" width="113" height="72"><path d="M327.513 188.193H287.82c-19.826 0-35.898 16.071-35.898 35.897 0 19.826 16.072 35.897 35.898 35.897h39.693c19.826 0 35.897-16.071 35.897-35.897 0-19.826-16.071-35.897-35.897-35.897z" fill="#34A853"/></mask><g mask="url(#a)"><path d="M299.71 155.944c6.171 13.667 7.956 32.587-8.554 39.312-6.257 2.548-13.372 2.921-20.002 1.846-12.229-1.985-23.253-8.771-34.39-13.815l-3.198 51.619h-39.97" stroke="#fff" stroke-width="1.733" stroke-linecap="round" stroke-linejoin="round"/></g><path d="M227.067 132.379a11.647 11.647 0 0 0-4.273 9.074 3.38 3.38 0 0 0 .52 2.011 3.32 3.32 0 0 0 2.089 1.049h.19c.035 0-.104-.113-.095-.044" stroke="#4285F4" stroke-width="1.733" stroke-linecap="round" stroke-linejoin="round"/><path d="M229.814 134.537a13.428 13.428 0 0 0-3.727 5.495c-.615 1.82-1.222 5.607 1.664 5.789 1.734.113 3.207-1.317 4.212-2.6.633-.797 2.739-3.466 1.309-4.333a.862.862 0 0 0-.503-.121c-1.655 0-3.345 1.733-4.064 3.085m3.64-3.059c2.14-.338 2.764-.13 4.498-.867" stroke="#4285F4" stroke-width="1.733" stroke-linecap="round" stroke-linejoin="round"/><mask id="b" maskUnits="userSpaceOnUse" x="69" y="152" width="64" height="65"><path d="M82.793 176.519a21.063 21.063 0 0 1 4.61-1.266l4.949-1.352a17.185 17.185 0 0 0 10.4-8.042l2.886-5.044 1.265-2.21.113.069c.363-.492.763-.955 1.196-1.387a14.47 14.47 0 0 1 10.202-4.289 14.465 14.465 0 0 1 10.247 4.181 14.464 14.464 0 0 1 3.218 15.741 14.46 14.46 0 0 1-3.11 4.708c-.434.429-.894.832-1.378 1.205l.061.121-2.193 1.283-5.009 2.929a17.152 17.152 0 0 0-7.93 10.452l-1.309 4.966a20.563 20.563 0 0 1-1.213 4.628 20.657 20.657 0 0 1-14.957 12.645 20.657 20.657 0 0 1-24.012-14.173 20.652 20.652 0 0 1 11.99-25.174l-.026.009z" fill="#4285F4"/></mask><g mask="url(#b)"><path d="M123.747 138.519l-2.431 24.31a1.525 1.525 0 0 0 1.366 1.669l24.086 2.409a1.526 1.526 0 0 0 1.669-1.366l2.431-24.311a1.525 1.525 0 0 0-1.366-1.669l-24.086-2.408a1.524 1.524 0 0 0-1.669 1.366z" stroke="#FBBC05" stroke-width="1.733" stroke-linecap="round" stroke-linejoin="round"/></g><path d="M246.012 141.973a4.324 4.324 0 1 0 .001-8.649 4.324 4.324 0 0 0-.001 8.649z" fill="#fff" stroke="#4285F4" stroke-width="1.733" stroke-linecap="round" stroke-linejoin="round"/><path d="M245.761 136.418l.26 1.265 1.256.269m29.095-37.839s3.874 1.006 5.052-1.482" stroke="#4285F4" stroke-width="1.733" stroke-linecap="round" stroke-linejoin="round"/><path d="M138.155 85.233a3.329 3.329 0 0 1-.806 5.26 15.052 15.052 0 0 1-20.158-20.626 3.331 3.331 0 0 1 2.47-1.656 3.325 3.325 0 0 1 2.808.98l15.686 16.042z" fill="#FBBC05"/></g><defs><clipPath id="clip0"><path fill="#fff" transform="translate(0 31.733)" d="M0 0h520v256.533H0z"/></clipPath></defs></svg>
\ No newline at end of file
diff --git a/chrome/browser/resources/chromeos/login/marketing_opt_in.html b/chrome/browser/resources/chromeos/login/marketing_opt_in.html
index 955147c..d99a281 100644
--- a/chrome/browser/resources/chromeos/login/marketing_opt_in.html
+++ b/chrome/browser/resources/chromeos/login/marketing_opt_in.html
@@ -135,22 +135,19 @@
           <div id="googleUrl">www.google.com</div>
         </div>
       </div>
-      <div slot="bottom-buttons">
-        <div>
-          <oobe-welcome-secondary-button
-              hidden="[[!isA11ySettingsButtonVisible_]]"
-              id="marketing-opt-in-accessibility-button"
-              on-click="onToggleAccessibilityPage_"
-              icon1x="marketing-opt-in-32:accessibility"
-              icon2x="marketing-opt-in-64:accessibility"
-              text-key="marketingOptInA11yButtonLabel">
-          </oobe-welcome-secondary-button>
-        </div>
-        <oobe-text-button on-click="onGetStarted_" class="focus-on-show"
-            inverse text-key="marketingOptInScreenAllSet"
-            id="marketing-opt-in-next-button">
-        </oobe-text-button>
-      </div>
+      <oobe-welcome-secondary-button slot="bottom-buttons"
+          hidden="[[!isA11ySettingsButtonVisible_]]"
+          id="marketing-opt-in-accessibility-button"
+          on-click="onToggleAccessibilityPage_"
+          icon1x="marketing-opt-in-32:accessibility"
+          icon2x="marketing-opt-in-64:accessibility"
+          text-key="marketingOptInA11yButtonLabel">
+      </oobe-welcome-secondary-button>
+      <oobe-text-button slot="bottom-buttons"
+          on-click="onGetStarted_" class="focus-on-show"
+          inverse text-key="marketingOptInScreenAllSet"
+          id="marketing-opt-in-next-button">
+      </oobe-text-button>
     </oobe-adaptive-dialog>
 
     <oobe-adaptive-dialog id="finalAccessibilityPage" role="dialog"
diff --git a/chrome/browser/resources/feedback_webui/feedback_resources.grd b/chrome/browser/resources/feedback_webui/feedback_resources.grd
index 70ca592..208263d8 100644
--- a/chrome/browser/resources/feedback_webui/feedback_resources.grd
+++ b/chrome/browser/resources/feedback_webui/feedback_resources.grd
@@ -16,7 +16,6 @@
       <include name="IDR_FEEDBACK_FEEDBACK_UTIL_JS" file="js/feedback_util.js" type="BINDATA" />
       <include name="IDR_FEEDBACK_FEEDBACK_JS" file="js/feedback.js" preprocess="true" type="BINDATA" />
       <include name="IDR_FEEDBACK_TAKE_SCREENSHOT_JS" file="js/take_screenshot.js" type="BINDATA" />
-      <include name="IDR_FEEDBACK_TOPBAR_HANDLER_JS" file="js/topbar_handlers.js" type="BINDATA" />
       <include name="IDR_FEEDBACK_FEEDBACK_CSS" file="css/feedback.css" type="BINDATA" />
       <include name="IDR_FEEDBACK_BUTTON_BUTTER_BAR_CLOSE_PNG"
                file="images/button_butter_bar_close.png" type="BINDATA" />
diff --git a/chrome/browser/resources/feedback_webui/html/default.html b/chrome/browser/resources/feedback_webui/html/default.html
index 78a9bd97..3114c032 100644
--- a/chrome/browser/resources/feedback_webui/html/default.html
+++ b/chrome/browser/resources/feedback_webui/html/default.html
@@ -5,24 +5,12 @@
   <meta charset="utf-8">
   <link rel="stylesheet" href="chrome://resources/css/text_defaults.css">
   <link rel="stylesheet" href="chrome://resources/css/apps/common.css"></link>
-  <link rel="stylesheet" href="chrome://resources/css/apps/topbutton_bar.css">
   </link>
   <link rel="stylesheet" href="../css/feedback.css">
 
   <script type="module" src="../js/feedback.js"></script>
 </head>
 <body>
-  <div id="title-bar" class="title-bar">
-    <span id="page-title">$i18n{pageTitle}</span>
-    <span class="topbutton-bar">
-      <button class="minimize-button" id="minimize-button" tabindex="-1"
-              aria-label="$i18n{minimizeBtnLabel}">
-      </button>
-      <button class="close-button" id="close-button" tabindex="-1"
-              aria-label="$i18n{closeBtnLabel}">
-      </button>
-    </span>
-  </div>
   <div id="content-pane" class="content">
     <p id="free-form-text">$i18n{freeFormText}</p>
     <textarea id="description-text" aria-labelledby="free-form-text"
diff --git a/chrome/browser/resources/feedback_webui/js/BUILD.gn b/chrome/browser/resources/feedback_webui/js/BUILD.gn
index 0800056..87599ba5 100644
--- a/chrome/browser/resources/feedback_webui/js/BUILD.gn
+++ b/chrome/browser/resources/feedback_webui/js/BUILD.gn
@@ -13,7 +13,6 @@
 
     # TODO(crbug.com/1167223): Fix and enable remaining js_library() targets.
     #":sys_info",
-    #":topbar_handlers",
   ]
 }
 
@@ -44,7 +43,3 @@
 
 js_library("take_screenshot") {
 }
-
-js_library("topbar_handlers") {
-  deps = [ "//ui/webui/resources/js:util" ]
-}
diff --git a/chrome/browser/resources/feedback_webui/js/topbar_handlers.js b/chrome/browser/resources/feedback_webui/js/topbar_handlers.js
deleted file mode 100644
index 1715221e..0000000
--- a/chrome/browser/resources/feedback_webui/js/topbar_handlers.js
+++ /dev/null
@@ -1,36 +0,0 @@
-// Copyright 2013 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// TODO(crbug.com/1167223): Either port this file to work as a WebUI, or delete.
-
-/**
- * Setup handlers for the minimize and close topbar buttons.
- */
-function initializeHandlers() {
-  // If this dialog is using system window controls, these elements aren't
-  // needed at all.
-  if (window.feedbackInfo.useSystemWindowFrame) {
-    $('minimize-button').hidden = true;
-    $('close-button').hidden = true;
-    return;
-  }
-  $('minimize-button').addEventListener('click', function(e) {
-    e.preventDefault();
-    chrome.app.window.current().minimize();
-  });
-
-  $('minimize-button').addEventListener('mousedown', function(e) {
-    e.preventDefault();
-  });
-
-  $('close-button').addEventListener('click', function() {
-    scheduleWindowClose();
-  });
-
-  $('close-button').addEventListener('mousedown', function(e) {
-    e.preventDefault();
-  });
-}
-
-window.addEventListener('DOMContentLoaded', initializeHandlers);
diff --git a/chrome/browser/resources/print_preview/ui/more_settings.html b/chrome/browser/resources/print_preview/ui/more_settings.html
index d1bda1ab..abfc5c7 100644
--- a/chrome/browser/resources/print_preview/ui/more_settings.html
+++ b/chrome/browser/resources/print_preview/ui/more_settings.html
@@ -18,8 +18,9 @@
 
   :host cr-expand-button {
     flex: 1;
-    margin-inline-end: calc(var(--print-preview-sidebar-margin) + 6px);
-    margin-inline-start: var(--print-preview-sidebar-margin);
+    /* Padding here as cr-expand-button has a background on hover. */
+    padding-inline-end: calc(var(--print-preview-sidebar-margin) + 6px);
+    padding-inline-start: var(--print-preview-sidebar-margin);
     --cr-expand-button-size: 28px;
   }
 
diff --git a/chrome/browser/resources/settings/chromeos/internet_page/network_summary_item.js b/chrome/browser/resources/settings/chromeos/internet_page/network_summary_item.js
index a6a35c6f..d2c33ba5 100644
--- a/chrome/browser/resources/settings/chromeos/internet_page/network_summary_item.js
+++ b/chrome/browser/resources/settings/chromeos/internet_page/network_summary_item.js
@@ -163,9 +163,16 @@
       return name ? this.i18n('networkListItemConnectingTo', name) :
                     this.i18n('networkListItemConnecting');
     }
-    if (networkState.type === mojom.NetworkType.kCellular && deviceState &&
-        deviceState.scanning) {
-      return this.i18n('internetMobileSearching');
+    if (networkState.type === mojom.NetworkType.kCellular && deviceState) {
+      // If there is no cellular SIM and the updated UI flag is disabled,
+      // simply display 'Off'. See b/162564761 for details.
+      if (deviceState.simAbsent &&
+          !loadTimeData.getBoolean('updatedCellularActivationUi')) {
+        return this.i18n('deviceOff');
+      }
+      if (deviceState.scanning) {
+        return this.i18n('internetMobileSearching');
+      }
     }
     return this.i18n('networkListItemNotConnected');
   },
diff --git a/chrome/browser/resources/settings/chromeos/os_settings.gni b/chrome/browser/resources/settings/chromeos/os_settings.gni
index 10f8766..4c50419 100644
--- a/chrome/browser/resources/settings/chromeos/os_settings.gni
+++ b/chrome/browser/resources/settings/chromeos/os_settings.gni
@@ -7,6 +7,39 @@
 import("//ui/webui/resources/cr_elements/chromeos/os_cr_elements.gni")
 import("../settings.gni")
 
+# Common namespace rewrites for all polymer_modulizer() or js_modulizer()
+# targets in Settings.
+settings_namespace_rewrites = [
+  "Polymer.DomIf|DomIf",
+  "Polymer.IronResizableBehavior|IronResizableBehavior",
+  "settings.AboutPageBrowserProxy|AboutPageBrowserProxy",
+  "settings.Account|Account",
+  "settings.AccountManagerBrowserProxy|AccountManagerBrowserProxy",
+  "settings.ExtensionControlBrowserProxy|ExtensionControlBrowserProxy",
+  "settings.FontsBrowserProxy|FontsBrowserProxy",
+  "settings.LanguagesBrowserProxy|LanguagesBrowserProxy",
+  "settings.LifetimeBrowserProxy|LifetimeBrowserProxy",
+  "settings.MetricsReporting|MetricsReporting",
+  "settings.MinimumRoutes|MinimumRoutes",
+  "settings.PageStatus|PageStatus",
+  "Settings.PrefUtil.prefToString|prefToString",
+  "Settings.PrefUtil.stringToPrefValue|stringToPrefValue",
+  "settings.PrivacyPageBrowserProxy|PrivacyPageBrowserProxy",
+  "settings.ProfileInfo|ProfileInfo",
+  "settings.ResolverOption|ResolverOption",
+  "settings.Route|Route",
+  "settings.SearchEnginesBrowserProxy|SearchEnginesBrowserProxy",
+  "settings.SearchRequest|SearchRequest",
+  "settings.SecureDnsMode|SecureDnsMode",
+  "settings.SecureDnsSetting|SecureDnsSetting",
+  "settings.SecureDnsUiManagementMode|SecureDnsUiManagementMode",
+  "settings.StatusAction|StatusAction",
+  "settings.StoredAccount|StoredAccount",
+  "settings.SyncBrowserProxy|SyncBrowserProxy",
+  "settings.SyncPrefs|SyncPrefs",
+  "settings.SyncStatus|SyncStatus",
+]
+
 os_settings_namespace_rewrites = settings_namespace_rewrites +
                                  cr_components_chromeos_namespace_rewrites +
                                  cr_elements_chromeos_namespace_rewrites + [
diff --git a/chrome/browser/resources/settings/languages_page/languages.js b/chrome/browser/resources/settings/languages_page/languages.js
index e6f967c..604d1f09 100644
--- a/chrome/browser/resources/settings/languages_page/languages.js
+++ b/chrome/browser/resources/settings/languages_page/languages.js
@@ -579,14 +579,14 @@
     const {on: spellCheckOnLanguages, off: spellCheckOffLanguages} =
         this.getSpellCheckLanguages_(args.supportedLanguages);
 
-    const alwaysTranslateLangauges =
+    const alwaysTranslateLanguages =
         args.alwaysTranslateCodes.map(code => this.getLanguage(code));
 
     const model = /** @type {!LanguagesModel} */ ({
       supported: args.supportedLanguages,
       enabled: enabledLanguageStates,
       translateTarget: args.translateTarget,
-      alwaysTranslate: alwaysTranslateLangauges,
+      alwaysTranslate: alwaysTranslateLanguages,
       spellCheckOnLanguages,
       spellCheckOffLanguages,
     });
diff --git a/chrome/browser/resources/settings/settings.gni b/chrome/browser/resources/settings/settings.gni
index 001b94566..9dcf9e0 100644
--- a/chrome/browser/resources/settings/settings.gni
+++ b/chrome/browser/resources/settings/settings.gni
@@ -9,36 +9,3 @@
       "js_module_root=../../chrome/browser/resources/settings/",
       "js_module_root=./gen/chrome/browser/resources/settings/",
     ]
-
-# Common namespace rewrites for all polymer_modulizer() or js_modulizer()
-# targets in Settings.
-settings_namespace_rewrites = [
-  "Polymer.DomIf|DomIf",
-  "Polymer.IronResizableBehavior|IronResizableBehavior",
-  "settings.AboutPageBrowserProxy|AboutPageBrowserProxy",
-  "settings.Account|Account",
-  "settings.AccountManagerBrowserProxy|AccountManagerBrowserProxy",
-  "settings.ExtensionControlBrowserProxy|ExtensionControlBrowserProxy",
-  "settings.FontsBrowserProxy|FontsBrowserProxy",
-  "settings.LanguagesBrowserProxy|LanguagesBrowserProxy",
-  "settings.LifetimeBrowserProxy|LifetimeBrowserProxy",
-  "settings.MetricsReporting|MetricsReporting",
-  "settings.MinimumRoutes|MinimumRoutes",
-  "settings.PageStatus|PageStatus",
-  "Settings.PrefUtil.prefToString|prefToString",
-  "Settings.PrefUtil.stringToPrefValue|stringToPrefValue",
-  "settings.PrivacyPageBrowserProxy|PrivacyPageBrowserProxy",
-  "settings.ProfileInfo|ProfileInfo",
-  "settings.ResolverOption|ResolverOption",
-  "settings.Route|Route",
-  "settings.SearchEnginesBrowserProxy|SearchEnginesBrowserProxy",
-  "settings.SearchRequest|SearchRequest",
-  "settings.SecureDnsMode|SecureDnsMode",
-  "settings.SecureDnsSetting|SecureDnsSetting",
-  "settings.SecureDnsUiManagementMode|SecureDnsUiManagementMode",
-  "settings.StatusAction|StatusAction",
-  "settings.StoredAccount|StoredAccount",
-  "settings.SyncBrowserProxy|SyncBrowserProxy",
-  "settings.SyncPrefs|SyncPrefs",
-  "settings.SyncStatus|SyncStatus",
-]
diff --git a/chrome/browser/resources/settings/site_settings/site_data_details_subpage.html b/chrome/browser/resources/settings/site_settings/site_data_details_subpage.html
index ed883ff..f4e8961c 100644
--- a/chrome/browser/resources/settings/site_settings/site_data_details_subpage.html
+++ b/chrome/browser/resources/settings/site_settings/site_data_details_subpage.html
@@ -1,8 +1,20 @@
     <style include="cr-shared-style settings-shared md-select iron-flex">
+      .cr-row {
+        padding-inline-start: 0;
+      }
+
       [first] {
         border-top: none;
       }
 
+      cr-expand-button {
+        padding: 0 var(--cr-section-padding);
+      }
+
+      .separator {
+        margin-inline-start: 0;
+      }
+
       .secondary,
       .start {
         max-width: 100%;
diff --git a/chrome/browser/sessions/tab_loader.cc b/chrome/browser/sessions/tab_loader.cc
index 56415d6c..8e6247c 100644
--- a/chrome/browser/sessions/tab_loader.cc
+++ b/chrome/browser/sessions/tab_loader.cc
@@ -13,6 +13,7 @@
 #include "base/system/sys_info.h"
 #include "base/threading/sequenced_task_runner_handle.h"
 #include "base/time/default_tick_clock.h"
+#include "base/trace_event/memory_pressure_level_proto.h"
 #include "base/trace_event/typed_macros.h"
 #include "build/build_config.h"
 #include "chrome/browser/browser_process.h"
@@ -373,7 +374,7 @@
       [&](perfetto::EventContext ctx) {
         auto* event = ctx.event<perfetto::protos::pbzero::ChromeTrackEvent>();
         auto* data = event->set_chrome_memory_pressure_notification();
-        data->set_level(base::MemoryPressureListener::LevelAsTraceEnum(
+        data->set_level(base::trace_event::MemoryPressureLevelToTraceEnum(
             memory_pressure_level));
       });
 
diff --git a/chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/long_screenshots/bitmap_generation/EntryManager.java b/chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/long_screenshots/bitmap_generation/EntryManager.java
index 89c1fbeb..85cc162 100644
--- a/chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/long_screenshots/bitmap_generation/EntryManager.java
+++ b/chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/long_screenshots/bitmap_generation/EntryManager.java
@@ -22,6 +22,7 @@
  * {@link generateInitialEntry}.
  */
 public class EntryManager {
+    private static final int KB_IN_BYTES = 1024;
     // List of all entries in correspondence of the webpage.
     private List<LongScreenshotsEntry> mEntries;
     // List of entries that are queued to generate the bitmap. Entries should only be queued
@@ -30,6 +31,8 @@
     private BitmapGenerator mGenerator;
     private @EntryStatus int mGeneratorStatus;
     private ScreenshotBoundsManager mBoundsManager;
+    private int mMemoryUsedInKb;
+    private int mMaxMemoryUsageInKb;
 
     /**
      * @param context An instance of current Android {@link Context}.
@@ -43,6 +46,8 @@
         mGenerator = new BitmapGenerator(tab, mBoundsManager, createBitmapGeneratorCallback());
         mGenerator.captureTab();
         updateGeneratorStatus(EntryStatus.CAPTURE_IN_PROGRESS);
+        // TODO(cb/1153969): Make this a finch param instead.
+        mMaxMemoryUsageInKb = 16 * 1024;
     }
 
     /**
@@ -51,8 +56,8 @@
      * generation and retrieve the bitmap.
      */
     public LongScreenshotsEntry generateInitialEntry() {
-        LongScreenshotsEntry entry =
-                new LongScreenshotsEntry(mGenerator, mBoundsManager.getInitialEntryBounds());
+        LongScreenshotsEntry entry = new LongScreenshotsEntry(
+                mGenerator, mBoundsManager.getInitialEntryBounds(), this::updateMemoryUsage);
         processEntry(entry, false);
         // Pre-compute these entries so that they are ready to go when the user starts scrolling.
         getPreviousEntry(entry.getId());
@@ -82,14 +87,19 @@
             return mEntries.get(found - 1);
         }
 
+        // Before generating a new bitmap, make sure too much memory has not already been used.
+        if (mMemoryUsedInKb >= mMaxMemoryUsageInKb) {
+            return LongScreenshotsEntry.createEntryWithStatus(EntryStatus.INSUFFICIENT_MEMORY);
+        }
+
         Rect bounds = mBoundsManager.calculateClipBoundsAbove(mEntries.get(0).getId());
         if (bounds == null) {
-            return LongScreenshotsEntry.createEntryWithStatus(
-                    null, bounds, EntryStatus.BOUNDS_ABOVE_CAPTURE);
+            return LongScreenshotsEntry.createEntryWithStatus(EntryStatus.BOUNDS_ABOVE_CAPTURE);
         }
 
         // found = 0
-        LongScreenshotsEntry newEntry = new LongScreenshotsEntry(mGenerator, bounds);
+        LongScreenshotsEntry newEntry =
+                new LongScreenshotsEntry(mGenerator, bounds, this::updateMemoryUsage);
         processEntry(newEntry, true);
         return newEntry;
     }
@@ -116,16 +126,21 @@
             return mEntries.get(found + 1);
         }
 
+        // Before generating a new bitmap, make sure too much memory has not already been used.
+        if (mMemoryUsedInKb >= mMaxMemoryUsageInKb) {
+            return LongScreenshotsEntry.createEntryWithStatus(EntryStatus.INSUFFICIENT_MEMORY);
+        }
+
         // found = last entry in the arraylist
         int newStartY = mEntries.get(mEntries.size() - 1).getEndYAxis() + 1;
 
         Rect bounds = mBoundsManager.calculateClipBoundsBelow(newStartY);
         if (bounds == null) {
-            return LongScreenshotsEntry.createEntryWithStatus(
-                    null, bounds, EntryStatus.BOUNDS_BELOW_CAPTURE);
+            return LongScreenshotsEntry.createEntryWithStatus(EntryStatus.BOUNDS_BELOW_CAPTURE);
         }
 
-        LongScreenshotsEntry newEntry = new LongScreenshotsEntry(mGenerator, bounds);
+        LongScreenshotsEntry newEntry =
+                new LongScreenshotsEntry(mGenerator, bounds, this::updateMemoryUsage);
         processEntry(newEntry, false);
         return newEntry;
     }
@@ -167,6 +182,10 @@
         }
     }
 
+    private void updateMemoryUsage(int bytedUsed) {
+        mMemoryUsedInKb += (bytedUsed / KB_IN_BYTES);
+    }
+
     /**
      * Creates the default BitmapGenerator to be used to retrieve the state of the generation. This
      * is the default implementation and should only be overridden for tests.
diff --git a/chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/long_screenshots/bitmap_generation/LongScreenshotsEntry.java b/chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/long_screenshots/bitmap_generation/LongScreenshotsEntry.java
index ea5bd61..519552c9 100644
--- a/chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/long_screenshots/bitmap_generation/LongScreenshotsEntry.java
+++ b/chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/long_screenshots/bitmap_generation/LongScreenshotsEntry.java
@@ -10,6 +10,8 @@
 import androidx.annotation.IntDef;
 import androidx.annotation.VisibleForTesting;
 
+import org.chromium.base.Callback;
+
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 
@@ -33,6 +35,7 @@
     // Generated bitmap
     private Bitmap mGeneratedBitmap;
     private EntryListener mEntryListener;
+    private Callback<Integer> mMemoryTracker;
 
     @IntDef({EntryStatus.UNKNOWN, EntryStatus.INSUFFICIENT_MEMORY, EntryStatus.GENERATION_ERROR,
             EntryStatus.BITMAP_GENERATED, EntryStatus.CAPTURE_COMPLETE,
@@ -67,14 +70,15 @@
      * @param generator BitmapGenerator to be used to capture and composite the website.
      * @param bounds The bounds of the entry.
      */
-    public LongScreenshotsEntry(BitmapGenerator generator, Rect bounds) {
+    public LongScreenshotsEntry(
+            BitmapGenerator generator, Rect bounds, Callback<Integer> memoryTracker) {
         mRect = bounds;
         mGenerator = generator;
+        mMemoryTracker = memoryTracker;
     }
 
-    public static LongScreenshotsEntry createEntryWithStatus(
-            BitmapGenerator generator, Rect bounds, @EntryStatus int status) {
-        LongScreenshotsEntry entry = new LongScreenshotsEntry(generator, bounds);
+    static LongScreenshotsEntry createEntryWithStatus(@EntryStatus int status) {
+        LongScreenshotsEntry entry = new LongScreenshotsEntry(null, null, null);
         entry.updateStatus(status);
         return entry;
     }
@@ -99,11 +103,11 @@
         return mRect == null ? -1 : mRect.top;
     }
 
-    public int getEndYAxis() {
+    int getEndYAxis() {
         return mRect == null ? -1 : mRect.bottom;
     }
 
-    public void generateBitmap() {
+    void generateBitmap() {
         if (mGenerator == null) {
             updateStatus(EntryStatus.GENERATION_ERROR);
             return;
@@ -136,6 +140,10 @@
     private void onBitmapGenerated(Bitmap bitmap) {
         // TODO(tgupta): Add metrics logging here.
         mGeneratedBitmap = bitmap;
+
+        if (mMemoryTracker != null && mGeneratedBitmap != null) {
+            mMemoryTracker.onResult(mGeneratedBitmap.getAllocationByteCount());
+        }
         updateStatus(EntryStatus.BITMAP_GENERATED);
     }
 
@@ -143,14 +151,14 @@
         updateStatus(EntryStatus.GENERATION_ERROR);
     }
 
-    public void updateStatus(@EntryStatus int status) {
+    void updateStatus(@EntryStatus int status) {
         mCurrentStatus = status;
         if (mEntryListener != null) {
             mEntryListener.onResult(mCurrentStatus);
         }
     }
 
-    public void destroy() {
+    void destroy() {
         if (mGenerator != null) {
             mGenerator.destroy();
             mGenerator = null;
diff --git a/chrome/browser/share/android/javatests/src/org/chromium/chrome/browser/share/long_screenshots/bitmap_generation/LongScreenshotsEntryTest.java b/chrome/browser/share/android/javatests/src/org/chromium/chrome/browser/share/long_screenshots/bitmap_generation/LongScreenshotsEntryTest.java
index 199c5c9..23f460b 100644
--- a/chrome/browser/share/android/javatests/src/org/chromium/chrome/browser/share/long_screenshots/bitmap_generation/LongScreenshotsEntryTest.java
+++ b/chrome/browser/share/android/javatests/src/org/chromium/chrome/browser/share/long_screenshots/bitmap_generation/LongScreenshotsEntryTest.java
@@ -6,6 +6,7 @@
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNull;
+import static org.junit.Assert.fail;
 import static org.mockito.Mockito.any;
 import static org.mockito.Mockito.when;
 
@@ -109,8 +110,13 @@
     public void testSuccessfulEntry() {
         TestBitmapGenerator testGenerator = new TestBitmapGenerator(new Rect(0, 0, 200, 1000));
 
-        LongScreenshotsEntry entry =
-                new LongScreenshotsEntry(testGenerator, new Rect(0, 1000, 0, 2000));
+        LongScreenshotsEntry entry = new LongScreenshotsEntry(
+                testGenerator, new Rect(0, 1000, 0, 2000), new Callback<Integer>() {
+                    @Override
+                    public void onResult(Integer result) {
+                        assertEquals((int) result, 2097152);
+                    }
+                });
         TestEntryListener entryListener = new TestEntryListener();
         entry.setListener(entryListener);
         entry.generateBitmap();
@@ -124,8 +130,13 @@
         TestBitmapGenerator testGenerator = new TestBitmapGenerator(new Rect(0, 0, 200, 1000));
         testGenerator.throwErrorOnComposite();
 
-        LongScreenshotsEntry entry =
-                new LongScreenshotsEntry(testGenerator, new Rect(0, 1000, 0, 2000));
+        LongScreenshotsEntry entry = new LongScreenshotsEntry(
+                testGenerator, new Rect(0, 1000, 0, 2000), new Callback<Integer>() {
+                    @Override
+                    public void onResult(Integer result) {
+                        fail("MemoryUsage should not be called");
+                    }
+                });
         TestEntryListener entryListener = new TestEntryListener();
         entry.setListener(entryListener);
         entry.generateBitmap();
diff --git a/chrome/browser/sharing/click_to_call/click_to_call_context_menu_observer.cc b/chrome/browser/sharing/click_to_call/click_to_call_context_menu_observer.cc
index 3189c612..4205cee 100644
--- a/chrome/browser/sharing/click_to_call/click_to_call_context_menu_observer.cc
+++ b/chrome/browser/sharing/click_to_call/click_to_call_context_menu_observer.cc
@@ -11,7 +11,6 @@
 #include "chrome/browser/renderer_context_menu/render_view_context_menu.h"
 #include "chrome/browser/sharing/click_to_call/click_to_call_metrics.h"
 #include "chrome/browser/sharing/click_to_call/click_to_call_ui_controller.h"
-#include "chrome/browser/sharing/click_to_call/feature.h"
 #include "chrome/browser/sharing/sharing_constants.h"
 #include "chrome/grit/generated_resources.h"
 #include "components/sync_device_info/device_info.h"
diff --git a/chrome/browser/sharing/click_to_call/click_to_call_context_menu_observer_unittest.cc b/chrome/browser/sharing/click_to_call/click_to_call_context_menu_observer_unittest.cc
index 9e91209..b71441aa 100644
--- a/chrome/browser/sharing/click_to_call/click_to_call_context_menu_observer_unittest.cc
+++ b/chrome/browser/sharing/click_to_call/click_to_call_context_menu_observer_unittest.cc
@@ -16,7 +16,6 @@
 #include "chrome/app/chrome_command_ids.h"
 #include "chrome/browser/renderer_context_menu/mock_render_view_context_menu.h"
 #include "chrome/browser/sharing/click_to_call/click_to_call_utils.h"
-#include "chrome/browser/sharing/click_to_call/feature.h"
 #include "chrome/browser/sharing/fake_device_info.h"
 #include "chrome/browser/sharing/features.h"
 #include "chrome/browser/sharing/mock_sharing_service.h"
diff --git a/chrome/browser/sharing/click_to_call/click_to_call_utils.cc b/chrome/browser/sharing/click_to_call/click_to_call_utils.cc
index d805e36..f9709ab 100644
--- a/chrome/browser/sharing/click_to_call/click_to_call_utils.cc
+++ b/chrome/browser/sharing/click_to_call/click_to_call_utils.cc
@@ -11,7 +11,6 @@
 #include "base/strings/string_util.h"
 #include "base/strings/utf_string_conversions.h"
 #include "chrome/browser/profiles/profile.h"
-#include "chrome/browser/sharing/click_to_call/feature.h"
 #include "chrome/browser/sharing/click_to_call/phone_number_regex.h"
 #include "chrome/browser/sharing/sharing_service.h"
 #include "chrome/browser/sharing/sharing_service_factory.h"
@@ -42,7 +41,7 @@
 
   SharingService* sharing_service =
       SharingServiceFactory::GetForBrowserContext(browser_context);
-  return sharing_service && base::FeatureList::IsEnabled(kClickToCallUI);
+  return sharing_service != nullptr;
 }
 
 }  // namespace
diff --git a/chrome/browser/sharing/click_to_call/click_to_call_utils_unittest.cc b/chrome/browser/sharing/click_to_call/click_to_call_utils_unittest.cc
index 88a575c..0231e3e 100644
--- a/chrome/browser/sharing/click_to_call/click_to_call_utils_unittest.cc
+++ b/chrome/browser/sharing/click_to_call/click_to_call_utils_unittest.cc
@@ -7,9 +7,7 @@
 #include <memory>
 
 #include "base/macros.h"
-#include "base/test/scoped_feature_list.h"
 #include "chrome/browser/sharing/click_to_call/click_to_call_utils.h"
-#include "chrome/browser/sharing/click_to_call/feature.h"
 #include "chrome/browser/sharing/mock_sharing_service.h"
 #include "chrome/browser/sharing/sharing_fcm_handler.h"
 #include "chrome/browser/sharing/sharing_fcm_sender.h"
@@ -72,7 +70,6 @@
     return create_service_ ? std::make_unique<MockSharingService>() : nullptr;
   }
 
-  base::test::ScopedFeatureList scoped_feature_list_;
   content::BrowserTaskEnvironment task_environment_;
   TestingProfile profile_;
   bool create_service_ = true;
@@ -83,27 +80,18 @@
 }  // namespace
 
 TEST_F(ClickToCallUtilsTest, NoSharingService_DoNotOfferAnyMenu) {
-  scoped_feature_list_.InitAndEnableFeature(kClickToCallUI);
   create_service_ = false;
   EXPECT_FALSE(ShouldOfferClickToCallForURL(&profile_, GURL(kTelUrl)));
   ExpectClickToCallDisabledForSelectionText(kSelectionTextWithNumber);
 }
 
-TEST_F(ClickToCallUtilsTest, UIFlagDisabled_DoNotOfferAnyMenu) {
-  scoped_feature_list_.InitAndDisableFeature(kClickToCallUI);
-  EXPECT_FALSE(ShouldOfferClickToCallForURL(&profile_, GURL(kTelUrl)));
-  ExpectClickToCallDisabledForSelectionText(kSelectionTextWithNumber);
-}
-
 TEST_F(ClickToCallUtilsTest, PolicyDisabled_DoNotOfferAnyMenu) {
-  scoped_feature_list_.InitAndEnableFeature(kClickToCallUI);
   profile_.GetPrefs()->SetBoolean(prefs::kClickToCallEnabled, false);
   EXPECT_FALSE(ShouldOfferClickToCallForURL(&profile_, GURL(kTelUrl)));
   ExpectClickToCallDisabledForSelectionText(kSelectionTextWithNumber);
 }
 
 TEST_F(ClickToCallUtilsTest, IncognitoProfile_DoNotOfferAnyMenu) {
-  scoped_feature_list_.InitAndEnableFeature(kClickToCallUI);
   EXPECT_FALSE(ShouldOfferClickToCallForURL(profile_.GetPrimaryOTRProfile(),
                                             GURL(kTelUrl)));
   ExpectClickToCallDisabledForSelectionText(kSelectionTextWithNumber,
@@ -111,24 +99,19 @@
 }
 
 TEST_F(ClickToCallUtilsTest, EmptyTelLink_DoNotOfferForLink) {
-  scoped_feature_list_.InitAndEnableFeature(kClickToCallUI);
   EXPECT_FALSE(ShouldOfferClickToCallForURL(&profile_, GURL(kEmptyTelUrl)));
 }
 
 TEST_F(ClickToCallUtilsTest, TelLink_OfferForLink) {
-  scoped_feature_list_.InitAndEnableFeature(kClickToCallUI);
   EXPECT_TRUE(ShouldOfferClickToCallForURL(&profile_, GURL(kTelUrl)));
 }
 
 TEST_F(ClickToCallUtilsTest, NonTelLink_DoNotOfferForLink) {
-  scoped_feature_list_.InitAndEnableFeature(kClickToCallUI);
   EXPECT_FALSE(ShouldOfferClickToCallForURL(&profile_, GURL(kNonTelUrl)));
 }
 
 TEST_F(ClickToCallUtilsTest,
        SelectionText_ValidPhoneNumberRegex_OfferForSelection) {
-  scoped_feature_list_.InitAndEnableFeature(kClickToCallUI);
-
   // Stores a mapping of selected text to expected phone number parsed.
   std::map<std::string, std::string> expectations;
   // Selection text only consists of the phone number.
@@ -164,7 +147,6 @@
 
 TEST_F(ClickToCallUtilsTest,
        SelectionText_InvalidPhoneNumberRegex_DoNotOfferForSelection) {
-  scoped_feature_list_.InitAndEnableFeature(kClickToCallUI);
   std::vector<std::string> invalid_selection_texts;
 
   // Does not contain any number.
@@ -187,7 +169,6 @@
 }
 
 TEST_F(ClickToCallUtilsTest, SelectionText_Length) {
-  scoped_feature_list_.InitAndEnableFeature(kClickToCallUI);
   // Expect text length of 30 to pass.
   EXPECT_NE(base::nullopt, ExtractPhoneNumberForClickToCall(
                                &profile_, " +1 2 3 4 5 6 7 8 9 0 1 2 3 45"));
@@ -197,7 +178,6 @@
 }
 
 TEST_F(ClickToCallUtilsTest, SelectionText_Digits) {
-  scoped_feature_list_.InitAndEnableFeature(kClickToCallUI);
   // Expect text with 15 digits to pass.
   EXPECT_NE(base::nullopt,
             ExtractPhoneNumberForClickToCall(&profile_, "+123456789012345"));
diff --git a/chrome/browser/sharing/click_to_call/feature.cc b/chrome/browser/sharing/click_to_call/feature.cc
deleted file mode 100644
index 09bfc55..0000000
--- a/chrome/browser/sharing/click_to_call/feature.cc
+++ /dev/null
@@ -1,15 +0,0 @@
-// Copyright 2019 The Chromium 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/sharing/click_to_call/feature.h"
-
-#if defined(OS_ANDROID)
-const base::Feature kClickToCallReceiver{"ClickToCallReceiver",
-                                         base::FEATURE_ENABLED_BY_DEFAULT};
-#endif  // defined(OS_ANDROID)
-
-#if BUILDFLAG(ENABLE_CLICK_TO_CALL)
-const base::Feature kClickToCallUI{"ClickToCallUI",
-                                   base::FEATURE_ENABLED_BY_DEFAULT};
-#endif  // BUILDFLAG(ENABLE_CLICK_TO_CALL)
diff --git a/chrome/browser/sharing/click_to_call/feature.h b/chrome/browser/sharing/click_to_call/feature.h
deleted file mode 100644
index b9000a4..0000000
--- a/chrome/browser/sharing/click_to_call/feature.h
+++ /dev/null
@@ -1,22 +0,0 @@
-// Copyright 2019 The Chromium 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_SHARING_CLICK_TO_CALL_FEATURE_H_
-#define CHROME_BROWSER_SHARING_CLICK_TO_CALL_FEATURE_H_
-
-#include "base/feature_list.h"
-#include "build/build_config.h"
-#include "chrome/common/buildflags.h"
-
-#if defined(OS_ANDROID)
-// Feature to allow devices to receive the click to call message.
-extern const base::Feature kClickToCallReceiver;
-#endif  // defined(OS_ANDROID)
-
-#if BUILDFLAG(ENABLE_CLICK_TO_CALL)
-// Feature to allow click to call gets processed on desktop.
-extern const base::Feature kClickToCallUI;
-#endif  // BUILDFLAG(ENABLE_CLICK_TO_CALL)
-
-#endif  // CHROME_BROWSER_SHARING_CLICK_TO_CALL_FEATURE_H_
diff --git a/chrome/browser/sharing/click_to_call/phone_number_regex.cc b/chrome/browser/sharing/click_to_call/phone_number_regex.cc
index a3e8dbb..c2b06750 100644
--- a/chrome/browser/sharing/click_to_call/phone_number_regex.cc
+++ b/chrome/browser/sharing/click_to_call/phone_number_regex.cc
@@ -11,7 +11,6 @@
 #include "base/task/post_task.h"
 #include "base/task/thread_pool.h"
 #include "base/time/time.h"
-#include "chrome/browser/sharing/click_to_call/feature.h"
 #include "third_party/re2/src/re2/re2.h"
 
 namespace {
@@ -40,8 +39,6 @@
 }
 
 void PrecompilePhoneNumberRegexesAsync() {
-  if (!base::FeatureList::IsEnabled(kClickToCallUI))
-    return;
   constexpr auto kParseDelay = base::TimeDelta::FromSeconds(15);
   base::ThreadPool::PostDelayedTask(
       FROM_HERE,
diff --git a/chrome/browser/sharing/sharing_device_registration.cc b/chrome/browser/sharing/sharing_device_registration.cc
index b997d47..0d13994b 100644
--- a/chrome/browser/sharing/sharing_device_registration.cc
+++ b/chrome/browser/sharing/sharing_device_registration.cc
@@ -13,7 +13,6 @@
 #include "base/time/time.h"
 #include "build/build_config.h"
 #include "chrome/browser/sharing/buildflags.h"
-#include "chrome/browser/sharing/click_to_call/feature.h"
 #include "chrome/browser/sharing/shared_clipboard/feature_flags.h"
 #include "chrome/browser/sharing/sharing_constants.h"
 #include "chrome/browser/sharing/sharing_device_registration_result.h"
@@ -297,8 +296,6 @@
 
 bool SharingDeviceRegistration::IsClickToCallSupported() const {
 #if defined(OS_ANDROID)
-  if (!base::FeatureList::IsEnabled(kClickToCallReceiver))
-    return false;
   JNIEnv* env = base::android::AttachCurrentThread();
   return Java_SharingJNIBridge_isTelephonySupported(env);
 #endif
diff --git a/chrome/browser/sharing/sharing_handler_registry_impl.cc b/chrome/browser/sharing/sharing_handler_registry_impl.cc
index 7a81cdd..4beaccb 100644
--- a/chrome/browser/sharing/sharing_handler_registry_impl.cc
+++ b/chrome/browser/sharing/sharing_handler_registry_impl.cc
@@ -14,9 +14,7 @@
 #include "chrome/browser/sharing/sharing_message_sender.h"
 
 #if defined(OS_ANDROID)
-#include "base/feature_list.h"
 #include "chrome/browser/sharing/click_to_call/click_to_call_message_handler_android.h"
-#include "chrome/browser/sharing/click_to_call/feature.h"
 #include "chrome/browser/sharing/shared_clipboard/shared_clipboard_message_handler_android.h"
 #include "chrome/browser/sharing/sms/sms_fetch_request_handler.h"
 #else
@@ -43,11 +41,9 @@
 
 #if defined(OS_ANDROID)
   // Note: IsClickToCallSupported() is not used as it requires JNI call.
-  if (base::FeatureList::IsEnabled(kClickToCallReceiver)) {
-    AddSharingHandler(
-        std::make_unique<ClickToCallMessageHandler>(),
-        {chrome_browser_sharing::SharingMessage::kClickToCallMessage});
-  }
+  AddSharingHandler(
+      std::make_unique<ClickToCallMessageHandler>(),
+      {chrome_browser_sharing::SharingMessage::kClickToCallMessage});
 
   if (sharing_device_registration->IsSmsFetcherSupported()) {
     AddSharingHandler(
diff --git a/chrome/browser/site_isolation/spellcheck_per_process_browsertest.cc b/chrome/browser/site_isolation/spellcheck_per_process_browsertest.cc
index ea585365..b0be309 100644
--- a/chrome/browser/site_isolation/spellcheck_per_process_browsertest.cc
+++ b/chrome/browser/site_isolation/spellcheck_per_process_browsertest.cc
@@ -128,13 +128,6 @@
                           FillSuggestionListCallback) override {}
 
 #if defined(OS_WIN)
-  void GetPerLanguageSuggestions(
-      const std::u16string& word,
-      GetPerLanguageSuggestionsCallback callback) override {
-    DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
-    std::move(callback).Run(std::vector<std::vector<std::u16string>>());
-  }
-
   void InitializeDictionaries(
       InitializeDictionariesCallback callback) override {
     if (base::FeatureList::IsEnabled(
diff --git a/chrome/browser/spellchecker/spell_check_host_chrome_impl.cc b/chrome/browser/spellchecker/spell_check_host_chrome_impl.cc
index cbd20a3..8d18a702 100644
--- a/chrome/browser/spellchecker/spell_check_host_chrome_impl.cc
+++ b/chrome/browser/spellchecker/spell_check_host_chrome_impl.cc
@@ -233,20 +233,6 @@
 }
 
 #if defined(OS_WIN)
-void SpellCheckHostChromeImpl::GetPerLanguageSuggestions(
-    const std::u16string& word,
-    GetPerLanguageSuggestionsCallback callback) {
-  SpellcheckService* spellcheck = GetSpellcheckService();
-
-  if (!spellcheck) {  // Teardown.
-    std::move(callback).Run({});
-    return;
-  }
-
-  spellcheck_platform::GetPerLanguageSuggestions(
-      spellcheck->platform_spell_checker(), word, std::move(callback));
-}
-
 void SpellCheckHostChromeImpl::InitializeDictionaries(
     InitializeDictionariesCallback callback) {
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
diff --git a/chrome/browser/spellchecker/spell_check_host_chrome_impl.h b/chrome/browser/spellchecker/spell_check_host_chrome_impl.h
index 8e8e394..e2bca28 100644
--- a/chrome/browser/spellchecker/spell_check_host_chrome_impl.h
+++ b/chrome/browser/spellchecker/spell_check_host_chrome_impl.h
@@ -85,10 +85,6 @@
                         RequestTextCheckCallback callback) override;
 
 #if defined(OS_WIN)
-  void GetPerLanguageSuggestions(
-      const std::u16string& word,
-      GetPerLanguageSuggestionsCallback callback) override;
-
   void InitializeDictionaries(InitializeDictionariesCallback callback) override;
 #endif  // defined(OS_WIN)
 
diff --git a/chrome/browser/spellchecker/spell_check_host_chrome_impl_win_browsertest.cc b/chrome/browser/spellchecker/spell_check_host_chrome_impl_win_browsertest.cc
index fa60a93b..917c4161 100644
--- a/chrome/browser/spellchecker/spell_check_host_chrome_impl_win_browsertest.cc
+++ b/chrome/browser/spellchecker/spell_check_host_chrome_impl_win_browsertest.cc
@@ -87,7 +87,6 @@
   }
 
   void RunSpellCheckReturnMessageTest();
-  void RunGetPerLanguageSuggestionsTest();
 
  protected:
   PlatformSpellChecker* platform_spell_checker_;
@@ -133,36 +132,6 @@
   EXPECT_EQ(result_[0].decoration, SpellCheckResult::SPELLING);
 }
 
-IN_PROC_BROWSER_TEST_F(SpellCheckHostChromeImplWinBrowserTest,
-                       GetPerLanguageSuggestions) {
-  RunGetPerLanguageSuggestionsTest();
-}
-
-void SpellCheckHostChromeImplWinBrowserTest::
-    RunGetPerLanguageSuggestionsTest() {
-  if (!spellcheck::WindowsVersionSupportsSpellchecker()) {
-    return;
-  }
-
-  spellcheck_platform::SetLanguage(
-      platform_spell_checker_, "en-US",
-      base::BindOnce(&SpellCheckHostChromeImplWinBrowserTest::
-                         SetLanguageCompletionCallback,
-                     base::Unretained(this)));
-  RunUntilResultReceived();
-
-  spell_check_host_->GetPerLanguageSuggestions(
-      base::UTF8ToUTF16("tihs"),
-      base::BindOnce(
-          &SpellCheckHostChromeImplWinBrowserTest::OnSuggestionResult,
-          base::Unretained(this)));
-  RunUntilResultReceived();
-
-  // Should have 1 vector of results, which should contain at least 1 suggestion
-  ASSERT_EQ(1U, suggestion_result_.size());
-  EXPECT_GT(suggestion_result_[0].size(), 0U);
-}
-
 class SpellCheckHostChromeImplWinBrowserTestDelayInit
     : public SpellCheckHostChromeImplWinBrowserTest {
  public:
@@ -202,8 +171,3 @@
                        SpellCheckReturnMessage) {
   RunSpellCheckReturnMessageTest();
 }
-
-IN_PROC_BROWSER_TEST_F(SpellCheckHostChromeImplWinBrowserTestDelayInit,
-                       GetPerLanguageSuggestions) {
-  RunGetPerLanguageSuggestionsTest();
-}
diff --git a/chrome/browser/tabmodel/android/java/src/org/chromium/chrome/browser/tabmodel/TabModelFilter.java b/chrome/browser/tabmodel/android/java/src/org/chromium/chrome/browser/tabmodel/TabModelFilter.java
index c8e460d..62f3a13 100644
--- a/chrome/browser/tabmodel/android/java/src/org/chromium/chrome/browser/tabmodel/TabModelFilter.java
+++ b/chrome/browser/tabmodel/android/java/src/org/chromium/chrome/browser/tabmodel/TabModelFilter.java
@@ -218,6 +218,13 @@
     }
 
     @Override
+    public void didCloseTab(Tab tab) {
+        for (TabModelObserver observer : mFilteredObservers) {
+            observer.didCloseTab(tab);
+        }
+    }
+
+    @Override
     public void didCloseTab(int tabId, boolean incognito) {
         for (TabModelObserver observer : mFilteredObservers) {
             observer.didCloseTab(tabId, incognito);
diff --git a/chrome/browser/tabmodel/android/java/src/org/chromium/chrome/browser/tabmodel/TabModelObserver.java b/chrome/browser/tabmodel/android/java/src/org/chromium/chrome/browser/tabmodel/TabModelObserver.java
index 5cc47f8..6209a9f 100644
--- a/chrome/browser/tabmodel/android/java/src/org/chromium/chrome/browser/tabmodel/TabModelObserver.java
+++ b/chrome/browser/tabmodel/android/java/src/org/chromium/chrome/browser/tabmodel/TabModelObserver.java
@@ -38,10 +38,20 @@
      *
      * @param tabId The ID of the tab that was destroyed.
      * @param incognito True if the closed tab was incognito.
+     *
+     * @deprecated Use {@link #didCloseTab(Tab)} instead
      */
+    @Deprecated
     default void didCloseTab(int tabId, boolean incognito) {}
 
     /**
+     * Called right after {@code tab} has been destroyed.
+     *
+     * @param tab The tab that was closed.
+     */
+    default void didCloseTab(Tab tab) {}
+
+    /**
      * Called before a tab will be added to the {@link TabModel}.
      *
      * @param tab The tab about to be added.
@@ -99,6 +109,11 @@
     default void multipleTabsPendingClosure(List<Tab> tabs, boolean isAllTabs) {}
 
     /**
+     * Called when an "all tabs" closure will happen.
+     */
+    default void willCloseAllTabs(boolean incognito) {}
+
+    /**
      * Called when an "all tabs" closure has been committed and can't be undone anymore.
      */
     default void allTabsClosureCommitted() {}
diff --git a/chrome/browser/themes/theme_service_unittest.cc b/chrome/browser/themes/theme_service_unittest.cc
index 24ad914..40ac26db 100644
--- a/chrome/browser/themes/theme_service_unittest.cc
+++ b/chrome/browser/themes/theme_service_unittest.cc
@@ -29,6 +29,7 @@
 #include "chrome/test/base/testing_profile_manager.h"
 #include "content/public/test/test_utils.h"
 #include "extensions/browser/extension_registry.h"
+#include "extensions/browser/extension_util.h"
 #include "extensions/browser/test_extension_registry_observer.h"
 #include "extensions/browser/uninstall_reason.h"
 #include "extensions/common/extension.h"
@@ -118,10 +119,17 @@
         extensions::UnpackedInstaller::Create(service_));
     extensions::TestExtensionRegistryObserver observer(registry_);
     installer->Load(temp_dir);
-    scoper.set_extension_id(observer.WaitForExtensionLoaded()->id());
+    std::string extenson_id = observer.WaitForExtensionLoaded()->id();
+    scoper.set_extension_id(extenson_id);
 
     waiter.WaitForThemeChanged();
 
+    // Make sure RegisterClient calls for storage are finished to avoid flaky
+    // crashes in QuotaManagerImpl::RegisterClient on test shutdown.
+    // TODO(crbug.com/1182630) : Remove this when 1182630 is fixed.
+    extensions::util::GetStoragePartitionForExtensionId(extenson_id, profile());
+    task_environment()->RunUntilIdle();
+
     return scoper;
   }
 
diff --git a/chrome/browser/translate/translate_manager_render_view_host_unittest.cc b/chrome/browser/translate/translate_manager_render_view_host_unittest.cc
index 3b3b3c6..6ecc439 100644
--- a/chrome/browser/translate/translate_manager_render_view_host_unittest.cc
+++ b/chrome/browser/translate/translate_manager_render_view_host_unittest.cc
@@ -493,7 +493,7 @@
 };
 
 // A list of languages to fake being returned by the translate server.
-// Use only langauges for which Chrome's copy of ICU has
+// Use only languages for which Chrome's copy of ICU has
 // display names in English locale. To save space, Chrome's copy of ICU
 // does not have the display name for a language unless it's in the
 // Accept-Language list.
diff --git a/chrome/browser/ui/android/strings/android_chrome_strings.grd b/chrome/browser/ui/android/strings/android_chrome_strings.grd
index 28ca4bcd..771e53b 100644
--- a/chrome/browser/ui/android/strings/android_chrome_strings.grd
+++ b/chrome/browser/ui/android/strings/android_chrome_strings.grd
@@ -2304,9 +2304,6 @@
       </message>
 
       <!-- Download UI -->
-      <message name="IDS_DOWNLOAD_NOTIFICATION_COMPLETED" desc="Download notification to be displayed when a download completes.">
-        Download complete
-      </message>
       <message name="IDS_DOWNLOAD_NOTIFICATION_PENDING" desc="Download notification to be displayed when a download has been scheduled but has not started being fetched from the network.">
         Download pending…
       </message>
diff --git a/chrome/browser/ui/color/tools/dump_colors.cc b/chrome/browser/ui/color/tools/dump_colors.cc
index 3240d5f4..1300a93 100644
--- a/chrome/browser/ui/color/tools/dump_colors.cc
+++ b/chrome/browser/ui/color/tools/dump_colors.cc
@@ -53,7 +53,7 @@
     // TODO(pkasting): Use standard provider setup functions once those exist.
     ui::AddCoreDefaultColorMixer(provider, dark_window, high_contrast);
     ui::AddNativeCoreColorMixer(provider, dark_window, high_contrast);
-    ui::AddUiColorMixer(provider);
+    ui::AddUiColorMixer(provider, dark_window, high_contrast);
     ui::AddNativeUiColorMixer(provider, dark_window, high_contrast);
 #if defined(OS_MAC)
     // Always keep this mixer after all non-embedder ui mixers.
diff --git a/chrome/browser/ui/views/location_bar/location_bar_view.cc b/chrome/browser/ui/views/location_bar/location_bar_view.cc
index 1334e42..5deeab8 100644
--- a/chrome/browser/ui/views/location_bar/location_bar_view.cc
+++ b/chrome/browser/ui/views/location_bar/location_bar_view.cc
@@ -27,7 +27,6 @@
 #include "chrome/browser/search_engines/template_url_service_factory.h"
 #include "chrome/browser/send_tab_to_self/send_tab_to_self_desktop_util.h"
 #include "chrome/browser/send_tab_to_self/send_tab_to_self_util.h"
-#include "chrome/browser/sharing/click_to_call/feature.h"
 #include "chrome/browser/sharing/features.h"
 #include "chrome/browser/sharing/shared_clipboard/feature_flags.h"
 #include "chrome/browser/ssl/security_state_tab_helper.h"
@@ -263,8 +262,7 @@
     // The send tab to self icon is intentionally the first one added so it is
     // the left most icon.
     params.types_enabled.push_back(PageActionIconType::kSendTabToSelf);
-    if (base::FeatureList::IsEnabled(kClickToCallUI))
-      params.types_enabled.push_back(PageActionIconType::kClickToCall);
+    params.types_enabled.push_back(PageActionIconType::kClickToCall);
     if (base::FeatureList::IsEnabled(kSharingQRCodeGenerator))
       params.types_enabled.push_back(PageActionIconType::kQRCodeGenerator);
     if (base::FeatureList::IsEnabled(kSharedClipboardUI))
diff --git a/chrome/browser/ui/views/sharing/click_to_call_browsertest.cc b/chrome/browser/ui/views/sharing/click_to_call_browsertest.cc
index e6fc025c..e2e4838e 100644
--- a/chrome/browser/ui/views/sharing/click_to_call_browsertest.cc
+++ b/chrome/browser/ui/views/sharing/click_to_call_browsertest.cc
@@ -12,7 +12,6 @@
 #include "base/strings/utf_string_conversions.h"
 #include "base/test/bind.h"
 #include "base/test/metrics/histogram_tester.h"
-#include "base/test/scoped_feature_list.h"
 #include "build/chromeos_buildflags.h"
 #include "chrome/app/chrome_command_ids.h"
 #include "chrome/browser/policy/policy_test_utils.h"
@@ -20,7 +19,6 @@
 #include "chrome/browser/sharing/click_to_call/click_to_call_metrics.h"
 #include "chrome/browser/sharing/click_to_call/click_to_call_ui_controller.h"
 #include "chrome/browser/sharing/click_to_call/click_to_call_utils.h"
-#include "chrome/browser/sharing/click_to_call/feature.h"
 #include "chrome/browser/sharing/features.h"
 #include "chrome/browser/sharing/sharing_constants.h"
 #include "chrome/browser/sharing/sharing_sync_preference.h"
@@ -61,10 +59,10 @@
 
 }  // namespace
 
-// Base browser tests for the Click To Call feature.
-class BaseClickToCallBrowserTest : public SharingBrowserTest {
+// Browser tests for the Click To Call feature.
+class ClickToCallBrowserTest : public SharingBrowserTest {
  public:
-  ~BaseClickToCallBrowserTest() override {}
+  ~ClickToCallBrowserTest() override = default;
 
   std::string GetTestPageURL() const override {
     return std::string(kTestPageURL);
@@ -80,8 +78,6 @@
   }
 
  protected:
-  base::test::ScopedFeatureList feature_list_;
-
   std::string HistogramName(const char* suffix) {
     return base::StrCat({"Sharing.ClickToCall", suffix});
   }
@@ -92,17 +88,6 @@
   }
 };
 
-// Browser tests for the Click To Call feature.
-class ClickToCallBrowserTest : public BaseClickToCallBrowserTest {
- public:
-  ClickToCallBrowserTest() {
-    feature_list_.InitAndEnableFeature(kClickToCallUI);
-  }
-
- protected:
-  base::test::ScopedFeatureList feature_list_;
-};
-
 // TODO(himanshujaju): Add UI checks.
 IN_PROC_BROWSER_TEST_F(ClickToCallBrowserTest,
                        ContextMenu_TelLink_SingleDeviceAvailable) {
@@ -486,9 +471,7 @@
     : public policy::PolicyTest,
       public testing::WithParamInterface<ClickToCallPolicy> {
  public:
-  ClickToCallPolicyTest() {
-    scoped_feature_list_.InitAndEnableFeature(kClickToCallUI);
-  }
+  ClickToCallPolicyTest() = default;
   ~ClickToCallPolicyTest() override = default;
 
   void SetUpInProcessBrowserTestFixture() override {
@@ -507,9 +490,6 @@
 
     provider_.UpdateChromePolicy(policies);
   }
-
- private:
-  base::test::ScopedFeatureList scoped_feature_list_;
 };
 
 IN_PROC_BROWSER_TEST_P(ClickToCallPolicyTest, RunTest) {
diff --git a/chrome/browser/ui/webui/feedback/feedback_dialog.cc b/chrome/browser/ui/webui/feedback/feedback_dialog.cc
index 8e7e396c8..7c9903b 100644
--- a/chrome/browser/ui/webui/feedback/feedback_dialog.cc
+++ b/chrome/browser/ui/webui/feedback/feedback_dialog.cc
@@ -105,11 +105,11 @@
 }
 
 bool FeedbackDialog::ShouldShowDialogTitle() const {
-  return false;
+  return true;
 }
 
 bool FeedbackDialog::ShouldShowCloseButton() const {
-  return false;
+  return true;
 }
 
 ui::WebDialogDelegate::FrameKind FeedbackDialog::GetWebDialogFrameKind() const {
diff --git a/chrome/build/linux.pgo.txt b/chrome/build/linux.pgo.txt
index 35453a8..3d3511e 100644
--- a/chrome/build/linux.pgo.txt
+++ b/chrome/build/linux.pgo.txt
@@ -1 +1 @@
-chrome-linux-master-1615982238-6d28cc0efb64698424ab4787b59036e1ccdfa770.profdata
+chrome-linux-master-1616003779-9d74256b5a2600ba4439134b7ce065bd2ac4a75c.profdata
diff --git a/chrome/build/mac.pgo.txt b/chrome/build/mac.pgo.txt
index 8d8f31d..0e5e9432 100644
--- a/chrome/build/mac.pgo.txt
+++ b/chrome/build/mac.pgo.txt
@@ -1 +1 @@
-chrome-mac-master-1615982238-186e2d53eaa4069708ca74b9e7517362982e38f2.profdata
+chrome-mac-master-1616003779-c43fade97bfa9228555ce027ef6b650a0ec5e85a.profdata
diff --git a/chrome/renderer/cart/commerce_hint_agent_browsertest.cc b/chrome/renderer/cart/commerce_hint_agent_browsertest.cc
index 2d0dbafc..3fd8b41 100644
--- a/chrome/renderer/cart/commerce_hint_agent_browsertest.cc
+++ b/chrome/renderer/cart/commerce_hint_agent_browsertest.cc
@@ -470,7 +470,8 @@
   base::test::ScopedFeatureList scoped_feature_list_;
 };
 
-IN_PROC_BROWSER_TEST_F(CommerceHintCacaoTest, Rejected) {
+// Flaky. crbug.com/1183852
+IN_PROC_BROWSER_TEST_F(CommerceHintCacaoTest, DISABLED_Rejected) {
   NavigateToURL("https://www.walmart.com/");
   SendXHR("/add-to-cart", "product: 123");
   WaitForCartCount(kEmptyExpected);
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn
index cb2cc07..b1155c7 100644
--- a/chrome/test/BUILD.gn
+++ b/chrome/test/BUILD.gn
@@ -3952,6 +3952,7 @@
       "../browser/download/android/download_manager_service_unittest.cc",
       "../browser/metrics/thread_watcher_android_unittest.cc",
       "../browser/notifications/notification_channels_provider_android_unittest.cc",
+      "../browser/optimization_guide/android/optimization_guide_tab_url_provider_android_unittest.cc",
       "../browser/password_manager/android/password_ui_view_android_unittest.cc",
       "../browser/search/contextual_search_policy_handler_android_unittest.cc",
       "../browser/translate/android/translate_bridge_unittest.cc",
@@ -4821,6 +4822,9 @@
       "../browser/search/drive/drive_service_unittest.cc",
       "../browser/search/ntp_features_unittest.cc",
       "../browser/search/task_module/task_module_service_unittest.cc",
+
+      # Android uses a different way of showing browser windows.
+      "../browser/optimization_guide/optimization_guide_tab_url_provider_unittest.cc",
     ]
     if (is_posix || is_fuchsia) {
       sources += [ "../browser/process_singleton_posix_unittest.cc" ]
diff --git a/chrome/test/base/chrome_test_launcher.cc b/chrome/test/base/chrome_test_launcher.cc
index 4a3d533..c7b3a0ff 100644
--- a/chrome/test/base/chrome_test_launcher.cc
+++ b/chrome/test/base/chrome_test_launcher.cc
@@ -70,10 +70,10 @@
 #include "testing/gtest/include/gtest/gtest.h"
 #endif
 
-int ChromeTestSuiteRunner::RunTestSuite(int argc, char** argv) {
-  ChromeTestSuite test_suite(argc, argv);
+// static
+int ChromeTestSuiteRunner::RunTestSuiteInternal(ChromeTestSuite* test_suite) {
   // Browser tests are expected not to tear-down various globals.
-  test_suite.DisableCheckForLeakedGlobals();
+  test_suite->DisableCheckForLeakedGlobals();
 #if defined(OS_ANDROID)
   // Android browser tests run child processes as threads instead.
   content::ContentTestSuiteBase::RegisterInProcessThreads();
@@ -85,7 +85,12 @@
   InstalledVersionPoller::ScopedDisableForTesting disable_polling(
       InstalledVersionPoller::MakeScopedDisableForTesting());
 #endif
-  return test_suite.Run();
+  return test_suite->Run();
+}
+
+int ChromeTestSuiteRunner::RunTestSuite(int argc, char** argv) {
+  ChromeTestSuite test_suite(argc, argv);
+  return RunTestSuiteInternal(&test_suite);
 }
 
 #if defined(OS_WIN)
diff --git a/chrome/test/base/chrome_test_launcher.h b/chrome/test/base/chrome_test_launcher.h
index dc62c8f..b590ada 100644
--- a/chrome/test/base/chrome_test_launcher.h
+++ b/chrome/test/base/chrome_test_launcher.h
@@ -17,6 +17,8 @@
 #include "chrome/app/chrome_main_delegate.h"
 #endif
 
+class ChromeTestSuite;
+
 // Allows a test suite to override the TestSuite class used. By default it is an
 // instance of ChromeTestSuite.
 class ChromeTestSuiteRunner {
@@ -27,6 +29,9 @@
   virtual ~ChromeTestSuiteRunner() = default;
 
   virtual int RunTestSuite(int argc, char** argv);
+
+ protected:
+  static int RunTestSuiteInternal(ChromeTestSuite* test_suite);
 };
 
 // Acts like normal ChromeMainDelegate but injects behaviour for browser tests.
diff --git a/chrome/test/base/interactive_ui_tests_main.cc b/chrome/test/base/interactive_ui_tests_main.cc
index 35ac201..1baf140 100644
--- a/chrome/test/base/interactive_ui_tests_main.cc
+++ b/chrome/test/base/interactive_ui_tests_main.cc
@@ -44,9 +44,6 @@
  protected:
   // ChromeTestSuite overrides:
   void Initialize() override {
-    // Browser tests are expected not to tear-down various globals.
-    base::TestSuite::DisableCheckForLeakedGlobals();
-
     ChromeTestSuite::Initialize();
 
 #if BUILDFLAG(IS_CHROMEOS_ASH)
@@ -148,7 +145,8 @@
 class InteractiveUITestSuiteRunner : public ChromeTestSuiteRunner {
  public:
   int RunTestSuite(int argc, char** argv) override {
-    return InteractiveUITestSuite(argc, argv).Run();
+    InteractiveUITestSuite test_suite(argc, argv);
+    return RunTestSuiteInternal(&test_suite);
   }
 };
 
diff --git a/chrome/test/data/extensions/pinning/calendar/gmbgaklkmjakoegficnlkhebmhkjfich-google-calendar-3.1.0.crx b/chrome/test/data/extensions/pinning/calendar/gmbgaklkmjakoegficnlkhebmhkjfich-google-calendar-3.1.0.crx
new file mode 100644
index 0000000..8cda685
--- /dev/null
+++ b/chrome/test/data/extensions/pinning/calendar/gmbgaklkmjakoegficnlkhebmhkjfich-google-calendar-3.1.0.crx
Binary files differ
diff --git a/chrome/test/data/extensions/pinning/calendar/gmbgaklkmjakoegficnlkhebmhkjfich-google-calendar-3.1.0.crx.INFO b/chrome/test/data/extensions/pinning/calendar/gmbgaklkmjakoegficnlkhebmhkjfich-google-calendar-3.1.0.crx.INFO
new file mode 100644
index 0000000..80bfad9e
--- /dev/null
+++ b/chrome/test/data/extensions/pinning/calendar/gmbgaklkmjakoegficnlkhebmhkjfich-google-calendar-3.1.0.crx.INFO
@@ -0,0 +1,3 @@
+CRX ID = gmbgaklkmjakoegficnlkhebmhkjfich
+
+This crx belongs to a version "3.1.0" of Google calendar extension from the Chrome Web Store, which is older than current available version.
diff --git a/chrome/test/data/extensions/pinning/no_update_url/key.pem b/chrome/test/data/extensions/pinning/no_update_url/key.pem
new file mode 100644
index 0000000..8f5e48a
--- /dev/null
+++ b/chrome/test/data/extensions/pinning/no_update_url/key.pem
@@ -0,0 +1,28 @@
+-----BEGIN PRIVATE KEY-----
+MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQD3DpzLha8o6vna
+yibkOtQyUaz6//SxLrAdHJNl96ydosgpezEDuszc+OXXWICHA31JFvoDSUcDH0yF
+JC1J1dbGn4ML3fqAMVHoyffeh0GWlq6A5LlvTm2SzzB1j6pOOLdJp61XbAaGCef7
+11Ke1IzDF/ZobVkm1oNrSz6mcfti7HguKvpMkbfllnl3oaVAZL0mr62QU4jjS7q+
+9FA0X9gs3vWzvMPMSrjfV9UC7mopfQCp7p+gt8mAGbs7yGSHtRffDkg+P2z79j0I
+/Q3WOAt+0tnTrgbW7NHaX6dOVh8ZEE/fPuyqptj4bI5dZlKQu+kvD6wQAQmWq7w/
+KWr8CRnBAgMBAAECggEANDfmdC4BY8imntPzfHHiv+/7e+H1ui2AE+ciUH8VAQyS
++hGLvpoRCvOgwO932xGlvXkZxvAKDmLjLZED8YM9LiYw8KUueUHzahymQ2abOlUG
+9T9i4l/VUR5xw6dl+7qJIwxJ2xx+FH6rhdNxJxkN136NGm1iVo8on9j7mPqmg7XF
+LrPdhaF5PPagju4rbzzcMqf+ubeErraXVAcEfGk8qq1NbWXxJmW5vYUlRKFh9Yz2
+Y4PZ6ZKc3Qc5q3jwI49VlZ6VXgDHLRXEsQF4WU6XJKTGLopkAQf+A9eWHoBuFsnB
+goHNfn9zi6Iir1sOBa6SVJ9UXz8Q35aLEK1PxwFmcQKBgQD+/o6/734D0in+ZXDD
+4lZWNNW9YiLlQDNk43J4+5+AQw8B4TTlr3nQtDlP7+EI3JuJkqBCvPCOaGl5rVpe
+odfFFY3TCxc8IGKc4gEfeXpcQcPIiVIx7LgGd0EqasmU48IE2hfNUwG4Rgr7Arr6
+rCGDIZCAqgeluWsEfCvQazNfLwKBgQD4CAqXxrvBHY3K4WykNLojiOAMY0zVfqnt
+uTyjVlz4G+WJUkidfxUQBxCSDWKRJioPXcndMjPH2eLYKhD1oBb2lfkx6veZy71J
+PWrK5rQQ6B8OkWOxz73LPg2rt+LyTuLzC5jRa6k6zxtN/E9INRiDamOTzkq0ZIuN
+3GnlAHBaDwKBgFqpHQ3m05H+NgoJ/QbYQUQTMu9LpgaGRf+X0oprRDFxJjPP42PK
+ePAYM8xVCFiDPxWxuSMhVBH904p8dQXyWunqmOsxJUiuZwFKZYXQRg3pH/f7wuqX
+NHKwEc5H+3XhaIkbFZ1d5t2ioaLPPLWFX8DkvSDR0zg77W3BzD4k7zXnAoGAbD5O
+KSiw3bmBHO4+FHdyWHmulZlseau6iyWRAhN5pUm/ZauERz+juIfswmCXCJNoPaaH
+p0H2eRxstDu/hQJlox2WUCOkBPl2VRU5mu0N3mb+zsPCh2ILCTy+iJdKssacoscH
+3TUn+KNT3jfjfba2SPvQEzwvPAS5JqLvj+IeJh8CgYEAqFPxVE0Fish61UNWz0Ky
++SO7SPvnqL/WHVFtnMFbeQwlhAaePFQChylySpSZSyg1qfihUISAkK3hG+nyw/5u
+v4qujEw/DwdkS/6r2hmnnbFpV1xUuIwEkaaKDnuvcIUl+a6l2xp1xpgFiQSripio
+5TMJhD/b3ksLZBX0qJn7XGw=
+-----END PRIVATE KEY-----
diff --git a/chrome/test/data/extensions/pinning/no_update_url/key.pem.INFO b/chrome/test/data/extensions/pinning/no_update_url/key.pem.INFO
new file mode 100644
index 0000000..929b731
--- /dev/null
+++ b/chrome/test/data/extensions/pinning/no_update_url/key.pem.INFO
@@ -0,0 +1 @@
+This is a key used to create different versions of extension with ID fdlpamochgodkfemfnickdlkabcfmbln used in extension pinning tests.
diff --git a/chrome/test/data/extensions/pinning/no_update_url/v1/manifest.json b/chrome/test/data/extensions/pinning/no_update_url/v1/manifest.json
new file mode 100644
index 0000000..0bfb06f8b
--- /dev/null
+++ b/chrome/test/data/extensions/pinning/no_update_url/v1/manifest.json
@@ -0,0 +1,5 @@
+{
+  "name": "Test Extension",
+  "version": "1",
+  "manifest_version": 2
+}
diff --git a/chrome/test/data/extensions/pinning/no_update_url/v2/manifest.json b/chrome/test/data/extensions/pinning/no_update_url/v2/manifest.json
new file mode 100644
index 0000000..8fcd80f1
--- /dev/null
+++ b/chrome/test/data/extensions/pinning/no_update_url/v2/manifest.json
@@ -0,0 +1,5 @@
+{
+  "name": "Test Extension",
+  "version": "2",
+  "manifest_version": 2
+}
diff --git a/chrome/test/data/extensions/pinning/update_url/key.pem b/chrome/test/data/extensions/pinning/update_url/key.pem
new file mode 100644
index 0000000..8f5e48a
--- /dev/null
+++ b/chrome/test/data/extensions/pinning/update_url/key.pem
@@ -0,0 +1,28 @@
+-----BEGIN PRIVATE KEY-----
+MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQD3DpzLha8o6vna
+yibkOtQyUaz6//SxLrAdHJNl96ydosgpezEDuszc+OXXWICHA31JFvoDSUcDH0yF
+JC1J1dbGn4ML3fqAMVHoyffeh0GWlq6A5LlvTm2SzzB1j6pOOLdJp61XbAaGCef7
+11Ke1IzDF/ZobVkm1oNrSz6mcfti7HguKvpMkbfllnl3oaVAZL0mr62QU4jjS7q+
+9FA0X9gs3vWzvMPMSrjfV9UC7mopfQCp7p+gt8mAGbs7yGSHtRffDkg+P2z79j0I
+/Q3WOAt+0tnTrgbW7NHaX6dOVh8ZEE/fPuyqptj4bI5dZlKQu+kvD6wQAQmWq7w/
+KWr8CRnBAgMBAAECggEANDfmdC4BY8imntPzfHHiv+/7e+H1ui2AE+ciUH8VAQyS
++hGLvpoRCvOgwO932xGlvXkZxvAKDmLjLZED8YM9LiYw8KUueUHzahymQ2abOlUG
+9T9i4l/VUR5xw6dl+7qJIwxJ2xx+FH6rhdNxJxkN136NGm1iVo8on9j7mPqmg7XF
+LrPdhaF5PPagju4rbzzcMqf+ubeErraXVAcEfGk8qq1NbWXxJmW5vYUlRKFh9Yz2
+Y4PZ6ZKc3Qc5q3jwI49VlZ6VXgDHLRXEsQF4WU6XJKTGLopkAQf+A9eWHoBuFsnB
+goHNfn9zi6Iir1sOBa6SVJ9UXz8Q35aLEK1PxwFmcQKBgQD+/o6/734D0in+ZXDD
+4lZWNNW9YiLlQDNk43J4+5+AQw8B4TTlr3nQtDlP7+EI3JuJkqBCvPCOaGl5rVpe
+odfFFY3TCxc8IGKc4gEfeXpcQcPIiVIx7LgGd0EqasmU48IE2hfNUwG4Rgr7Arr6
+rCGDIZCAqgeluWsEfCvQazNfLwKBgQD4CAqXxrvBHY3K4WykNLojiOAMY0zVfqnt
+uTyjVlz4G+WJUkidfxUQBxCSDWKRJioPXcndMjPH2eLYKhD1oBb2lfkx6veZy71J
+PWrK5rQQ6B8OkWOxz73LPg2rt+LyTuLzC5jRa6k6zxtN/E9INRiDamOTzkq0ZIuN
+3GnlAHBaDwKBgFqpHQ3m05H+NgoJ/QbYQUQTMu9LpgaGRf+X0oprRDFxJjPP42PK
+ePAYM8xVCFiDPxWxuSMhVBH904p8dQXyWunqmOsxJUiuZwFKZYXQRg3pH/f7wuqX
+NHKwEc5H+3XhaIkbFZ1d5t2ioaLPPLWFX8DkvSDR0zg77W3BzD4k7zXnAoGAbD5O
+KSiw3bmBHO4+FHdyWHmulZlseau6iyWRAhN5pUm/ZauERz+juIfswmCXCJNoPaaH
+p0H2eRxstDu/hQJlox2WUCOkBPl2VRU5mu0N3mb+zsPCh2ILCTy+iJdKssacoscH
+3TUn+KNT3jfjfba2SPvQEzwvPAS5JqLvj+IeJh8CgYEAqFPxVE0Fish61UNWz0Ky
++SO7SPvnqL/WHVFtnMFbeQwlhAaePFQChylySpSZSyg1qfihUISAkK3hG+nyw/5u
+v4qujEw/DwdkS/6r2hmnnbFpV1xUuIwEkaaKDnuvcIUl+a6l2xp1xpgFiQSripio
+5TMJhD/b3ksLZBX0qJn7XGw=
+-----END PRIVATE KEY-----
diff --git a/chrome/test/data/extensions/pinning/update_url/key.pem.INFO b/chrome/test/data/extensions/pinning/update_url/key.pem.INFO
new file mode 100644
index 0000000..929b731
--- /dev/null
+++ b/chrome/test/data/extensions/pinning/update_url/key.pem.INFO
@@ -0,0 +1 @@
+This is a key used to create different versions of extension with ID fdlpamochgodkfemfnickdlkabcfmbln used in extension pinning tests.
diff --git a/chrome/test/data/extensions/pinning/update_url/v1/manifest.json b/chrome/test/data/extensions/pinning/update_url/v1/manifest.json
new file mode 100644
index 0000000..2fc5449
--- /dev/null
+++ b/chrome/test/data/extensions/pinning/update_url/v1/manifest.json
@@ -0,0 +1,6 @@
+{
+  "name": "Test Extension",
+  "version": "1",
+  "manifest_version": 2,
+  "update_url": "http://update.extension/pinning/update_url/update_manifest.xml"
+}
diff --git a/chrome/test/data/extensions/pinning/update_url/v2/manifest.json b/chrome/test/data/extensions/pinning/update_url/v2/manifest.json
new file mode 100644
index 0000000..69f24aa
--- /dev/null
+++ b/chrome/test/data/extensions/pinning/update_url/v2/manifest.json
@@ -0,0 +1,6 @@
+{
+  "name": "Test Extension",
+  "version": "2",
+  "manifest_version": 2,
+  "update_url": "http://update.extension/pinning/update_url/update_manifest.xml"
+}
diff --git a/chrome/test/data/optimization_guide/amp.html b/chrome/test/data/optimization_guide/amp.html
new file mode 100644
index 0000000..a738133
--- /dev/null
+++ b/chrome/test/data/optimization_guide/amp.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<html amp>
+<head>
+  <title>Test Page</title>
+
+  <!-- This script is provided by the browsertest, but is intentionally slow.
+    This provides important time between commit and first layout for any text
+    dump requests to make it to the renderer, reducing flakes. -->
+  <script type="text/javascript" src="/slow-first-layout.js"></script>
+</head>
+<body>
+  <p>AMP</p>
+</body>
+</html>
diff --git a/chrome/test/data/optimization_guide/hello_world.html b/chrome/test/data/optimization_guide/hello_world.html
index b46bc4a..a03c846 100644
--- a/chrome/test/data/optimization_guide/hello_world.html
+++ b/chrome/test/data/optimization_guide/hello_world.html
@@ -11,6 +11,6 @@
 <body>
   <p>hello</p>
 
-  <script async type="text/javascript" src="hello_world.js"></script>
+  <script async type="text/javascript" src="/slow-add-world-text.js"></script>
 </body>
 </html>
diff --git a/chrome/test/data/optimization_guide/hello_world.js b/chrome/test/data/optimization_guide/hello_world.js
deleted file mode 100644
index 10492407..0000000
--- a/chrome/test/data/optimization_guide/hello_world.js
+++ /dev/null
@@ -1,7 +0,0 @@
-// Copyright 2021 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-var p = document.createElement('p');
-p.innerHTML = 'world';
-document.body.appendChild(p);
diff --git a/chrome/test/data/webui/BUILD.gn b/chrome/test/data/webui/BUILD.gn
index 1a14bef..ce08268 100644
--- a/chrome/test/data/webui/BUILD.gn
+++ b/chrome/test/data/webui/BUILD.gn
@@ -177,15 +177,7 @@
       "$root_gen_dir/chrome/test/data/webui/mock_timer.m.js",
       "$root_gen_dir/chrome/test/data/webui/net_internals/main_test.js",
       "$root_gen_dir/chrome/test/data/webui/net_internals/test_util.js",
-      "$root_gen_dir/chrome/test/data/webui/settings/fake_input_method_private.m.js",
-      "$root_gen_dir/chrome/test/data/webui/settings/fake_language_settings_private.m.js",
-      "$root_gen_dir/chrome/test/data/webui/settings/fake_settings_private.m.js",
       "$root_gen_dir/chrome/test/data/webui/settings/route_tests.js",
-      "$root_gen_dir/chrome/test/data/webui/settings/test_languages_browser_proxy.m.js",
-      "$root_gen_dir/chrome/test/data/webui/settings/test_lifetime_browser_proxy.m.js",
-      "$root_gen_dir/chrome/test/data/webui/settings/test_profile_info_browser_proxy.m.js",
-      "$root_gen_dir/chrome/test/data/webui/settings/test_search_engines_browser_proxy.m.js",
-      "$root_gen_dir/chrome/test/data/webui/settings/test_sync_browser_proxy.m.js",
       "$root_gen_dir/chrome/test/data/webui/test_browser_proxy.m.js",
       "$root_gen_dir/chrome/test/data/webui/test_store.m.js",
       "$root_gen_dir/chrome/test/data/webui/test_util.m.js",
@@ -488,11 +480,13 @@
     ":modulize_local",
     "./cr_components/chromeos:modulize",
     "./js:modulize",
-    "./settings:modulize",
   ]
 
   if (is_chromeos_ash) {
-    deps += [ "./nearby_share/shared:modulize" ]
+    deps += [
+      "./nearby_share/shared:modulize",
+      "./settings/chromeos:modulize",
+    ]
   }
 }
 
diff --git a/chrome/test/data/webui/cr_components/chromeos/cellular_setup/activation_code_page_test.js b/chrome/test/data/webui/cr_components/chromeos/cellular_setup/activation_code_page_test.js
index 3fe9f6c4..ee03b6a 100644
--- a/chrome/test/data/webui/cr_components/chromeos/cellular_setup/activation_code_page_test.js
+++ b/chrome/test/data/webui/cr_components/chromeos/cellular_setup/activation_code_page_test.js
@@ -69,7 +69,7 @@
     const startScanningButton = activationCodePage.$$('#startScanningButton');
     const scanFinishContainer = activationCodePage.$$('#scanFinishContainer');
     const switchCameraButton = activationCodePage.$$('#switchCameraButton');
-    const tryAgainButton = activationCodePage.$$('#tryAgainButton');
+    const useCameraAgainButton = activationCodePage.$$('#useCameraAgainButton');
     const scanSuccessContainer = activationCodePage.$$('#scanSuccessContainer');
     const scanFailureContainer = activationCodePage.$$('#scanFailureContainer');
     const spinner = activationCodePage.$$('paper-spinner-lite');
@@ -81,7 +81,7 @@
     assertTrue(!!startScanningButton);
     assertTrue(!!scanFinishContainer);
     assertTrue(!!switchCameraButton);
-    assertTrue(!!tryAgainButton);
+    assertTrue(!!useCameraAgainButton);
     assertTrue(!!scanSuccessContainer);
     assertTrue(!!scanFailureContainer);
     assertTrue(!!spinner);
@@ -116,8 +116,8 @@
     assertFalse(scanSuccessContainer.hidden);
     assertTrue(scanFailureContainer.hidden);
 
-    // Click the 'Try Again' button.
-    tryAgainButton.click();
+    // Click the 'Use camera again' button.
+    useCameraAgainButton.click();
     await flushAsync();
 
     // The video should be visible and start scanning UI hidden.
@@ -139,8 +139,8 @@
     assertTrue(scanFailureContainer.hidden);
     assertFalse(activationCodePage.showError);
 
-    // Click the 'Try Again' button.
-    tryAgainButton.click();
+    // Click the 'Use camera again' button.
+    useCameraAgainButton.click();
     await flushAsync();
 
     // The video should be visible and start scanning UI hidden.
@@ -328,4 +328,53 @@
         assertFalse(startScanningContainer.hidden);
         assertTrue(scanFinishContainer.hidden);
       });
+
+  test(
+      'Install error after scanning should show error on camera',
+      async function() {
+        await flushAsync();
+        const input = activationCodePage.$$('#activationCode');
+        const startScanningContainer =
+            activationCodePage.$$('#startScanningContainer');
+        const startScanningButton =
+            activationCodePage.$$('#startScanningButton');
+        const scanFinishContainer =
+            activationCodePage.$$('#scanFinishContainer');
+        const scanInstallFailureHeader =
+            activationCodePage.$$('#scanInstallFailureHeader');
+        const scanSucessHeader = activationCodePage.$$('#scanSucessHeader');
+        assertTrue(!!input);
+        assertTrue(!!startScanningContainer);
+        assertTrue(!!startScanningButton);
+        assertTrue(!!scanFinishContainer);
+        assertTrue(!!scanInstallFailureHeader);
+        assertTrue(!!scanSucessHeader);
+        assertFalse(input.invalid);
+
+        // Click the start scanning button.
+        startScanningButton.click();
+        await flushAsync();
+
+        // Mock camera scanning a code.
+        await intervalFunction();
+        await flushAsync();
+
+        // The code detected UI should be showing.
+        assertTrue(startScanningContainer.hidden);
+        assertFalse(scanFinishContainer.hidden);
+        assertFalse(scanSucessHeader.hidden);
+        assertTrue(scanInstallFailureHeader.hidden);
+
+        // Mock an install error.
+        activationCodePage.showError = true;
+
+        // The scan install failure UI should be showing.
+        assertTrue(startScanningContainer.hidden);
+        assertFalse(scanFinishContainer.hidden);
+        assertTrue(scanSucessHeader.hidden);
+        assertFalse(scanInstallFailureHeader.hidden);
+
+        // There should be no error displayed on the input.
+        assertFalse(input.invalid);
+      });
 });
diff --git a/chrome/test/data/webui/settings/BUILD.gn b/chrome/test/data/webui/settings/BUILD.gn
index 13c4cbe..debccaf 100644
--- a/chrome/test/data/webui/settings/BUILD.gn
+++ b/chrome/test/data/webui/settings/BUILD.gn
@@ -15,51 +15,6 @@
   in_files = [ "route_tests.js" ]
 }
 
-group("modulize") {
-  public_deps = [ ":modulize_local" ]
-
-  if (is_chromeos_ash) {
-    deps = [ "./chromeos:modulize" ]
-  }
-}
-
-js_modulizer("modulize_local") {
-  input_files = [
-    "fake_input_method_private.js",
-    "fake_language_settings_private.js",
-    "fake_settings_private.js",
-    "test_languages_browser_proxy.js",
-    "test_lifetime_browser_proxy.js",
-    "test_profile_info_browser_proxy.js",
-    "test_search_engines_browser_proxy.js",
-    "test_sync_browser_proxy.js",
-  ]
-
-  namespace_rewrites = settings_namespace_rewrites + test_namespace_rewrites + [
-                         "export_passwords_tests.run|run",
-                         "settings.buildRouterForTesting|buildRouter",
-                         "settings.defaultSettingLabel|defaultSettingLabel",
-                         "settings.FakeLanguageSettingsPrivate|FakeLanguageSettingsPrivate",
-                         "settings.FakeInputMethodPrivate|FakeInputMethodPrivate",
-                         "settings.FakeSettingsPrivate|FakeSettingsPrivate",
-                         "settings.getFakeLanguagePrefs|getFakeLanguagePrefs",
-                         "settings.kMenuCloseDelay|kMenuCloseDelay",
-                         "settings.setLanguageSettingsPrivateApiForTest|setLanguageSettingsPrivateApiForTest",
-                         "settings.setPageVisibilityForTesting|setPageVisibilityForTesting",
-                         "settings.setSearchManagerForTesting|setSearchManagerForTesting",
-                         "settings.TestLanguagesBrowserProxy|TestLanguagesBrowserProxy",
-                         "settings.TestLifetimeBrowserProxy|TestLifetimeBrowserProxy",
-                         "settings_page_test_util.getPage|getPage",
-                         "settings_page_test_util.getSection|getSection",
-                         "settings_search.createSampleSearchEngine|createSampleSearchEngine",
-                         "settings_search.TestSearchEnginesBrowserProxy|TestSearchEnginesBrowserProxy",
-                         "sync_test_util.getSyncAllPrefs|getSyncAllPrefs",
-                         "sync_test_util.setupRouterWithSyncRoutes|setupRouterWithSyncRoutes",
-                         "sync_test_util.simulateSyncStatus|simulateSyncStatus",
-                         "sync_test_util.simulateStoredAccounts|simulateStoredAccounts",
-                       ]
-}
-
 js_type_check("closure_compile") {
   is_polymer3 = true
   closure_flags = settings_closure_flags + [
@@ -100,9 +55,9 @@
     #":edit_dictionary_page_test",
     #":ensure_lazy_loaded",
     #":extension_controlled_indicator_tests",
-    ":fake_input_method_private.m",
-    ":fake_language_settings_private.m",
-    ":fake_settings_private.m",
+    ":fake_input_method_private",
+    ":fake_language_settings_private",
+    ":fake_settings_private",
 
     #":help_page_v3_test",
     #":idle_load_tests",
@@ -192,9 +147,9 @@
 
     #":test_extension_control_browser_proxy",
     ":test_hats_browser_proxy",
-    ":test_languages_browser_proxy.m",
+    ":test_languages_browser_proxy",
     ":test_languages_metrics_proxy",
-    ":test_lifetime_browser_proxy.m",
+    ":test_lifetime_browser_proxy",
     ":test_local_data_browser_proxy",
     ":test_metrics_browser_proxy",
     ":test_open_window_proxy",
@@ -203,9 +158,9 @@
 
     #":test_profile_info_browser_proxy",
     #":test_reset_browser_proxy",
-    ":test_search_engines_browser_proxy.m",
+    ":test_search_engines_browser_proxy",
     ":test_site_settings_prefs_browser_proxy",
-    ":test_sync_browser_proxy.m",
+    ":test_sync_browser_proxy",
     ":test_util",
 
     #":zoom_levels_tests",
@@ -215,7 +170,7 @@
 js_library("about_page_tests") {
   deps = [
     ":test_about_page_browser_proxy",
-    ":test_lifetime_browser_proxy.m",
+    ":test_lifetime_browser_proxy",
     "..:chai_assert",
     "//chrome/browser/resources/settings:settings",
     "//ui/webui/resources/js:load_time_data.m",
@@ -267,38 +222,30 @@
   externs_list = [ "$externs_path/mocha-2.5.js" ]
 }
 
-js_library("fake_language_settings_private.m") {
-  sources = [ "$root_gen_dir/chrome/test/data/webui/settings/fake_language_settings_private.m.js" ]
+js_library("fake_language_settings_private") {
   deps = [
     "..:chai_assert",
     "..:fake_chrome_event.m",
     "..:test_browser_proxy.m",
     "//ui/webui/resources/js:cr.m",
   ]
-  extra_deps = [ ":modulize_local" ]
 }
 
-js_library("fake_input_method_private.m") {
-  sources = [ "$root_gen_dir/chrome/test/data/webui/settings/fake_input_method_private.m.js" ]
-  extra_deps = [ ":modulize_local" ]
+js_library("fake_input_method_private") {
 }
 
-js_library("fake_settings_private.m") {
-  sources = [
-    "$root_gen_dir/chrome/test/data/webui/settings/fake_settings_private.m.js",
-  ]
+js_library("fake_settings_private") {
   deps = [
     "..:chai_assert",
     "..:fake_chrome_event.m",
   ]
-  extra_deps = [ ":modulize_local" ]
 }
 
 js_library("languages_page_metrics_test_cros") {
   deps = [
-    ":fake_language_settings_private.m",
-    ":fake_settings_private.m",
-    ":test_languages_browser_proxy.m",
+    ":fake_language_settings_private",
+    ":fake_settings_private",
+    ":test_languages_browser_proxy",
     ":test_languages_metrics_proxy",
     "..:chai_assert",
     "..:test_util.m",
@@ -310,9 +257,9 @@
 
 js_library("languages_page_metrics_test_browser") {
   deps = [
-    ":fake_language_settings_private.m",
-    ":fake_settings_private.m",
-    ":test_languages_browser_proxy.m",
+    ":fake_language_settings_private",
+    ":fake_settings_private",
+    ":test_languages_browser_proxy",
     "..:chai_assert",
     "..:test_util.m",
     "//chrome/browser/resources/settings:lazy_load",
@@ -341,7 +288,7 @@
     ":test_metrics_browser_proxy",
     ":test_privacy_page_browser_proxy",
     ":test_site_settings_prefs_browser_proxy",
-    ":test_sync_browser_proxy.m",
+    ":test_sync_browser_proxy",
     "..:chai_assert",
     "..:test_util.m",
     "//chrome/browser/resources/settings:lazy_load",
@@ -391,7 +338,7 @@
 js_library("safety_check_page_test") {
   deps = [
     ":test_hats_browser_proxy",
-    ":test_lifetime_browser_proxy.m",
+    ":test_lifetime_browser_proxy",
     ":test_metrics_browser_proxy",
     ":test_open_window_proxy",
     ":test_password_manager_proxy",
@@ -436,7 +383,7 @@
 
 js_library("search_page_test") {
   deps = [
-    ":test_search_engines_browser_proxy.m",
+    ":test_search_engines_browser_proxy",
     "..:chai_assert",
     "//chrome/browser/resources/settings:settings",
   ]
@@ -467,7 +414,7 @@
   deps = [
     ":test_metrics_browser_proxy",
     ":test_privacy_page_browser_proxy",
-    ":test_sync_browser_proxy.m",
+    ":test_sync_browser_proxy",
     "..:chai_assert",
     "..:test_util.m",
     "//chrome/browser/resources/settings:lazy_load",
@@ -522,7 +469,6 @@
     "//chrome/browser/resources/settings:settings",
     "//ui/webui/resources/js:cr.m",
   ]
-  extra_deps = [ ":modulize_local" ]
 }
 
 js_library("test_clear_browsing_data_browser_proxy") {
@@ -540,16 +486,14 @@
   ]
 }
 
-js_library("test_languages_browser_proxy.m") {
-  sources = [ "$root_gen_dir/chrome/test/data/webui/settings/test_languages_browser_proxy.m.js" ]
+js_library("test_languages_browser_proxy") {
   deps = [
-    ":fake_input_method_private.m",
-    ":fake_language_settings_private.m",
+    ":fake_input_method_private",
+    ":fake_language_settings_private",
     "..:test_browser_proxy.m",
     "//chrome/browser/resources/settings:lazy_load",
     "//ui/webui/resources/js:cr.m",
   ]
-  extra_deps = [ ":modulize_local" ]
 }
 
 js_library("test_languages_metrics_proxy") {
@@ -559,13 +503,11 @@
   ]
 }
 
-js_library("test_lifetime_browser_proxy.m") {
-  sources = [ "$root_gen_dir/chrome/test/data/webui/settings/test_lifetime_browser_proxy.m.js" ]
+js_library("test_lifetime_browser_proxy") {
   deps = [
     "..:test_browser_proxy.m",
     "//chrome/browser/resources/settings:settings",
   ]
-  extra_deps = [ ":modulize_local" ]
 }
 
 js_library("test_local_data_browser_proxy") {
@@ -575,13 +517,11 @@
   ]
 }
 
-js_library("test_search_engines_browser_proxy.m") {
-  sources = [ "$root_gen_dir/chrome/test/data/webui/settings/test_search_engines_browser_proxy.m.js" ]
+js_library("test_search_engines_browser_proxy") {
   deps = [
     "..:test_browser_proxy.m",
     "//chrome/browser/resources/settings:settings",
   ]
-  extra_deps = [ ":modulize_local" ]
 }
 
 js_library("test_site_settings_prefs_browser_proxy") {
@@ -592,13 +532,11 @@
   ]
 }
 
-js_library("test_sync_browser_proxy.m") {
-  sources = [ "$root_gen_dir/chrome/test/data/webui/settings/test_sync_browser_proxy.m.js" ]
+js_library("test_sync_browser_proxy") {
   deps = [
     "..:test_browser_proxy.m",
     "//chrome/browser/resources/settings:settings",
   ]
-  extra_deps = [ ":modulize" ]
 }
 
 js_library("test_util") {
diff --git a/chrome/test/data/webui/settings/a11y/edit_dictionary_a11y_v3_test.js b/chrome/test/data/webui/settings/a11y/edit_dictionary_a11y_v3_test.js
index a4fb5f0d..aed50af 100644
--- a/chrome/test/data/webui/settings/a11y/edit_dictionary_a11y_v3_test.js
+++ b/chrome/test/data/webui/settings/a11y/edit_dictionary_a11y_v3_test.js
@@ -5,8 +5,8 @@
 import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 import {LanguagesBrowserProxyImpl} from 'chrome://settings/lazy_load.js';
 import {Router, routes} from 'chrome://settings/settings.js';
-import {FakeLanguageSettingsPrivate} from 'chrome://test/settings/fake_language_settings_private.m.js';
-import {TestLanguagesBrowserProxy} from 'chrome://test/settings/test_languages_browser_proxy.m.js';
+import {FakeLanguageSettingsPrivate} from 'chrome://test/settings/fake_language_settings_private.js';
+import {TestLanguagesBrowserProxy} from 'chrome://test/settings/test_languages_browser_proxy.js';
 import {flushTasks} from 'chrome://test/test_util.m.js';
 
 const fakeLanguageSettingsPrivate = new FakeLanguageSettingsPrivate();
diff --git a/chrome/test/data/webui/settings/a11y/sign_out_a11y_v3_test.js b/chrome/test/data/webui/settings/a11y/sign_out_a11y_v3_test.js
index 6a8a9fd..0f50bf6 100644
--- a/chrome/test/data/webui/settings/a11y/sign_out_a11y_v3_test.js
+++ b/chrome/test/data/webui/settings/a11y/sign_out_a11y_v3_test.js
@@ -7,7 +7,7 @@
 import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 import {routes, StatusAction, SyncBrowserProxyImpl} from 'chrome://settings/settings.js';
 import {simulateSyncStatus} from 'chrome://test/settings/sync_test_util.js';
-import {TestSyncBrowserProxy} from 'chrome://test/settings/test_sync_browser_proxy.m.js';
+import {TestSyncBrowserProxy} from 'chrome://test/settings/test_sync_browser_proxy.js';
 import {waitBeforeNextRender} from 'chrome://test/test_util.m.js';
 
 // Set the URL of the page to render to load the correct view upon
diff --git a/chrome/test/data/webui/settings/about_page_tests.js b/chrome/test/data/webui/settings/about_page_tests.js
index 69fbf72..b3eea21 100644
--- a/chrome/test/data/webui/settings/about_page_tests.js
+++ b/chrome/test/data/webui/settings/about_page_tests.js
@@ -11,7 +11,7 @@
 import {assertEquals, assertFalse, assertNotEquals, assertTrue} from '../chai_assert.js';
 
 import {TestAboutPageBrowserProxy} from './test_about_page_browser_proxy.js';
-import {TestLifetimeBrowserProxy} from './test_lifetime_browser_proxy.m.js';
+import {TestLifetimeBrowserProxy} from './test_lifetime_browser_proxy.js';
 
 // clang-format on
 
diff --git a/chrome/test/data/webui/settings/autofill_page_test.js b/chrome/test/data/webui/settings/autofill_page_test.js
index 853eeea..7c8b740 100644
--- a/chrome/test/data/webui/settings/autofill_page_test.js
+++ b/chrome/test/data/webui/settings/autofill_page_test.js
@@ -6,7 +6,7 @@
 import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 import {AutofillManagerImpl, PaymentsManagerImpl} from 'chrome://settings/lazy_load.js';
 import {CrSettingsPrefs, MultiStoreExceptionEntry, MultiStorePasswordUiEntry, OpenWindowProxyImpl, PasswordManagerImpl, Router, routes, SettingsPluralStringProxyImpl} from 'chrome://settings/settings.js';
-import {FakeSettingsPrivate} from 'chrome://test/settings/fake_settings_private.m.js';
+import {FakeSettingsPrivate} from 'chrome://test/settings/fake_settings_private.js';
 import {AutofillManagerExpectations, createAddressEntry, createCreditCardEntry, createExceptionEntry, createPasswordEntry, PaymentsManagerExpectations, TestAutofillManager, TestPaymentsManager} from 'chrome://test/settings/passwords_and_autofill_fake_data.js';
 import {makeCompromisedCredential} from 'chrome://test/settings/passwords_and_autofill_fake_data.js';
 import {TestOpenWindowProxy} from 'chrome://test/settings/test_open_window_proxy.js';
diff --git a/chrome/test/data/webui/settings/avatar_icon_test.js b/chrome/test/data/webui/settings/avatar_icon_test.js
index ae446cc2..0069273 100644
--- a/chrome/test/data/webui/settings/avatar_icon_test.js
+++ b/chrome/test/data/webui/settings/avatar_icon_test.js
@@ -7,7 +7,7 @@
 import 'chrome://settings/lazy_load.js';
 import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 import {SyncBrowserProxy, SyncBrowserProxyImpl} from 'chrome://settings/settings.js';
-import {TestSyncBrowserProxy} from 'chrome://test/settings/test_sync_browser_proxy.m.js';
+import {TestSyncBrowserProxy} from 'chrome://test/settings/test_sync_browser_proxy.js';
 
 suite('AvatarIcon', function() {
   /** @type {SyncBrowserProxy} */
diff --git a/chrome/test/data/webui/settings/chromeos/input_method_options_page_test.js b/chrome/test/data/webui/settings/chromeos/input_method_options_page_test.js
index be03119..7fdfb7e 100644
--- a/chrome/test/data/webui/settings/chromeos/input_method_options_page_test.js
+++ b/chrome/test/data/webui/settings/chromeos/input_method_options_page_test.js
@@ -6,7 +6,7 @@
 // #import 'chrome://os-settings/chromeos/lazy_load.js';
 // #import {CrSettingsPrefs, Router, routes} from 'chrome://os-settings/chromeos/os_settings.js';
 // #import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
-// #import {FakeSettingsPrivate} from '../fake_settings_private.m.js';
+// #import {FakeSettingsPrivate} from '../fake_settings_private.js';
 // #import {waitAfterNextRender} from '../../test_util.m.js';
 // clang-format on
 
diff --git a/chrome/test/data/webui/settings/chromeos/input_page_test.js b/chrome/test/data/webui/settings/chromeos/input_page_test.js
index 31ca7d04..21fb1e0 100644
--- a/chrome/test/data/webui/settings/chromeos/input_page_test.js
+++ b/chrome/test/data/webui/settings/chromeos/input_page_test.js
@@ -7,8 +7,8 @@
 // #import {CrSettingsPrefs, Router, routes} from 'chrome://os-settings/chromeos/os_settings.js';
 // #import {keyDownOn} from 'chrome://resources/polymer/v3_0/iron-test-helpers/mock-interactions.js';
 // #import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
-// #import {getFakeLanguagePrefs} from '../fake_language_settings_private.m.js'
-// #import {FakeSettingsPrivate} from '../fake_settings_private.m.js';
+// #import {getFakeLanguagePrefs} from '../fake_language_settings_private.js'
+// #import {FakeSettingsPrivate} from '../fake_settings_private.js';
 // #import {TestLanguagesBrowserProxy} from './test_os_languages_browser_proxy.m.js';
 // #import {TestLanguagesMetricsProxy} from './test_os_languages_metrics_proxy.m.js';
 // #import {assertEquals, assertFalse, assertTrue} from '../../chai_assert.js';
diff --git a/chrome/test/data/webui/settings/chromeos/os_edit_dictionary_page_test.js b/chrome/test/data/webui/settings/chromeos/os_edit_dictionary_page_test.js
index 64bac97..1c7e215 100644
--- a/chrome/test/data/webui/settings/chromeos/os_edit_dictionary_page_test.js
+++ b/chrome/test/data/webui/settings/chromeos/os_edit_dictionary_page_test.js
@@ -6,8 +6,8 @@
 // #import {LanguagesBrowserProxyImpl} from 'chrome://os-settings/chromeos/lazy_load.js';
 // #import {CrSettingsPrefs} from 'chrome://os-settings/chromeos/os_settings.js';
 // #import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
-// #import {FakeLanguageSettingsPrivate} from '../fake_language_settings_private.m.js';
-// #import {FakeSettingsPrivate} from '../fake_settings_private.m.js';
+// #import {FakeLanguageSettingsPrivate} from '../fake_language_settings_private.js';
+// #import {FakeSettingsPrivate} from '../fake_settings_private.js';
 // #import {TestLanguagesBrowserProxy} from './test_os_languages_browser_proxy.m.js';
 // clang-format on
 
diff --git a/chrome/test/data/webui/settings/chromeos/os_languages_page_tests.js b/chrome/test/data/webui/settings/chromeos/os_languages_page_tests.js
index f2a6ba84..b2fba58 100644
--- a/chrome/test/data/webui/settings/chromeos/os_languages_page_tests.js
+++ b/chrome/test/data/webui/settings/chromeos/os_languages_page_tests.js
@@ -7,8 +7,8 @@
 // #import {CrSettingsPrefs, Router, routes} from 'chrome://os-settings/chromeos/os_settings.js';
 // #import {assert} from 'chrome://resources/js/assert.m.js';
 // #import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
-// #import {getFakeLanguagePrefs} from '../fake_language_settings_private.m.js'
-// #import {FakeSettingsPrivate} from '../fake_settings_private.m.js';
+// #import {getFakeLanguagePrefs} from '../fake_language_settings_private.js'
+// #import {FakeSettingsPrivate} from '../fake_settings_private.js';
 // #import {TestLanguagesBrowserProxy} from './test_os_languages_browser_proxy.m.js';
 // #import {TestLanguagesMetricsProxy} from './test_os_languages_metrics_proxy.m.js';
 // #import {assertEquals, assertFalse, assertTrue} from '../../chai_assert.js';
diff --git a/chrome/test/data/webui/settings/chromeos/os_languages_page_v2_tests.js b/chrome/test/data/webui/settings/chromeos/os_languages_page_v2_tests.js
index 91c787f1..f4bbb01 100644
--- a/chrome/test/data/webui/settings/chromeos/os_languages_page_v2_tests.js
+++ b/chrome/test/data/webui/settings/chromeos/os_languages_page_v2_tests.js
@@ -8,8 +8,8 @@
 // #import {assert} from 'chrome://resources/js/assert.m.js';
 // #import {keyDownOn} from 'chrome://resources/polymer/v3_0/iron-test-helpers/mock-interactions.js';
 // #import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
-// #import {getFakeLanguagePrefs} from '../fake_language_settings_private.m.js'
-// #import {FakeSettingsPrivate} from '../fake_settings_private.m.js';
+// #import {getFakeLanguagePrefs} from '../fake_language_settings_private.js'
+// #import {FakeSettingsPrivate} from '../fake_settings_private.js';
 // #import {TestLanguagesBrowserProxy} from './test_os_languages_browser_proxy.m.js';
 // #import {TestLanguagesMetricsProxy} from './test_os_languages_metrics_proxy.m.js';
 // #import {TestLifetimeBrowserProxy} from './test_os_lifetime_browser_proxy.m.js';
diff --git a/chrome/test/data/webui/settings/chromeos/os_people_page_test.js b/chrome/test/data/webui/settings/chromeos/os_people_page_test.js
index 5798b6d..3a68cae 100644
--- a/chrome/test/data/webui/settings/chromeos/os_people_page_test.js
+++ b/chrome/test/data/webui/settings/chromeos/os_people_page_test.js
@@ -10,7 +10,7 @@
 // #import {assertEquals, assertFalse, assertTrue} from '../../chai_assert.js';
 // #import {assert} from 'chrome://resources/js/assert.m.js';
 // #import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
-// #import {TestProfileInfoBrowserProxy} from 'chrome://test/settings/test_profile_info_browser_proxy.m.js';
+// #import {TestProfileInfoBrowserProxy} from 'chrome://test/settings/test_profile_info_browser_proxy.js';
 // #import {TestSyncBrowserProxy} from './test_os_sync_browser_proxy.m.js';
 // #import {FakeQuickUnlockPrivate} from './fake_quick_unlock_private.m.js';
 // #import {webUIListenerCallback} from 'chrome://resources/js/cr.m.js';
diff --git a/chrome/test/data/webui/settings/chromeos/quick_unlock_authenticate_browsertest_chromeos.js b/chrome/test/data/webui/settings/chromeos/quick_unlock_authenticate_browsertest_chromeos.js
index 3598effc..c68702d 100644
--- a/chrome/test/data/webui/settings/chromeos/quick_unlock_authenticate_browsertest_chromeos.js
+++ b/chrome/test/data/webui/settings/chromeos/quick_unlock_authenticate_browsertest_chromeos.js
@@ -11,7 +11,7 @@
 // #import {FakeQuickUnlockPrivate} from './fake_quick_unlock_private.m.js';
 // #import {FakeQuickUnlockUma} from './fake_quick_unlock_uma.m.js';
 // #import {LockScreenProgress} from 'chrome://resources/cr_components/chromeos/quick_unlock/lock_screen_constants.m.js';
-// #import {FakeSettingsPrivate} from 'chrome://test/settings/fake_settings_private.m.js';
+// #import {FakeSettingsPrivate} from 'chrome://test/settings/fake_settings_private.js';
 // #import {CrSettingsPrefs, Router, routes} from 'chrome://os-settings/chromeos/os_settings.js';
 // #import {getDeepActiveElement} from 'chrome://resources/js/util.m.js';
 // #import {eventToPromise, waitAfterNextRender, waitBeforeNextRender} from '../../../test_util.m.js';
diff --git a/chrome/test/data/webui/settings/chromeos/test_os_languages_browser_proxy.js b/chrome/test/data/webui/settings/chromeos/test_os_languages_browser_proxy.js
index 54f6265..578ed93 100644
--- a/chrome/test/data/webui/settings/chromeos/test_os_languages_browser_proxy.js
+++ b/chrome/test/data/webui/settings/chromeos/test_os_languages_browser_proxy.js
@@ -4,8 +4,8 @@
 
 // clang-format off
 // #import {LanguagesBrowserProxy} from 'chrome://os-settings/chromeos/lazy_load.js';
-// #import {FakeInputMethodPrivate} from '../fake_input_method_private.m.js';
-// #import {FakeLanguageSettingsPrivate} from '../fake_language_settings_private.m.js';
+// #import {FakeInputMethodPrivate} from '../fake_input_method_private.js';
+// #import {FakeLanguageSettingsPrivate} from '../fake_language_settings_private.js';
 // #import {TestBrowserProxy} from '../../test_browser_proxy.m.js';
 // clang-format on
 
diff --git a/chrome/test/data/webui/settings/clear_browsing_data_test.js b/chrome/test/data/webui/settings/clear_browsing_data_test.js
index 0feb7ae9..61b1f81 100644
--- a/chrome/test/data/webui/settings/clear_browsing_data_test.js
+++ b/chrome/test/data/webui/settings/clear_browsing_data_test.js
@@ -10,7 +10,7 @@
 import {ClearBrowsingDataBrowserProxyImpl} from 'chrome://settings/lazy_load.js';
 import {Router, routes, StatusAction, SyncBrowserProxyImpl} from 'chrome://settings/settings.js';
 import {TestClearBrowsingDataBrowserProxy} from 'chrome://test/settings/test_clear_browsing_data_browser_proxy.js';
-import {TestSyncBrowserProxy} from 'chrome://test/settings/test_sync_browser_proxy.m.js';
+import {TestSyncBrowserProxy} from 'chrome://test/settings/test_sync_browser_proxy.js';
 import {eventToPromise, isChildVisible, isVisible, whenAttributeIs} from 'chrome://test/test_util.m.js';
 
 // clang-format on
diff --git a/chrome/test/data/webui/settings/edit_dictionary_page_test.js b/chrome/test/data/webui/settings/edit_dictionary_page_test.js
index d5c138e..a9f6c312cf 100644
--- a/chrome/test/data/webui/settings/edit_dictionary_page_test.js
+++ b/chrome/test/data/webui/settings/edit_dictionary_page_test.js
@@ -7,9 +7,9 @@
 import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 import {LanguagesBrowserProxyImpl} from 'chrome://settings/lazy_load.js';
 import {CrSettingsPrefs} from 'chrome://settings/settings.js';
-import {FakeLanguageSettingsPrivate} from 'chrome://test/settings/fake_language_settings_private.m.js';
-import {FakeSettingsPrivate} from 'chrome://test/settings/fake_settings_private.m.js';
-import {TestLanguagesBrowserProxy} from 'chrome://test/settings/test_languages_browser_proxy.m.js';
+import {FakeLanguageSettingsPrivate} from 'chrome://test/settings/fake_language_settings_private.js';
+import {FakeSettingsPrivate} from 'chrome://test/settings/fake_settings_private.js';
+import {TestLanguagesBrowserProxy} from 'chrome://test/settings/test_languages_browser_proxy.js';
 
 // clang-format on
 
diff --git a/chrome/test/data/webui/settings/fake_input_method_private.js b/chrome/test/data/webui/settings/fake_input_method_private.js
index 95e09aa..51a625b 100644
--- a/chrome/test/data/webui/settings/fake_input_method_private.js
+++ b/chrome/test/data/webui/settings/fake_input_method_private.js
@@ -6,34 +6,29 @@
  * @fileoverview Fake implementation of chrome.inputMethodPrivate
  * for testing.
  */
-cr.define('settings', function() {
   /**
    * Fake of the chrome.inputMethodsPrivate API. Only methods that are called
    * during testing have been implemented.
    *
    * @constructor
    */
-  /* #export */ function FakeInputMethodPrivate() {}
+export function FakeInputMethodPrivate() {}
 
-  FakeInputMethodPrivate.prototype = {
-    getCurrentInputMethod: function(callback) {
-      callback(null);
-    },
+FakeInputMethodPrivate.prototype = {
+  getCurrentInputMethod: function(callback) {
+    callback(null);
+  },
 
-    setCurrentInputMethod: () => {},
+  setCurrentInputMethod: () => {},
 
-    get onChanged() {
-      return {
-        addListener: function() {
-          // Nothing to do here.
-        },
-        removeListener: function() {
-          // Nothing to do here.
-        },
-      };
-    },
-  };
-
-  // #cr_define_end
-  return {FakeInputMethodPrivate: FakeInputMethodPrivate};
-});
+  get onChanged() {
+    return {
+      addListener: function() {
+        // Nothing to do here.
+      },
+      removeListener: function() {
+        // Nothing to do here.
+      },
+    };
+  },
+};
diff --git a/chrome/test/data/webui/settings/fake_language_settings_private.js b/chrome/test/data/webui/settings/fake_language_settings_private.js
index e2b6a4f3..f9fc68b5 100644
--- a/chrome/test/data/webui/settings/fake_language_settings_private.js
+++ b/chrome/test/data/webui/settings/fake_language_settings_private.js
@@ -7,559 +7,543 @@
  * for testing.
  */
 
-// #import {assert, assertNotReached} from 'chrome://resources/js/assert.m.js';
-// #import {isChromeOS} from 'chrome://resources/js/cr.m.js';
-// #import {FakeChromeEvent} from '../fake_chrome_event.m.js';
-// #import {TestBrowserProxy} from '../test_browser_proxy.m.js';
+import {assert, assertNotReached} from 'chrome://resources/js/assert.m.js';
+import {isChromeOS} from 'chrome://resources/js/cr.m.js';
+import {FakeChromeEvent} from '../fake_chrome_event.m.js';
+import {TestBrowserProxy} from '../test_browser_proxy.m.js';
 
-cr.define('settings', function() {
+/**
+ * Fake of the chrome.languageSettingsPrivate API.
+ * @implements {LanguageSettingsPrivate}
+ */
+export class FakeLanguageSettingsPrivate extends TestBrowserProxy {
+  constructor() {
+    // List of method names expected to be tested with whenCalled()
+    super([
+      'getSpellcheckWords',
+    ]);
+
+    // /** @type {!SettingsPrefsElement} */
+    this.settingsPrefs_ = null;
+
+    /**
+     * Called when the pref for the dictionaries used for spell checking
+     * changes or the status of one of the spell check dictionaries changes.
+     * @type {!ChromeEvent}
+     */
+    this.onSpellcheckDictionariesChanged =
+        /** @type {!ChromeEvent} */ (new FakeChromeEvent());
+
+    /**
+     * Called when words are added to and/or removed from the custom spell
+     * check dictionary.
+     * @type {!ChromeEvent}
+     */
+    this.onCustomDictionaryChanged =
+        /** @type {!ChromeEvent} */ (new FakeChromeEvent());
+
+    /**
+     * Called when an input method is added.
+     * @type {!ChromeEvent}
+     */
+    this.onInputMethodAdded =
+        /** @type {!ChromeEvent} */ (new FakeChromeEvent());
+
+    this.onInputMethodRemoved =
+        /** @type {!ChromeEvent} */ (new FakeChromeEvent());
+
+    /** @type {!Array<!chrome.languageSettingsPrivate.Language>} */
+    this.languages = [
+      {
+        // English and some variants.
+        code: 'en',
+        displayName: 'English',
+        nativeDisplayName: 'English',
+        supportsTranslate: true,
+      },
+      {
+        code: 'en-CA',
+        displayName: 'English (Canada)',
+        nativeDisplayName: 'English (Canada)',
+        supportsSpellcheck: true,
+        supportsUI: true,
+      },
+      {
+        code: 'en-US',
+        displayName: 'English (United States)',
+        nativeDisplayName: 'English (United States)',
+        supportsSpellcheck: true,
+        supportsUI: true,
+      },
+      {
+        // A standalone language.
+        code: 'sw',
+        displayName: 'Swahili',
+        nativeDisplayName: 'Kiswahili',
+        supportsSpellcheck: true,
+        supportsTranslate: true,
+        supportsUI: true,
+      },
+      {
+        // A standalone language that doesn't support anything.
+        code: 'tk',
+        displayName: 'Turkmen',
+        nativeDisplayName: 'Turkmen'
+      },
+      {
+        // Edge cases:
+        // Norwegian is the macrolanguage for "nb" (see below).
+        code: 'no',
+        displayName: 'Norwegian',
+        nativeDisplayName: 'norsk',
+        supportsTranslate: true,
+      },
+      {
+        // Norwegian language codes don't start with "no-" but should still
+        // fall under the Norwegian macrolanguage.
+        // TODO(michaelpg): Test this is ordered correctly.
+        code: 'nb',
+        displayName: 'Norwegian Bokmål',
+        nativeDisplayName: 'norsk bokmål',
+        supportsSpellcheck: true,
+        supportsUI: true,
+      },
+      {
+        // A language where displayName and nativeDisplayName have different
+        // values. Used for testing search functionality.
+        code: 'el',
+        displayName: 'Greek',
+        nativeDisplayName: 'Ελληνικά',
+        supportsUI: true,
+      },
+      {
+        // A fake language for ARC IMEs which is for internal use only. The
+        // value of the |code| must be the same as |kArcImeLanguage| in
+        // ui/base/ime/chromeos/extension_ime_util.cc.
+        code: '_arc_ime_language_',
+        displayName: 'Keyboard apps',
+      },
+      {
+        // Hebrew. This is used to test that the old language code "iw"
+        // still works.
+        code: 'he',
+        displayName: 'Hebrew',
+        nativeDisplayName: 'Hebrew',
+        supportsUI: true,
+      },
+    ];
+
+    /** @type {!Array<string>} */
+    this.alwaysTranslateList = ['de, es, nl'];
+
+    /** @type {!Array<!chrome.languageSettingsPrivate.InputMethod>} */
+    this.componentExtensionImes = [
+      {
+        id: '_comp_ime_jkghodnilhceideoidjikpgommlajknkxkb:us::eng',
+        displayName: 'US keyboard',
+        languageCodes: ['en', 'en-US'],
+        tags: ['US keyboard', 'English', 'English(United States)'],
+        enabled: true,
+      },
+      {
+        id: '_comp_ime_fgoepimhcoialccpbmpnnblemnepkkaoxkb:us:dvorak:eng',
+        displayName: 'US Dvorak keyboard',
+        languageCodes: ['en', 'en-US'],
+        tags: ['US Dvorak keyboard', 'English', 'English(United States)'],
+        enabled: true,
+      },
+      {
+        id: '_comp_ime_abcdefghijklmnopqrstuvwxyzabcdefxkb:sw:sw',
+        displayName: 'Swahili keyboard',
+        languageCodes: ['sw', 'tk'],
+        tags: ['Swahili keyboard', 'Swahili', 'Turkmen'],
+        enabled: false,
+      },
+      {
+        id: 'ime_abcdefghijklmnopqrstuvwxyzabcdefxkb:us:sw',
+        displayName: 'US Swahili keyboard',
+        languageCodes: ['en', 'en-US', 'sw'],
+        tags: [
+          'US Swahili keyboard', 'English', 'English(United States)', 'Swahili'
+        ],
+        enabled: false,
+      },
+      {
+        id: '_comp_ime_abcdefghijklmnopqrstuvwxyzabcdefxkb:us:intl',
+        displayName: 'US International keyboard',
+        languageCodes: ['en-US'],
+        tags: ['US International keyboard', 'English(United States)'],
+        enabled: false,
+        isProhibitedByPolicy: true,
+      },
+      {
+        id: '_comp_ime_abcdefghijklmnopqrstuvwxyzabcdefxkb:vi:vi',
+        displayName: 'Vietnamese keyboard',
+        languageCodes: ['vi'],
+        tags: ['Vietnamese keyboard', 'Vietnamese'],
+        enabled: false,
+      },
+    ];
+  }
+
+  /** @param {SettingsPrefsElement} settingsPrefs */
+  setSettingsPrefs(settingsPrefs) {
+    this.settingsPrefs_ = settingsPrefs;
+  }
+
+  // LanguageSettingsPrivate fake.
+
   /**
-   * Fake of the chrome.languageSettingsPrivate API.
-   * @implements {LanguageSettingsPrivate}
+   * Gets languages available for translate, spell checking, input and locale.
+   * @param {function(!Array<!chrome.languageSettingsPrivate.Language>)}
+   *     callback
    */
-  /* #export */ class FakeLanguageSettingsPrivate extends TestBrowserProxy {
-    constructor() {
-      // List of method names expected to be tested with whenCalled()
-      super([
-        'getSpellcheckWords',
-      ]);
+  getLanguageList(callback) {
+    setTimeout(function() {
+      callback(
+          /** @type {!Array<!chrome.languageSettingsPrivate.Language>} */ (
+              JSON.parse(JSON.stringify(this.languages))));
+    }.bind(this));
+  }
 
-      // /** @type {!SettingsPrefsElement} */
-      this.settingsPrefs_ = null;
+  /**
+   * Gets languages that should always be automatically translated.
+   * @param {function(!Array<!string>)}
+   *     callback
+   */
+  getAlwaysTranslateLanguages(callback) {
+    setTimeout(function() {
+      callback(
+          /** @type {!Array<!string>} */ (this.alwaysTranslateList));
+    }.bind(this));
+  }
 
-      /**
-       * Called when the pref for the dictionaries used for spell checking
-       * changes or the status of one of the spell check dictionaries changes.
-       * @type {!ChromeEvent}
-       */
-      this.onSpellcheckDictionariesChanged =
-          /** @type {!ChromeEvent} */ (new FakeChromeEvent());
-
-      /**
-       * Called when words are added to and/or removed from the custom spell
-       * check dictionary.
-       * @type {!ChromeEvent}
-       */
-      this.onCustomDictionaryChanged =
-          /** @type {!ChromeEvent} */ (new FakeChromeEvent());
-
-      /**
-       * Called when an input method is added.
-       * @type {!ChromeEvent}
-       */
-      this.onInputMethodAdded =
-          /** @type {!ChromeEvent} */ (new FakeChromeEvent());
-
-      /**
-       * Called when an input method is removed.
-       * @type {!ChromeEvent}
-       */
-      this.onInputMethodRemoved =
-          /** @type {!ChromeEvent} */ (new FakeChromeEvent());
-
-      /** @type {!Array<!chrome.languageSettingsPrivate.Language>} */
-      this.languages = [
-        {
-          // English and some variants.
-          code: 'en',
-          displayName: 'English',
-          nativeDisplayName: 'English',
-          supportsTranslate: true,
-        },
-        {
-          code: 'en-CA',
-          displayName: 'English (Canada)',
-          nativeDisplayName: 'English (Canada)',
-          supportsSpellcheck: true,
-          supportsUI: true,
-        },
-        {
-          code: 'en-US',
-          displayName: 'English (United States)',
-          nativeDisplayName: 'English (United States)',
-          supportsSpellcheck: true,
-          supportsUI: true,
-        },
-        {
-          // A standalone language.
-          code: 'sw',
-          displayName: 'Swahili',
-          nativeDisplayName: 'Kiswahili',
-          supportsSpellcheck: true,
-          supportsTranslate: true,
-          supportsUI: true,
-        },
-        {
-          // A standalone language that doesn't support anything.
-          code: 'tk',
-          displayName: 'Turkmen',
-          nativeDisplayName: 'Turkmen'
-        },
-        {
-          // Edge cases:
-          // Norwegian is the macrolanguage for "nb" (see below).
-          code: 'no',
-          displayName: 'Norwegian',
-          nativeDisplayName: 'norsk',
-          supportsTranslate: true,
-        },
-        {
-          // Norwegian language codes don't start with "no-" but should still
-          // fall under the Norwegian macrolanguage.
-          // TODO(michaelpg): Test this is ordered correctly.
-          code: 'nb',
-          displayName: 'Norwegian Bokmål',
-          nativeDisplayName: 'norsk bokmål',
-          supportsSpellcheck: true,
-          supportsUI: true,
-        },
-        {
-          // A language where displayName and nativeDisplayName have different
-          // values. Used for testing search functionality.
-          code: 'el',
-          displayName: 'Greek',
-          nativeDisplayName: 'Ελληνικά',
-          supportsUI: true,
-        },
-        {
-          // A fake language for ARC IMEs which is for internal use only. The
-          // value of the |code| must be the same as |kArcImeLanguage| in
-          // ui/base/ime/chromeos/extension_ime_util.cc.
-          code: '_arc_ime_language_',
-          displayName: 'Keyboard apps',
-        },
-        {
-          // Hebrew. This is used to test that the old language code "iw"
-          // still works.
-          code: 'he',
-          displayName: 'Hebrew',
-          nativeDisplayName: 'Hebrew',
-          supportsUI: true,
-        },
-      ];
-
-      /** @type {!Array<string>} */
-      this.alwaysTranslateList = ['de, es, nl'];
-
-      /** @type {!Array<!chrome.languageSettingsPrivate.InputMethod>} */
-      this.componentExtensionImes = [
-        {
-          id: '_comp_ime_jkghodnilhceideoidjikpgommlajknkxkb:us::eng',
-          displayName: 'US keyboard',
-          languageCodes: ['en', 'en-US'],
-          tags: ['US keyboard', 'English', 'English(United States)'],
-          enabled: true,
-        },
-        {
-          id: '_comp_ime_fgoepimhcoialccpbmpnnblemnepkkaoxkb:us:dvorak:eng',
-          displayName: 'US Dvorak keyboard',
-          languageCodes: ['en', 'en-US'],
-          tags: ['US Dvorak keyboard', 'English', 'English(United States)'],
-          enabled: true,
-        },
-        {
-          id: '_comp_ime_abcdefghijklmnopqrstuvwxyzabcdefxkb:sw:sw',
-          displayName: 'Swahili keyboard',
-          languageCodes: ['sw', 'tk'],
-          tags: ['Swahili keyboard', 'Swahili', 'Turkmen'],
-          enabled: false,
-        },
-        {
-          id: 'ime_abcdefghijklmnopqrstuvwxyzabcdefxkb:us:sw',
-          displayName: 'US Swahili keyboard',
-          languageCodes: ['en', 'en-US', 'sw'],
-          tags: [
-            'US Swahili keyboard', 'English', 'English(United States)',
-            'Swahili'
-          ],
-          enabled: false,
-        },
-        {
-          id: '_comp_ime_abcdefghijklmnopqrstuvwxyzabcdefxkb:us:intl',
-          displayName: 'US International keyboard',
-          languageCodes: ['en-US'],
-          tags: ['US International keyboard', 'English(United States)'],
-          enabled: false,
-          isProhibitedByPolicy: true,
-        },
-        {
-          id: '_comp_ime_abcdefghijklmnopqrstuvwxyzabcdefxkb:vi:vi',
-          displayName: 'Vietnamese keyboard',
-          languageCodes: ['vi'],
-          tags: ['Vietnamese keyboard', 'Vietnamese'],
-          enabled: false,
-        },
-      ];
-    }
-
-    /** @param {SettingsPrefsElement} settingsPrefs */
-    setSettingsPrefs(settingsPrefs) {
-      this.settingsPrefs_ = settingsPrefs;
-    }
-
-    // LanguageSettingsPrivate fake.
-
-    /**
-     * Gets languages available for translate, spell checking, input and locale.
-     * @param {function(!Array<!chrome.languageSettingsPrivate.Language>)}
-     *     callback
-     */
-    getLanguageList(callback) {
-      setTimeout(function() {
-        callback(
-            /** @type {!Array<!chrome.languageSettingsPrivate.Language>} */ (
-                JSON.parse(JSON.stringify(this.languages))));
-      }.bind(this));
-    }
-
-    /**
-     * Gets languages that should always be automatically translated.
-     * @param {function(!Array<!string>)}
-     *     callback
-     */
-    getAlwaysTranslateLanguages(callback) {
-      setTimeout(function() {
-        callback(
-            /** @type {!Array<!string>} */ (this.alwaysTranslateList));
-      }.bind(this));
-    }
-
-    /**
-     * Sets whether a given language should always be automatically translated.
-     * @param {string} languageCode
-     * @param {boolean} alwaysTranslate
-     */
-    setLanguageAlwaysTranslateState(languageCode, alwaysTranslate) {
-      if (alwaysTranslate) {
-        this.alwaysTranslateList.push(languageCode);
-      } else {
-        const index = this.alwaysTranslateList.indexOf(languageCode);
-        if (index === -1) {
-          return;
-        }
-        this.alwaysTranslateList.splice(index, 1);
-      }
-    }
-
-    /**
-     * Enables a language, adding it to the Accept-Language list (used to decide
-     * which languages to translate, generate the Accept-Language header, etc.).
-     * @param {string} languageCode
-     */
-    enableLanguage(languageCode) {
-      let languageCodes =
-          this.settingsPrefs_.get('prefs.intl.accept_languages.value');
-      const languages = languageCodes.split(',');
-      if (languages.indexOf(languageCode) !== -1) {
-        return;
-      }
-      languages.push(languageCode);
-      languageCodes = languages.join(',');
-      this.settingsPrefs_.set(
-          'prefs.intl.accept_languages.value', languageCodes);
-      if (cr.isChromeOS) {
-        this.settingsPrefs_.set(
-            'prefs.settings.language.preferred_languages.value', languageCodes);
-      }
-    }
-
-    /**
-     * Disables a language, removing it from the Accept-Language list.
-     * @param {string} languageCode
-     */
-    disableLanguage(languageCode) {
-      let languageCodes =
-          this.settingsPrefs_.get('prefs.intl.accept_languages.value');
-      const languages = languageCodes.split(',');
-      const index = languages.indexOf(languageCode);
+  /**
+   * Sets whether a given language should always be automatically translated.
+   * @param {string} languageCode
+   * @param {boolean} alwaysTranslate
+   */
+  setLanguageAlwaysTranslateState(languageCode, alwaysTranslate) {
+    if (alwaysTranslate) {
+      this.alwaysTranslateList.push(languageCode);
+    } else {
+      const index = this.alwaysTranslateList.indexOf(languageCode);
       if (index === -1) {
         return;
       }
-      languages.splice(index, 1);
-      languageCodes = languages.join(',');
-      this.settingsPrefs_.set(
-          'prefs.intl.accept_languages.value', languageCodes);
-      if (cr.isChromeOS) {
-        this.settingsPrefs_.set(
-            'prefs.settings.language.preferred_languages.value', languageCodes);
-      }
-    }
-
-    /**
-     * Enables/Disables translation for the given language.
-     * This respectively removes/adds the language to the blocked set in the
-     * preferences.
-     * @param {string} languageCode
-     * @param {boolean} enable
-     */
-    setEnableTranslationForLanguage(languageCode, enable) {
-      const index =
-          this.settingsPrefs_.get('prefs.translate_blocked_languages.value')
-              .indexOf(languageCode);
-      if (enable) {
-        if (index === -1) {
-          return;
-        }
-        this.settingsPrefs_.splice(
-            'prefs.translate_blocked_languages.value', index, 1);
-      } else {
-        if (index !== -1) {
-          return;
-        }
-        this.settingsPrefs_.push(
-            'prefs.translate_blocked_languages.value', languageCode);
-      }
-    }
-
-    /**
-     * Moves a language inside the language list.
-     * Movement is determined by the |moveType| parameter.
-     * @param {string} languageCode
-     * @param {chrome.languageSettingsPrivate.MoveType} moveType
-     */
-    moveLanguage(languageCode, moveType) {
-      let languageCodes =
-          this.settingsPrefs_.get('prefs.intl.accept_languages.value');
-      const languages = languageCodes.split(',');
-      const index = languages.indexOf(languageCode);
-
-      if (moveType === chrome.languageSettingsPrivate.MoveType.TOP) {
-        if (index < 1) {
-          return;
-        }
-
-        languages.splice(index, 1);
-        languages.unshift(languageCode);
-      } else if (moveType === chrome.languageSettingsPrivate.MoveType.UP) {
-        if (index < 1) {
-          return;
-        }
-
-        const temp = languages[index - 1];
-        languages[index - 1] = languageCode;
-        languages[index] = temp;
-      } else if (moveType === chrome.languageSettingsPrivate.MoveType.DOWN) {
-        if (index === -1 || index === languages.length - 1) {
-          return;
-        }
-
-        const temp = languages[index + 1];
-        languages[index + 1] = languageCode;
-        languages[index] = temp;
-      }
-
-      languageCodes = languages.join(',');
-      this.settingsPrefs_.set(
-          'prefs.intl.accept_languages.value', languageCodes);
-      if (cr.isChromeOS) {
-        this.settingsPrefs_.set(
-            'prefs.settings.language.preferred_languages.value', languageCodes);
-      }
-    }
-
-    /**
-     * Gets the current status of the chosen spell check dictionaries.
-     * @param {function(!Array<
-     *     !chrome.languageSettingsPrivate.SpellcheckDictionaryStatus>):void}
-     *     callback
-     */
-    getSpellcheckDictionaryStatuses(callback) {
-      callback([]);
-    }
-
-    /**
-     * Gets the custom spell check words, in sorted order.
-     * @param {function(!Array<string>):void} callback
-     */
-    getSpellcheckWords(callback) {
-      callback([]);
-      this.methodCalled('getSpellcheckWords');
-    }
-
-    /**
-     * Adds a word to the custom dictionary.
-     * @param {string} word
-     */
-    addSpellcheckWord(word) {
-      /** @type {FakeChromeEvent} */ (this.onCustomDictionaryChanged)
-          .callListeners([word], []);
-    }
-
-    /**
-     * Removes a word from the custom dictionary.
-     * @param {string} word
-     */
-    removeSpellcheckWord(word) {
-      /** @type {FakeChromeEvent} */ (this.onCustomDictionaryChanged)
-          .callListeners([], [word]);
-    }
-
-    /**
-     * Gets the translate target language (in most cases, the display locale).
-     * @param {function(string):void} callback
-     */
-    getTranslateTargetLanguage(callback) {
-      setTimeout(callback.bind(null, 'en'));
-    }
-
-    /**
-     * Gets all supported input methods, including third-party IMEs. Chrome OS
-     * only.
-     * @param {function(!chrome.languageSettingsPrivate.InputMethodLists):void}
-     *     callback
-     */
-    getInputMethodLists(callback) {
-      if (!cr.isChromeOS) {
-        assertNotReached();
-      }
-      callback({
-        componentExtensionImes:
-            /** @type {!Array<!chrome.languageSettingsPrivate.InputMethod>} */ (
-                JSON.parse(JSON.stringify(this.componentExtensionImes))),
-        thirdPartyExtensionImes: [],
-      });
-    }
-
-    /**
-     * Adds the input method to the current user's list of enabled input
-     * methods, enabling the input method for the current user. Chrome OS only.
-     * @param {string} inputMethodId
-     */
-    addInputMethod(inputMethodId) {
-      assert(cr.isChromeOS);
-      const inputMethod = this.componentExtensionImes.find(function(ime) {
-        return ime.id === inputMethodId;
-      });
-      assert(!!inputMethod);
-      inputMethod.enabled = true;
-      const prefPath = 'prefs.settings.language.preload_engines.value';
-      const enabledInputMethods = this.settingsPrefs_.get(prefPath).split(',');
-      enabledInputMethods.push(inputMethodId);
-      this.settingsPrefs_.set(prefPath, enabledInputMethods.join(','));
-    }
-
-    /**
-     * Removes the input method from the current user's list of enabled input
-     * methods, disabling the input method for the current user. Chrome OS only.
-     * @param {string} inputMethodId
-     */
-    removeInputMethod(inputMethodId) {
-      assert(cr.isChromeOS);
-      const inputMethod = this.componentExtensionImes.find(function(ime) {
-        return ime.id === inputMethodId;
-      });
-      assert(!!inputMethod);
-      inputMethod.enabled = false;
-      this.settingsPrefs_.set(
-          'prefs.settings.language.preload_engines.value',
-          this.settingsPrefs_
-              .get('prefs.settings.language.preload_engines.value')
-              .replace(inputMethodId, ''));
-    }
-
-    /**
-     * Tries to download the dictionary after a failed download.
-     * @param {string} languageCode
-     */
-    retryDownloadDictionary(languageCode) {
-      /** @type {FakeChromeEvent} */ (this.onSpellcheckDictionariesChanged)
-          .callListeners([
-            {languageCode, isReady: false, isDownlading: true},
-          ]);
-      /** @type {FakeChromeEvent} */ (this.onSpellcheckDictionariesChanged)
-          .callListeners([
-            {languageCode, isReady: false, downloadFailed: true},
-          ]);
+      this.alwaysTranslateList.splice(index, 1);
     }
   }
 
+  /**
+   * Enables a language, adding it to the Accept-Language list (used to decide
+   * which languages to translate, generate the Accept-Language header, etc.).
+   * @param {string} languageCode
+   */
+  enableLanguage(languageCode) {
+    let languageCodes =
+        this.settingsPrefs_.get('prefs.intl.accept_languages.value');
+    const languages = languageCodes.split(',');
+    if (languages.indexOf(languageCode) !== -1) {
+      return;
+    }
+    languages.push(languageCode);
+    languageCodes = languages.join(',');
+    this.settingsPrefs_.set('prefs.intl.accept_languages.value', languageCodes);
+    if (isChromeOS) {
+      this.settingsPrefs_.set(
+          'prefs.settings.language.preferred_languages.value', languageCodes);
+    }
+  }
+
+  /**
+   * Disables a language, removing it from the Accept-Language list.
+   * @param {string} languageCode
+   */
+  disableLanguage(languageCode) {
+    let languageCodes =
+        this.settingsPrefs_.get('prefs.intl.accept_languages.value');
+    const languages = languageCodes.split(',');
+    const index = languages.indexOf(languageCode);
+    if (index === -1) {
+      return;
+    }
+    languages.splice(index, 1);
+    languageCodes = languages.join(',');
+    this.settingsPrefs_.set('prefs.intl.accept_languages.value', languageCodes);
+    if (isChromeOS) {
+      this.settingsPrefs_.set(
+          'prefs.settings.language.preferred_languages.value', languageCodes);
+    }
+  }
+
+  /**
+   * Enables/Disables translation for the given language.
+   * This respectively removes/adds the language to the blocked set in the
+   * preferences.
+   * @param {string} languageCode
+   * @param {boolean} enable
+   */
+  setEnableTranslationForLanguage(languageCode, enable) {
+    const index =
+        this.settingsPrefs_.get('prefs.translate_blocked_languages.value')
+            .indexOf(languageCode);
+    if (enable) {
+      if (index === -1) {
+        return;
+      }
+      this.settingsPrefs_.splice(
+          'prefs.translate_blocked_languages.value', index, 1);
+    } else {
+      if (index !== -1) {
+        return;
+      }
+      this.settingsPrefs_.push(
+          'prefs.translate_blocked_languages.value', languageCode);
+    }
+  }
+
+  /**
+   * Moves a language inside the language list.
+   * Movement is determined by the |moveType| parameter.
+   * @param {string} languageCode
+   * @param {chrome.languageSettingsPrivate.MoveType} moveType
+   */
+  moveLanguage(languageCode, moveType) {
+    let languageCodes =
+        this.settingsPrefs_.get('prefs.intl.accept_languages.value');
+    const languages = languageCodes.split(',');
+    const index = languages.indexOf(languageCode);
+
+    if (moveType === chrome.languageSettingsPrivate.MoveType.TOP) {
+      if (index < 1) {
+        return;
+      }
+
+      languages.splice(index, 1);
+      languages.unshift(languageCode);
+    } else if (moveType === chrome.languageSettingsPrivate.MoveType.UP) {
+      if (index < 1) {
+        return;
+      }
+
+      const temp = languages[index - 1];
+      languages[index - 1] = languageCode;
+      languages[index] = temp;
+    } else if (moveType === chrome.languageSettingsPrivate.MoveType.DOWN) {
+      if (index === -1 || index === languages.length - 1) {
+        return;
+      }
+
+      const temp = languages[index + 1];
+      languages[index + 1] = languageCode;
+      languages[index] = temp;
+    }
+
+    languageCodes = languages.join(',');
+    this.settingsPrefs_.set('prefs.intl.accept_languages.value', languageCodes);
+    if (isChromeOS) {
+      this.settingsPrefs_.set(
+          'prefs.settings.language.preferred_languages.value', languageCodes);
+    }
+  }
+
+  /**
+   * Gets the current status of the chosen spell check dictionaries.
+   * @param {function(!Array<
+   *     !chrome.languageSettingsPrivate.SpellcheckDictionaryStatus>):void}
+   *     callback
+   */
+  getSpellcheckDictionaryStatuses(callback) {
+    callback([]);
+  }
+
+  /**
+   * Gets the custom spell check words, in sorted order.
+   * @param {function(!Array<string>):void} callback
+   */
+  getSpellcheckWords(callback) {
+    callback([]);
+    this.methodCalled('getSpellcheckWords');
+  }
+
+  /**
+   * Adds a word to the custom dictionary.
+   * @param {string} word
+   */
+  addSpellcheckWord(word) {
+    /** @type {FakeChromeEvent} */ (this.onCustomDictionaryChanged)
+        .callListeners([word], []);
+  }
+
+  /**
+   * Removes a word from the custom dictionary.
+   * @param {string} word
+   */
+  removeSpellcheckWord(word) {
+    /** @type {FakeChromeEvent} */ (this.onCustomDictionaryChanged)
+        .callListeners([], [word]);
+  }
+
+  /**
+   * Gets the translate target language (in most cases, the display locale).
+   * @param {function(string):void} callback
+   */
+  getTranslateTargetLanguage(callback) {
+    setTimeout(callback.bind(null, 'en'));
+  }
+
+  /**
+   * Gets all supported input methods, including third-party IMEs. Chrome OS
+   * only.
+   * @param {function(!chrome.languageSettingsPrivate.InputMethodLists):void}
+   *     callback
+   */
+  getInputMethodLists(callback) {
+    if (!isChromeOS) {
+      assertNotReached();
+    }
+    callback({
+      componentExtensionImes:
+          /** @type {!Array<!chrome.languageSettingsPrivate.InputMethod>} */ (
+              JSON.parse(JSON.stringify(this.componentExtensionImes))),
+      thirdPartyExtensionImes: [],
+    });
+  }
+
+  /**
+   * Adds the input method to the current user's list of enabled input
+   * methods, enabling the input method for the current user. Chrome OS only.
+   * @param {string} inputMethodId
+   */
+  addInputMethod(inputMethodId) {
+    assert(isChromeOS);
+    const inputMethod = this.componentExtensionImes.find(function(ime) {
+      return ime.id === inputMethodId;
+    });
+    assert(!!inputMethod);
+    inputMethod.enabled = true;
+    const prefPath = 'prefs.settings.language.preload_engines.value';
+    const enabledInputMethods = this.settingsPrefs_.get(prefPath).split(',');
+    enabledInputMethods.push(inputMethodId);
+    this.settingsPrefs_.set(prefPath, enabledInputMethods.join(','));
+  }
+
+  /**
+   * Removes the input method from the current user's list of enabled input
+   * methods, disabling the input method for the current user. Chrome OS only.
+   * @param {string} inputMethodId
+   */
+  removeInputMethod(inputMethodId) {
+    assert(isChromeOS);
+    const inputMethod = this.componentExtensionImes.find(function(ime) {
+      return ime.id === inputMethodId;
+    });
+    assert(!!inputMethod);
+    inputMethod.enabled = false;
+    this.settingsPrefs_.set(
+        'prefs.settings.language.preload_engines.value',
+        this.settingsPrefs_.get('prefs.settings.language.preload_engines.value')
+            .replace(inputMethodId, ''));
+  }
+
+  /**
+   * Tries to download the dictionary after a failed download.
+   * @param {string} languageCode
+   */
+  retryDownloadDictionary(languageCode) {
+    /** @type {FakeChromeEvent} */ (this.onSpellcheckDictionariesChanged)
+        .callListeners([
+          {languageCode, isReady: false, isDownlading: true},
+        ]);
+    /** @type {FakeChromeEvent} */ (this.onSpellcheckDictionariesChanged)
+        .callListeners([
+          {languageCode, isReady: false, downloadFailed: true},
+        ]);
+  }
+}
+
   // List of language-related preferences suitable for testing.
-  /* #export */ function getFakeLanguagePrefs() {
-    const fakePrefs = [
-      {
-        key: 'browser.enable_spellchecking',
-        type: chrome.settingsPrivate.PrefType.BOOLEAN,
-        value: true,
-      },
-      {
-        key: 'intl.app_locale',
-        type: chrome.settingsPrivate.PrefType.STRING,
-        value: 'en-US',
-      },
-      {
-        key: 'intl.accept_languages',
-        type: chrome.settingsPrivate.PrefType.STRING,
-        value: 'en-US,sw',
-      },
-      {
-        key: 'intl.forced_languages',
-        type: chrome.settingsPrivate.PrefType.LIST,
-        value: '',
-      },
-      {
-        key: 'spellcheck.blocked_dictionaries',
-        type: chrome.settingsPrivate.PrefType.LIST,
-        value: [],
-      },
-      {
-        key: 'spellcheck.dictionaries',
-        type: chrome.settingsPrivate.PrefType.LIST,
-        value: ['en-US'],
-      },
-      {
-        key: 'spellcheck.forced_dictionaries',
-        type: chrome.settingsPrivate.PrefType.LIST,
-        value: [],
-      },
-      {
-        key: 'spellcheck.use_spelling_service',
-        type: chrome.settingsPrivate.PrefType.BOOLEAN,
-        value: false,
-      },
-      {
-        key: 'translate.enabled',
-        type: chrome.settingsPrivate.PrefType.BOOLEAN,
-        value: true,
-      },
-      {
-        key: 'translate_blocked_languages',
-        type: chrome.settingsPrivate.PrefType.LIST,
-        value: ['en-US'],
-      }
-    ];
-    if (cr.isChromeOS) {
-      fakePrefs.push({
-        key: 'settings.language.preferred_languages',
-        type: chrome.settingsPrivate.PrefType.STRING,
-        value: 'en-US,sw',
-      });
-      fakePrefs.push({
-        key: 'settings.language.preload_engines',
-        type: chrome.settingsPrivate.PrefType.STRING,
-        value: '_comp_ime_jkghodnilhceideoidjikpgommlajknkxkb:us::eng,' +
-            '_comp_ime_fgoepimhcoialccpbmpnnblemnepkkaoxkb:us:dvorak:eng',
-      });
-      fakePrefs.push({
-        key: 'settings.language.enabled_extension_imes',
-        type: chrome.settingsPrivate.PrefType.STRING,
-        value: '',
-      });
-      fakePrefs.push({
-        key: 'settings.language.ime_menu_activated',
-        type: chrome.settingsPrivate.PrefType.BOOLEAN,
-        value: false,
-      });
-      fakePrefs.push({
-        key: 'settings.language.allowed_input_methods',
-        type: chrome.settingsPrivate.PrefType.LIST,
-        value: [],
-      });
+export function getFakeLanguagePrefs() {
+  const fakePrefs = [
+    {
+      key: 'browser.enable_spellchecking',
+      type: chrome.settingsPrivate.PrefType.BOOLEAN,
+      value: true,
+    },
+    {
+      key: 'intl.app_locale',
+      type: chrome.settingsPrivate.PrefType.STRING,
+      value: 'en-US',
+    },
+    {
+      key: 'intl.accept_languages',
+      type: chrome.settingsPrivate.PrefType.STRING,
+      value: 'en-US,sw',
+    },
+    {
+      key: 'intl.forced_languages',
+      type: chrome.settingsPrivate.PrefType.LIST,
+      value: '',
+    },
+    {
+      key: 'spellcheck.blocked_dictionaries',
+      type: chrome.settingsPrivate.PrefType.LIST,
+      value: [],
+    },
+    {
+      key: 'spellcheck.dictionaries',
+      type: chrome.settingsPrivate.PrefType.LIST,
+      value: ['en-US'],
+    },
+    {
+      key: 'spellcheck.forced_dictionaries',
+      type: chrome.settingsPrivate.PrefType.LIST,
+      value: [],
+    },
+    {
+      key: 'spellcheck.use_spelling_service',
+      type: chrome.settingsPrivate.PrefType.BOOLEAN,
+      value: false,
+    },
+    {
+      key: 'translate.enabled',
+      type: chrome.settingsPrivate.PrefType.BOOLEAN,
+      value: true,
+    },
+    {
+      key: 'translate_blocked_languages',
+      type: chrome.settingsPrivate.PrefType.LIST,
+      value: ['en-US'],
     }
-    return fakePrefs;
+  ];
+  if (isChromeOS) {
+    fakePrefs.push({
+      key: 'settings.language.preferred_languages',
+      type: chrome.settingsPrivate.PrefType.STRING,
+      value: 'en-US,sw',
+    });
+    fakePrefs.push({
+      key: 'settings.language.preload_engines',
+      type: chrome.settingsPrivate.PrefType.STRING,
+      value: '_comp_ime_jkghodnilhceideoidjikpgommlajknkxkb:us::eng,' +
+          '_comp_ime_fgoepimhcoialccpbmpnnblemnepkkaoxkb:us:dvorak:eng',
+    });
+    fakePrefs.push({
+      key: 'settings.language.enabled_extension_imes',
+      type: chrome.settingsPrivate.PrefType.STRING,
+      value: '',
+    });
+    fakePrefs.push({
+      key: 'settings.language.ime_menu_activated',
+      type: chrome.settingsPrivate.PrefType.BOOLEAN,
+      value: false,
+    });
+    fakePrefs.push({
+      key: 'settings.language.allowed_input_methods',
+      type: chrome.settingsPrivate.PrefType.LIST,
+      value: [],
+    });
   }
-  // #cr_define_end
-  return {
-    FakeLanguageSettingsPrivate: FakeLanguageSettingsPrivate,
-    getFakeLanguagePrefs: getFakeLanguagePrefs,
-  };
-});
+  return fakePrefs;
+}
diff --git a/chrome/test/data/webui/settings/fake_settings_private.js b/chrome/test/data/webui/settings/fake_settings_private.js
index a8bbda1..11b8cb56 100644
--- a/chrome/test/data/webui/settings/fake_settings_private.js
+++ b/chrome/test/data/webui/settings/fake_settings_private.js
@@ -3,27 +3,26 @@
 // found in the LICENSE file.
 
 // clang-format off
-// #import {assertEquals, assertNotEquals} from '../chai_assert.js';
-// #import {FakeChromeEvent} from '../fake_chrome_event.m.js';
+import {assertEquals, assertNotEquals} from '../chai_assert.js';
+import {FakeChromeEvent} from '../fake_chrome_event.m.js';
 // clang-format on
 
 /** @fileoverview Fake implementation of chrome.settingsPrivate for testing. */
-cr.define('settings', function() {
   /**
    * @typedef {Array<{key: string,
    *               type: chrome.settingsPrivate.PrefType,
    *               values: !Array<*>}>}
    */
-  /* #export */ let FakeSettingsPrivatePref;
+export let FakeSettingsPrivatePref;
 
-  /**
-   * Creates a deep copy of the object.
-   * @param {*} obj
-   * @return {*}
-   */
-  function deepCopy(obj) {
-    return JSON.parse(JSON.stringify(obj));
-  }
+/**
+ * Creates a deep copy of the object.
+ * @param {*} obj
+ * @return {*}
+ */
+function deepCopy(obj) {
+  return JSON.parse(JSON.stringify(obj));
+}
 
   /**
    * Fake of chrome.settingsPrivate API. Use by setting
@@ -31,125 +30,118 @@
    * FakeSettingsPrivate to settings-prefs#initialize().
    * @implements {SettingsPrivate}
    */
-  /* #export */ class FakeSettingsPrivate {
-    /** @param {Array<!settings.FakeSettingsPrivatePref>=} opt_initialPrefs */
-    constructor(opt_initialPrefs) {
-      this.disallowSetPref_ = false;
+export class FakeSettingsPrivate {
+  /** @param {Array<!FakeSettingsPrivatePref>=} opt_initialPrefs */
+  constructor(opt_initialPrefs) {
+    this.disallowSetPref_ = false;
+    this.failNextSetPref_ = false;
+
+    this.prefs = {};
+
+    if (!opt_initialPrefs) {
+      return;
+    }
+    for (const pref of opt_initialPrefs) {
+      this.addPref_(pref.type, pref.key, pref.value);
+    }
+
+    // chrome.settingsPrivate override.
+    this.onPrefsChanged = /** @type {!ChromeEvent} */ (new FakeChromeEvent());
+  }
+
+  // chrome.settingsPrivate overrides.
+  getAllPrefs(callback) {
+    // Send a copy of prefs to keep our internal state private.
+    const prefs = [];
+    for (const key in this.prefs) {
+      prefs.push(deepCopy(this.prefs[key]));
+    }
+
+    // Run the callback asynchronously to test that the prefs aren't actually
+    // used before they become available.
+    setTimeout(callback.bind(null, prefs));
+  }
+
+  setPref(key, value, pageId, callback) {
+    const pref = this.prefs[key];
+    assertNotEquals(undefined, pref);
+    assertEquals(typeof value, typeof pref.value);
+    assertEquals(Array.isArray(value), Array.isArray(pref.value));
+
+    if (this.failNextSetPref_) {
+      callback(false);
       this.failNextSetPref_ = false;
-
-      this.prefs = {};
-
-      if (!opt_initialPrefs) {
-        return;
-      }
-      for (const pref of opt_initialPrefs) {
-        this.addPref_(pref.type, pref.key, pref.value);
-      }
-
-      // chrome.settingsPrivate override.
-      this.onPrefsChanged = /** @type {!ChromeEvent} */ (new FakeChromeEvent());
+      return;
     }
+    assertNotEquals(true, this.disallowSetPref_);
 
-    // chrome.settingsPrivate overrides.
-    getAllPrefs(callback) {
-      // Send a copy of prefs to keep our internal state private.
-      const prefs = [];
-      for (const key in this.prefs) {
-        prefs.push(deepCopy(this.prefs[key]));
-      }
+    const changed = JSON.stringify(pref.value) !== JSON.stringify(value);
+    pref.value = deepCopy(value);
+    callback(true);
 
-      // Run the callback asynchronously to test that the prefs aren't actually
-      // used before they become available.
-      setTimeout(callback.bind(null, prefs));
-    }
-
-    setPref(key, value, pageId, callback) {
-      const pref = this.prefs[key];
-      assertNotEquals(undefined, pref);
-      assertEquals(typeof value, typeof pref.value);
-      assertEquals(Array.isArray(value), Array.isArray(pref.value));
-
-      if (this.failNextSetPref_) {
-        callback(false);
-        this.failNextSetPref_ = false;
-        return;
-      }
-      assertNotEquals(true, this.disallowSetPref_);
-
-      const changed = JSON.stringify(pref.value) !== JSON.stringify(value);
-      pref.value = deepCopy(value);
-      callback(true);
-
-      // Like chrome.settingsPrivate, send a notification when prefs change.
-      if (changed) {
-        this.sendPrefChanges([{key: key, value: deepCopy(value)}]);
-      }
-    }
-
-    getPref(key, callback) {
-      const pref = this.prefs[key];
-      assertNotEquals(undefined, pref);
-      callback(
-          /** @type {!chrome.settingsPrivate.PrefObject} */ (deepCopy(pref)));
-    }
-
-    // Functions used by tests.
-
-    /** Instructs the API to return a failure when setPref is next called. */
-    failNextSetPref() {
-      this.failNextSetPref_ = true;
-    }
-
-    /** Instructs the API to assert (fail the test) if setPref is called. */
-    disallowSetPref() {
-      this.disallowSetPref_ = true;
-    }
-
-    allowSetPref() {
-      this.disallowSetPref_ = false;
-    }
-
-    /**
-     * Notifies the listeners of pref changes.
-     * @param {!Array<{key: string, value: *}>} changes
-     */
-    sendPrefChanges(changes) {
-      const prefs = [];
-      for (const change of changes) {
-        const pref = this.prefs[change.key];
-        assertNotEquals(undefined, pref);
-        pref.value = change.value;
-        prefs.push(deepCopy(pref));
-      }
-      /** @type {FakeChromeEvent} */ (this.onPrefsChanged).callListeners(prefs);
-    }
-
-    /** @override */
-    getDefaultZoom() {}
-
-    /** @override */
-    setDefaultZoom() {}
-
-    // Private methods for use by the fake API.
-
-    /**
-     * @param {!chrome.settingsPrivate.PrefType} type
-     * @param {string} key
-     * @param {*} value
-     * @private
-     */
-    addPref_(type, key, value) {
-      this.prefs[key] = {
-        type: type,
-        key: key,
-        value: value,
-      };
+    // Like chrome.settingsPrivate, send a notification when prefs change.
+    if (changed) {
+      this.sendPrefChanges([{key: key, value: deepCopy(value)}]);
     }
   }
 
-  // #cr_define_end
-  return {
-    FakeSettingsPrivate: FakeSettingsPrivate,
-    FakeSettingsPrivatePref: FakeSettingsPrivatePref,
-  };
-});
+  getPref(key, callback) {
+    const pref = this.prefs[key];
+    assertNotEquals(undefined, pref);
+    callback(
+        /** @type {!chrome.settingsPrivate.PrefObject} */ (deepCopy(pref)));
+  }
+
+  // Functions used by tests.
+
+  /** Instructs the API to return a failure when setPref is next called. */
+  failNextSetPref() {
+    this.failNextSetPref_ = true;
+  }
+
+  /** Instructs the API to assert (fail the test) if setPref is called. */
+  disallowSetPref() {
+    this.disallowSetPref_ = true;
+  }
+
+  allowSetPref() {
+    this.disallowSetPref_ = false;
+  }
+
+  /**
+   * Notifies the listeners of pref changes.
+   * @param {!Array<{key: string, value: *}>} changes
+   */
+  sendPrefChanges(changes) {
+    const prefs = [];
+    for (const change of changes) {
+      const pref = this.prefs[change.key];
+      assertNotEquals(undefined, pref);
+      pref.value = change.value;
+      prefs.push(deepCopy(pref));
+    }
+    /** @type {FakeChromeEvent} */ (this.onPrefsChanged).callListeners(prefs);
+  }
+
+  /** @override */
+  getDefaultZoom() {}
+
+  /** @override */
+  setDefaultZoom() {}
+
+  // Private methods for use by the fake API.
+
+  /**
+   * @param {!chrome.settingsPrivate.PrefType} type
+   * @param {string} key
+   * @param {*} value
+   * @private
+   */
+  addPref_(type, key, value) {
+    this.prefs[key] = {
+      type: type,
+      key: key,
+      value: value,
+    };
+  }
+}
diff --git a/chrome/test/data/webui/settings/languages_page_metrics_test_browser.js b/chrome/test/data/webui/settings/languages_page_metrics_test_browser.js
index 38b74aa7..24c2055 100644
--- a/chrome/test/data/webui/settings/languages_page_metrics_test_browser.js
+++ b/chrome/test/data/webui/settings/languages_page_metrics_test_browser.js
@@ -11,9 +11,9 @@
 import {TestBrowserProxy} from '../test_browser_proxy.m.js';
 import {fakeDataBind} from '../test_util.m.js';
 
-import {getFakeLanguagePrefs} from './fake_language_settings_private.m.js';
-import {FakeSettingsPrivate} from './fake_settings_private.m.js';
-import {TestLanguagesBrowserProxy} from './test_languages_browser_proxy.m.js';
+import {getFakeLanguagePrefs} from './fake_language_settings_private.js';
+import {FakeSettingsPrivate} from './fake_settings_private.js';
+import {TestLanguagesBrowserProxy} from './test_languages_browser_proxy.js';
 
 /**
  * A test version of LanguageSettingsMetricsProxy.
diff --git a/chrome/test/data/webui/settings/languages_page_metrics_test_cros.js b/chrome/test/data/webui/settings/languages_page_metrics_test_cros.js
index 0bd1193..f7a646c 100644
--- a/chrome/test/data/webui/settings/languages_page_metrics_test_cros.js
+++ b/chrome/test/data/webui/settings/languages_page_metrics_test_cros.js
@@ -10,9 +10,9 @@
 import {assertEquals, assertFalse, assertTrue} from '../chai_assert.js';
 import {fakeDataBind} from '../test_util.m.js';
 
-import {getFakeLanguagePrefs} from './fake_language_settings_private.m.js';
-import {FakeSettingsPrivate} from './fake_settings_private.m.js';
-import {TestLanguagesBrowserProxy} from './test_languages_browser_proxy.m.js';
+import {getFakeLanguagePrefs} from './fake_language_settings_private.js';
+import {FakeSettingsPrivate} from './fake_settings_private.js';
+import {TestLanguagesBrowserProxy} from './test_languages_browser_proxy.js';
 import {TestLanguagesMetricsProxy} from './test_languages_metrics_proxy.js';
 
 // TODO(crbug/1109431): Remove this test once migration is complete.
diff --git a/chrome/test/data/webui/settings/languages_page_tests.js b/chrome/test/data/webui/settings/languages_page_tests.js
index 671e639b..e8da1c4 100644
--- a/chrome/test/data/webui/settings/languages_page_tests.js
+++ b/chrome/test/data/webui/settings/languages_page_tests.js
@@ -7,9 +7,9 @@
 import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 import {LanguagesBrowserProxyImpl} from 'chrome://settings/lazy_load.js';
 import {CrSettingsPrefs} from 'chrome://settings/settings.js';
-import {getFakeLanguagePrefs} from 'chrome://test/settings/fake_language_settings_private.m.js';
-import {FakeSettingsPrivate} from 'chrome://test/settings/fake_settings_private.m.js';
-import {TestLanguagesBrowserProxy} from 'chrome://test/settings/test_languages_browser_proxy.m.js';
+import {getFakeLanguagePrefs} from 'chrome://test/settings/fake_language_settings_private.js';
+import {FakeSettingsPrivate} from 'chrome://test/settings/fake_settings_private.js';
+import {TestLanguagesBrowserProxy} from 'chrome://test/settings/test_languages_browser_proxy.js';
 import {fakeDataBind, isChildVisible} from 'chrome://test/test_util.m.js';
 
 // clang-format on
diff --git a/chrome/test/data/webui/settings/languages_subpage_tests.js b/chrome/test/data/webui/settings/languages_subpage_tests.js
index 61e8b21c..6f9be841 100644
--- a/chrome/test/data/webui/settings/languages_subpage_tests.js
+++ b/chrome/test/data/webui/settings/languages_subpage_tests.js
@@ -9,9 +9,9 @@
 import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 import {kMenuCloseDelay, LanguagesBrowserProxyImpl} from 'chrome://settings/lazy_load.js';
 import {CrSettingsPrefs} from 'chrome://settings/settings.js';
-import {getFakeLanguagePrefs} from 'chrome://test/settings/fake_language_settings_private.m.js';
-import {FakeSettingsPrivate} from 'chrome://test/settings/fake_settings_private.m.js';
-import {TestLanguagesBrowserProxy} from 'chrome://test/settings/test_languages_browser_proxy.m.js';
+import {getFakeLanguagePrefs} from 'chrome://test/settings/fake_language_settings_private.js';
+import {FakeSettingsPrivate} from 'chrome://test/settings/fake_settings_private.js';
+import {TestLanguagesBrowserProxy} from 'chrome://test/settings/test_languages_browser_proxy.js';
 import {eventToPromise, fakeDataBind} from 'chrome://test/test_util.m.js';
 
 // clang-format on
diff --git a/chrome/test/data/webui/settings/languages_tests.js b/chrome/test/data/webui/settings/languages_tests.js
index 4611667b..1482c54 100644
--- a/chrome/test/data/webui/settings/languages_tests.js
+++ b/chrome/test/data/webui/settings/languages_tests.js
@@ -7,9 +7,9 @@
 import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 import {LanguagesBrowserProxyImpl} from 'chrome://settings/lazy_load.js';
 import {CrSettingsPrefs} from 'chrome://settings/settings.js';
-import {getFakeLanguagePrefs} from 'chrome://test/settings/fake_language_settings_private.m.js';
-import {FakeSettingsPrivate} from 'chrome://test/settings/fake_settings_private.m.js';
-import {TestLanguagesBrowserProxy} from 'chrome://test/settings/test_languages_browser_proxy.m.js';
+import {getFakeLanguagePrefs} from 'chrome://test/settings/fake_language_settings_private.js';
+import {FakeSettingsPrivate} from 'chrome://test/settings/fake_settings_private.js';
+import {TestLanguagesBrowserProxy} from 'chrome://test/settings/test_languages_browser_proxy.js';
 import {fakeDataBind} from 'chrome://test/test_util.m.js';
 
 // clang-format on
diff --git a/chrome/test/data/webui/settings/password_check_test.js b/chrome/test/data/webui/settings/password_check_test.js
index 5903495..f9d6348 100644
--- a/chrome/test/data/webui/settings/password_check_test.js
+++ b/chrome/test/data/webui/settings/password_check_test.js
@@ -17,7 +17,7 @@
 import {getSyncAllPrefs,simulateSyncStatus} from 'chrome://test/settings/sync_test_util.js';
 import {TestOpenWindowProxy} from 'chrome://test/settings/test_open_window_proxy.js';
 import {TestPasswordManagerProxy} from 'chrome://test/settings/test_password_manager_proxy.js';
-import {TestSyncBrowserProxy} from 'chrome://test/settings/test_sync_browser_proxy.m.js';
+import {TestSyncBrowserProxy} from 'chrome://test/settings/test_sync_browser_proxy.js';
 import {eventToPromise} from 'chrome://test/test_util.m.js';
 
 // clang-format on
diff --git a/chrome/test/data/webui/settings/passwords_device_section_test.js b/chrome/test/data/webui/settings/passwords_device_section_test.js
index da8f837..56633225 100644
--- a/chrome/test/data/webui/settings/passwords_device_section_test.js
+++ b/chrome/test/data/webui/settings/passwords_device_section_test.js
@@ -9,7 +9,7 @@
 import {createMultiStorePasswordEntry, createPasswordEntry, PasswordDeviceSectionElementFactory} from 'chrome://test/settings/passwords_and_autofill_fake_data.js';
 import {simulateStoredAccounts, simulateSyncStatus} from 'chrome://test/settings/sync_test_util.js';
 import {TestPasswordManagerProxy} from 'chrome://test/settings/test_password_manager_proxy.js';
-import {TestSyncBrowserProxy} from 'chrome://test/settings/test_sync_browser_proxy.m.js';
+import {TestSyncBrowserProxy} from 'chrome://test/settings/test_sync_browser_proxy.js';
 import {eventToPromise} from 'chrome://test/test_util.m.js';
 import {assertEquals, assertTrue} from '../chai_assert.js';
 
diff --git a/chrome/test/data/webui/settings/people_page_sync_controls_test.js b/chrome/test/data/webui/settings/people_page_sync_controls_test.js
index 02deb4e3..347a4eb 100644
--- a/chrome/test/data/webui/settings/people_page_sync_controls_test.js
+++ b/chrome/test/data/webui/settings/people_page_sync_controls_test.js
@@ -10,7 +10,7 @@
 import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 import {Router, StatusAction,SyncBrowserProxyImpl} from 'chrome://settings/settings.js';
 import {getSyncAllPrefs, setupRouterWithSyncRoutes} from 'chrome://test/settings/sync_test_util.js';
-import {TestSyncBrowserProxy} from 'chrome://test/settings/test_sync_browser_proxy.m.js';
+import {TestSyncBrowserProxy} from 'chrome://test/settings/test_sync_browser_proxy.js';
 import {waitBeforeNextRender} from 'chrome://test/test_util.m.js';
 
 // clang-format on
diff --git a/chrome/test/data/webui/settings/people_page_sync_page_interactive_test.js b/chrome/test/data/webui/settings/people_page_sync_page_interactive_test.js
index 9376a4c..055e312a 100644
--- a/chrome/test/data/webui/settings/people_page_sync_page_interactive_test.js
+++ b/chrome/test/data/webui/settings/people_page_sync_page_interactive_test.js
@@ -9,7 +9,7 @@
 import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 import {Router, SyncBrowserProxyImpl} from 'chrome://settings/settings.js';
 import {setupRouterWithSyncRoutes} from 'chrome://test/settings/sync_test_util.js';
-import {TestSyncBrowserProxy} from 'chrome://test/settings/test_sync_browser_proxy.m.js';
+import {TestSyncBrowserProxy} from 'chrome://test/settings/test_sync_browser_proxy.js';
 
 // clang-format on
 
diff --git a/chrome/test/data/webui/settings/people_page_sync_page_test.js b/chrome/test/data/webui/settings/people_page_sync_page_test.js
index c173cad..2d79b7ed 100644
--- a/chrome/test/data/webui/settings/people_page_sync_page_test.js
+++ b/chrome/test/data/webui/settings/people_page_sync_page_test.js
@@ -10,7 +10,7 @@
 import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 import {PageStatus, Router, routes, StatusAction, SyncBrowserProxyImpl} from 'chrome://settings/settings.js';
 import {getSyncAllPrefs, setupRouterWithSyncRoutes, simulateStoredAccounts} from 'chrome://test/settings/sync_test_util.js';
-import {TestSyncBrowserProxy} from 'chrome://test/settings/test_sync_browser_proxy.m.js';
+import {TestSyncBrowserProxy} from 'chrome://test/settings/test_sync_browser_proxy.js';
 import {eventToPromise, waitBeforeNextRender} from 'chrome://test/test_util.m.js';
 
 // clang-format on
diff --git a/chrome/test/data/webui/settings/people_page_test.js b/chrome/test/data/webui/settings/people_page_test.js
index 7688231..1777078 100644
--- a/chrome/test/data/webui/settings/people_page_test.js
+++ b/chrome/test/data/webui/settings/people_page_test.js
@@ -11,8 +11,8 @@
 import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 import {pageVisibility, ProfileInfoBrowserProxyImpl, Router, routes, StatusAction, SyncBrowserProxyImpl} from 'chrome://settings/settings.js';
 import {simulateStoredAccounts, simulateSyncStatus} from 'chrome://test/settings/sync_test_util.js';
-import {TestProfileInfoBrowserProxy} from 'chrome://test/settings/test_profile_info_browser_proxy.m.js';
-import {TestSyncBrowserProxy} from 'chrome://test/settings/test_sync_browser_proxy.m.js';
+import {TestProfileInfoBrowserProxy} from 'chrome://test/settings/test_profile_info_browser_proxy.js';
+import {TestSyncBrowserProxy} from 'chrome://test/settings/test_sync_browser_proxy.js';
 import {TestBrowserProxy} from 'chrome://test/test_browser_proxy.m.js';
 import {waitBeforeNextRender} from 'chrome://test/test_util.m.js';
 // clang-format on
diff --git a/chrome/test/data/webui/settings/people_page_test_cros.js b/chrome/test/data/webui/settings/people_page_test_cros.js
index 1620655..79aca09 100644
--- a/chrome/test/data/webui/settings/people_page_test_cros.js
+++ b/chrome/test/data/webui/settings/people_page_test_cros.js
@@ -8,8 +8,8 @@
 import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 import {AccountManagerBrowserProxyImpl, pageVisibility, ProfileInfoBrowserProxyImpl, Router, SyncBrowserProxyImpl} from 'chrome://settings/settings.js';
 import {simulateSyncStatus} from 'chrome://test/settings/sync_test_util.js';
-import {TestProfileInfoBrowserProxy} from 'chrome://test/settings/test_profile_info_browser_proxy.m.js';
-import {TestSyncBrowserProxy} from 'chrome://test/settings/test_sync_browser_proxy.m.js';
+import {TestProfileInfoBrowserProxy} from 'chrome://test/settings/test_profile_info_browser_proxy.js';
+import {TestSyncBrowserProxy} from 'chrome://test/settings/test_sync_browser_proxy.js';
 import {TestBrowserProxy} from 'chrome://test/test_browser_proxy.m.js';
 // clang-format on
 
diff --git a/chrome/test/data/webui/settings/personalization_options_test.js b/chrome/test/data/webui/settings/personalization_options_test.js
index 2feecc18..edebb90 100644
--- a/chrome/test/data/webui/settings/personalization_options_test.js
+++ b/chrome/test/data/webui/settings/personalization_options_test.js
@@ -9,7 +9,7 @@
 import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 import {PrivacyPageBrowserProxyImpl, StatusAction, SyncBrowserProxyImpl} from 'chrome://settings/settings.js';
 import {TestPrivacyPageBrowserProxy} from 'chrome://test/settings/test_privacy_page_browser_proxy.js';
-import {TestSyncBrowserProxy} from 'chrome://test/settings/test_sync_browser_proxy.m.js';
+import {TestSyncBrowserProxy} from 'chrome://test/settings/test_sync_browser_proxy.js';
 import {eventToPromise, isChildVisible, isVisible} from 'chrome://test/test_util.m.js';
 
 // clang-format on
diff --git a/chrome/test/data/webui/settings/prefs_test_cases.js b/chrome/test/data/webui/settings/prefs_test_cases.js
index d6be1ee..f27b2a8b 100644
--- a/chrome/test/data/webui/settings/prefs_test_cases.js
+++ b/chrome/test/data/webui/settings/prefs_test_cases.js
@@ -3,7 +3,7 @@
 // found in the LICENSE file.
 
 // clang-format off
-import {FakeSettingsPrivatePref} from 'chrome://test/settings/fake_settings_private.m.js';
+import {FakeSettingsPrivatePref} from 'chrome://test/settings/fake_settings_private.js';
 // clang-format on
 
 /**
diff --git a/chrome/test/data/webui/settings/prefs_tests.js b/chrome/test/data/webui/settings/prefs_tests.js
index faa580f..1f78a5c 100644
--- a/chrome/test/data/webui/settings/prefs_tests.js
+++ b/chrome/test/data/webui/settings/prefs_tests.js
@@ -4,7 +4,7 @@
 
 // clang-format off
 import {CrSettingsPrefs} from 'chrome://settings/settings.js';
-import {FakeSettingsPrivate} from 'chrome://test/settings/fake_settings_private.m.js';
+import {FakeSettingsPrivate} from 'chrome://test/settings/fake_settings_private.js';
 import {prefsTestCases} from 'chrome://test/settings/prefs_test_cases.js';
 // clang-format on
 
diff --git a/chrome/test/data/webui/settings/safety_check_page_test.js b/chrome/test/data/webui/settings/safety_check_page_test.js
index d269550..53da477 100644
--- a/chrome/test/data/webui/settings/safety_check_page_test.js
+++ b/chrome/test/data/webui/settings/safety_check_page_test.js
@@ -12,7 +12,7 @@
 import {TestBrowserProxy} from '../test_browser_proxy.m.js';
 
 import {TestHatsBrowserProxy} from './test_hats_browser_proxy.js';
-import {TestLifetimeBrowserProxy} from './test_lifetime_browser_proxy.m.js';
+import {TestLifetimeBrowserProxy} from './test_lifetime_browser_proxy.js';
 import {TestMetricsBrowserProxy} from './test_metrics_browser_proxy.js';
 import {TestOpenWindowProxy} from './test_open_window_proxy.js';
 import {TestPasswordManagerProxy} from './test_password_manager_proxy.js';
diff --git a/chrome/test/data/webui/settings/search_engines_page_test.js b/chrome/test/data/webui/settings/search_engines_page_test.js
index b13b3e7..5e59adec 100644
--- a/chrome/test/data/webui/settings/search_engines_page_test.js
+++ b/chrome/test/data/webui/settings/search_engines_page_test.js
@@ -9,7 +9,7 @@
 import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 import {ExtensionControlBrowserProxyImpl, SearchEnginesBrowserProxyImpl} from 'chrome://settings/settings.js';
 import {TestExtensionControlBrowserProxy} from 'chrome://test/settings/test_extension_control_browser_proxy.js';
-import {TestSearchEnginesBrowserProxy} from 'chrome://test/settings/test_search_engines_browser_proxy.m.js';
+import {TestSearchEnginesBrowserProxy} from 'chrome://test/settings/test_search_engines_browser_proxy.js';
 import {eventToPromise} from 'chrome://test/test_util.m.js';
 // clang-format on
 
diff --git a/chrome/test/data/webui/settings/search_page_test.js b/chrome/test/data/webui/settings/search_page_test.js
index 3c92a4b2..11200fa 100644
--- a/chrome/test/data/webui/settings/search_page_test.js
+++ b/chrome/test/data/webui/settings/search_page_test.js
@@ -11,7 +11,7 @@
 
 import {assertEquals, assertFalse, assertNotReached, assertTrue} from '../chai_assert.js';
 
-import {createSampleSearchEngine, TestSearchEnginesBrowserProxy} from './test_search_engines_browser_proxy.m.js';
+import {createSampleSearchEngine, TestSearchEnginesBrowserProxy} from './test_search_engines_browser_proxy.js';
 
 // clang-format on
 
diff --git a/chrome/test/data/webui/settings/sync_account_control_test.js b/chrome/test/data/webui/settings/sync_account_control_test.js
index fbc4b00..29b795ae 100644
--- a/chrome/test/data/webui/settings/sync_account_control_test.js
+++ b/chrome/test/data/webui/settings/sync_account_control_test.js
@@ -7,7 +7,7 @@
 import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 import {MAX_SIGNIN_PROMO_IMPRESSION, Router, routes, StatusAction, SyncBrowserProxyImpl} from 'chrome://settings/settings.js';
 import {setupRouterWithSyncRoutes, simulateStoredAccounts} from 'chrome://test/settings/sync_test_util.js';
-import {TestSyncBrowserProxy} from 'chrome://test/settings/test_sync_browser_proxy.m.js';
+import {TestSyncBrowserProxy} from 'chrome://test/settings/test_sync_browser_proxy.js';
 import {isChildVisible, isVisible} from 'chrome://test/test_util.m.js';
 
 // clang-format on
diff --git a/chrome/test/data/webui/settings/system_page_tests.js b/chrome/test/data/webui/settings/system_page_tests.js
index 3a72ce1..98d18ac 100644
--- a/chrome/test/data/webui/settings/system_page_tests.js
+++ b/chrome/test/data/webui/settings/system_page_tests.js
@@ -7,7 +7,7 @@
 import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 import {SystemPageBrowserProxyImpl} from 'chrome://settings/lazy_load.js';
 import {LifetimeBrowserProxyImpl} from 'chrome://settings/settings.js';
-import {TestLifetimeBrowserProxy} from 'chrome://test/settings/test_lifetime_browser_proxy.m.js';
+import {TestLifetimeBrowserProxy} from 'chrome://test/settings/test_lifetime_browser_proxy.js';
 import {TestBrowserProxy} from 'chrome://test/test_browser_proxy.m.js';
 // clang-format on
 
diff --git a/chrome/test/data/webui/settings/test_languages_browser_proxy.js b/chrome/test/data/webui/settings/test_languages_browser_proxy.js
index 707d3c7..6ff2c1c4 100644
--- a/chrome/test/data/webui/settings/test_languages_browser_proxy.js
+++ b/chrome/test/data/webui/settings/test_languages_browser_proxy.js
@@ -3,68 +3,61 @@
 // found in the LICENSE file.
 
 // clang-format off
-// #import {FakeInputMethodPrivate} from './fake_input_method_private.m.js';
-// #import {FakeLanguageSettingsPrivate} from './fake_language_settings_private.m.js';
-// #import {isChromeOS, isWindows} from 'chrome://resources/js/cr.m.js';
-// #import {LanguagesBrowserProxy} from 'chrome://settings/lazy_load.js';
-// #import {TestBrowserProxy} from '../test_browser_proxy.m.js';
+import {isChromeOS, isWindows} from 'chrome://resources/js/cr.m.js';
+import {LanguagesBrowserProxy} from 'chrome://settings/lazy_load.js';
+
+import {TestBrowserProxy} from '../test_browser_proxy.m.js';
+
+import {FakeInputMethodPrivate} from './fake_input_method_private.js';
+import {FakeLanguageSettingsPrivate} from './fake_language_settings_private.js';
 // clang-format on
 
-cr.define('settings', function() {
-  /** @implements {settings.LanguagesBrowserProxy} */
-  /* #export */ class TestLanguagesBrowserProxy extends TestBrowserProxy {
-    constructor() {
-      const methodNames = [];
-      if (cr.isChromeOS || cr.isWindows) {
-        methodNames.push(
-            'getProspectiveUILanguage', 'setProspectiveUILanguage');
-      }
-
-      super(methodNames);
-
-      /** @private {!LanguageSettingsPrivate} */
-      this.languageSettingsPrivate_ =
-          new settings.FakeLanguageSettingsPrivate();
-
-      /** @private {!InputMethodPrivate} */
-      this.inputMethodPrivate_ = /** @type{!InputMethodPrivate} */ (
-          new settings.FakeInputMethodPrivate());
+/** @implements {LanguagesBrowserProxy} */
+export class TestLanguagesBrowserProxy extends TestBrowserProxy {
+  constructor() {
+    const methodNames = [];
+    if (isChromeOS || isWindows) {
+      methodNames.push('getProspectiveUILanguage', 'setProspectiveUILanguage');
     }
 
-    /** @override */
-    getLanguageSettingsPrivate() {
-      return this.languageSettingsPrivate_;
-    }
+    super(methodNames);
 
-    /** @param {!LanguageSettingsPrivate} languageSettingsPrivate */
-    setLanguageSettingsPrivate(languageSettingsPrivate) {
-      this.languageSettingsPrivate_ = languageSettingsPrivate;
-    }
+    /** @private {!LanguageSettingsPrivate} */
+    this.languageSettingsPrivate_ = new FakeLanguageSettingsPrivate();
+
+    /** @private {!InputMethodPrivate} */
+    this.inputMethodPrivate_ =
+        /** @type{!InputMethodPrivate} */ (new FakeInputMethodPrivate());
   }
 
-  if (cr.isChromeOS || cr.isWindows) {
-    /** @override */
-    TestLanguagesBrowserProxy.prototype.getProspectiveUILanguage = function() {
-      this.methodCalled('getProspectiveUILanguage');
-      return Promise.resolve('en-US');
-    };
-
-    /** @override */
-    TestLanguagesBrowserProxy.prototype.setProspectiveUILanguage = function(
-        language) {
-      this.methodCalled('setProspectiveUILanguage', language);
-    };
+  /** @override */
+  getLanguageSettingsPrivate() {
+    return this.languageSettingsPrivate_;
   }
 
-  if (cr.isChromeOS) {
-    /** @override */
-    TestLanguagesBrowserProxy.prototype.getInputMethodPrivate = function() {
-      return this.inputMethodPrivate_;
-    };
+  /** @param {!LanguageSettingsPrivate} languageSettingsPrivate */
+  setLanguageSettingsPrivate(languageSettingsPrivate) {
+    this.languageSettingsPrivate_ = languageSettingsPrivate;
   }
+}
 
-  // #cr_define_end
-  return {
-    TestLanguagesBrowserProxy: TestLanguagesBrowserProxy,
+if (isChromeOS || isWindows) {
+  /** @override */
+  TestLanguagesBrowserProxy.prototype.getProspectiveUILanguage = function() {
+    this.methodCalled('getProspectiveUILanguage');
+    return Promise.resolve('en-US');
   };
-});
+
+  /** @override */
+  TestLanguagesBrowserProxy.prototype.setProspectiveUILanguage = function(
+      language) {
+    this.methodCalled('setProspectiveUILanguage', language);
+  };
+}
+
+if (isChromeOS) {
+  /** @override */
+  TestLanguagesBrowserProxy.prototype.getInputMethodPrivate = function() {
+    return this.inputMethodPrivate_;
+  };
+}
diff --git a/chrome/test/data/webui/settings/test_lifetime_browser_proxy.js b/chrome/test/data/webui/settings/test_lifetime_browser_proxy.js
index 3a1dda3..7866099d 100644
--- a/chrome/test/data/webui/settings/test_lifetime_browser_proxy.js
+++ b/chrome/test/data/webui/settings/test_lifetime_browser_proxy.js
@@ -2,52 +2,46 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-// #import {isChromeOS} from 'chrome://resources/js/cr.m.js';
-// #import {TestBrowserProxy} from '../test_browser_proxy.m.js';
-// #import {LifetimeBrowserProxy} from 'chrome://settings/settings.js';
+import {isChromeOS} from 'chrome://resources/js/cr.m.js';
+import {LifetimeBrowserProxy} from 'chrome://settings/settings.js';
 
-cr.define('settings', function() {
-  /**
-   * A test version of LifetimeBrowserProxy.
-   *
-   * @implements {settings.LifetimeBrowserProxy}
-   */
-  /* #export */ class TestLifetimeBrowserProxy extends TestBrowserProxy {
-    constructor() {
-      const methodNames = ['restart', 'relaunch'];
-      if (cr.isChromeOS) {
-        methodNames.push('signOutAndRestart', 'factoryReset');
-      }
+import {TestBrowserProxy} from '../test_browser_proxy.m.js';
 
-      super(methodNames);
+/**
+ * A test version of LifetimeBrowserProxy.
+ *
+ * @implements {LifetimeBrowserProxy}
+ */
+export class TestLifetimeBrowserProxy extends TestBrowserProxy {
+  constructor() {
+    const methodNames = ['restart', 'relaunch'];
+    if (isChromeOS) {
+      methodNames.push('signOutAndRestart', 'factoryReset');
     }
 
-    /** @override */
-    restart() {
-      this.methodCalled('restart');
-    }
-
-    /** @override */
-    relaunch() {
-      this.methodCalled('relaunch');
-    }
+    super(methodNames);
   }
 
-  if (cr.isChromeOS) {
-    /** @override */
-    TestLifetimeBrowserProxy.prototype.signOutAndRestart = function() {
-      this.methodCalled('signOutAndRestart');
-    };
-
-    /** @override */
-    TestLifetimeBrowserProxy.prototype.factoryReset = function(
-        requestTpmFirmwareUpdate) {
-      this.methodCalled('factoryReset', requestTpmFirmwareUpdate);
-    };
+  /** @override */
+  restart() {
+    this.methodCalled('restart');
   }
 
-  // #cr_define_end
-  return {
-    TestLifetimeBrowserProxy: TestLifetimeBrowserProxy,
+  /** @override */
+  relaunch() {
+    this.methodCalled('relaunch');
+  }
+}
+
+if (isChromeOS) {
+  /** @override */
+  TestLifetimeBrowserProxy.prototype.signOutAndRestart = function() {
+    this.methodCalled('signOutAndRestart');
   };
-});
+
+  /** @override */
+  TestLifetimeBrowserProxy.prototype.factoryReset = function(
+      requestTpmFirmwareUpdate) {
+    this.methodCalled('factoryReset', requestTpmFirmwareUpdate);
+  };
+}
diff --git a/chrome/test/data/webui/settings/test_profile_info_browser_proxy.js b/chrome/test/data/webui/settings/test_profile_info_browser_proxy.js
index 9cde7f0..51e0c62 100644
--- a/chrome/test/data/webui/settings/test_profile_info_browser_proxy.js
+++ b/chrome/test/data/webui/settings/test_profile_info_browser_proxy.js
@@ -2,10 +2,10 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-// #import {TestBrowserProxy} from 'chrome://test/test_browser_proxy.m.js';
+import {TestBrowserProxy} from 'chrome://test/test_browser_proxy.m.js';
 
-/** @implements {settings.ProfileInfoBrowserProxy} */
-/* #export */ class TestProfileInfoBrowserProxy extends TestBrowserProxy {
+/** @implements {ProfileInfoBrowserProxy} */
+export class TestProfileInfoBrowserProxy extends TestBrowserProxy {
   constructor() {
     super([
       'getProfileInfo',
diff --git a/chrome/test/data/webui/settings/test_search_engines_browser_proxy.js b/chrome/test/data/webui/settings/test_search_engines_browser_proxy.js
index 705c753..b7a3fa9a 100644
--- a/chrome/test/data/webui/settings/test_search_engines_browser_proxy.js
+++ b/chrome/test/data/webui/settings/test_search_engines_browser_proxy.js
@@ -3,108 +3,101 @@
 // found in the LICENSE file.
 
 // clang-format off
-// #import {TestBrowserProxy} from '../test_browser_proxy.m.js';
-// #import {SearchEnginesBrowserProxy, SearchEnginesInfo, SearchEngine} from 'chrome://settings/settings.js';
+import { SearchEngine,SearchEnginesBrowserProxy, SearchEnginesInfo} from 'chrome://settings/settings.js';
+
+import {TestBrowserProxy} from '../test_browser_proxy.m.js';
 // clang-format on
 
-cr.define('settings_search', function() {
-  /**
-   * A test version of SearchEnginesBrowserProxy. Provides helper methods
-   * for allowing tests to know when a method was called, as well as
-   * specifying mock responses.
-   *
-   * @implements {settings.SearchEnginesBrowserProxy}
-   */
-  /* #export */ class TestSearchEnginesBrowserProxy extends TestBrowserProxy {
-    constructor() {
-      super([
-        'getSearchEnginesList',
-        'removeSearchEngine',
-        'searchEngineEditCancelled',
-        'searchEngineEditCompleted',
-        'searchEngineEditStarted',
-        'setDefaultSearchEngine',
-        'validateSearchEngineInput',
-      ]);
+/**
+ * A test version of SearchEnginesBrowserProxy. Provides helper methods
+ * for allowing tests to know when a method was called, as well as
+ * specifying mock responses.
+ *
+ * @implements {SearchEnginesBrowserProxy}
+ */
+export class TestSearchEnginesBrowserProxy extends TestBrowserProxy {
+  constructor() {
+    super([
+      'getSearchEnginesList',
+      'removeSearchEngine',
+      'searchEngineEditCancelled',
+      'searchEngineEditCompleted',
+      'searchEngineEditStarted',
+      'setDefaultSearchEngine',
+      'validateSearchEngineInput',
+    ]);
 
-      /** @private {!SearchEnginesInfo} */
-      this.searchEnginesInfo_ = {defaults: [], others: [], extensions: []};
-    }
-
-    /** @override */
-    setDefaultSearchEngine(modelIndex) {
-      this.methodCalled('setDefaultSearchEngine', modelIndex);
-    }
-
-    /** @override */
-    removeSearchEngine(modelIndex) {
-      this.methodCalled('removeSearchEngine', modelIndex);
-    }
-
-    /** @override */
-    searchEngineEditStarted(modelIndex) {
-      this.methodCalled('searchEngineEditStarted', modelIndex);
-    }
-
-    /** @override */
-    searchEngineEditCancelled() {
-      this.methodCalled('searchEngineEditCancelled');
-    }
-
-    /** @override */
-    searchEngineEditCompleted(searchEngine, keyword, queryUrl) {
-      this.methodCalled('searchEngineEditCompleted');
-    }
-
-    /** @override */
-    getSearchEnginesList() {
-      this.methodCalled('getSearchEnginesList');
-      return Promise.resolve(this.searchEnginesInfo_);
-    }
-
-    /** @override */
-    validateSearchEngineInput(fieldName, fieldValue) {
-      this.methodCalled('validateSearchEngineInput');
-      return Promise.resolve(true);
-    }
-
-    /**
-     * Sets the response to be returned by |getSearchEnginesList|.
-     * @param {!SearchEnginesInfo} searchEnginesInfo
-     */
-    setSearchEnginesInfo(searchEnginesInfo) {
-      this.searchEnginesInfo_ = searchEnginesInfo;
-    }
+    /** @private {!SearchEnginesInfo} */
+    this.searchEnginesInfo_ = {defaults: [], others: [], extensions: []};
   }
 
+  /** @override */
+  setDefaultSearchEngine(modelIndex) {
+    this.methodCalled('setDefaultSearchEngine', modelIndex);
+  }
+
+  /** @override */
+  removeSearchEngine(modelIndex) {
+    this.methodCalled('removeSearchEngine', modelIndex);
+  }
+
+  /** @override */
+  searchEngineEditStarted(modelIndex) {
+    this.methodCalled('searchEngineEditStarted', modelIndex);
+  }
+
+  /** @override */
+  searchEngineEditCancelled() {
+    this.methodCalled('searchEngineEditCancelled');
+  }
+
+  /** @override */
+  searchEngineEditCompleted(searchEngine, keyword, queryUrl) {
+    this.methodCalled('searchEngineEditCompleted');
+  }
+
+  /** @override */
+  getSearchEnginesList() {
+    this.methodCalled('getSearchEnginesList');
+    return Promise.resolve(this.searchEnginesInfo_);
+  }
+
+  /** @override */
+  validateSearchEngineInput(fieldName, fieldValue) {
+    this.methodCalled('validateSearchEngineInput');
+    return Promise.resolve(true);
+  }
+
+  /**
+   * Sets the response to be returned by |getSearchEnginesList|.
+   * @param {!SearchEnginesInfo} searchEnginesInfo
+   */
+  setSearchEnginesInfo(searchEnginesInfo) {
+    this.searchEnginesInfo_ = searchEnginesInfo;
+  }
+}
+
   /**
    * @param {boolean} canBeDefault
    * @param {boolean} canBeEdited
    * @param {boolean} canBeRemoved
    * @return {!SearchEngine}
    */
-  /* #export */ function createSampleSearchEngine(
-      canBeDefault, canBeEdited, canBeRemoved) {
-    return {
-      canBeDefault: canBeDefault,
-      canBeEdited: canBeEdited,
-      canBeRemoved: canBeRemoved,
-      default: false,
-      displayName: 'Google',
-      iconURL: 'http://www.google.com/favicon.ico',
-      id: 0,
-      isOmniboxExtension: false,
-      keyword: 'google.com',
-      modelIndex: 0,
-      name: 'Google',
-      url: 'https://search.foo.com/search?p=%s',
-      urlLocked: false,
-    };
-  }
-
-  // #cr_define_end
+export function createSampleSearchEngine(
+    canBeDefault, canBeEdited, canBeRemoved) {
   return {
-    createSampleSearchEngine: createSampleSearchEngine,
-    TestSearchEnginesBrowserProxy: TestSearchEnginesBrowserProxy,
+    canBeDefault: canBeDefault,
+    canBeEdited: canBeEdited,
+    canBeRemoved: canBeRemoved,
+    default: false,
+    displayName: 'Google',
+    iconURL: 'http://www.google.com/favicon.ico',
+    id: 0,
+    isOmniboxExtension: false,
+    keyword: 'google.com',
+    modelIndex: 0,
+    name: 'Google',
+    url: 'https://search.foo.com/search?p=%s',
+    urlLocked: false,
   };
-});
+}
diff --git a/chrome/test/data/webui/settings/test_sync_browser_proxy.js b/chrome/test/data/webui/settings/test_sync_browser_proxy.js
index 567f747..dcb84105 100644
--- a/chrome/test/data/webui/settings/test_sync_browser_proxy.js
+++ b/chrome/test/data/webui/settings/test_sync_browser_proxy.js
@@ -3,13 +3,14 @@
 // found in the LICENSE file.
 
 // clang-format off
-// #import {PageStatus, StoredAccount, SyncBrowserProxy, SyncStatus} from 'chrome://settings/settings.js';
-// #import {TestBrowserProxy} from '../test_browser_proxy.m.js';
-// #import {isChromeOS} from 'chrome://resources/js/cr.m.js';
+import {isChromeOS} from 'chrome://resources/js/cr.m.js';
+import {PageStatus, StoredAccount, SyncBrowserProxy, SyncStatus} from 'chrome://settings/settings.js';
+
+import {TestBrowserProxy} from '../test_browser_proxy.m.js';
 // clang-format on
 
-/** @implements {settings.SyncBrowserProxy} */
-/* #export */ class TestSyncBrowserProxy extends TestBrowserProxy {
+/** @implements {SyncBrowserProxy} */
+export class TestSyncBrowserProxy extends TestBrowserProxy {
   constructor() {
     const methodNames = [
       'didNavigateAwayFromSyncPage',
@@ -28,7 +29,7 @@
       'startSyncingWithEmail',
     ];
 
-    if (cr.isChromeOS) {
+    if (isChromeOS) {
       methodNames.push('turnOnSync', 'turnOffSync');
     }
 
@@ -42,10 +43,10 @@
     this.encryptionPassphraseSuccess = false;
     /** @type {boolean} */
     this.decryptionPassphraseSuccess = false;
-    /** @type {!Array<!settings.StoredAccount>} */
+    /** @type {!Array<!StoredAccount>} */
     this.storedAccounts = [];
-    /** @type {!settings.SyncStatus} */
-    this.syncStatus = /** @type {!settings.SyncStatus} */ (
+    /** @type {!SyncStatus} */
+    this.syncStatus = /** @type {!SyncStatus} */ (
         {signedIn: true, signedInUsername: 'fakeUsername'});
   }
 
@@ -110,7 +111,7 @@
   /** @override */
   setSyncDatatypes(syncPrefs) {
     this.methodCalled('setSyncDatatypes', syncPrefs);
-    return Promise.resolve(settings.PageStatus.CONFIGURE);
+    return Promise.resolve(PageStatus.CONFIGURE);
   }
 
   /** @override */
@@ -140,7 +141,7 @@
   startKeyRetrieval() {}
 }
 
-if (cr.isChromeOS) {
+if (isChromeOS) {
   /** @override */
   TestSyncBrowserProxy.prototype.turnOnSync = function() {
     this.methodCalled('turnOnSync');
diff --git a/chromecast/browser/accessibility/flutter/ax_tree_source_flutter_unittest.cc b/chromecast/browser/accessibility/flutter/ax_tree_source_flutter_unittest.cc
index 951a480..e4cf2c2 100644
--- a/chromecast/browser/accessibility/flutter/ax_tree_source_flutter_unittest.cc
+++ b/chromecast/browser/accessibility/flutter/ax_tree_source_flutter_unittest.cc
@@ -6,6 +6,8 @@
 
 #include <string>
 
+#include "base/unguessable_token.h"
+#include "base/util/values/values_util.h"
 #include "chromecast/browser/accessibility/flutter/flutter_semantics_node_wrapper.h"
 #include "content/public/browser/content_browser_client.h"
 #include "content/public/browser/tts_controller.h"
@@ -475,7 +477,12 @@
   child = AddChild(&event2, root2, 2, 0, 0, 400, 600, false);
   child = AddChild(&event2, root2, 3, 400, 0, 400, 600, false);
   child = AddChild(&event2, root2, 4, 0, 0, 200, 200, false);
-  child->set_plugin_id("1234");
+
+  // We need a plugin id that is the right length. Here we use
+  base::UnguessableToken token = base::UnguessableToken::Create();
+  std::string token_to_string =
+      util::UnguessableTokenToValue(token).GetString();
+  child->set_plugin_id(token_to_string);
 
   // focus should move to node with child tree
   CallNotifyAccessibilityEvent(&event2);
diff --git a/chromecast/browser/accessibility/flutter/flutter_semantics_node_wrapper.cc b/chromecast/browser/accessibility/flutter/flutter_semantics_node_wrapper.cc
index 762adb5..a099473 100644
--- a/chromecast/browser/accessibility/flutter/flutter_semantics_node_wrapper.cc
+++ b/chromecast/browser/accessibility/flutter/flutter_semantics_node_wrapper.cc
@@ -418,16 +418,14 @@
       for (CastWebContents* contents : all_contents) {
         if (contents->id() == web_contents_id) {
           content::WebContents* web_contents = contents->web_contents();
-          out_data->AddStringAttribute(
-              ax::mojom::StringAttribute::kChildTreeId,
-              web_contents->GetMainFrame()->GetAXTreeID().ToString());
+          out_data->AddChildTreeId(web_contents->GetMainFrame()->GetAXTreeID());
           break;
         }
       }
     } else {
       // Use the value as a tree id.
-      out_data->AddStringAttribute(ax::mojom::StringAttribute::kChildTreeId,
-                                   ax_tree_id);
+      ui::AXTreeID child_ax_tree_id = ui::AXTreeID::FromString(ax_tree_id);
+      out_data->AddChildTreeId(child_ax_tree_id);
     }
   }
 
diff --git a/chromeos/network/cellular_esim_connection_handler.cc b/chromeos/network/cellular_esim_connection_handler.cc
index 823604f..23d399a 100644
--- a/chromeos/network/cellular_esim_connection_handler.cc
+++ b/chromeos/network/cellular_esim_connection_handler.cc
@@ -270,6 +270,7 @@
 
   TransitionToConnectionState(ConnectionState::kInhibitingScans);
   cellular_inhibitor_->InhibitCellularScanning(
+      CellularInhibitor::InhibitReason::kConnectingToProfile,
       base::BindOnce(&CellularESimConnectionHandler::OnInhibitScanResult,
                      weak_ptr_factory_.GetWeakPtr()));
 }
diff --git a/chromeos/network/cellular_esim_connection_handler_unittest.cc b/chromeos/network/cellular_esim_connection_handler_unittest.cc
index 791d317..38606628c1 100644
--- a/chromeos/network/cellular_esim_connection_handler_unittest.cc
+++ b/chromeos/network/cellular_esim_connection_handler_unittest.cc
@@ -184,11 +184,14 @@
   std::unique_ptr<CellularInhibitor::InhibitLock> InhibitCellular() {
     base::RunLoop run_loop;
     std::unique_ptr<CellularInhibitor::InhibitLock> inhibit_lock;
-    inhibitor_.InhibitCellularScanning(base::BindLambdaForTesting(
-        [&](std::unique_ptr<CellularInhibitor::InhibitLock> new_inhibit_lock) {
-          inhibit_lock = std::move(new_inhibit_lock);
-          run_loop.Quit();
-        }));
+    inhibitor_.InhibitCellularScanning(
+        CellularInhibitor::InhibitReason::kRemovingProfile,
+        base::BindLambdaForTesting(
+            [&](std::unique_ptr<CellularInhibitor::InhibitLock>
+                    new_inhibit_lock) {
+              inhibit_lock = std::move(new_inhibit_lock);
+              run_loop.Quit();
+            }));
     run_loop.Run();
     return inhibit_lock;
   }
diff --git a/chromeos/network/cellular_esim_uninstall_handler.cc b/chromeos/network/cellular_esim_uninstall_handler.cc
index bdff4e8..2660cd9 100644
--- a/chromeos/network/cellular_esim_uninstall_handler.cc
+++ b/chromeos/network/cellular_esim_uninstall_handler.cc
@@ -135,6 +135,7 @@
 
 void CellularESimUninstallHandler::AttemptShillInhibit() {
   cellular_inhibitor_->InhibitCellularScanning(
+      CellularInhibitor::InhibitReason::kRemovingProfile,
       base::BindOnce(&CellularESimUninstallHandler::OnShillInhibit,
                      weak_ptr_factory_.GetWeakPtr()));
 }
diff --git a/chromeos/network/cellular_inhibitor.cc b/chromeos/network/cellular_inhibitor.cc
index 3887477..b07a2bd 100644
--- a/chromeos/network/cellular_inhibitor.cc
+++ b/chromeos/network/cellular_inhibitor.cc
@@ -5,6 +5,7 @@
 #include "chromeos/network/cellular_inhibitor.h"
 
 #include <memory>
+#include <sstream>
 
 #include "base/bind.h"
 #include "base/callback_helpers.h"
@@ -27,6 +28,14 @@
 
 }  // namespace
 
+CellularInhibitor::InhibitRequest::InhibitRequest(
+    InhibitReason inhibit_reason,
+    InhibitCallback inhibit_callback)
+    : inhibit_reason(inhibit_reason),
+      inhibit_callback(std::move(inhibit_callback)) {}
+
+CellularInhibitor::InhibitRequest::~InhibitRequest() = default;
+
 CellularInhibitor::InhibitLock::InhibitLock(base::OnceClosure unlock_callback)
     : unlock_callback_(std::move(unlock_callback)) {}
 
@@ -49,11 +58,38 @@
   network_state_handler_->AddObserver(this, FROM_HERE);
 }
 
-void CellularInhibitor::InhibitCellularScanning(InhibitCallback callback) {
-  inhibit_requests_.push(std::move(callback));
+void CellularInhibitor::InhibitCellularScanning(InhibitReason reason,
+                                                InhibitCallback callback) {
+  inhibit_requests_.push(
+      std::make_unique<InhibitRequest>(reason, std::move(callback)));
   ProcessRequests();
 }
 
+base::Optional<CellularInhibitor::InhibitReason>
+CellularInhibitor::GetInhibitReason() const {
+  if (state_ == State::kIdle)
+    return base::nullopt;
+
+  return inhibit_requests_.front()->inhibit_reason;
+}
+
+void CellularInhibitor::AddObserver(Observer* observer) {
+  observer_list_.AddObserver(observer);
+}
+
+void CellularInhibitor::RemoveObserver(Observer* observer) {
+  observer_list_.RemoveObserver(observer);
+}
+
+bool CellularInhibitor::HasObserver(Observer* observer) const {
+  return observer_list_.HasObserver(observer);
+}
+
+void CellularInhibitor::NotifyInhibitStateChanged() {
+  for (auto& observer : observer_list_)
+    observer.OnInhibitStateChanged();
+}
+
 void CellularInhibitor::DeviceListChanged() {
   CheckScanningIfNeeded();
 }
@@ -68,8 +104,24 @@
 }
 
 void CellularInhibitor::TransitionToState(State state) {
-  NET_LOG(EVENT) << "CellularInhibitor state: " << state_ << " => " << state;
+  State old_state = state_;
   state_ = state;
+
+  bool was_inhibited = old_state != State::kIdle;
+  bool is_inhibited = state_ != State::kIdle;
+
+  std::stringstream ss;
+  ss << "CellularInhibitor state: " << old_state << " => " << state_;
+
+  if (!is_inhibited) {
+    DCHECK(!GetInhibitReason());
+    NET_LOG(EVENT) << ss.str();
+  } else {
+    NET_LOG(EVENT) << ss.str() << state_ << ", reason: " << *GetInhibitReason();
+  }
+
+  if (was_inhibited != is_inhibited)
+    NotifyInhibitStateChanged();
 }
 
 void CellularInhibitor::ProcessRequests() {
@@ -95,11 +147,11 @@
     std::unique_ptr<InhibitLock> lock = std::make_unique<InhibitLock>(
         base::BindOnce(&CellularInhibitor::AttemptUninhibit,
                        weak_ptr_factory_.GetWeakPtr(), /*attempts_so_far=*/0));
-    std::move(inhibit_requests_.front()).Run(std::move(lock));
+    std::move(inhibit_requests_.front()->inhibit_callback).Run(std::move(lock));
     return;
   }
 
-  std::move(inhibit_requests_.front()).Run(nullptr);
+  std::move(inhibit_requests_.front()->inhibit_callback).Run(nullptr);
   PopRequestAndProcessNext();
 }
 
@@ -246,3 +298,26 @@
 }
 
 }  // namespace chromeos
+
+std::ostream& operator<<(
+    std::ostream& stream,
+    const chromeos::CellularInhibitor::InhibitReason& inhibit_reason) {
+  switch (inhibit_reason) {
+    case chromeos::CellularInhibitor::InhibitReason::kInstallingProfile:
+      stream << "[Installing profile]";
+      break;
+    case chromeos::CellularInhibitor::InhibitReason::kRenamingProfile:
+      stream << "[Renaming profile]";
+      break;
+    case chromeos::CellularInhibitor::InhibitReason::kRemovingProfile:
+      stream << "[Removing profile]";
+      break;
+    case chromeos::CellularInhibitor::InhibitReason::kConnectingToProfile:
+      stream << "[Connecting to profile]";
+      break;
+    case chromeos::CellularInhibitor::InhibitReason::kRefreshingProfileList:
+      stream << "[Refreshing profile list]";
+      break;
+  }
+  return stream;
+}
diff --git a/chromeos/network/cellular_inhibitor.h b/chromeos/network/cellular_inhibitor.h
index d73d940..7d455856 100644
--- a/chromeos/network/cellular_inhibitor.h
+++ b/chromeos/network/cellular_inhibitor.h
@@ -8,6 +8,8 @@
 #include "base/component_export.h"
 #include "base/containers/queue.h"
 #include "base/memory/weak_ptr.h"
+#include "base/observer_list.h"
+#include "base/observer_list_types.h"
 #include "chromeos/network/network_handler_callbacks.h"
 #include "chromeos/network/network_state_handler_observer.h"
 
@@ -50,6 +52,23 @@
     base::OnceClosure unlock_callback_;
   };
 
+  class Observer : public base::CheckedObserver {
+   public:
+    ~Observer() override = default;
+
+    // Invoked when the inhibit state has changed; observers should use the
+    // GetInhibitReason() function to determine the current state.
+    virtual void OnInhibitStateChanged() = 0;
+  };
+
+  enum class InhibitReason {
+    kInstallingProfile,
+    kRenamingProfile,
+    kRemovingProfile,
+    kConnectingToProfile,
+    kRefreshingProfileList
+  };
+
   // Callback which returns InhibitLock on inhibit success or nullptr on
   // failure.
   using InhibitCallback =
@@ -58,9 +77,31 @@
   // Puts the Cellular device in Inhibited state and returns an InhibitLock
   // object which when destroyed automatically uninhibits the Cellular device. A
   // call to this method will block until the last issues lock is deleted.
-  void InhibitCellularScanning(InhibitCallback callback);
+  void InhibitCellularScanning(InhibitReason reason, InhibitCallback callback);
+
+  // Returns the reason that cellular scanning is currently inhibited, or null
+  // if it is not inhibited.
+  base::Optional<InhibitReason> GetInhibitReason() const;
+
+  void AddObserver(Observer* observer);
+  void RemoveObserver(Observer* observer);
+  bool HasObserver(Observer* observer) const;
+
+ protected:
+  void NotifyInhibitStateChanged();
 
  private:
+  struct InhibitRequest {
+    InhibitRequest(InhibitReason inhibit_reason,
+                   InhibitCallback inhibit_callback);
+    InhibitRequest(const InhibitRequest&) = delete;
+    InhibitRequest& operator=(const InhibitRequest&) = delete;
+    ~InhibitRequest();
+
+    InhibitReason inhibit_reason;
+    InhibitCallback inhibit_callback;
+  };
+
   enum class State {
     kIdle,
     kInhibiting,
@@ -105,11 +146,17 @@
   NetworkDeviceHandler* network_device_handler_ = nullptr;
 
   State state_ = State::kIdle;
-  base::queue<InhibitCallback> inhibit_requests_;
+  base::queue<std::unique_ptr<InhibitRequest>> inhibit_requests_;
+
+  base::ObserverList<Observer> observer_list_;
 
   base::WeakPtrFactory<CellularInhibitor> weak_ptr_factory_{this};
 };
 
 }  // namespace chromeos
 
+std::ostream& operator<<(
+    std::ostream& stream,
+    const chromeos::CellularInhibitor::InhibitReason& inhibit_reason);
+
 #endif  // CHROMEOS_NETWORK_CELLULAR_INHIBITOR_H_
diff --git a/chromeos/network/cellular_inhibitor_unittest.cc b/chromeos/network/cellular_inhibitor_unittest.cc
index 920279eb..3c3b02fe 100644
--- a/chromeos/network/cellular_inhibitor_unittest.cc
+++ b/chromeos/network/cellular_inhibitor_unittest.cc
@@ -30,6 +30,20 @@
 
 enum class GetInhibitedPropertyResult { kTrue, kFalse, kOperationFailed };
 
+class TestObserver : public CellularInhibitor::Observer {
+ public:
+  TestObserver() = default;
+  ~TestObserver() override = default;
+
+  size_t num_observer_events() const { return num_observer_events_; }
+
+ private:
+  // CellularInhibitor::Observer:
+  void OnInhibitStateChanged() override { ++num_observer_events_; }
+
+  size_t num_observer_events_ = 0u;
+};
+
 }  // namespace
 
 class CellularInhibitorTest : public testing::Test {
@@ -45,35 +59,43 @@
     helper_.device_test()->ClearDevices();
     cellular_inhibitor_.Init(helper_.network_state_handler(),
                              helper_.network_device_handler());
+    cellular_inhibitor_.AddObserver(&observer_);
   }
 
+  void TearDown() override { cellular_inhibitor_.RemoveObserver(&observer_); }
+
   void AddCellularDevice() {
     helper_.device_test()->AddDevice(kDefaultCellularDevicePath,
                                      shill::kTypeCellular, "cellular1");
     base::RunLoop().RunUntilIdle();
   }
 
-  std::unique_ptr<CellularInhibitor::InhibitLock>
-  InhibitCellularScanningSync() {
+  std::unique_ptr<CellularInhibitor::InhibitLock> InhibitCellularScanningSync(
+      CellularInhibitor::InhibitReason inhibit_reason) {
     base::RunLoop run_loop;
 
     std::unique_ptr<CellularInhibitor::InhibitLock> inhibit_lock;
-    cellular_inhibitor_.InhibitCellularScanning(base::BindLambdaForTesting(
-        [&](std::unique_ptr<CellularInhibitor::InhibitLock> result) {
-          inhibit_lock = std::move(result);
-          run_loop.Quit();
-        }));
+    cellular_inhibitor_.InhibitCellularScanning(
+        inhibit_reason,
+        base::BindLambdaForTesting(
+            [&](std::unique_ptr<CellularInhibitor::InhibitLock> result) {
+              inhibit_lock = std::move(result);
+              run_loop.Quit();
+            }));
 
     run_loop.Run();
     return inhibit_lock;
   }
 
   void InhibitCellularScanning(
+      CellularInhibitor::InhibitReason inhibit_reason,
       std::unique_ptr<CellularInhibitor::InhibitLock>& lock) {
-    cellular_inhibitor_.InhibitCellularScanning(base::BindLambdaForTesting(
-        [&](std::unique_ptr<CellularInhibitor::InhibitLock> result) {
-          lock = std::move(result);
-        }));
+    cellular_inhibitor_.InhibitCellularScanning(
+        inhibit_reason,
+        base::BindLambdaForTesting(
+            [&](std::unique_ptr<CellularInhibitor::InhibitLock> result) {
+              lock = std::move(result);
+            }));
   }
 
   void SetScanning(bool is_scanning) {
@@ -101,6 +123,14 @@
                      : GetInhibitedPropertyResult::kFalse;
   }
 
+  base::Optional<CellularInhibitor::InhibitReason> GetInhibitReason() const {
+    return cellular_inhibitor_.GetInhibitReason();
+  }
+
+  size_t GetNumObserverEvents() const {
+    return observer_.num_observer_events();
+  }
+
  private:
   void GetPropertiesCallback(const std::string& device_path,
                              base::Optional<base::Value> properties) {
@@ -116,6 +146,7 @@
   base::test::SingleThreadTaskEnvironment task_environment_;
   NetworkStateTestHelper helper_;
   CellularInhibitor cellular_inhibitor_;
+  TestObserver observer_;
 
   std::unique_ptr<base::DictionaryValue> properties_;
 };
@@ -124,16 +155,33 @@
   AddCellularDevice();
 
   std::unique_ptr<CellularInhibitor::InhibitLock> inhibit_lock =
-      InhibitCellularScanningSync();
+      InhibitCellularScanningSync(
+          CellularInhibitor::InhibitReason::kInstallingProfile);
   // Ensure that a valid lock is returned and Inhibit property is set on
   // Cellular device.
   EXPECT_TRUE(inhibit_lock);
+  EXPECT_EQ(1u, GetNumObserverEvents());
+  EXPECT_EQ(CellularInhibitor::InhibitReason::kInstallingProfile,
+            GetInhibitReason());
   EXPECT_EQ(GetInhibitedPropertyResult::kTrue, GetInhibitedProperty());
 
   // Ensure that deleting lock uninhibits the Cellular device.
   inhibit_lock.reset();
   base::RunLoop().RunUntilIdle();
   EXPECT_EQ(GetInhibitedPropertyResult::kFalse, GetInhibitedProperty());
+
+  // At this point, we are still in the inhibited state, since scanning has not
+  // yet started and stopped.
+  EXPECT_EQ(1u, GetNumObserverEvents());
+  EXPECT_EQ(CellularInhibitor::InhibitReason::kInstallingProfile,
+            GetInhibitReason());
+
+  // Start and stop scanning; this should alert observers that the inhibit
+  // operation has completed.
+  SetScanning(true);
+  SetScanning(false);
+  EXPECT_EQ(2u, GetNumObserverEvents());
+  EXPECT_FALSE(GetInhibitReason().has_value());
 }
 
 TEST_F(CellularInhibitorTest, SuccessMultipleRequests) {
@@ -142,13 +190,18 @@
   // Make two inhibit requests in parallel.
   std::unique_ptr<CellularInhibitor::InhibitLock> inhibit_lock;
   std::unique_ptr<CellularInhibitor::InhibitLock> inhibit_lock2;
-  InhibitCellularScanning(inhibit_lock);
-  InhibitCellularScanning(inhibit_lock2);
+  InhibitCellularScanning(CellularInhibitor::InhibitReason::kInstallingProfile,
+                          inhibit_lock);
+  InhibitCellularScanning(CellularInhibitor::InhibitReason::kRemovingProfile,
+                          inhibit_lock2);
   base::RunLoop().RunUntilIdle();
 
   // Ensure that the only one inhibit lock has been granted.
   EXPECT_TRUE(inhibit_lock);
   EXPECT_FALSE(inhibit_lock2);
+  EXPECT_EQ(1u, GetNumObserverEvents());
+  EXPECT_EQ(CellularInhibitor::InhibitReason::kInstallingProfile,
+            GetInhibitReason());
   EXPECT_EQ(GetInhibitedPropertyResult::kTrue, GetInhibitedProperty());
 
   // Release the first lock; though this causes the Inhibited property to false,
@@ -156,6 +209,9 @@
   // flow has not yet started.
   inhibit_lock.reset();
   base::RunLoop().RunUntilIdle();
+  EXPECT_EQ(1u, GetNumObserverEvents());
+  EXPECT_EQ(CellularInhibitor::InhibitReason::kInstallingProfile,
+            GetInhibitReason());
   EXPECT_EQ(GetInhibitedPropertyResult::kFalse, GetInhibitedProperty());
   EXPECT_FALSE(inhibit_lock2);
 
@@ -163,6 +219,9 @@
   // inhibit flow within Shill. This still should not start the next inhibit
   // flow.
   SetScanning(true);
+  EXPECT_EQ(1u, GetNumObserverEvents());
+  EXPECT_EQ(CellularInhibitor::InhibitReason::kInstallingProfile,
+            GetInhibitReason());
   EXPECT_EQ(GetInhibitedPropertyResult::kFalse, GetInhibitedProperty());
   EXPECT_FALSE(inhibit_lock2);
 
@@ -170,11 +229,17 @@
   // set.
   SetScanning(false);
   EXPECT_TRUE(inhibit_lock2);
+  EXPECT_EQ(3u, GetNumObserverEvents());
+  EXPECT_EQ(CellularInhibitor::InhibitReason::kRemovingProfile,
+            GetInhibitReason());
   EXPECT_EQ(GetInhibitedPropertyResult::kTrue, GetInhibitedProperty());
 
   // Ensure that inhibited property is set to false when all locks are deleted.
   inhibit_lock2.reset();
   base::RunLoop().RunUntilIdle();
+  EXPECT_EQ(3u, GetNumObserverEvents());
+  EXPECT_EQ(CellularInhibitor::InhibitReason::kRemovingProfile,
+            GetInhibitReason());
   EXPECT_EQ(GetInhibitedPropertyResult::kFalse, GetInhibitedProperty());
 }
 
@@ -183,7 +248,8 @@
   // since the device cannot be inhibited if it does not exist.
 
   std::unique_ptr<CellularInhibitor::InhibitLock> inhibit_lock =
-      InhibitCellularScanningSync();
+      InhibitCellularScanningSync(
+          CellularInhibitor::InhibitReason::kInstallingProfile);
   EXPECT_EQ(GetInhibitedPropertyResult::kOperationFailed,
             GetInhibitedProperty());
   EXPECT_FALSE(inhibit_lock);
diff --git a/chromeos/network/cellular_metrics_logger.cc b/chromeos/network/cellular_metrics_logger.cc
index 0a24063..07a007f 100644
--- a/chromeos/network/cellular_metrics_logger.cc
+++ b/chromeos/network/cellular_metrics_logger.cc
@@ -99,10 +99,60 @@
   }
 }
 
+CellularMetricsLogger::SimType CellularMetricsLogger::GetSimType(
+    const NetworkState* network) {
+  return network->eid().empty() ? SimType::kPSim : SimType::kESim;
+}
+
+CellularMetricsLogger::ConnectResult
+CellularMetricsLogger::NetworkConnectionErrorToConnectResult(
+    const std::string& error_name) {
+  if (error_name == NetworkConnectionHandler::kErrorNotFound)
+    return CellularMetricsLogger::ConnectResult::kInvalidGuid;
+
+  if (error_name == NetworkConnectionHandler::kErrorConnected ||
+      error_name == NetworkConnectionHandler::kErrorConnecting) {
+    return CellularMetricsLogger::ConnectResult::kInvalidState;
+  }
+
+  if (error_name == NetworkConnectionHandler::kErrorConnectCanceled)
+    return CellularMetricsLogger::ConnectResult::kCanceled;
+
+  if (error_name == NetworkConnectionHandler::kErrorPassphraseRequired ||
+      error_name == NetworkConnectionHandler::kErrorBadPassphrase ||
+      error_name == NetworkConnectionHandler::kErrorCertificateRequired ||
+      error_name == NetworkConnectionHandler::kErrorConfigurationRequired ||
+      error_name == NetworkConnectionHandler::kErrorAuthenticationRequired ||
+      error_name == NetworkConnectionHandler::kErrorCertLoadTimeout ||
+      error_name == NetworkConnectionHandler::kErrorConfigureFailed) {
+    return CellularMetricsLogger::ConnectResult::kNotConfigured;
+  }
+
+  if (error_name == NetworkConnectionHandler::kErrorBlockedByPolicy)
+    return CellularMetricsLogger::ConnectResult::kBlocked;
+
+  return CellularMetricsLogger::ConnectResult::kUnknown;
+}
+
+void CellularMetricsLogger::LogCellularConnectionSuccessHistogram(
+    CellularMetricsLogger::ConnectResult start_connect_result,
+    CellularMetricsLogger::SimType sim_type) {
+  if (sim_type == SimType::kPSim) {
+    UMA_HISTOGRAM_ENUMERATION("Network.Cellular.PSim.ConnectionSuccess",
+                              start_connect_result);
+  } else {
+    UMA_HISTOGRAM_ENUMERATION("Network.Cellular.ESim.ConnectionSuccess",
+                              start_connect_result);
+  }
+}
+
 CellularMetricsLogger::ConnectionInfo::ConnectionInfo(
     const std::string& network_guid,
-    bool is_connected)
-    : network_guid(network_guid), is_connected(is_connected) {}
+    bool is_connected,
+    bool is_connecting)
+    : network_guid(network_guid),
+      is_connected(is_connected),
+      is_connecting(is_connecting) {}
 
 CellularMetricsLogger::ConnectionInfo::ConnectionInfo(
     const std::string& network_guid)
@@ -191,7 +241,8 @@
 
     guid_to_connection_info_map_.insert_or_assign(
         guid,
-        std::make_unique<ConnectionInfo>(guid, network->IsConnectedState()));
+        std::make_unique<ConnectionInfo>(guid, network->IsConnectedState(),
+                                         network->IsConnectingState()));
   }
 }
 
@@ -233,6 +284,10 @@
   }
 
   CheckForTimeToConnectedMetric(network);
+  // Check for connection failures triggered by shill changes, unlike in
+  // ConnectFailed() which is triggered by connection attempt failures at
+  // chrome layers.
+  CheckForShillConnectionFailureMetric(network);
   CheckForConnectionStateMetric(network);
 }
 
@@ -258,9 +313,16 @@
     return;
 
   if (network->IsConnectedState()) {
-    UMA_HISTOGRAM_MEDIUM_TIMES(
-        "Network.Cellular.Connection.TimeToConnected",
-        base::TimeTicks::Now() - *connection_info->last_connect_start_time);
+    base::TimeDelta time_to_connected =
+        base::TimeTicks::Now() - *connection_info->last_connect_start_time;
+
+    if (GetSimType(network) == SimType::kPSim) {
+      UMA_HISTOGRAM_MEDIUM_TIMES("Network.Cellular.PSim.TimeToConnected",
+                                 time_to_connected);
+    } else {
+      UMA_HISTOGRAM_MEDIUM_TIMES("Network.Cellular.ESim.TimeToConnected",
+                                 time_to_connected);
+    }
   }
 
   // This is hit when the network is no longer in connecting state,
@@ -269,6 +331,22 @@
   connection_info->last_connect_start_time.reset();
 }
 
+void CellularMetricsLogger::ConnectFailed(const std::string& service_path,
+                                          const std::string& error_name) {
+  const NetworkState* network =
+      network_state_handler_->GetNetworkState(service_path);
+  if (!network || network->type().empty() ||
+      !network->Matches(NetworkTypePattern::Cellular())) {
+    return;
+  }
+
+  // Check for connection failures at chrome layers, instead of connection
+  // failures triggered by shill which is tracked in
+  // CheckForShillConnectionFailureMetric().
+  LogCellularConnectionSuccessHistogram(
+      NetworkConnectionErrorToConnectResult(error_name), GetSimType(network));
+}
+
 void CellularMetricsLogger::DisconnectRequested(
     const std::string& service_path) {
   const NetworkState* network =
@@ -301,9 +379,31 @@
 }
 
 void CellularMetricsLogger::LogCellularDisconnectionsHistogram(
-    ConnectionState connection_state) {
-  UMA_HISTOGRAM_ENUMERATION("Network.Cellular.Connection.Disconnections",
-                            connection_state);
+    ConnectionState connection_state,
+    CellularMetricsLogger::SimType sim_type) {
+  if (sim_type == SimType::kPSim) {
+    UMA_HISTOGRAM_ENUMERATION("Network.Cellular.PSim.Disconnections",
+                              connection_state);
+  } else {
+    UMA_HISTOGRAM_ENUMERATION("Network.Cellular.ESim.Disconnections",
+                              connection_state);
+  }
+}
+
+void CellularMetricsLogger::CheckForShillConnectionFailureMetric(
+    const NetworkState* network) {
+  ConnectionInfo* connection_info =
+      GetConnectionInfoForCellularNetwork(network->guid());
+
+  // If the network connection state just failed.
+  if (!network->IsConnectingOrConnected() && connection_info->is_connecting) {
+    // Note: Currently all shill errors that result in a connection failure are
+    // mapped to CellularMetricsLogger::ConnectResult::kUnknown.
+    LogCellularConnectionSuccessHistogram(
+        CellularMetricsLogger::ConnectResult::kUnknown, GetSimType(network));
+  }
+
+  connection_info->is_connecting = network->IsConnectingState();
 }
 
 void CellularMetricsLogger::CheckForConnectionStateMetric(
@@ -318,7 +418,10 @@
   connection_info->is_connected = new_is_connected;
 
   if (new_is_connected) {
-    LogCellularDisconnectionsHistogram(ConnectionState::kConnected);
+    LogCellularConnectionSuccessHistogram(
+        CellularMetricsLogger::ConnectResult::kSuccess, GetSimType(network));
+    LogCellularDisconnectionsHistogram(ConnectionState::kConnected,
+                                       GetSimType(network));
     connection_info->last_disconnect_request_time.reset();
     return;
   }
@@ -342,7 +445,8 @@
       time_since_disconnect_requested < kDisconnectRequestTimeout) {
     return;
   }
-  LogCellularDisconnectionsHistogram(ConnectionState::kDisconnected);
+  LogCellularDisconnectionsHistogram(ConnectionState::kDisconnected,
+                                     GetSimType(network));
 }
 
 void CellularMetricsLogger::CheckForESimProfileStatusMetric() {
@@ -428,7 +532,7 @@
   size_t esim_profiles = 0;
 
   for (const auto* network : network_list) {
-    if (!network->eid().empty())
+    if (GetSimType(network) == SimType::kESim)
       esim_profiles++;
     else
       psim_networks++;
diff --git a/chromeos/network/cellular_metrics_logger.h b/chromeos/network/cellular_metrics_logger.h
index a1a49e3..5b1be1d 100644
--- a/chromeos/network/cellular_metrics_logger.h
+++ b/chromeos/network/cellular_metrics_logger.h
@@ -75,10 +75,13 @@
   void OnShuttingDown() override;
 
   // NetworkConnectionObserver::
+  void ConnectFailed(const std::string& service_path,
+                     const std::string& error_name) override;
   void DisconnectRequested(const std::string& service_path) override;
 
  private:
   friend class CellularMetricsLoggerTest;
+  FRIEND_TEST_ALL_PREFIXES(CellularMetricsLoggerTest, CellularConnectResult);
   FRIEND_TEST_ALL_PREFIXES(CellularMetricsLoggerTest,
                            CellularESimProfileStatusAtLoginTest);
   FRIEND_TEST_ALL_PREFIXES(CellularMetricsLoggerTest,
@@ -109,10 +112,13 @@
   // Stores connection related information for a cellular network.
   struct ConnectionInfo {
     ConnectionInfo(const std::string& network_guid);
-    ConnectionInfo(const std::string& network_guid, bool is_connected);
+    ConnectionInfo(const std::string& network_guid,
+                   bool is_connected,
+                   bool is_connecting);
     ~ConnectionInfo();
     const std::string network_guid;
     base::Optional<bool> is_connected;
+    base::Optional<bool> is_connecting;
     base::Optional<base::TimeTicks> last_disconnect_request_time;
     base::Optional<base::TimeTicks> last_connect_start_time;
   };
@@ -124,7 +130,7 @@
   };
 
   // Usage type for cellular network. These values are persisted to logs.
-  // Entries should not be renumbered and numberic values should never
+  // Entries should not be renumbered and numeric values should never
   // be reused.
   enum class CellularUsage {
     kConnectedAndOnlyNetwork = 0,
@@ -157,7 +163,7 @@
   };
 
   // Cellular connection state. These values are persisted to logs.
-  // Entries should not be renumbered and numberic values should
+  // Entries should not be renumbered and numeric values should
   // never be reused.
   enum class ConnectionState {
     kConnected = 0,
@@ -184,6 +190,22 @@
     kMaxValue = kErrorUnknown,
   };
 
+  // Result of connecting to a cellular network. These values are persisted to
+  // logs. Entries should not be renumbered and numeric values should never be
+  // reused.
+  enum class ConnectResult {
+    kUnknown = 0,
+    kInvalidGuid = 1,
+    kInvalidState = 2,
+    kCanceled = 3,
+    kNotConfigured = 4,
+    kBlocked = 5,
+    kSuccess = 6,
+    kMaxValue = kSuccess
+  };
+
+  SimType GetSimType(const NetworkState* network);
+
   // Convert shill error name string to SimPinOperationResult enum.
   static SimPinOperationResult GetSimPinOperationResultForShillError(
       const std::string& shill_error_name);
@@ -191,8 +213,18 @@
   // Convert shill activation state string to PSimActivationState enum
   PSimActivationState PSimActivationStateToEnum(const std::string& state);
 
+  // Converts a NetworkConnectionHandler string error to a ConnectResult enum.
+  ConnectResult NetworkConnectionErrorToConnectResult(
+      const std::string& error_name);
+
   // Helper method to save cellular disconnections histogram.
-  void LogCellularDisconnectionsHistogram(ConnectionState connection_state);
+  void LogCellularDisconnectionsHistogram(
+      ConnectionState connection_state,
+      CellularMetricsLogger::SimType sim_type);
+
+  void LogCellularConnectionSuccessHistogram(
+      ConnectResult start_connect_result,
+      CellularMetricsLogger::SimType sim_type);
 
   void OnInitializationTimeout();
 
@@ -211,6 +243,9 @@
   // if |is_esim_profile_status_logged_| is false.
   void CheckForESimProfileStatusMetric();
 
+  // Tracks errors from shill that result in an unsuccessful connection.
+  void CheckForShillConnectionFailureMetric(const NetworkState* network);
+
   // This checks the state of connected networks and logs
   // cellular network usage histogram. Histogram is only logged
   // when usage state changes.
diff --git a/chromeos/network/cellular_metrics_logger_unittest.cc b/chromeos/network/cellular_metrics_logger_unittest.cc
index ee4adb4f..aa4a6363 100644
--- a/chromeos/network/cellular_metrics_logger_unittest.cc
+++ b/chromeos/network/cellular_metrics_logger_unittest.cc
@@ -12,6 +12,7 @@
 #include "base/test/task_environment.h"
 #include "chromeos/login/login_state/login_state.h"
 #include "chromeos/network/cellular_esim_profile.h"
+#include "chromeos/network/network_connection_handler.h"
 #include "chromeos/network/network_state_handler.h"
 #include "chromeos/network/network_state_test_helper.h"
 #include "chromeos/network/test_cellular_esim_profile_handler.h"
@@ -46,10 +47,20 @@
 const char kESimStatusAtLoginHistogram[] =
     "Network.Cellular.ESim.StatusAtLogin";
 
-const char kTimeToConnectedHistogram[] =
-    "Network.Cellular.Connection.TimeToConnected";
-const char kDisconnectionsHistogram[] =
-    "Network.Cellular.Connection.Disconnections";
+const char kPSimTimeToConnectedHistogram[] =
+    "Network.Cellular.PSim.TimeToConnected";
+const char kESimTimeToConnectedHistogram[] =
+    "Network.Cellular.ESim.TimeToConnected";
+
+const char kPSimDisconnectionsHistogram[] =
+    "Network.Cellular.PSim.Disconnections";
+const char kESimDisconnectionsHistogram[] =
+    "Network.Cellular.ESim.Disconnections";
+
+const char kPSimConnectionSuccessHistogram[] =
+    "Network.Cellular.PSim.ConnectionSuccess";
+const char kESimConnectionSuccessHistogram[] =
+    "Network.Cellular.ESim.ConnectionSuccess";
 
 }  // namespace
 
@@ -466,6 +477,84 @@
   histogram_tester.ExpectTotalCount(kPSimStatusAtLoginHistogram, 3);
 }
 
+TEST_F(CellularMetricsLoggerTest, CellularConnectResult) {
+  InitCellular();
+  base::HistogramTester histogram_tester;
+  const base::Value kFailure(shill::kStateFailure);
+  const base::Value kAssocStateValue(shill::kStateAssociation);
+
+  // Set cellular networks to connecting state.
+  service_client_test()->SetServiceProperty(
+      kTestCellularServicePath, shill::kStateProperty, kAssocStateValue);
+  service_client_test()->SetServiceProperty(
+      kTestCellularServicePath2, shill::kStateProperty, kAssocStateValue);
+  base::RunLoop().RunUntilIdle();
+  histogram_tester.ExpectTotalCount(kESimConnectionSuccessHistogram, 0);
+  histogram_tester.ExpectTotalCount(kPSimConnectionSuccessHistogram, 0);
+
+  // Set cellular networks to failed state.
+  service_client_test()->SetServiceProperty(kTestCellularServicePath,
+                                            shill::kStateProperty,
+                                            base::Value(shill::kStateFailure));
+  service_client_test()->SetServiceProperty(kTestCellularServicePath2,
+                                            shill::kStateProperty,
+                                            base::Value(shill::kStateFailure));
+  base::RunLoop().RunUntilIdle();
+  histogram_tester.ExpectTotalCount(kESimConnectionSuccessHistogram, 1);
+  histogram_tester.ExpectTotalCount(kPSimConnectionSuccessHistogram, 1);
+
+  histogram_tester.ExpectBucketCount(
+      kESimConnectionSuccessHistogram,
+      CellularMetricsLogger::ConnectResult::kUnknown, 1);
+  histogram_tester.ExpectBucketCount(
+      kPSimConnectionSuccessHistogram,
+      CellularMetricsLogger::ConnectResult::kUnknown, 1);
+
+  // Set cellular networks to connecting state.
+  service_client_test()->SetServiceProperty(
+      kTestCellularServicePath, shill::kStateProperty, kAssocStateValue);
+  service_client_test()->SetServiceProperty(
+      kTestCellularServicePath2, shill::kStateProperty, kAssocStateValue);
+  base::RunLoop().RunUntilIdle();
+
+  // Set cellular networks to connected state.
+  service_client_test()->SetServiceProperty(kTestCellularServicePath,
+                                            shill::kStateProperty,
+                                            base::Value(shill::kStateOnline));
+  service_client_test()->SetServiceProperty(kTestCellularServicePath2,
+                                            shill::kStateProperty,
+                                            base::Value(shill::kStateOnline));
+  base::RunLoop().RunUntilIdle();
+
+  histogram_tester.ExpectTotalCount(kESimConnectionSuccessHistogram, 2);
+  histogram_tester.ExpectTotalCount(kPSimConnectionSuccessHistogram, 2);
+
+  histogram_tester.ExpectBucketCount(
+      kESimConnectionSuccessHistogram,
+      CellularMetricsLogger::ConnectResult::kSuccess, 1);
+  histogram_tester.ExpectBucketCount(
+      kPSimConnectionSuccessHistogram,
+      CellularMetricsLogger::ConnectResult::kSuccess, 1);
+
+  // Simulate chrome connect failure
+  cellular_metrics_logger()->ConnectFailed(
+      kTestCellularServicePath,
+      NetworkConnectionHandler::kErrorConnectCanceled);
+  cellular_metrics_logger()->ConnectFailed(
+      kTestCellularServicePath2,
+      NetworkConnectionHandler::kErrorConnectCanceled);
+
+  histogram_tester.ExpectTotalCount(kESimConnectionSuccessHistogram, 3);
+  histogram_tester.ExpectTotalCount(kPSimConnectionSuccessHistogram, 3);
+
+  histogram_tester.ExpectBucketCount(
+      kESimConnectionSuccessHistogram,
+      CellularMetricsLogger::ConnectResult::kCanceled, 1);
+  histogram_tester.ExpectBucketCount(
+      kPSimConnectionSuccessHistogram,
+      CellularMetricsLogger::ConnectResult::kCanceled, 1);
+}
+
 TEST_F(CellularMetricsLoggerTest, CellularTimeToConnectedTest) {
   constexpr base::TimeDelta kTestConnectionTime =
       base::TimeDelta::FromMilliseconds(321);
@@ -482,7 +571,7 @@
   service_client_test()->SetServiceProperty(
       kTestCellularServicePath, shill::kStateProperty, kOnlineStateValue);
   base::RunLoop().RunUntilIdle();
-  histogram_tester.ExpectTotalCount(kTimeToConnectedHistogram, 0);
+  histogram_tester.ExpectTotalCount(kPSimTimeToConnectedHistogram, 0);
 
   // Set cellular networks to activated state and connecting state.
   service_client_test()->SetServiceProperty(
@@ -502,7 +591,7 @@
   service_client_test()->SetServiceProperty(
       kTestCellularServicePath, shill::kStateProperty, kOnlineStateValue);
   base::RunLoop().RunUntilIdle();
-  histogram_tester.ExpectTimeBucketCount(kTimeToConnectedHistogram,
+  histogram_tester.ExpectTimeBucketCount(kPSimTimeToConnectedHistogram,
                                          kTestConnectionTime, 1);
 
   // Should log second network's connection time independently.
@@ -510,7 +599,7 @@
   service_client_test()->SetServiceProperty(
       kTestCellularServicePath2, shill::kStateProperty, kOnlineStateValue);
   base::RunLoop().RunUntilIdle();
-  histogram_tester.ExpectTimeBucketCount(kTimeToConnectedHistogram,
+  histogram_tester.ExpectTimeBucketCount(kESimTimeToConnectedHistogram,
                                          2 * kTestConnectionTime, 1);
 }
 
@@ -523,9 +612,14 @@
   // Should log connected state.
   service_client_test()->SetServiceProperty(
       kTestCellularServicePath, shill::kStateProperty, kOnlineStateValue);
+  service_client_test()->SetServiceProperty(
+      kTestCellularServicePath2, shill::kStateProperty, kOnlineStateValue);
   base::RunLoop().RunUntilIdle();
   histogram_tester.ExpectBucketCount(
-      kDisconnectionsHistogram,
+      kPSimDisconnectionsHistogram,
+      CellularMetricsLogger::ConnectionState::kConnected, 1);
+  histogram_tester.ExpectBucketCount(
+      kESimDisconnectionsHistogram,
       CellularMetricsLogger::ConnectionState::kConnected, 1);
 
   // Should not log user initiated disconnections.
@@ -536,33 +630,51 @@
       kTestCellularServicePath, shill::kStateProperty, kIdleStateValue);
   base::RunLoop().RunUntilIdle();
   histogram_tester.ExpectBucketCount(
-      kDisconnectionsHistogram,
+      kPSimDisconnectionsHistogram,
+      CellularMetricsLogger::ConnectionState::kDisconnected, 0);
+  histogram_tester.ExpectBucketCount(
+      kESimDisconnectionsHistogram,
       CellularMetricsLogger::ConnectionState::kDisconnected, 0);
 
   // Should log non user initiated disconnects.
   service_client_test()->SetServiceProperty(
       kTestCellularServicePath, shill::kStateProperty, kOnlineStateValue);
+  service_client_test()->SetServiceProperty(
+      kTestCellularServicePath2, shill::kStateProperty, kOnlineStateValue);
   base::RunLoop().RunUntilIdle();
   service_client_test()->SetServiceProperty(
       kTestCellularServicePath, shill::kStateProperty, kIdleStateValue);
+  service_client_test()->SetServiceProperty(
+      kTestCellularServicePath2, shill::kStateProperty, kIdleStateValue);
   base::RunLoop().RunUntilIdle();
   histogram_tester.ExpectBucketCount(
-      kDisconnectionsHistogram,
+      kPSimDisconnectionsHistogram,
+      CellularMetricsLogger::ConnectionState::kDisconnected, 1);
+  histogram_tester.ExpectBucketCount(
+      kESimDisconnectionsHistogram,
       CellularMetricsLogger::ConnectionState::kDisconnected, 1);
 
   // Should log non user initiated disconnects when a previous
   // disconnect request timed out.
   service_client_test()->SetServiceProperty(
       kTestCellularServicePath, shill::kStateProperty, kOnlineStateValue);
+  service_client_test()->SetServiceProperty(
+      kTestCellularServicePath2, shill::kStateProperty, kOnlineStateValue);
   base::RunLoop().RunUntilIdle();
   cellular_metrics_logger()->DisconnectRequested(kTestCellularServicePath);
+  cellular_metrics_logger()->DisconnectRequested(kTestCellularServicePath2);
   task_environment_.FastForwardBy(
       CellularMetricsLogger::kDisconnectRequestTimeout * 2);
   service_client_test()->SetServiceProperty(
       kTestCellularServicePath, shill::kStateProperty, kIdleStateValue);
+  service_client_test()->SetServiceProperty(
+      kTestCellularServicePath2, shill::kStateProperty, kIdleStateValue);
   base::RunLoop().RunUntilIdle();
   histogram_tester.ExpectBucketCount(
-      kDisconnectionsHistogram,
+      kPSimDisconnectionsHistogram,
+      CellularMetricsLogger::ConnectionState::kDisconnected, 2);
+  histogram_tester.ExpectBucketCount(
+      kESimDisconnectionsHistogram,
       CellularMetricsLogger::ConnectionState::kDisconnected, 2);
 }
 
diff --git a/chromeos/network/test_cellular_inhibitor.h b/chromeos/network/test_cellular_inhibitor.h
index bcd91f14..74c52e3 100644
--- a/chromeos/network/test_cellular_inhibitor.h
+++ b/chromeos/network/test_cellular_inhibitor.h
@@ -16,6 +16,8 @@
   TestCellularInhibitor();
   ~TestCellularInhibitor() override;
 
+  using CellularInhibitor::NotifyInhibitStateChanged;
+
  private:
   // CellularESimProfileHandler:
   bool HasScanningStarted() override;
diff --git a/chromeos/profiles/atom.afdo.newest.txt b/chromeos/profiles/atom.afdo.newest.txt
index 3297fe5..4cb000d4 100644
--- a/chromeos/profiles/atom.afdo.newest.txt
+++ b/chromeos/profiles/atom.afdo.newest.txt
@@ -1 +1 @@
-chromeos-chrome-amd64-atom-91-4430.19-1615803233-benchmark-91.0.4448.0-r1-redacted.afdo.xz
+chromeos-chrome-amd64-atom-91-4430.19-1615803233-benchmark-91.0.4449.0-r1-redacted.afdo.xz
diff --git a/chromeos/profiles/bigcore.afdo.newest.txt b/chromeos/profiles/bigcore.afdo.newest.txt
index c48f52a..55afb4f 100644
--- a/chromeos/profiles/bigcore.afdo.newest.txt
+++ b/chromeos/profiles/bigcore.afdo.newest.txt
@@ -1 +1 @@
-chromeos-chrome-amd64-bigcore-91-4430.19-1615806685-benchmark-91.0.4448.0-r1-redacted.afdo.xz
+chromeos-chrome-amd64-bigcore-91-4430.19-1615806685-benchmark-91.0.4449.0-r1-redacted.afdo.xz
diff --git a/chromeos/services/cellular_setup/esim_profile.cc b/chromeos/services/cellular_setup/esim_profile.cc
index 4ba2f1f9..158f974e 100644
--- a/chromeos/services/cellular_setup/esim_profile.cc
+++ b/chromeos/services/cellular_setup/esim_profile.cc
@@ -108,9 +108,11 @@
   EnsureProfileExistsOnEuiccCallback perform_install_profile_callback =
       base::BindOnce(&ESimProfile::PerformInstallProfile,
                      weak_ptr_factory_.GetWeakPtr(), confirmation_code);
-  esim_manager_->cellular_inhibitor()->InhibitCellularScanning(base::BindOnce(
-      &ESimProfile::EnsureProfileExistsOnEuicc, weak_ptr_factory_.GetWeakPtr(),
-      std::move(perform_install_profile_callback)));
+  esim_manager_->cellular_inhibitor()->InhibitCellularScanning(
+      CellularInhibitor::InhibitReason::kInstallingProfile,
+      base::BindOnce(&ESimProfile::EnsureProfileExistsOnEuicc,
+                     weak_ptr_factory_.GetWeakPtr(),
+                     std::move(perform_install_profile_callback)));
 }
 
 void ESimProfile::UninstallProfile(UninstallProfileCallback callback) {
@@ -198,9 +200,11 @@
   EnsureProfileExistsOnEuiccCallback perform_set_profile_nickname_callback =
       base::BindOnce(&ESimProfile::PerformSetProfileNickname,
                      weak_ptr_factory_.GetWeakPtr(), nickname);
-  esim_manager_->cellular_inhibitor()->InhibitCellularScanning(base::BindOnce(
-      &ESimProfile::EnsureProfileExistsOnEuicc, weak_ptr_factory_.GetWeakPtr(),
-      std::move(perform_set_profile_nickname_callback)));
+  esim_manager_->cellular_inhibitor()->InhibitCellularScanning(
+      CellularInhibitor::InhibitReason::kRenamingProfile,
+      base::BindOnce(&ESimProfile::EnsureProfileExistsOnEuicc,
+                     weak_ptr_factory_.GetWeakPtr(),
+                     std::move(perform_set_profile_nickname_callback)));
 }
 
 void ESimProfile::UpdateProperties(
diff --git a/chromeos/services/cellular_setup/euicc.cc b/chromeos/services/cellular_setup/euicc.cc
index 2758d65..4473c3de 100644
--- a/chromeos/services/cellular_setup/euicc.cc
+++ b/chromeos/services/cellular_setup/euicc.cc
@@ -111,15 +111,18 @@
   // currently being installed to prevent multiple attempts for the same
   // activation code.
   NET_LOG(USER) << "Attempting installation with code " << activation_code;
-  esim_manager_->cellular_inhibitor()->InhibitCellularScanning(base::BindOnce(
-      &Euicc::PerformInstallProfileFromActivationCode,
-      weak_ptr_factory_.GetWeakPtr(), activation_code, confirmation_code,
-      CreateTimedInstallProfileCallback(std::move(callback))));
+  esim_manager_->cellular_inhibitor()->InhibitCellularScanning(
+      CellularInhibitor::InhibitReason::kInstallingProfile,
+      base::BindOnce(&Euicc::PerformInstallProfileFromActivationCode,
+                     weak_ptr_factory_.GetWeakPtr(), activation_code,
+                     confirmation_code,
+                     CreateTimedInstallProfileCallback(std::move(callback))));
 }
 
 void Euicc::RequestPendingProfiles(RequestPendingProfilesCallback callback) {
   NET_LOG(EVENT) << "Requesting Pending profiles";
   esim_manager_->cellular_inhibitor()->InhibitCellularScanning(
+      CellularInhibitor::InhibitReason::kRefreshingProfileList,
       base::BindOnce(&Euicc::PerformRequestPendingProfiles,
                      weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
 }
diff --git a/chromeos/services/libassistant/conversation_controller.cc b/chromeos/services/libassistant/conversation_controller.cc
index 7cbcbefe8..8eaf23a 100644
--- a/chromeos/services/libassistant/conversation_controller.cc
+++ b/chromeos/services/libassistant/conversation_controller.cc
@@ -15,7 +15,6 @@
 #include "chromeos/services/assistant/public/cpp/features.h"
 #include "chromeos/services/assistant/public/cpp/migration/libassistant_v1_api.h"
 #include "chromeos/services/libassistant/public/mojom/conversation_controller.mojom.h"
-#include "chromeos/services/libassistant/service_controller.h"
 #include "chromeos/strings/grit/chromeos_strings.h"
 #include "libassistant/shared/internal_api/assistant_manager_delegate.h"
 #include "libassistant/shared/internal_api/assistant_manager_internal.h"
@@ -207,20 +206,14 @@
 // ConversationController
 ////////////////////////////////////////////////////////////////////////////////
 
-ConversationController::ConversationController(
-    ServiceController* service_controller)
+ConversationController::ConversationController()
     : receiver_(this),
-      service_controller_(service_controller),
       assistant_manager_delegate_(
           std::make_unique<AssistantManagerDelegateImpl>(this)),
       action_module_(std::make_unique<assistant::action::CrosActionModule>(
           assistant::features::IsAppSupportEnabled(),
           assistant::features::IsWaitSchedulingEnabled())),
       mojom_task_runner_(base::SequencedTaskRunnerHandle::Get()) {
-  // TODO(jeroendh): We should not pass in the |ServiceController| into this
-  // constructor. Instead, we should access the |AssistantManager| through
-  // the methods offered by |AssistantManagerObserver|.
-  DCHECK(service_controller_);
   action_module_->AddObserver(this);
 }
 
@@ -265,16 +258,27 @@
   v1_api->SetActionModule(action_module_.get());
 }
 
+void ConversationController::OnAssistantManagerRunning(
+    assistant_client::AssistantManager* assistant_manager,
+    assistant_client::AssistantManagerInternal* assistant_manager_internal) {
+  // Only when Libassistant is running we can start sending queries.
+  assistant_manager_internal_ = assistant_manager_internal;
+  requests_are_allowed_ = true;
+}
+
+void ConversationController::OnDestroyingAssistantManager(
+    assistant_client::AssistantManager* assistant_manager,
+    assistant_client::AssistantManagerInternal* assistant_manager_internal) {
+  assistant_manager_internal_ = nullptr;
+}
+
 void ConversationController::SendTextQuery(const std::string& query,
                                            AssistantQuerySource source,
                                            bool allow_tts) {
-  // DCHECKs if this function gets invoked after the service has been fully
-  // started.
-  // TODO(meilinw): only check for the |ServiceState::kRunning| state instead
-  // after it has been wired up.
-  DCHECK(service_controller_->IsStarted())
-      << "Libassistant service is not ready to handle queries.";
-  DCHECK(assistant_manager_internal());
+  DCHECK(requests_are_allowed_)
+      << "Should not receive requests before Libassistant is running";
+  if (!assistant_manager_internal_)
+    return;
 
   // Configs |VoicelessOptions|.
   assistant_client::VoicelessOptions options;
@@ -291,12 +295,17 @@
   // Builds text interaction.
   std::string interaction = assistant::CreateTextQueryInteraction(query);
 
-  assistant_manager_internal()->SendVoicelessInteraction(
+  assistant_manager_internal_->SendVoicelessInteraction(
       interaction, /*description=*/"text_query", options, [](auto) {});
 }
 
 void ConversationController::StartEditReminderInteraction(
     const std::string& client_id) {
+  DCHECK(requests_are_allowed_)
+      << "Should not receive requests before Libassistant is running";
+  if (!assistant_manager_internal_)
+    return;
+
   SendVoicelessInteraction(assistant::CreateEditReminderInteraction(client_id),
                            /*description=*/std::string(),
                            /*is_user_initiated=*/true);
@@ -305,6 +314,11 @@
 void ConversationController::RetrieveNotification(
     AssistantNotification notification,
     int32_t action_index) {
+  DCHECK(requests_are_allowed_)
+      << "Should not receive requests before Libassistant is running";
+  if (!assistant_manager_internal_)
+    return;
+
   const std::string request_interaction =
       assistant::SerializeNotificationRequestInteraction(
           notification.server_id, notification.consistency_token,
@@ -317,9 +331,9 @@
 
 void ConversationController::DismissNotification(
     AssistantNotification notification) {
-  // |assistant_manager_internal()| may not exist if we are dismissing
-  // notifications as part of a shutdown sequence.
-  if (!assistant_manager_internal())
+  DCHECK(requests_are_allowed_)
+      << "Should not receive requests before Libassistant is running";
+  if (!assistant_manager_internal_)
     return;
 
   const std::string dismissed_interaction =
@@ -330,13 +344,18 @@
   assistant_client::VoicelessOptions options;
   options.obfuscated_gaia_id = notification.obfuscated_gaia_id;
 
-  assistant_manager_internal()->SendVoicelessInteraction(
+  assistant_manager_internal_->SendVoicelessInteraction(
       dismissed_interaction, /*description=*/"DismissNotification", options,
       [](auto) {});
 }
 
 void ConversationController::SendAssistantFeedback(
     const AssistantFeedback& feedback) {
+  DCHECK(requests_are_allowed_)
+      << "Should not receive requests before Libassistant is running";
+  if (!assistant_manager_internal_)
+    return;
+
   std::string raw_image_data(feedback.screenshot_png.begin(),
                              feedback.screenshot_png.end());
   const std::string interaction = assistant::CreateSendFeedbackInteraction(
@@ -419,7 +438,7 @@
   assistant_client::VoicelessOptions options;
   options.obfuscated_gaia_id = interaction.user_id;
 
-  assistant_manager_internal()->SendVoicelessInteraction(
+  assistant_manager_internal_->SendVoicelessInteraction(
       interaction_proto, /*description=*/"open_provider_response", options,
       [](auto) {});
 }
@@ -466,14 +485,9 @@
   assistant_client::VoicelessOptions voiceless_options;
   voiceless_options.is_user_initiated = is_user_initiated;
 
-  assistant_manager_internal()->SendVoicelessInteraction(
+  assistant_manager_internal_->SendVoicelessInteraction(
       interaction, description, voiceless_options, [](auto) {});
 }
 
-assistant_client::AssistantManagerInternal*
-ConversationController::assistant_manager_internal() {
-  return service_controller_->assistant_manager_internal();
-}
-
 }  // namespace libassistant
 }  // namespace chromeos
diff --git a/chromeos/services/libassistant/conversation_controller.h b/chromeos/services/libassistant/conversation_controller.h
index 2c716ed..dad8d04 100644
--- a/chromeos/services/libassistant/conversation_controller.h
+++ b/chromeos/services/libassistant/conversation_controller.h
@@ -29,8 +29,6 @@
 
 namespace libassistant {
 
-class ServiceController;
-
 class COMPONENT_EXPORT(LIBASSISTANT_SERVICE) ConversationController
     : public mojom::ConversationController,
       public AssistantManagerObserver,
@@ -40,7 +38,7 @@
   using AssistantQuerySource = ::chromeos::assistant::AssistantQuerySource;
   using AssistantFeedback = ::chromeos::assistant::AssistantFeedback;
 
-  explicit ConversationController(ServiceController* service_controller);
+  ConversationController();
   ConversationController(const ConversationController&) = delete;
   ConversationController& operator=(const ConversationController&) = delete;
   ~ConversationController() override;
@@ -60,6 +58,14 @@
       assistant_client::AssistantManager* assistant_manager,
       assistant_client::AssistantManagerInternal* assistant_manager_internal)
       override;
+  void OnAssistantManagerRunning(
+      assistant_client::AssistantManager* assistant_manager,
+      assistant_client::AssistantManagerInternal* assistant_manager_internal)
+      override;
+  void OnDestroyingAssistantManager(
+      assistant_client::AssistantManager* assistant_manager,
+      assistant_client::AssistantManagerInternal* assistant_manager_internal)
+      override;
 
   // mojom::ConversationController implementation:
   void SendTextQuery(const std::string& query,
@@ -99,16 +105,17 @@
                                 const std::string& description,
                                 bool is_user_initiated);
 
-  assistant_client::AssistantManagerInternal* assistant_manager_internal();
-
   mojo::Receiver<mojom::ConversationController> receiver_;
   mojo::RemoteSet<mojom::ConversationObserver> observers_;
   mojo::RemoteSet<mojom::AuthenticationStateObserver>
       authentication_state_observers_;
   mojo::Remote<mojom::NotificationDelegate> notification_delegate_;
 
-  // Owned by |LibassistantService|.
-  ServiceController* const service_controller_;
+  assistant_client::AssistantManagerInternal* assistant_manager_internal_ =
+      nullptr;
+  // False until libassistant is running for the first time.
+  // Any request that comes in before that is an error and will be DCHECK'ed.
+  bool requests_are_allowed_ = false;
 
   std::unique_ptr<AssistantManagerDelegateImpl> assistant_manager_delegate_;
   std::unique_ptr<assistant::action::CrosActionModule> action_module_;
diff --git a/chromeos/services/libassistant/conversation_observer_unittest.cc b/chromeos/services/libassistant/conversation_observer_unittest.cc
index 24f18c8..a5ce2e23 100644
--- a/chromeos/services/libassistant/conversation_observer_unittest.cc
+++ b/chromeos/services/libassistant/conversation_observer_unittest.cc
@@ -8,6 +8,7 @@
 #include "chromeos/assistant/internal/action/cros_action_module.h"
 #include "chromeos/assistant/internal/test_support/fake_assistant_manager.h"
 #include "chromeos/assistant/internal/test_support/fake_assistant_manager_internal.h"
+#include "chromeos/services/libassistant/conversation_controller.h"
 #include "chromeos/services/libassistant/libassistant_service.h"
 #include "chromeos/services/libassistant/public/cpp/android_app_info.h"
 #include "chromeos/services/libassistant/public/mojom/conversation_observer.mojom.h"
@@ -113,12 +114,14 @@
 
 }  // namespace
 
-class ConversationObserverTest : public ::testing::Test {
+class AssistantConversationObserverTest : public ::testing::Test {
  public:
-  ConversationObserverTest() = default;
-  ConversationObserverTest(const ConversationObserverTest&) = delete;
-  ConversationObserverTest& operator=(const ConversationObserverTest&) = delete;
-  ~ConversationObserverTest() override = default;
+  AssistantConversationObserverTest() = default;
+  AssistantConversationObserverTest(const AssistantConversationObserverTest&) =
+      delete;
+  AssistantConversationObserverTest& operator=(
+      const AssistantConversationObserverTest&) = delete;
+  ~AssistantConversationObserverTest() override = default;
 
   void SetUp() override {
     service_tester_.conversation_controller().AddRemoteObserver(
@@ -126,6 +129,10 @@
 
     service_tester_.Start();
 
+    controller().OnAssistantManagerRunning(
+        &service_tester_.assistant_manager(),
+        &service_tester_.assistant_manager_internal());
+
     action_module_helper_ = std::make_unique<CrosActionModuleHelper>(
         static_cast<assistant::action::CrosActionModule*>(
             service_tester_.assistant_manager_internal().action_module()));
@@ -141,6 +148,10 @@
 
   ConversationObserverMock& observer_mock() { return observer_mock_; }
 
+  ConversationController& controller() {
+    return service_tester_.service().conversation_controller();
+  }
+
  private:
   base::test::SingleThreadTaskEnvironment environment_;
   ::testing::StrictMock<ConversationObserverMock> observer_mock_;
@@ -148,7 +159,7 @@
   std::unique_ptr<CrosActionModuleHelper> action_module_helper_;
 };
 
-TEST_F(ConversationObserverTest,
+TEST_F(AssistantConversationObserverTest,
        ShouldReceiveOnTurnFinishedEventWhenFinishedNormally) {
   EXPECT_CALL(
       observer_mock(),
@@ -160,7 +171,7 @@
   observer_mock().FlushForTesting();
 }
 
-TEST_F(ConversationObserverTest,
+TEST_F(AssistantConversationObserverTest,
        ShouldReceiveOnTurnFinishedEventWhenBeingInterrupted) {
   EXPECT_CALL(
       observer_mock(),
@@ -172,7 +183,7 @@
   observer_mock().FlushForTesting();
 }
 
-TEST_F(ConversationObserverTest,
+TEST_F(AssistantConversationObserverTest,
        ShouldReceiveOnTtsStartedEventWhenFinishingNormally) {
   EXPECT_CALL(observer_mock(), OnTtsStarted(/*due_to_error=*/false));
 
@@ -180,7 +191,7 @@
   observer_mock().FlushForTesting();
 }
 
-TEST_F(ConversationObserverTest,
+TEST_F(AssistantConversationObserverTest,
        ShouldReceiveOnTtsStartedEventWhenErrorOccured) {
   EXPECT_CALL(observer_mock(), OnTtsStarted(/*due_to_error=*/true));
 
@@ -188,7 +199,7 @@
   observer_mock().FlushForTesting();
 }
 
-TEST_F(ConversationObserverTest, ShouldReceiveOnHtmlResponse) {
+TEST_F(AssistantConversationObserverTest, ShouldReceiveOnHtmlResponse) {
   const std::string fake_html = "<h1>Hello world!</h1>";
   EXPECT_CALL(observer_mock(), OnHtmlResponse(fake_html, ""));
 
@@ -197,7 +208,7 @@
   observer_mock().FlushForTesting();
 }
 
-TEST_F(ConversationObserverTest, ShouldReceiveOnTextResponse) {
+TEST_F(AssistantConversationObserverTest, ShouldReceiveOnTextResponse) {
   const std::string fake_text = "I'm a text response";
   EXPECT_CALL(observer_mock(), OnTextResponse(fake_text));
 
@@ -205,7 +216,7 @@
   observer_mock().FlushForTesting();
 }
 
-TEST_F(ConversationObserverTest, ShouldReceiveOnSuggestionsResponse) {
+TEST_F(AssistantConversationObserverTest, ShouldReceiveOnSuggestionsResponse) {
   const std::string fake_text = "text";
   const std::string fake_icon_url = "https://icon-url/";
   const std::string fake_action_url = "https://action-url/";
@@ -224,7 +235,7 @@
   observer_mock().FlushForTesting();
 }
 
-TEST_F(ConversationObserverTest, ShouldReceiveOnOpenUrlResponse) {
+TEST_F(AssistantConversationObserverTest, ShouldReceiveOnOpenUrlResponse) {
   const std::string fake_url = "https://fake-url/";
   EXPECT_CALL(observer_mock(),
               OnOpenUrlResponse(GURL(fake_url), /*in_background=*/false));
@@ -233,7 +244,7 @@
   observer_mock().FlushForTesting();
 }
 
-TEST_F(ConversationObserverTest, ShouldReceiveOnOpenAppResponse) {
+TEST_F(AssistantConversationObserverTest, ShouldReceiveOnOpenAppResponse) {
   assistant::AndroidAppInfo fake_app_info;
   fake_app_info.package_name = "fake package name";
   fake_app_info.version = 123;
@@ -255,7 +266,7 @@
   observer_mock().FlushForTesting();
 }
 
-TEST_F(ConversationObserverTest, ShouldReceiveOnWaitStarted) {
+TEST_F(AssistantConversationObserverTest, ShouldReceiveOnWaitStarted) {
   EXPECT_CALL(observer_mock(), OnWaitStarted());
 
   action_module_helper().ScheduleWait();
diff --git a/chromeos/services/libassistant/libassistant_service.cc b/chromeos/services/libassistant/libassistant_service.cc
index 0a3a466f..b589510b 100644
--- a/chromeos/services/libassistant/libassistant_service.cc
+++ b/chromeos/services/libassistant/libassistant_service.cc
@@ -62,7 +62,7 @@
       libassistant_factory_(
           FactoryOrDefault(std::move(factory), &platform_api_)),
       service_controller_(libassistant_factory_.get()),
-      conversation_controller_(&service_controller_),
+      conversation_controller_(),
       conversation_state_listener_(
           &speech_recognition_observers_,
           conversation_controller_.conversation_observers(),
diff --git a/chromeos/services/libassistant/libassistant_service.h b/chromeos/services/libassistant/libassistant_service.h
index a4ba932..ad0e2d9 100644
--- a/chromeos/services/libassistant/libassistant_service.h
+++ b/chromeos/services/libassistant/libassistant_service.h
@@ -66,6 +66,10 @@
           chromeos::libassistant::mojom::AuthenticationStateObserver> observer)
       override;
 
+  ConversationController& conversation_controller() {
+    return conversation_controller_;
+  }
+
  private:
   ServiceController& service_controller() { return service_controller_; }
 
diff --git a/chromeos/services/network_config/BUILD.gn b/chromeos/services/network_config/BUILD.gn
index 1f73bbf9..cbff7ebd 100644
--- a/chromeos/services/network_config/BUILD.gn
+++ b/chromeos/services/network_config/BUILD.gn
@@ -11,6 +11,7 @@
   ]
 
   deps = [
+    "//ash/constants",
     "//base",
     "//chromeos/components/sync_wifi:network_eligibility_checker",
     "//chromeos/dbus/hermes",
diff --git a/chromeos/services/network_config/cros_network_config.cc b/chromeos/services/network_config/cros_network_config.cc
index a1dc435..38d6b2097 100644
--- a/chromeos/services/network_config/cros_network_config.cc
+++ b/chromeos/services/network_config/cros_network_config.cc
@@ -6,6 +6,7 @@
 
 #include <vector>
 
+#include "ash/constants/ash_features.h"
 #include "base/containers/flat_map.h"
 #include "base/optional.h"
 #include "base/strings/string_util.h"
@@ -512,7 +513,15 @@
   result->mac_address =
       network_util::FormattedMacAddress(device->mac_address());
   result->scanning = device->scanning();
-  result->device_state = technology_state;
+
+  // Before multi-SIM support was in place, the Cellular device would always be
+  // disabled anytime that a SIM was absent. Special-case this logic to ensure
+  // that users with the flag off will still see a disabled UI in this case.
+  if (device->IsSimAbsent() && !features::IsCellularActivationUiEnabled())
+    result->device_state = mojom::DeviceStateType::kDisabled;
+  else
+    result->device_state = technology_state;
+
   result->managed_network_available =
       !device->available_managed_network_path().empty();
   result->sim_absent = device->IsSimAbsent();
diff --git a/chromeos/services/network_config/cros_network_config_unittest.cc b/chromeos/services/network_config/cros_network_config_unittest.cc
index 24afa9ad..47008b9 100644
--- a/chromeos/services/network_config/cros_network_config_unittest.cc
+++ b/chromeos/services/network_config/cros_network_config_unittest.cc
@@ -790,6 +790,20 @@
   EXPECT_EQ(kTestProfileName, network->name);
 }
 
+TEST_F(CrosNetworkConfigTest, SimAbsentMeansCellularIsDisabled) {
+  mojom::DeviceStatePropertiesPtr cellular =
+      GetDeviceStateFromList(mojom::NetworkType::kCellular);
+  EXPECT_EQ(mojom::DeviceStateType::kEnabled, cellular->device_state);
+
+  helper().device_test()->SetDeviceProperty(
+      kCellularDevicePath, shill::kSIMPresentProperty, base::Value(false),
+      /*notify_changed=*/true);
+  base::RunLoop().RunUntilIdle();
+
+  cellular = GetDeviceStateFromList(mojom::NetworkType::kCellular);
+  EXPECT_EQ(mojom::DeviceStateType::kDisabled, cellular->device_state);
+}
+
 TEST_F(CrosNetworkConfigTest, GetDeviceStateList) {
   std::vector<mojom::DeviceStatePropertiesPtr> devices = GetDeviceStateList();
   ASSERT_EQ(4u, devices.size());
diff --git a/components/BUILD.gn b/components/BUILD.gn
index 0d385ac..249d57c 100644
--- a/components/BUILD.gn
+++ b/components/BUILD.gn
@@ -626,6 +626,7 @@
       "dom_distiller/content/browser/test/test_util.cc",
       "dom_distiller/content/browser/test/test_util.h",
       "metrics/content/subprocess_metrics_provider_browsertest.cc",
+      "optimization_guide/content/renderer/page_text_agent_browsertest.cc",
       "paint_preview/renderer/paint_preview_recorder_browsertest.cc",
       "security_state/content/content_utils_browsertest.cc",
       "ukm/content/source_url_recorder_browsertest.cc",
@@ -660,6 +661,7 @@
       "//components/dom_distiller/core:test_support",
       "//components/error_page/content/browser:browser_tests",
       "//components/metrics:content",
+      "//components/optimization_guide/content/renderer",
       "//components/paint_preview/renderer",
       "//components/password_manager/content/browser",
       "//components/performance_manager:browser_tests",
diff --git a/components/browser_ui/strings/android/browser_ui_strings.grd b/components/browser_ui/strings/android/browser_ui_strings.grd
index ac88a5de..34343ac 100644
--- a/components/browser_ui/strings/android/browser_ui_strings.grd
+++ b/components/browser_ui/strings/android/browser_ui_strings.grd
@@ -398,6 +398,9 @@
       <message name="IDS_DOWNLOAD_NOTIFICATION_CANCEL_BUTTON" desc="Text on the button that cancels a download.">
         Cancel
       </message>
+      <message name="IDS_DOWNLOAD_NOTIFICATION_COMPLETED" desc="Download notification to be displayed when a download completes.">
+        Download complete
+      </message>
       <message name="IDS_DOWNLOAD_NOTIFICATION_COMPLETED_WITH_SIZE" desc="Download notification to be displayed when a download completes, includes the size of the download as well after a separator character.">
         Download complete <ph name="SEPARATOR">•</ph> <ph name="BYTES_DOWNLOADED">%1$s<ex>4.3 MB</ex></ph>
       </message>
diff --git a/components/browser_ui/util/DEPS b/components/browser_ui/util/DEPS
index 735adf49..606d278a 100644
--- a/components/browser_ui/util/DEPS
+++ b/components/browser_ui/util/DEPS
@@ -1,3 +1,4 @@
 include_rules = [
   "+components/embedder_support/android",
+  "+components/url_formatter/android",
 ]
diff --git a/components/browser_ui/util/android/BUILD.gn b/components/browser_ui/util/android/BUILD.gn
index aeb9c62..bd140b4 100644
--- a/components/browser_ui/util/android/BUILD.gn
+++ b/components/browser_ui/util/android/BUILD.gn
@@ -23,9 +23,11 @@
     "//base:base_java",
     "//cc:cc_java",
     "//components/embedder_support/android:util_java",
+    "//components/url_formatter/android:url_formatter_java",
     "//third_party/androidx:androidx_annotation_annotation_java",
     "//third_party/androidx:androidx_collection_collection_java",
     "//third_party/androidx:androidx_core_core_java",
+    "//url:gurl_java",
   ]
   resources_package = "org.chromium.components.browser_ui.util"
 }
diff --git a/components/browser_ui/util/android/java/src/org/chromium/components/browser_ui/util/DownloadUtils.java b/components/browser_ui/util/android/java/src/org/chromium/components/browser_ui/util/DownloadUtils.java
index af7d2ee0..f978f0b 100644
--- a/components/browser_ui/util/android/java/src/org/chromium/components/browser_ui/util/DownloadUtils.java
+++ b/components/browser_ui/util/android/java/src/org/chromium/components/browser_ui/util/DownloadUtils.java
@@ -15,6 +15,10 @@
 import org.chromium.base.ContextUtils;
 import org.chromium.base.ThreadUtils;
 import org.chromium.components.embedder_support.util.UrlConstants;
+import org.chromium.components.embedder_support.util.UrlUtilities;
+import org.chromium.components.url_formatter.SchemeDisplay;
+import org.chromium.components.url_formatter.UrlFormatter;
+import org.chromium.url.GURL;
 
 /**
  * A class containing some utility static methods.
@@ -23,6 +27,10 @@
     private static final int[] BYTES_STRINGS = {
             R.string.download_ui_kb, R.string.download_ui_mb, R.string.download_ui_gb};
 
+    // Limit the origin length so that the eTLD+1 cannot be hidden. If the origin exceeds this
+    // length the eTLD+1 is extracted and shown.
+    private static final int MAX_ORIGIN_LENGTH = 40;
+
     /**
      * Format the number of bytes into KB, MB, or GB and return the corresponding generated string.
      * @param context Context to use.
@@ -104,4 +112,22 @@
         }
         return originalUri;
     }
+
+    /**
+     * Adjusts a URL for display to the user in the subtext of an Android notification.
+     *
+     * @param url The full URL.
+     * @param return The URL that should be displayed, or null if the input was invalid.
+     */
+    public static String formatUrlForDisplayInNotification(GURL url) {
+        if (GURL.isEmptyOrInvalid(url)) return null;
+
+        String formattedUrl =
+                UrlFormatter.formatUrlForSecurityDisplay(url, SchemeDisplay.OMIT_HTTP_AND_HTTPS);
+        if (formattedUrl.length() <= MAX_ORIGIN_LENGTH) return formattedUrl;
+
+        // The origin is too long. Strip down to eTLD+1.
+        return UrlUtilities.getDomainAndRegistry(
+                url.getSpec(), false /* includePrivateRegistries */);
+    }
 }
diff --git a/components/crash/android/java/src/org/chromium/components/crash/browser/ProcessExitReasonFromSystem.java b/components/crash/android/java/src/org/chromium/components/crash/browser/ProcessExitReasonFromSystem.java
index d4dae99c..e0d89962 100644
--- a/components/crash/android/java/src/org/chromium/components/crash/browser/ProcessExitReasonFromSystem.java
+++ b/components/crash/android/java/src/org/chromium/components/crash/browser/ProcessExitReasonFromSystem.java
@@ -25,8 +25,13 @@
  * ActivityManager, and record to UMA.
  */
 public class ProcessExitReasonFromSystem {
+    /**
+     * Get the exit reason of the most recent chrome process that died and had |pid| as the process
+     * ID. Only available on R+ devices, returns -1 otherwise.
+     * @return ApplicationExitInfo.Reason
+     */
     @TargetApi(Build.VERSION_CODES.R)
-    private static int getExitReason(int pid) {
+    public static int getExitReason(int pid) {
         if (Build.VERSION.SDK_INT < Build.VERSION_CODES.R) {
             return -1;
         }
@@ -71,11 +76,18 @@
     }
 
     @CalledByNative
-    public static void recordExitReasonToUma(int pid, String umaName) {
-        int system_reason = getExitReason(pid);
+    private static void recordExitReasonToUma(int pid, String umaName) {
+        recordAsEnumHistogram(umaName, getExitReason(pid));
+    }
+
+    /**
+     * Records the given |systemReason| (given by #getExitReason) to UMA with the given |umaName|.
+     * @see #getExitReason
+     */
+    public static void recordAsEnumHistogram(String umaName, int systemReason) {
         @ExitReason
         int reason;
-        switch (system_reason) {
+        switch (systemReason) {
             case ApplicationExitInfo.REASON_ANR:
                 reason = ExitReason.REASON_ANR;
                 break;
diff --git a/components/full_restore/BUILD.gn b/components/full_restore/BUILD.gn
index 0bc9c8df..37f33e1 100644
--- a/components/full_restore/BUILD.gn
+++ b/components/full_restore/BUILD.gn
@@ -9,6 +9,8 @@
     "app_launch_info.h",
     "app_restore_data.cc",
     "app_restore_data.h",
+    "arc_read_handler.cc",
+    "arc_read_handler.h",
     "arc_save_handler.cc",
     "arc_save_handler.h",
     "full_restore_file_handler.cc",
diff --git a/components/full_restore/arc_read_handler.cc b/components/full_restore/arc_read_handler.cc
new file mode 100644
index 0000000..d8b1f98b6
--- /dev/null
+++ b/components/full_restore/arc_read_handler.cc
@@ -0,0 +1,96 @@
+// Copyright 2021 The Chromium 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/full_restore/arc_read_handler.h"
+
+#include "components/full_restore/full_restore_info.h"
+#include "components/full_restore/full_restore_read_handler.h"
+#include "components/full_restore/window_info.h"
+
+namespace full_restore {
+
+ArcReadHandler::ArcReadHandler(const base::FilePath& profile_path)
+    : profile_path_(profile_path) {}
+
+ArcReadHandler::~ArcReadHandler() = default;
+
+void ArcReadHandler::AddRestoreData(const std::string& app_id,
+                                    int32_t window_id) {
+  window_id_to_app_id_[window_id] = app_id;
+}
+
+void ArcReadHandler::OnTaskCreated(const std::string& app_id,
+                                   int32_t task_id,
+                                   int32_t session_id) {
+  auto it = session_id_to_window_id_.find(session_id);
+  if (it == session_id_to_window_id_.end())
+    return;
+
+  int32_t restore_window_id = it->second;
+  session_id_to_window_id_.erase(it);
+  task_id_to_window_id_[task_id] = restore_window_id;
+}
+
+void ArcReadHandler::OnTaskDestroyed(int32_t task_id) {
+  auto it = task_id_to_window_id_.find(task_id);
+  if (it == task_id_to_window_id_.end())
+    return;
+
+  int32_t window_id = it->second;
+  task_id_to_window_id_.erase(it);
+
+  RemoveAppRestoreData(window_id);
+}
+
+bool ArcReadHandler::HasRestoreData(int32_t window_id) {
+  return base::Contains(window_id_to_app_id_, window_id);
+}
+
+int32_t ArcReadHandler::GetArcRestoreWindowId(int32_t task_id) {
+  auto it = task_id_to_window_id_.find(task_id);
+  if (it != task_id_to_window_id_.end())
+    return it->second;
+
+  // If |session_id_to_window_id_| is empty, that means there is no ARC apps
+  // launched.
+  if (session_id_to_window_id_.empty())
+    return 0;
+
+  // If |session_id_to_window_id_| is not empty, that means there are ARC
+  // apps launched. Returns -1 to add the ARC app window to the hidden
+  // container.
+  return kParentToHiddenContainer;
+}
+
+int32_t ArcReadHandler::GetArcSessionId() {
+  if (session_id_ < kArcSessionIdOffsetForRestoredLaunching) {
+    LOG(WARNING) << "ARC session id is overflow: " << session_id_;
+    session_id_ = kArcSessionIdOffsetForRestoredLaunching;
+  }
+
+  return ++session_id_;
+}
+
+void ArcReadHandler::SetArcSessionIdForWindowId(int32_t session_id,
+                                                int32_t window_id) {
+  DCHECK_GT(session_id, full_restore::kArcSessionIdOffsetForRestoredLaunching);
+  session_id_to_window_id_[session_id] = window_id;
+}
+
+void ArcReadHandler::RemoveAppRestoreData(int32_t window_id) {
+  if (window_id == 0 || window_id == kParentToHiddenContainer)
+    return;
+
+  auto it = window_id_to_app_id_.find(window_id);
+  if (it == window_id_to_app_id_.end())
+    return;
+
+  const std::string& app_id = it->second;
+  FullRestoreReadHandler::GetInstance()->RemoveAppRestoreData(
+      profile_path_, app_id, window_id);
+
+  window_id_to_app_id_.erase(it);
+}
+
+}  // namespace full_restore
diff --git a/components/full_restore/arc_read_handler.h b/components/full_restore/arc_read_handler.h
new file mode 100644
index 0000000..6ed6889
--- /dev/null
+++ b/components/full_restore/arc_read_handler.h
@@ -0,0 +1,76 @@
+// Copyright 2021 The Chromium 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_FULL_RESTORE_ARC_READ_HANDLER_H_
+#define COMPONENTS_FULL_RESTORE_ARC_READ_HANDLER_H_
+
+#include <map>
+
+#include "base/component_export.h"
+#include "base/files/file_path.h"
+#include "components/full_restore/full_restore_utils.h"
+
+namespace full_restore {
+
+// ArcReadHandler is a helper class for FullRestoreReadHandler to handle ARC app
+// windows special cases, e.g. ARC task creation, ARC session id, etc.
+class COMPONENT_EXPORT(FULL_RESTORE) ArcReadHandler {
+ public:
+  explicit ArcReadHandler(const base::FilePath& profile_path);
+  ArcReadHandler(const ArcReadHandler&) = delete;
+  ArcReadHandler& operator=(const ArcReadHandler&) = delete;
+  ~ArcReadHandler();
+
+  // Sets |app_id| and |window_id| to |window_id_to_app_id_| to record that
+  // there is a restore data for |app_id| and |window_id|.
+  void AddRestoreData(const std::string& app_id, int32_t window_id);
+
+  // Invoked when the task is created for an ARC app.
+  void OnTaskCreated(const std::string& app_id,
+                     int32_t task_id,
+                     int32_t session_id);
+
+  // Invoked when the task is destroyed for an ARC app.
+  void OnTaskDestroyed(int32_t task_id);
+
+  // Returns true if there is restore data for |window_id|, otherwise returns
+  // false.
+  bool HasRestoreData(int32_t window_id);
+
+  // Returns the restore window id for the ARC app's |task_id|.
+  int32_t GetArcRestoreWindowId(int32_t task_id);
+
+  // Generates the ARC session id (1,000,000,001 - INT_MAX) for restored ARC
+  // apps.
+  int32_t GetArcSessionId();
+
+  // Sets |session_id| for |window_id| to |session_id_to_window_id_|.
+  // |session_id| is assigned when ARC apps are restored.
+  void SetArcSessionIdForWindowId(int32_t session_id, int32_t window_id);
+
+ private:
+  friend class FullRestoreReadHandlerTestApi;
+
+  // Removes AppRestoreData for |restore_window_id|.
+  void RemoveAppRestoreData(int32_t restore_window_id);
+
+  // The user profile path for ARC app windows.
+  base::FilePath profile_path_;
+
+  // The map from the window id to the app id for ARC app windows. The window id
+  // is saved in the window property |kRestoreWindowIdKey|.
+  std::map<int32_t, std::string> window_id_to_app_id_;
+
+  int32_t session_id_ = full_restore::kArcSessionIdOffsetForRestoredLaunching;
+
+  // The map from the arc session id to the window id.
+  std::map<int32_t, int32_t> session_id_to_window_id_;
+
+  // The map from the arc task id to the window id.
+  std::map<int32_t, int32_t> task_id_to_window_id_;
+};
+
+}  // namespace full_restore
+
+#endif  // COMPONENTS_FULL_RESTORE_ARC_READ_HANDLER_H_
diff --git a/components/full_restore/arc_save_handler.cc b/components/full_restore/arc_save_handler.cc
index d1c96d6..a776b41 100644
--- a/components/full_restore/arc_save_handler.cc
+++ b/components/full_restore/arc_save_handler.cc
@@ -14,6 +14,15 @@
 
 namespace full_restore {
 
+namespace {
+
+// Repeat timer interval between each checking that whether a task is created
+// for each app launching.
+constexpr base::TimeDelta kCheckCycleInterval =
+    base::TimeDelta::FromSeconds(30);
+
+}  // namespace
+
 ArcSaveHandler::ArcSaveHandler(const base::FilePath& profile_path)
     : profile_path_(profile_path) {}
 
@@ -25,7 +34,10 @@
   // Save |app_launch_info| to |session_id_to_app_launch_info_|, and wait for
   // the ARC task to be created.
   int32_t session_id = app_launch_info->arc_session_id.value();
-  session_id_to_app_launch_info_[session_id] = std::move(app_launch_info);
+  session_id_to_app_launch_info_[session_id] =
+      std::make_pair(std::move(app_launch_info), base::TimeTicks::Now());
+
+  MaybeStartCheckTimer();
 }
 
 void ArcSaveHandler::ModifyWindowInfo(int task_id,
@@ -74,8 +86,10 @@
 
   task_id_to_app_id_[task_id] = app_id;
 
-  auto app_launch_info = std::move(it->second);
+  auto app_launch_info = std::move(it->second.first);
   session_id_to_app_launch_info_.erase(it);
+  if (session_id_to_app_launch_info_.empty())
+    check_timer_.Stop();
 
   app_launch_info->window_id = task_id;
   FullRestoreSaveHandler::GetInstance()->AddAppLaunchInfo(
@@ -114,4 +128,28 @@
   return ++session_id_;
 }
 
+void ArcSaveHandler::MaybeStartCheckTimer() {
+  if (!check_timer_.IsRunning()) {
+    check_timer_.Start(
+        FROM_HERE, kCheckCycleInterval,
+        base::BindRepeating(&ArcSaveHandler::CheckTasksForAppLaunching,
+                            weak_factory_.GetWeakPtr()));
+  }
+}
+
+void ArcSaveHandler::CheckTasksForAppLaunching() {
+  std::set<int32_t> session_ids;
+  for (const auto& it : session_id_to_app_launch_info_) {
+    base::TimeDelta time_delta = base::TimeTicks::Now() - it.second.second;
+    if (time_delta > kCheckCycleInterval)
+      session_ids.insert(it.first);
+  }
+
+  for (auto id : session_ids)
+    session_id_to_app_launch_info_.erase(id);
+
+  if (session_id_to_app_launch_info_.empty())
+    check_timer_.Stop();
+}
+
 }  // namespace full_restore
diff --git a/components/full_restore/arc_save_handler.h b/components/full_restore/arc_save_handler.h
index a111031b..bfb928b 100644
--- a/components/full_restore/arc_save_handler.h
+++ b/components/full_restore/arc_save_handler.h
@@ -12,6 +12,8 @@
 
 #include "base/component_export.h"
 #include "base/files/file_path.h"
+#include "base/memory/weak_ptr.h"
+#include "base/timer/timer.h"
 
 namespace aura {
 class Window;
@@ -20,7 +22,6 @@
 namespace full_restore {
 
 struct AppLaunchInfo;
-class FullRestoreSaveHandlerTestApi;
 struct WindowInfo;
 
 // ArcSaveHandler is a helper class for FullRestoreSaveHandler to handle ARC app
@@ -28,6 +29,8 @@
 class COMPONENT_EXPORT(FULL_RESTORE) ArcSaveHandler {
  public:
   using AppLaunchInfoPtr = std::unique_ptr<AppLaunchInfo>;
+  using SessionIdMap =
+      std::map<int32_t, std::pair<AppLaunchInfoPtr, base::TimeTicks>>;
 
   explicit ArcSaveHandler(const base::FilePath& profile_path);
   ArcSaveHandler(const ArcSaveHandler&) = delete;
@@ -61,13 +64,21 @@
  private:
   friend class FullRestoreSaveHandlerTestApi;
 
+  // Starts the timer to check whether a task is created for the app launching
+  // (if timer isn't already running).
+  void MaybeStartCheckTimer();
+
+  // Check whether a task is created for each app launching. If not, remove the
+  // app launching record.
+  void CheckTasksForAppLaunching();
+
   // The user profile path for ARC app.
   base::FilePath profile_path_;
 
   int32_t session_id_ = 0;
 
   // The map from the ARC session id to the app launch info.
-  std::map<int32_t, AppLaunchInfoPtr> session_id_to_app_launch_info_;
+  SessionIdMap session_id_to_app_launch_info_;
 
   // The map from the task id to the app id. The task id is saved in the window
   // property. This map is used to find the app id when save the window info.
@@ -78,6 +89,13 @@
   // windows, whose tasks have not been created. Once the task for the window is
   // created, the window is removed from |arc_window_candidates_|.
   std::set<aura::Window*> arc_window_candidates_;
+
+  // Timer used to whether a task is created.  App launching could have failed.
+  // If an app is launched without a task created, the launch record should be
+  // removed from |session_id_to_app_launch_info_|.
+  base::RepeatingTimer check_timer_;
+
+  base::WeakPtrFactory<ArcSaveHandler> weak_factory_{this};
 };
 
 }  // namespace full_restore
diff --git a/components/full_restore/full_restore_read_and_save_unittest.cc b/components/full_restore/full_restore_read_and_save_unittest.cc
index d250312..5ffc88d 100644
--- a/components/full_restore/full_restore_read_and_save_unittest.cc
+++ b/components/full_restore/full_restore_read_and_save_unittest.cc
@@ -45,6 +45,43 @@
 
 }  // namespace
 
+class FullRestoreReadHandlerTestApi {
+ public:
+  explicit FullRestoreReadHandlerTestApi(FullRestoreReadHandler* read_handler)
+      : read_handler_(read_handler) {}
+
+  FullRestoreReadHandlerTestApi(const FullRestoreReadHandlerTestApi&) = delete;
+  FullRestoreReadHandlerTestApi& operator=(
+      const FullRestoreReadHandlerTestApi&) = delete;
+  ~FullRestoreReadHandlerTestApi() = default;
+
+  const ArcReadHandler* GetArcReadHander() const {
+    DCHECK(read_handler_);
+    return read_handler_->arc_read_handler_.get();
+  }
+
+  const std::map<int32_t, std::string>& GetArcWindowIdMap() const {
+    const auto* arc_read_handler = GetArcReadHander();
+    DCHECK(arc_read_handler);
+    return arc_read_handler->window_id_to_app_id_;
+  }
+
+  const std::map<int32_t, int32_t>& GetArcSessionIdMap() const {
+    const auto* arc_read_handler = GetArcReadHander();
+    DCHECK(arc_read_handler);
+    return arc_read_handler->session_id_to_window_id_;
+  }
+
+  const std::map<int32_t, int32_t>& GetArcTaskIdMap() const {
+    const auto* arc_read_handler = GetArcReadHander();
+    DCHECK(arc_read_handler);
+    return arc_read_handler->task_id_to_window_id_;
+  }
+
+ private:
+  FullRestoreReadHandler* read_handler_;
+};
+
 class FullRestoreSaveHandlerTestApi {
  public:
   explicit FullRestoreSaveHandlerTestApi(FullRestoreSaveHandler* save_handler)
@@ -55,30 +92,51 @@
       const FullRestoreSaveHandlerTestApi&) = delete;
   ~FullRestoreSaveHandlerTestApi() = default;
 
-  const ArcSaveHandler* GetArcSaveHanderForTesting() const {
+  const ArcSaveHandler* GetArcSaveHander() const {
     DCHECK(save_handler_);
     return save_handler_->arc_save_handler_.get();
   }
 
-  const std::map<int32_t, FullRestoreSaveHandler::AppLaunchInfoPtr>&
-  GetArcSessionIdMapForTesting() const {
-    DCHECK(save_handler_);
-    ArcSaveHandler* arc_save_handler = save_handler_->arc_save_handler_.get();
-
+  const ArcSaveHandler::SessionIdMap& GetArcSessionIdMap() const {
+    const auto* arc_save_handler = GetArcSaveHander();
     DCHECK(arc_save_handler);
     return arc_save_handler->session_id_to_app_launch_info_;
   }
 
-  const std::map<int32_t, std::string>& GetArcTaskIdMapForTesting() const {
-    DCHECK(save_handler_);
-    ArcSaveHandler* arc_save_handler = save_handler_->arc_save_handler_.get();
-
+  const std::map<int32_t, std::string>& GetArcTaskIdMap() const {
+    const auto* arc_save_handler = GetArcSaveHander();
     DCHECK(arc_save_handler);
     return arc_save_handler->task_id_to_app_id_;
   }
 
+  void ModifyLaunchTime(int32_t session_id) {
+    auto& session_id_to_app_launch_info =
+        arc_save_handler()->session_id_to_app_launch_info_;
+    auto it = session_id_to_app_launch_info.find(session_id);
+    if (it == session_id_to_app_launch_info.end())
+      return;
+
+    // If there is no task created for the session id in 30 seconds, the session
+    // id record is removed. So set the record time as 31 seconds ago, so that
+    // CheckTasksForAppLaunching can remove the session id record to simulate
+    // the task is not created for the session id.
+    it->second.second = it->second.second - base::TimeDelta::FromSeconds(31);
+  }
+
+  base::RepeatingTimer* GetArcCheckTimer() {
+    return &arc_save_handler()->check_timer_;
+  }
+
+  void CheckArcTasks() { arc_save_handler()->CheckTasksForAppLaunching(); }
+
  private:
-  FullRestoreSaveHandler* save_handler_ = nullptr;
+  ArcSaveHandler* arc_save_handler() {
+    DCHECK(save_handler_);
+    DCHECK(save_handler_->arc_save_handler_.get());
+    return save_handler_->arc_save_handler_.get();
+  }
+
+  FullRestoreSaveHandler* save_handler_;
 };
 
 // Unit tests for restore data.
@@ -283,10 +341,9 @@
 
   // Add an ARC app launch info.
   AddArcAppLaunchInfo(GetPath());
-  const ArcSaveHandler* arc_save_handler =
-      test_api.GetArcSaveHanderForTesting();
+  const ArcSaveHandler* arc_save_handler = test_api.GetArcSaveHander();
   ASSERT_TRUE(arc_save_handler);
-  const auto& arc_session_id_map = test_api.GetArcSessionIdMapForTesting();
+  const auto& arc_session_id_map = test_api.GetArcSessionIdMap();
   EXPECT_EQ(1u, arc_session_id_map.size());
   auto session_it = arc_session_id_map.find(kArcSessionId1);
   EXPECT_TRUE(session_it != arc_session_id_map.end());
@@ -295,7 +352,7 @@
   // cleared.
   save_handler->OnTaskCreated(kAppId, kArcTaskId1, kArcSessionId1);
   EXPECT_TRUE(arc_session_id_map.empty());
-  const auto& task_id_map = test_api.GetArcTaskIdMapForTesting();
+  const auto& task_id_map = test_api.GetArcTaskIdMap();
   EXPECT_EQ(1u, task_id_map.size());
   auto task_id = task_id_map.find(kArcTaskId1);
   EXPECT_TRUE(task_id != task_id_map.end());
@@ -315,7 +372,7 @@
   EXPECT_TRUE(restore_data->app_id_to_launch_list().empty());
 }
 
-TEST_F(FullRestoreReadAndSaveTest, ArcWindowRestore) {
+TEST_F(FullRestoreReadAndSaveTest, ArcLaunchWithoutTask) {
   FullRestoreSaveHandler* save_handler = FullRestoreSaveHandler::GetInstance();
   FullRestoreSaveHandlerTestApi test_api(save_handler);
 
@@ -324,16 +381,61 @@
 
   // Add an ARC app launch info.
   AddArcAppLaunchInfo(GetPath());
-  const ArcSaveHandler* arc_save_handler =
-      test_api.GetArcSaveHanderForTesting();
+
+  // Verify the ARC app launch info is saved to |arc_session_id_map|.
+  const auto& arc_session_id_map = test_api.GetArcSessionIdMap();
+  EXPECT_EQ(1u, arc_session_id_map.size());
+  auto session_it = arc_session_id_map.find(kArcSessionId1);
+  EXPECT_TRUE(session_it != arc_session_id_map.end());
+
+  // Verify the ARC check timer starts running.
+  base::RepeatingTimer* arc_check_timer = test_api.GetArcCheckTimer();
+  EXPECT_TRUE(arc_check_timer->IsRunning());
+
+  // Simulate more than 30 seconds have passed, OnTaskCreated is not called, and
+  // the ARC check timer is expired to remove the ARC app launch info.
+  test_api.ModifyLaunchTime(kArcSessionId1);
+  test_api.CheckArcTasks();
+  EXPECT_TRUE(arc_session_id_map.empty());
+  EXPECT_TRUE(test_api.GetArcTaskIdMap().empty());
+  EXPECT_FALSE(arc_check_timer->IsRunning());
+
+  // Verify the timer in FullRestoreSaveHandler is not running, because there is
+  // no app launching info to save.
+  EXPECT_FALSE(timer->IsRunning());
+  task_environment().RunUntilIdle();
+
+  ReadFromFile(GetPath());
+
+  // Verify there is not restore data.
+  const auto* restore_data = GetRestoreData(GetPath());
+  ASSERT_TRUE(restore_data);
+  EXPECT_TRUE(restore_data->app_id_to_launch_list().empty());
+}
+
+TEST_F(FullRestoreReadAndSaveTest, ArcWindowRestore) {
+  FullRestoreSaveHandler* save_handler = FullRestoreSaveHandler::GetInstance();
+  FullRestoreSaveHandlerTestApi save_test_api(save_handler);
+
+  save_handler->SetPrimaryProfilePath(GetPath());
+  base::OneShotTimer* timer = save_handler->GetTimerForTesting();
+
+  // Add an ARC app launch info.
+  AddArcAppLaunchInfo(GetPath());
+  const ArcSaveHandler* arc_save_handler = save_test_api.GetArcSaveHander();
   ASSERT_TRUE(arc_save_handler);
-  EXPECT_EQ(1u, test_api.GetArcSessionIdMapForTesting().size());
+  EXPECT_EQ(1u, save_test_api.GetArcSessionIdMap().size());
+
+  // Verify the ARC check timer starts running.
+  base::RepeatingTimer* arc_check_timer = save_test_api.GetArcCheckTimer();
+  EXPECT_TRUE(arc_check_timer->IsRunning());
 
   // Create a task. Since we have got the task, the arc session id map can be
   // cleared.
   save_handler->OnTaskCreated(kAppId, kArcTaskId1, kArcSessionId1);
-  EXPECT_TRUE(test_api.GetArcSessionIdMapForTesting().empty());
-  EXPECT_EQ(1u, test_api.GetArcTaskIdMapForTesting().size());
+  EXPECT_TRUE(save_test_api.GetArcSessionIdMap().empty());
+  EXPECT_EQ(1u, save_test_api.GetArcTaskIdMap().size());
+  EXPECT_FALSE(arc_check_timer->IsRunning());
 
   // Modify the window info.
   CreateWindowInfo(kArcTaskId1, kActivationIndex1, ash::AppType::ARC_APP);
@@ -346,6 +448,11 @@
   const auto* restore_data = GetRestoreData(GetPath());
   ASSERT_TRUE(restore_data);
 
+  FullRestoreReadHandler* read_handler = FullRestoreReadHandler::GetInstance();
+  FullRestoreReadHandlerTestApi read_test_api(read_handler);
+  ASSERT_TRUE(read_test_api.GetArcReadHander());
+  EXPECT_EQ(1u, read_test_api.GetArcWindowIdMap().size());
+
   // Verify the map from app ids to launch list:
   // std::map<app_id, std::map<window_id, std::unique_ptr<AppRestoreData>>>
   const auto& launch_list = restore_data->app_id_to_launch_list();
@@ -366,28 +473,30 @@
   EXPECT_TRUE(data->activation_index.has_value());
   EXPECT_EQ(kActivationIndex1, data->activation_index.value());
 
-  // Simulate the ARC app launching, and set the arc session id |kArcSessionId2|
-  // for the window id |kId1|.
-  FullRestoreReadHandler* read_handler = FullRestoreReadHandler::GetInstance();
-  read_handler->SetArcSessionIdForWindowId(kArcSessionId2, kId1);
+  // Simulate the ARC app launching, and set the arc session id kArcSessionId2
+  // for the restore window id |kArcTaskId1|.
+  read_handler->SetArcSessionIdForWindowId(kArcSessionId2, kArcTaskId1);
+  EXPECT_EQ(1u, read_test_api.GetArcSessionIdMap().size());
 
   // Call OnTaskCreated to simulate that the ARC app with |kAppId| has been
-  // launched, and the new task id |kArcTaskId| has been created with
+  // launched, and the new task id |kArcTaskId2| has been created with
   // |kArcSessionId2| returned.
   read_handler->OnTaskCreated(kAppId, kArcTaskId2, kArcSessionId2);
+  EXPECT_EQ(1u, read_test_api.GetArcTaskIdMap().size());
 
-  // Since we have got the new task with |kArcSessionId2|, the map from the arc
-  // session id to the window id can be cleared. And verify that we can get the
-  // restore window id with the new |kArcTaskId2|.
-  EXPECT_TRUE(read_handler->GetArcSessionIdMapForTesting().empty());
-  EXPECT_EQ(kId1, full_restore::GetArcRestoreWindowId(kArcTaskId2));
+  // Since we have got the new task with |kArcSessionId2|, the arc session id
+  // map can be cleared. And verify that we can get the restore window id
+  // |kArcTaskId1| with the new |kArcTaskId2|.
+  EXPECT_TRUE(read_test_api.GetArcSessionIdMap().empty());
+  EXPECT_EQ(kArcTaskId1, full_restore::GetArcRestoreWindowId(kArcTaskId2));
 
   // Call OnTaskDestroyed to simulate the ARC app launching has been finished
   // for |kArcTaskId2|, and verify the task id map is now empty and a invalid
   // value is returned when trying to get the restore window id.
   read_handler->OnTaskDestroyed(kArcTaskId2);
-  EXPECT_EQ(-1, full_restore::GetArcRestoreWindowId(kArcTaskId2));
-  EXPECT_TRUE(read_handler->GetArcTaskIdMapForTesting().empty());
+  EXPECT_EQ(0, full_restore::GetArcRestoreWindowId(kArcTaskId2));
+  EXPECT_TRUE(read_test_api.GetArcTaskIdMap().empty());
+  EXPECT_TRUE(read_test_api.GetArcWindowIdMap().empty());
 }
 
 }  // namespace full_restore
diff --git a/components/full_restore/full_restore_read_handler.cc b/components/full_restore/full_restore_read_handler.cc
index 29a2f7d..1b7cdce 100644
--- a/components/full_restore/full_restore_read_handler.cc
+++ b/components/full_restore/full_restore_read_handler.cc
@@ -40,11 +40,15 @@
 
   if (window->GetProperty(aura::client::kAppType) ==
       static_cast<int>(ash::AppType::ARC_APP)) {
-    if (!base::Contains(window_id_to_app_restore_info_, window_id))
+    // If there isn't restore data for ARC apps, we don't need to handle ARC app
+    // windows restoration.
+    if (!arc_read_handler_)
       return;
 
-    observed_windows_.AddObservation(window);
-    FullRestoreInfo::GetInstance()->OnWindowInitialized(window);
+    if (arc_read_handler_->HasRestoreData(window_id)) {
+      observed_windows_.AddObservation(window);
+      FullRestoreInfo::GetInstance()->OnWindowInitialized(window);
+    }
     return;
   }
 
@@ -77,17 +81,13 @@
 void FullRestoreReadHandler::OnTaskCreated(const std::string& app_id,
                                            int32_t task_id,
                                            int32_t session_id) {
-  auto it = arc_session_id_to_window_id_.find(session_id);
-  if (it == arc_session_id_to_window_id_.end())
-    return;
-
-  arc_task_id_to_app_id_window_id_[task_id] =
-      std::make_pair(app_id, it->second);
-  arc_session_id_to_window_id_.erase(it);
+  if (arc_read_handler_)
+    arc_read_handler_->OnTaskCreated(app_id, task_id, session_id);
 }
 
 void FullRestoreReadHandler::OnTaskDestroyed(int32_t task_id) {
-  arc_task_id_to_app_id_window_id_.erase(task_id);
+  if (arc_read_handler_)
+    arc_read_handler_->OnTaskDestroyed(task_id);
 }
 
 void FullRestoreReadHandler::ReadFromFile(const base::FilePath& profile_path,
@@ -188,11 +188,10 @@
 }
 
 int32_t FullRestoreReadHandler::GetArcRestoreWindowId(int32_t task_id) {
-  auto it = arc_task_id_to_app_id_window_id_.find(task_id);
-  if (it == arc_task_id_to_app_id_window_id_.end())
-    return -1;
+  if (!arc_read_handler_)
+    return 0;
 
-  return it->second.second;
+  return arc_read_handler_->GetArcRestoreWindowId(task_id);
 }
 
 void FullRestoreReadHandler::ModifyWidgetParams(
@@ -232,19 +231,14 @@
 }
 
 int32_t FullRestoreReadHandler::GetArcSessionId() {
-  if (arc_session_id_ < kArcSessionIdOffsetForRestoredLaunching) {
-    LOG(WARNING) << "ARC session id is overflow: " << arc_session_id_;
-    arc_session_id_ = kArcSessionIdOffsetForRestoredLaunching;
-  }
-
-  return ++arc_session_id_;
+  DCHECK(arc_read_handler_);
+  return arc_read_handler_->GetArcSessionId();
 }
 
 void FullRestoreReadHandler::SetArcSessionIdForWindowId(int32_t arc_session_id,
                                                         int32_t window_id) {
-  DCHECK_GT(arc_session_id,
-            full_restore::kArcSessionIdOffsetForRestoredLaunching);
-  arc_session_id_to_window_id_[arc_session_id] = window_id;
+  DCHECK(arc_read_handler_);
+  arc_read_handler_->SetArcSessionIdForWindowId(arc_session_id, window_id);
 }
 
 void FullRestoreReadHandler::OnGetRestoreData(
@@ -256,10 +250,19 @@
 
     for (auto it = restore_data->app_id_to_launch_list().begin();
          it != restore_data->app_id_to_launch_list().end(); it++) {
+      const std::string& app_id = it->first;
       for (auto data_it = it->second.begin(); data_it != it->second.end();
            data_it++) {
-        window_id_to_app_restore_info_[data_it->first] =
-            std::make_pair(profile_path, it->first);
+        int32_t window_id = data_it->first;
+        // Only ARC app launch parameters have event_flag.
+        if (data_it->second->event_flag.has_value()) {
+          if (!arc_read_handler_)
+            arc_read_handler_ = std::make_unique<ArcReadHandler>(profile_path);
+          arc_read_handler_->AddRestoreData(app_id, window_id);
+        } else {
+          window_id_to_app_restore_info_[window_id] =
+              std::make_pair(profile_path, app_id);
+        }
       }
     }
   }
diff --git a/components/full_restore/full_restore_read_handler.h b/components/full_restore/full_restore_read_handler.h
index f5a029a..cb98fbd 100644
--- a/components/full_restore/full_restore_read_handler.h
+++ b/components/full_restore/full_restore_read_handler.h
@@ -7,12 +7,14 @@
 
 #include <map>
 #include <memory>
+#include <utility>
 
 #include "base/callback.h"
 #include "base/component_export.h"
 #include "base/files/file_path.h"
 #include "base/memory/weak_ptr.h"
 #include "base/scoped_multi_source_observation.h"
+#include "components/full_restore/arc_read_handler.h"
 #include "components/full_restore/full_restore_utils.h"
 #include "ui/aura/env_observer.h"
 #include "ui/aura/window.h"
@@ -120,16 +122,9 @@
   // |arc session id| is assigned when ARC apps are restored.
   void SetArcSessionIdForWindowId(int32_t arc_session_id, int32_t window_id);
 
-  const std::map<int32_t, int32_t>& GetArcSessionIdMapForTesting() const {
-    return arc_session_id_to_window_id_;
-  }
-
-  const std::map<int32_t, std::pair<std::string, int32_t>>&
-  GetArcTaskIdMapForTesting() const {
-    return arc_task_id_to_app_id_window_id_;
-  }
-
  private:
+  friend class FullRestoreReadHandlerTestApi;
+
   // Invoked when reading the restore data from |profile_path| is finished, and
   // calls |callback| to notify that the reading operation is done.
   void OnGetRestoreData(const base::FilePath& profile_path,
@@ -153,15 +148,7 @@
   std::map<int32_t, std::pair<base::FilePath, std::string>>
       window_id_to_app_restore_info_;
 
-  int32_t arc_session_id_ =
-      full_restore::kArcSessionIdOffsetForRestoredLaunching;
-
-  // The map from the arc session id to the window id.
-  std::map<int32_t, int32_t> arc_session_id_to_window_id_;
-
-  // The map from the arc task id to the app id and the window id.
-  std::map<int32_t, std::pair<std::string, int32_t>>
-      arc_task_id_to_app_id_window_id_;
+  std::unique_ptr<ArcReadHandler> arc_read_handler_;
 
   base::ScopedMultiSourceObservation<aura::Window, aura::WindowObserver>
       observed_windows_{this};
diff --git a/components/full_restore/full_restore_save_handler.h b/components/full_restore/full_restore_save_handler.h
index cdae069a..e16f40f 100644
--- a/components/full_restore/full_restore_save_handler.h
+++ b/components/full_restore/full_restore_save_handler.h
@@ -29,7 +29,6 @@
 
 struct AppLaunchInfo;
 class FullRestoreFileHandler;
-class FullRestoreSaveHandlerTestApi;
 class RestoreData;
 struct WindowInfo;
 
diff --git a/components/full_restore/full_restore_utils.h b/components/full_restore/full_restore_utils.h
index 98290a13..2e354a52 100644
--- a/components/full_restore/full_restore_utils.h
+++ b/components/full_restore/full_restore_utils.h
@@ -35,6 +35,10 @@
 // assigned for ARC session ids.
 constexpr int32_t kArcSessionIdOffsetForRestoredLaunching = 1000000000;
 
+// If the ARC task is not created when the window is initialized, set the
+// restore window id as -1, to add the ARC app window to the hidden container.
+constexpr int32_t kParentToHiddenContainer = -1;
+
 // A property key to indicate the id for the window to be saved in RestoreData.
 // For web apps, browser windows or Chrome app windows, this is the session id.
 // For ARC apps, this is the task id.
diff --git a/components/history/core/browser/history_types.h b/components/history/core/browser/history_types.h
index 8dc11ef..6786a5f 100644
--- a/components/history/core/browser/history_types.h
+++ b/components/history/core/browser/history_types.h
@@ -104,7 +104,7 @@
   // (https://github.com/WICG/floc) is an API that intends to provide callers
   // with coarse-grained information about the user’s browsing interests. The
   // history URL visits is a main source of computation, but some visits are
-  // ineligible to be included, so we use this bit to represent its eligiblity.
+  // ineligible to be included, so we use this bit to represent its eligibility.
   //
   // Currently this bit is "true" if the IP of this url visit was publicly
   // routable, i.e. the IP is NOT within the ranges reserved for "private"
@@ -129,7 +129,7 @@
     return visit_time < other.visit_time;
   }
 
-  // We allow the implicit copy constuctor and operator=.
+  // We allow the implicit copy constructor and operator=.
 };
 
 // We pass around vectors of visits a lot
@@ -300,7 +300,7 @@
   QueryURLResult& operator=(QueryURLResult&&) noexcept;
   ~QueryURLResult();
 
-  // Indicates whether the call to HistoryBackend::QueryURL was successfull
+  // Indicates whether the call to HistoryBackend::QueryURL was successful
   // or not. If false, then both |row| and |visits| fields are undefined.
   bool success = false;
   URLRow row;
diff --git a/components/history/core/browser/url_row.h b/components/history/core/browser/url_row.h
index 764de67..813ece09 100644
--- a/components/history/core/browser/url_row.h
+++ b/components/history/core/browser/url_row.h
@@ -144,7 +144,7 @@
   // is usually for subframes.
   bool hidden_ = false;
 
-  // We support the implicit copy constuctor and operator=.
+  // We support the implicit copy constructor and operator=.
 };
 typedef std::vector<URLRow> URLRows;
 
diff --git a/components/metrics/metrics_log.cc b/components/metrics/metrics_log.cc
index cd0c8cc..807f90c 100644
--- a/components/metrics/metrics_log.cc
+++ b/components/metrics/metrics_log.cc
@@ -214,12 +214,7 @@
 
   metrics::SystemProfileProto::Hardware* hardware =
       system_profile->mutable_hardware();
-#if !defined(OS_IOS)
-  // On iOS, OperatingSystemArchitecture() returns values like iPad4,4 which is
-  // not the actual CPU architecture. Don't set it until the API is fixed. See
-  // crbug.com/370104 for details.
   hardware->set_cpu_architecture(base::SysInfo::OperatingSystemArchitecture());
-#endif
   auto app_os_arch = base::SysInfo::ProcessCPUArchitecture();
   if (!app_os_arch.empty())
     hardware->set_app_cpu_architecture(app_os_arch);
diff --git a/components/metrics/metrics_log_unittest.cc b/components/metrics/metrics_log_unittest.cc
index 8ecc01d..19462db6 100644
--- a/components/metrics/metrics_log_unittest.cc
+++ b/components/metrics/metrics_log_unittest.cc
@@ -178,9 +178,7 @@
 #endif
   metrics::SystemProfileProto::Hardware* hardware =
       system_profile->mutable_hardware();
-#if !defined(OS_IOS)
   hardware->set_cpu_architecture(base::SysInfo::OperatingSystemArchitecture());
-#endif
   auto app_os_arch = base::SysInfo::ProcessCPUArchitecture();
   if (!app_os_arch.empty())
     hardware->set_app_cpu_architecture(app_os_arch);
diff --git a/components/optimization_guide/content/browser/BUILD.gn b/components/optimization_guide/content/browser/BUILD.gn
index 65aa5f4..82d63f3 100644
--- a/components/optimization_guide/content/browser/BUILD.gn
+++ b/components/optimization_guide/content/browser/BUILD.gn
@@ -16,6 +16,8 @@
     "page_content_annotations_service.h",
     "page_content_annotations_web_contents_helper.cc",
     "page_content_annotations_web_contents_helper.h",
+    "page_text_dump_result.cc",
+    "page_text_dump_result.h",
     "page_text_observer.cc",
     "page_text_observer.h",
   ]
@@ -68,6 +70,7 @@
   testonly = true
   sources = [
     "page_content_annotations_web_contents_helper_unittest.cc",
+    "page_text_dump_result_unittest.cc",
     "page_text_observer_unittest.cc",
   ]
   if (build_with_tflite_lib) {
diff --git a/components/optimization_guide/content/browser/page_content_annotations_web_contents_helper.cc b/components/optimization_guide/content/browser/page_content_annotations_web_contents_helper.cc
index 091bb9b..ce248d8e 100644
--- a/components/optimization_guide/content/browser/page_content_annotations_web_contents_helper.cc
+++ b/components/optimization_guide/content/browser/page_content_annotations_web_contents_helper.cc
@@ -5,7 +5,6 @@
 #include "components/optimization_guide/content/browser/page_content_annotations_web_contents_helper.h"
 
 #include "base/bind.h"
-#include "base/strings/utf_string_conversions.h"
 #include "components/optimization_guide/content/browser/page_content_annotations_service.h"
 #include "components/optimization_guide/core/optimization_guide_features.h"
 #include "content/public/browser/navigation_handle.h"
@@ -40,22 +39,19 @@
 PageContentAnnotationsWebContentsHelper::MaybeRequestFrameTextDump(
     content::NavigationHandle* navigation_handle) {
   DCHECK(navigation_handle->HasCommitted());
+  DCHECK(navigation_handle->IsInMainFrame());
 
   if (!navigation_handle->GetURL().SchemeIsHTTPOrHTTPS())
     return nullptr;
 
-  if (!navigation_handle->IsInMainFrame())
-    return nullptr;
-  // TODO(crbug/1177102): Change above to also annotate content for subframes
-  // when API exposes whether it makes up most of the frame.
-
   // TODO(crbug/1177102): Figure out how to deal with same document navigations.
 
   std::unique_ptr<PageTextObserver::ConsumerTextDumpRequest> request =
       std::make_unique<PageTextObserver::ConsumerTextDumpRequest>();
   request->max_size = max_size_for_text_dump_;
   request->events = {mojom::TextDumpEvent::kFirstLayout};
-  request->callback = base::BindRepeating(
+  request->dump_amp_subframes = true;
+  request->callback = base::BindOnce(
       &PageContentAnnotationsWebContentsHelper::OnTextDumpReceived,
       weak_ptr_factory_.GetWeakPtr(),
       PageContentAnnotationsService::CreateHistoryVisitFromWebContents(
@@ -65,8 +61,20 @@
 
 void PageContentAnnotationsWebContentsHelper::OnTextDumpReceived(
     const HistoryVisit& visit,
-    const std::u16string& text) {
-  page_content_annotations_service_->Annotate(visit, base::UTF16ToUTF8(text));
+    const PageTextDumpResult& result) {
+  if (result.empty()) {
+    return;
+  }
+
+  // If the page had AMP frames, then only use that content. Otherwise, use the
+  // mainframe.
+  if (result.GetAMPTextContent()) {
+    page_content_annotations_service_->Annotate(visit,
+                                                *result.GetAMPTextContent());
+    return;
+  }
+  page_content_annotations_service_->Annotate(
+      visit, *result.GetMainFrameTextContent());
 }
 
 WEB_CONTENTS_USER_DATA_KEY_IMPL(PageContentAnnotationsWebContentsHelper)
diff --git a/components/optimization_guide/content/browser/page_content_annotations_web_contents_helper.h b/components/optimization_guide/content/browser/page_content_annotations_web_contents_helper.h
index 286d6ec..7911424d 100644
--- a/components/optimization_guide/content/browser/page_content_annotations_web_contents_helper.h
+++ b/components/optimization_guide/content/browser/page_content_annotations_web_contents_helper.h
@@ -6,6 +6,7 @@
 #define COMPONENTS_OPTIMIZATION_GUIDE_CONTENT_BROWSER_PAGE_CONTENT_ANNOTATIONS_WEB_CONTENTS_HELPER_H_
 
 #include "base/memory/weak_ptr.h"
+#include "components/optimization_guide/content/browser/page_text_dump_result.h"
 #include "components/optimization_guide/content/browser/page_text_observer.h"
 #include "content/public/browser/web_contents_user_data.h"
 
@@ -49,7 +50,7 @@
 
   // Callback invoked when a text dump has been received for the |visit|.
   void OnTextDumpReceived(const HistoryVisit& visit,
-                          const std::u16string& test);
+                          const PageTextDumpResult& result);
 
   // Not owned. Guaranteed to outlive |this|.
   content::WebContents* web_contents_;
diff --git a/components/optimization_guide/content/browser/page_content_annotations_web_contents_helper_unittest.cc b/components/optimization_guide/content/browser/page_content_annotations_web_contents_helper_unittest.cc
index 3ccee54..d9792a8 100644
--- a/components/optimization_guide/content/browser/page_content_annotations_web_contents_helper_unittest.cc
+++ b/components/optimization_guide/content/browser/page_content_annotations_web_contents_helper_unittest.cc
@@ -6,7 +6,9 @@
 
 #include "base/strings/utf_string_conversions.h"
 #include "components/optimization_guide/content/browser/page_content_annotations_service.h"
+#include "components/optimization_guide/content/browser/page_text_dump_result.h"
 #include "components/optimization_guide/content/browser/test_optimization_guide_decider.h"
+#include "components/optimization_guide/core/optimization_guide_features.h"
 #include "content/public/test/mock_navigation_handle.h"
 #include "content/public/test/navigation_simulator.h"
 #include "content/public/test/test_renderer_host.h"
@@ -91,19 +93,11 @@
   TestPageTextObserver* page_text_observer() { return page_text_observer_; }
 
   std::unique_ptr<PageTextObserver::ConsumerTextDumpRequest>
-  RequestTextDumpForUrl(const GURL& url, bool is_main_frame) {
-    content::RenderFrameHost* frame = main_rfh();
-    if (!is_main_frame) {
-      // Commit main frame, so we can attach the subframe.
-      content::NavigationSimulator::NavigateAndCommitFromBrowser(
-          web_contents(), GURL("http://test.com"));
-      frame = content::RenderFrameHostTester::For(main_rfh())
-                  ->AppendChild("subframe");
-    }
-    content::MockNavigationHandle navigation_handle(url, frame);
+  RequestTextDumpForUrl(const GURL& url) {
+    content::MockNavigationHandle navigation_handle(url, main_rfh());
     navigation_handle.set_url(url);
     // PageTextObserver is guaranteed to call MaybeRequestFrameTextDump after
-    // the navigation has been committeed.
+    // the navigation has been committed.
     navigation_handle.set_has_committed(true);
     return helper()->MaybeRequestFrameTextDump(&navigation_handle);
   }
@@ -121,15 +115,7 @@
 
 TEST_F(PageContentAnnotationsWebContentsHelperTest,
        DoesNotRequestForNonHttpHttps) {
-  EXPECT_EQ(
-      RequestTextDumpForUrl(GURL("chrome://new-tab"), /*is_main_frame=*/true),
-      nullptr);
-}
-
-TEST_F(PageContentAnnotationsWebContentsHelperTest, DoesNotRequestForSubframe) {
-  EXPECT_EQ(
-      RequestTextDumpForUrl(GURL("http://test.com"), /*is_main_frame=*/false),
-      nullptr);
+  EXPECT_EQ(RequestTextDumpForUrl(GURL("chrome://new-tab")), nullptr);
 }
 
 TEST_F(PageContentAnnotationsWebContentsHelperTest,
@@ -141,11 +127,23 @@
       web_contents(), GURL("http://test.com"));
 
   std::unique_ptr<PageTextObserver::ConsumerTextDumpRequest> request =
-      RequestTextDumpForUrl(GURL("http://test.com"), /*is_main_frame=*/true);
+      RequestTextDumpForUrl(GURL("http://test.com"));
   ASSERT_TRUE(request);
+  ASSERT_TRUE(request->callback);
+  EXPECT_EQ(features::MaxSizeForPageContentTextDump(), request->max_size);
+  EXPECT_TRUE(request->dump_amp_subframes);
+  EXPECT_EQ(std::set<mojom::TextDumpEvent>{mojom::TextDumpEvent::kFirstLayout},
+            request->events);
 
   // Invoke OnTextDumpReceived.
-  std::move(request->callback).Run(base::ASCIIToUTF16("some text"));
+  FrameTextDumpResult frame_result =
+      FrameTextDumpResult::Initialize(mojom::TextDumpEvent::kFirstLayout,
+                                      content::GlobalFrameRoutingId(),
+                                      /*amp_frame=*/false)
+          .CompleteWithContents(base::ASCIIToUTF16("some text"));
+  PageTextDumpResult result;
+  result.AddFrameTextDumpResult(frame_result);
+  std::move(request->callback).Run(std::move(result));
 
   base::Optional<std::pair<HistoryVisit, std::string>> last_annotation_request =
       service()->last_annotation_request();
diff --git a/components/optimization_guide/content/browser/page_text_dump_result.cc b/components/optimization_guide/content/browser/page_text_dump_result.cc
new file mode 100644
index 0000000..628438a
--- /dev/null
+++ b/components/optimization_guide/content/browser/page_text_dump_result.cc
@@ -0,0 +1,147 @@
+// Copyright 2021 The Chromium 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/optimization_guide/content/browser/page_text_dump_result.h"
+
+#include <algorithm>
+
+#include "base/strings/string_util.h"
+#include "base/strings/stringprintf.h"
+
+namespace optimization_guide {
+
+namespace {
+
+std::string TextDumpEventToString(mojom::TextDumpEvent value) {
+  switch (value) {
+    case mojom::TextDumpEvent::kFirstLayout:
+      return "kFirstLayout";
+    case mojom::TextDumpEvent::kFinishedLoad:
+      return "kFinishedLoad";
+  }
+}
+
+}  // namespace
+
+PageTextDumpResult::PageTextDumpResult() = default;
+PageTextDumpResult::PageTextDumpResult(const PageTextDumpResult&) = default;
+PageTextDumpResult::~PageTextDumpResult() = default;
+
+void PageTextDumpResult::AddFrameTextDumpResult(
+    const FrameTextDumpResult& frame_result) {
+  DCHECK(frame_result.IsCompleted());
+  frame_results_.emplace(frame_result);
+}
+
+base::Optional<std::string> PageTextDumpResult::GetAMPTextContent() const {
+  if (empty()) {
+    return base::nullopt;
+  }
+
+  // AMP frames are sorted in beginning, so if there are none then return null.
+  if (!frame_results_.begin()->amp_frame()) {
+    return base::nullopt;
+  }
+
+  std::vector<std::string> amp_text;
+  for (const FrameTextDumpResult& frame_result : frame_results_) {
+    DCHECK(frame_result.utf8_contents());
+
+    if (!frame_result.amp_frame()) {
+      break;
+    }
+
+    amp_text.push_back(*frame_result.utf8_contents());
+  }
+  DCHECK(!amp_text.empty());
+
+  return base::JoinString(amp_text, " ");
+}
+
+base::Optional<std::string> PageTextDumpResult::GetMainFrameTextContent()
+    const {
+  if (empty()) {
+    return base::nullopt;
+  }
+
+  // Mainframes are sorted to the end.
+  if (frame_results_.rbegin()->amp_frame()) {
+    return base::nullopt;
+  }
+
+  // There should only be one mainframe.
+  DCHECK(frame_results_.rbegin()->utf8_contents());
+  return *frame_results_.rbegin()->utf8_contents();
+}
+
+base::Optional<std::string> PageTextDumpResult::GetAllFramesTextContent()
+    const {
+  if (empty()) {
+    return base::nullopt;
+  }
+
+  std::vector<std::string> text;
+  for (const FrameTextDumpResult& frame_result : frame_results_) {
+    DCHECK(frame_result.utf8_contents());
+    text.push_back(*frame_result.utf8_contents());
+  }
+  DCHECK(!text.empty());
+
+  return base::JoinString(text, " ");
+}
+
+FrameTextDumpResult::FrameTextDumpResult() = default;
+FrameTextDumpResult::~FrameTextDumpResult() = default;
+FrameTextDumpResult::FrameTextDumpResult(const FrameTextDumpResult&) = default;
+
+// static
+FrameTextDumpResult FrameTextDumpResult::Initialize(
+    mojom::TextDumpEvent event,
+    content::GlobalFrameRoutingId rfh_id,
+    bool amp_frame) {
+  FrameTextDumpResult result;
+  result.event_ = event;
+  result.rfh_id_ = rfh_id;
+  result.amp_frame_ = amp_frame;
+  return result;
+}
+
+FrameTextDumpResult FrameTextDumpResult::CompleteWithContents(
+    const std::u16string& contents) const {
+  DCHECK(!IsCompleted());
+  DCHECK(!contents.empty());
+
+  FrameTextDumpResult copy = *this;
+  copy.contents_ = contents;
+  return copy;
+}
+
+bool FrameTextDumpResult::IsCompleted() const {
+  return !!contents();
+}
+
+base::Optional<std::string> FrameTextDumpResult::utf8_contents() const {
+  if (!contents_) {
+    return base::nullopt;
+  }
+  return base::UTF16ToUTF8(*contents_);
+}
+
+std::ostream& operator<<(std::ostream& os, const FrameTextDumpResult& frame) {
+  return os << base::StringPrintf(
+             "event:%s rfh_id:(%d,%d) amp_frame:%s contents:%s",
+             TextDumpEventToString(frame.event()).c_str(),
+             frame.rfh_id().child_id, frame.rfh_id().frame_routing_id,
+             frame.amp_frame() ? "true" : "false",
+             frame.utf8_contents().value_or("null").c_str());
+}
+
+std::ostream& operator<<(std::ostream& os, const PageTextDumpResult& page) {
+  for (const FrameTextDumpResult& frame : page.frame_results()) {
+    os << frame << "\n";
+  }
+  return os;
+}
+
+}  // namespace optimization_guide
diff --git a/components/optimization_guide/content/browser/page_text_dump_result.h b/components/optimization_guide/content/browser/page_text_dump_result.h
new file mode 100644
index 0000000..2381e8f
--- /dev/null
+++ b/components/optimization_guide/content/browser/page_text_dump_result.h
@@ -0,0 +1,144 @@
+// Copyright 2021 The Chromium 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_OPTIMIZATION_GUIDE_CONTENT_BROWSER_PAGE_TEXT_DUMP_RESULT_H_
+#define COMPONENTS_OPTIMIZATION_GUIDE_CONTENT_BROWSER_PAGE_TEXT_DUMP_RESULT_H_
+
+#include <set>
+#include <string>
+
+#include "base/optional.h"
+#include "components/optimization_guide/content/mojom/page_text_service.mojom.h"
+#include "content/public/browser/global_routing_id.h"
+
+namespace optimization_guide {
+
+// This class contains the text dump from a single renderer and associated
+// metadata. The lifecycle of this class is in two phases, preliminary and
+// final. Preliminary is when the frame metadata is known, but the text dump has
+// not been received yet. Once the text dump is received, then the dump is
+// completed.
+class FrameTextDumpResult {
+ public:
+  ~FrameTextDumpResult();
+  FrameTextDumpResult(const FrameTextDumpResult&);
+
+  // Creates a preliminary instance with the given metadata.
+  static FrameTextDumpResult Initialize(mojom::TextDumpEvent event,
+                                        content::GlobalFrameRoutingId rfh_id,
+                                        bool amp_frame);
+
+  // Returns a copy of |this| that is completed with |contents|. This is only
+  // expected to be called once on Preliminary instances.
+  FrameTextDumpResult CompleteWithContents(
+      const std::u16string& contents) const;
+
+  // Whether the class instance is completed yet.
+  bool IsCompleted() const;
+
+  // The text dump contents. Set only for completed instances.
+  const base::Optional<std::u16string>& contents() const { return contents_; }
+
+  // The text dump contents, decoded to UTF-8 as a best effort. Set only for
+  // completed instances.
+  base::Optional<std::string> utf8_contents() const;
+
+  // The event at which the text dump is taken. Set for both preliminary and
+  // completed instances.
+  mojom::TextDumpEvent event() const { return event_; }
+
+  // The unique identifier for the content::RenderFrameHost that the text dump
+  // was taken in. Set for both preliminary and completed instances.
+  content::GlobalFrameRoutingId rfh_id() const { return rfh_id_; }
+
+  // Whether the frame the text dump is taken in an AMP frame. Set for both
+  // preliminary and completed instances.
+  bool amp_frame() const { return amp_frame_; }
+
+  // These objects are sorted in the following manner:
+  // * AMP frames first - When there are AMP frames on a page, it is expected
+  // that they will contain the most content.
+  // * Longer contents first - Most consumers will only be interested in some of
+  // the page text. In this case, ensure that the biggest blob of text comes
+  // first since that is most likely to be the main content on the page.
+  // * Later events first - The later in the page's lifetime a text dump is
+  // taken, the more likely that it is complete.
+  // * Everything else is just done willy-nilly for completeness of equality
+  // checking.
+  inline bool operator<(const FrameTextDumpResult& rhs) const {
+    if (amp_frame() != rhs.amp_frame()) {
+      return amp_frame();
+    }
+
+    size_t lhs_size = contents() ? contents()->size() : 0;
+    size_t rhs_size = rhs.contents() ? rhs.contents()->size() : 0;
+    if (lhs_size != rhs_size) {
+      // Note the reverse ordering to put longer contents first.
+      return lhs_size > rhs_size;
+    }
+
+    if (event() != rhs.event()) {
+      // Note the reverse ordering to put later events first.
+      return event() > rhs.event();
+    }
+
+    return std::tie(rfh_id_, contents_) < std::tie(rhs.rfh_id_, rhs.contents_);
+  }
+
+  inline bool operator==(const FrameTextDumpResult& other) const {
+    return std::tie(event_, contents_, rfh_id_, amp_frame_) ==
+           std::tie(other.event_, other.contents_, other.rfh_id_,
+                    other.amp_frame_);
+  }
+
+ private:
+  FrameTextDumpResult();
+
+  mojom::TextDumpEvent event_;
+  base::Optional<std::u16string> contents_;
+  content::GlobalFrameRoutingId rfh_id_;
+  bool amp_frame_ = false;
+};
+
+// Contains 0 or more FrameTextDumpResults from the same page load.
+class PageTextDumpResult {
+ public:
+  PageTextDumpResult();
+  PageTextDumpResult(const PageTextDumpResult&);
+  ~PageTextDumpResult();
+
+  // Adds another frame text dump to |this|.
+  void AddFrameTextDumpResult(const FrameTextDumpResult& frame_result);
+
+  // Returns the concatenation of all AMP frames. nullopt if no AMP frames are
+  // present.
+  base::Optional<std::string> GetAMPTextContent() const;
+
+  // Returns the concatenation of the mainframe. nullopt if not present.
+  base::Optional<std::string> GetMainFrameTextContent() const;
+
+  // Returns the concatenation of all frames, AMP or main. nullopt if |empty()|.
+  base::Optional<std::string> GetAllFramesTextContent() const;
+
+  bool empty() const { return frame_results_.empty(); }
+
+  const std::set<FrameTextDumpResult>& frame_results() const {
+    return frame_results_;
+  }
+
+  inline bool operator==(const PageTextDumpResult& other) const {
+    return frame_results_ == other.frame_results_;
+  }
+
+ private:
+  std::set<FrameTextDumpResult> frame_results_;
+};
+
+// Useful for debugging.
+std::ostream& operator<<(std::ostream& os, const FrameTextDumpResult& frame);
+std::ostream& operator<<(std::ostream& os, const PageTextDumpResult& page);
+
+}  // namespace optimization_guide
+
+#endif  // COMPONENTS_OPTIMIZATION_GUIDE_CONTENT_BROWSER_PAGE_TEXT_DUMP_RESULT_H_
diff --git a/components/optimization_guide/content/browser/page_text_dump_result_unittest.cc b/components/optimization_guide/content/browser/page_text_dump_result_unittest.cc
new file mode 100644
index 0000000..fb958c1
--- /dev/null
+++ b/components/optimization_guide/content/browser/page_text_dump_result_unittest.cc
@@ -0,0 +1,177 @@
+// Copyright 2021 The Chromium 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/optimization_guide/content/browser/page_text_dump_result.h"
+
+#include "base/strings/utf_string_conversions.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace optimization_guide {
+
+TEST(FrameTextDumpResultTest, Preliminary) {
+  content::GlobalFrameRoutingId id(1, 2);
+  FrameTextDumpResult frame_result =
+      FrameTextDumpResult::Initialize(mojom::TextDumpEvent::kFirstLayout, id,
+                                      /*amp_frame=*/false);
+
+  EXPECT_FALSE(frame_result.IsCompleted());
+  EXPECT_EQ(mojom::TextDumpEvent::kFirstLayout, frame_result.event());
+  EXPECT_EQ(id, frame_result.rfh_id());
+  EXPECT_FALSE(frame_result.amp_frame());
+}
+
+TEST(FrameTextDumpResultTest, Equality) {
+  FrameTextDumpResult starting_frame_result =
+      FrameTextDumpResult::Initialize(mojom::TextDumpEvent::kFirstLayout,
+                                      content::GlobalFrameRoutingId(1, 2),
+                                      /*amp_frame=*/false)
+          .CompleteWithContents(base::ASCIIToUTF16("abc"));
+
+  FrameTextDumpResult different_event =
+      FrameTextDumpResult::Initialize(mojom::TextDumpEvent::kFinishedLoad,
+                                      content::GlobalFrameRoutingId(1, 2),
+                                      /*amp_frame=*/false)
+          .CompleteWithContents(base::ASCIIToUTF16("abc"));
+
+  FrameTextDumpResult different_id =
+      FrameTextDumpResult::Initialize(mojom::TextDumpEvent::kFirstLayout,
+                                      content::GlobalFrameRoutingId(2, 1),
+                                      /*amp_frame=*/false)
+          .CompleteWithContents(base::ASCIIToUTF16("abc"));
+
+  FrameTextDumpResult different_amp_frame =
+      FrameTextDumpResult::Initialize(mojom::TextDumpEvent::kFirstLayout,
+                                      content::GlobalFrameRoutingId(1, 2),
+                                      /*amp_frame=*/true)
+          .CompleteWithContents(base::ASCIIToUTF16("abc"));
+
+  FrameTextDumpResult different_contents =
+      FrameTextDumpResult::Initialize(mojom::TextDumpEvent::kFirstLayout,
+                                      content::GlobalFrameRoutingId(1, 2),
+                                      /*amp_frame=*/false)
+          .CompleteWithContents(base::ASCIIToUTF16("abcd"));
+
+  std::vector<FrameTextDumpResult> all_frame_results = {
+      starting_frame_result, different_event,    different_id,
+      different_amp_frame,   different_contents,
+  };
+  for (size_t lhs = 0; lhs < all_frame_results.size() - 1; lhs++) {
+    for (size_t rhs = lhs + 1; rhs < all_frame_results.size(); rhs++) {
+      SCOPED_TRACE(base::StringPrintf("lhs: %zu, rhs: %zu", lhs, rhs));
+      EXPECT_FALSE(all_frame_results[lhs] == all_frame_results[rhs]);
+    }
+  }
+}
+
+TEST(FrameTextDumpResultTest, Ordering) {
+  FrameTextDumpResult starting_frame_result =
+      FrameTextDumpResult::Initialize(mojom::TextDumpEvent::kFirstLayout,
+                                      content::GlobalFrameRoutingId(1, 2),
+                                      /*amp_frame=*/false)
+          .CompleteWithContents(base::ASCIIToUTF16("abc"));
+
+  FrameTextDumpResult later_event_frame_result =
+      FrameTextDumpResult::Initialize(mojom::TextDumpEvent::kFinishedLoad,
+                                      content::GlobalFrameRoutingId(1, 2),
+                                      /*amp_frame=*/false)
+          .CompleteWithContents(base::ASCIIToUTF16("abc"));
+
+  FrameTextDumpResult longer_frame_result =
+      FrameTextDumpResult::Initialize(mojom::TextDumpEvent::kFirstLayout,
+                                      content::GlobalFrameRoutingId(1, 2),
+                                      /*amp_frame=*/false)
+          .CompleteWithContents(base::ASCIIToUTF16("abcd"));
+
+  FrameTextDumpResult amp_frame_result =
+      FrameTextDumpResult::Initialize(mojom::TextDumpEvent::kFirstLayout,
+                                      content::GlobalFrameRoutingId(1, 2),
+                                      /*amp_frame=*/true)
+          .CompleteWithContents(base::ASCIIToUTF16("abc"));
+
+  FrameTextDumpResult longer_amp_frame_result =
+      FrameTextDumpResult::Initialize(mojom::TextDumpEvent::kFirstLayout,
+                                      content::GlobalFrameRoutingId(1, 2),
+                                      /*amp_frame=*/true)
+          .CompleteWithContents(base::ASCIIToUTF16("abcd"));
+
+  std::set<FrameTextDumpResult> ordered_set{
+      starting_frame_result, later_event_frame_result, longer_frame_result,
+      amp_frame_result, longer_amp_frame_result};
+  EXPECT_THAT(ordered_set, ::testing::ElementsAreArray({
+                               longer_amp_frame_result,
+                               amp_frame_result,
+                               longer_frame_result,
+                               later_event_frame_result,
+                               starting_frame_result,
+                           }));
+}
+
+TEST(PageTextDumpResultTest, Empty) {
+  PageTextDumpResult page_result;
+  EXPECT_TRUE(page_result.empty());
+  EXPECT_EQ(base::nullopt, page_result.GetAMPTextContent());
+  EXPECT_EQ(base::nullopt, page_result.GetMainFrameTextContent());
+  EXPECT_EQ(base::nullopt, page_result.GetAllFramesTextContent());
+}
+
+TEST(PageTextDumpResultTest, OneAMP) {
+  PageTextDumpResult page_result;
+  page_result.AddFrameTextDumpResult(
+      FrameTextDumpResult::Initialize(mojom::TextDumpEvent::kFinishedLoad,
+                                      content::GlobalFrameRoutingId(2, 1),
+                                      /*amp_frame=*/true)
+          .CompleteWithContents(base::ASCIIToUTF16("amp frame")));
+
+  ASSERT_TRUE(page_result.GetAMPTextContent());
+  EXPECT_EQ("amp frame", *page_result.GetAMPTextContent());
+
+  EXPECT_FALSE(page_result.GetMainFrameTextContent());
+
+  ASSERT_TRUE(page_result.GetAllFramesTextContent());
+  EXPECT_EQ("amp frame", *page_result.GetAllFramesTextContent());
+}
+
+TEST(PageTextDumpResultTest, OneMainframe) {
+  PageTextDumpResult page_result;
+  page_result.AddFrameTextDumpResult(
+      FrameTextDumpResult::Initialize(mojom::TextDumpEvent::kFirstLayout,
+                                      content::GlobalFrameRoutingId(1, 2),
+                                      /*amp_frame=*/false)
+          .CompleteWithContents(base::ASCIIToUTF16("mainframe")));
+
+  EXPECT_FALSE(page_result.GetAMPTextContent());
+
+  ASSERT_TRUE(page_result.GetMainFrameTextContent());
+  EXPECT_EQ("mainframe", *page_result.GetMainFrameTextContent());
+
+  ASSERT_TRUE(page_result.GetAllFramesTextContent());
+  EXPECT_EQ("mainframe", *page_result.GetAllFramesTextContent());
+}
+
+TEST(PageTextDumpResultTest, OneAMPOneMF) {
+  PageTextDumpResult page_result;
+  page_result.AddFrameTextDumpResult(
+      FrameTextDumpResult::Initialize(mojom::TextDumpEvent::kFirstLayout,
+                                      content::GlobalFrameRoutingId(1, 2),
+                                      /*amp_frame=*/false)
+          .CompleteWithContents(base::ASCIIToUTF16("mainframe")));
+
+  page_result.AddFrameTextDumpResult(
+      FrameTextDumpResult::Initialize(mojom::TextDumpEvent::kFinishedLoad,
+                                      content::GlobalFrameRoutingId(2, 1),
+                                      /*amp_frame=*/true)
+          .CompleteWithContents(base::ASCIIToUTF16("amp frame")));
+
+  ASSERT_TRUE(page_result.GetAMPTextContent());
+  EXPECT_EQ("amp frame", *page_result.GetAMPTextContent());
+
+  ASSERT_TRUE(page_result.GetMainFrameTextContent());
+  EXPECT_EQ("mainframe", *page_result.GetMainFrameTextContent());
+
+  ASSERT_TRUE(page_result.GetAllFramesTextContent());
+  EXPECT_EQ("amp frame mainframe", *page_result.GetAllFramesTextContent());
+}
+
+}  // namespace optimization_guide
diff --git a/components/optimization_guide/content/browser/page_text_observer.cc b/components/optimization_guide/content/browser/page_text_observer.cc
index 14e8126..2131e11b 100644
--- a/components/optimization_guide/content/browser/page_text_observer.cc
+++ b/components/optimization_guide/content/browser/page_text_observer.cc
@@ -10,10 +10,14 @@
 #include <vector>
 
 #include "base/bind.h"
+#include "base/feature_list.h"
 #include "base/memory/scoped_refptr.h"
 #include "base/strings/strcat.h"
 #include "base/threading/sequenced_task_runner_handle.h"
 #include "base/time/time.h"
+#include "components/optimization_guide/core/optimization_guide_features.h"
+#include "content/public/browser/browser_task_traits.h"
+#include "content/public/browser/browser_thread.h"
 #include "content/public/browser/navigation_handle.h"
 #include "content/public/browser/render_frame_host.h"
 #include "content/public/browser/render_process_host.h"
@@ -35,9 +39,10 @@
 // in stack.
 class PageTextChunkConsumer : public mojom::PageTextConsumer {
  public:
-  PageTextChunkConsumer(mojo::PendingReceiver<mojom::PageTextConsumer> receiver,
-                        uint32_t max_size,
-                        base::OnceCallback<void(std::u16string)> on_complete)
+  PageTextChunkConsumer(
+      mojo::PendingReceiver<mojom::PageTextConsumer> receiver,
+      uint32_t max_size,
+      base::OnceCallback<void(const std::u16string&)> on_complete)
       : remaining_size_(max_size),
         on_complete_(std::move(on_complete)),
         receiver_(this, std::move(receiver)) {
@@ -88,7 +93,7 @@
   // While |on_complete_| is non-null, the mojo pipe is also bound. Once the
   // |on_complete_| callback is run, this class is no longer active and can be
   // deleted (in stack with the callback).
-  base::OnceCallback<void(std::u16string)> on_complete_;
+  base::OnceCallback<void(const std::u16string&)> on_complete_;
   mojo::Receiver<mojom::PageTextConsumer> receiver_;
 
   // All chunks that have been read from the data pipe. These will be
@@ -130,6 +135,7 @@
 // along with copies of the scoped_refptr, making mojo the effective owner of
 // this class. Note that one or more requests have been made at this point and
 // this class will remain alive until they have all been completed and handled.
+// The count of outgoing requests sent to a renderer is returned.
 //
 // (4) The PageTextConsumer will receive chunks of the dumped page text until
 // either (a) all chunks have been read, or (b) the max length is reached. When
@@ -137,39 +143,71 @@
 // back to RequestMediator in a callback.
 //
 // (5) The callback to RequestMediator is the end of the line for a text dump
-// response as the original requests' callbacks are now passed the dumped
-// string16. At this time, the self scoped_refptr will fall out of scope and be
-// destroyed. If it was the last such pointer, then |this| will be destroyed.
+// response as the |on_complete_| callback is run. At this time, the self
+// scoped_refptr will fall out of scope and be destroyed. If it was the last
+// such pointer, then |this| will be destroyed.
 class RequestMediator : public base::RefCounted<RequestMediator> {
  public:
   RequestMediator() = default;
 
   void AddConsumerRequest(
-      std::unique_ptr<PageTextObserver::ConsumerTextDumpRequest> request) {
-    DCHECK(request->callback);
-    DCHECK(!request->events.empty());
-    DCHECK_GT(request->max_size, 0U);
+      const PageTextObserver::ConsumerTextDumpRequest& request) {
+    DCHECK(!request.events.empty());
+    DCHECK_GT(request.max_size, 0U);
 
-    for (mojom::TextDumpEvent event : request->events) {
+    for (mojom::TextDumpEvent event : request.events) {
       if (max_size_by_event_.find(event) == max_size_by_event_.end()) {
         max_size_by_event_.emplace(event, 0U);
       }
       auto event_to_max_size_iter = max_size_by_event_.find(event);
       event_to_max_size_iter->second =
-          std::max(event_to_max_size_iter->second, request->max_size);
+          std::max(event_to_max_size_iter->second, request.max_size);
     }
-
-    requests_.push_back(std::move(request));
   }
 
-  void MakeSelfOwnedAndDispatchRequests(
+  void AddAMPRequest(const PageTextObserver::ConsumerTextDumpRequest& request) {
+    if (!request.dump_amp_subframes) {
+      return;
+    }
+
+    auto iter = max_size_by_event_.find(mojom::TextDumpEvent::kFinishedLoad);
+    if (iter == max_size_by_event_.end()) {
+      max_size_by_event_.emplace(mojom::TextDumpEvent::kFinishedLoad,
+                                 request.max_size);
+      return;
+    }
+
+    iter->second = std::max(iter->second, request.max_size);
+  }
+
+  size_t MakeSelfOwnedAndDispatchRequests(
       scoped_refptr<RequestMediator> self,
-      mojo::AssociatedRemote<mojom::PageTextService> renderer_text_service) {
+      base::RepeatingCallback<void(base::Optional<FrameTextDumpResult>)>
+          on_frame_text_dump_complete,
+      content::RenderFrameHost* rfh) {
     DCHECK_EQ(self.get(), this);
 
+    if (max_size_by_event_.empty()) {
+      return 0;
+    }
+
+    on_frame_text_dump_complete_ = std::move(on_frame_text_dump_complete);
+
+    mojo::AssociatedRemote<mojom::PageTextService> renderer_text_service;
+    rfh->GetRemoteAssociatedInterfaces()->GetInterface(&renderer_text_service);
+
+    bool is_subframe = rfh->GetMainFrame() != rfh;
+    auto rfh_id = rfh->GetGlobalFrameRoutingId();
+
     for (const auto& event_to_max_size_iter : max_size_by_event_) {
       mojo::PendingRemote<mojom::PageTextConsumer> consumer_remote;
 
+      FrameTextDumpResult preliminary_result = FrameTextDumpResult::Initialize(
+          event_to_max_size_iter.first, rfh_id,
+          // Note that subframes only take text dumps iff they are an AMP
+          // frame. If that even changes, this won't work anymore.
+          /*amp_frame=*/is_subframe);
+
       std::unique_ptr<PageTextChunkConsumer> consumer =
           std::make_unique<PageTextChunkConsumer>(
               consumer_remote.InitWithNewPipeAndPassReceiver(),
@@ -178,8 +216,7 @@
               // passed to |consumer|. See comment at end of method for more
               // detail.
               base::BindOnce(&RequestMediator::OnPageTextAsString,
-                             base::Unretained(this), self,
-                             event_to_max_size_iter.first));
+                             base::Unretained(this), self, preliminary_result));
 
       auto request = mojom::PageTextDumpRequest::New();
       request->max_size = event_to_max_size_iter.second;
@@ -195,6 +232,8 @@
       // destroy |this| (if it was the last owning reference).
       consumers_.emplace(std::move(consumer));
     }
+
+    return max_size_by_event_.size();
   }
 
  private:
@@ -202,18 +241,23 @@
   ~RequestMediator() = default;
 
   void OnPageTextAsString(scoped_refptr<RequestMediator> self,
-                          mojom::TextDumpEvent event,
-                          const std::u16string page_text) {
-    for (const auto& request : requests_) {
-      if (request->events.find(event) != request->events.end()) {
-        request->callback.Run(page_text);
-      }
+                          const FrameTextDumpResult& preliminary_result,
+                          const std::u16string& page_text) {
+    DCHECK(on_frame_text_dump_complete_);
+
+    if (page_text.empty()) {
+      on_frame_text_dump_complete_.Run(base::nullopt);
+      return;
     }
+
+    on_frame_text_dump_complete_.Run(
+        preliminary_result.CompleteWithContents(page_text));
   }
 
-  // All text dumps requests.
-  std::vector<std::unique_ptr<PageTextObserver::ConsumerTextDumpRequest>>
-      requests_;
+  // Called whenever a text dump is completed for an event. This called as many
+  // times as events requested, which can be greater than 1.
+  base::RepeatingCallback<void(base::Optional<FrameTextDumpResult>)>
+      on_frame_text_dump_complete_;
 
   // All |PageTextChunkConsumer|'s that are owned by this.
   std::set<std::unique_ptr<PageTextChunkConsumer>> consumers_;
@@ -239,13 +283,19 @@
   return PageTextObserver::FromWebContents(web_contents);
 }
 
-void PageTextObserver::DidFinishNavigation(content::NavigationHandle* handle) {
-  if (consumers_.empty()) {
+void PageTextObserver::DidStartNavigation(content::NavigationHandle* handle) {
+  if (!handle->IsInMainFrame()) {
     return;
   }
 
+  requests_.clear();
+  page_result_.reset();
+  outstanding_requests_ = 0;
+  outstanding_requests_grace_timer_.reset();
+}
+
+void PageTextObserver::DidFinishNavigation(content::NavigationHandle* handle) {
   // Only main frames are supported for right now.
-  // TODO(crbug/1163244): Add subframe support.
   if (!handle->IsInMainFrame()) {
     return;
   }
@@ -254,23 +304,97 @@
     return;
   }
 
-  content::RenderFrameHost* render_frame_host = handle->GetRenderFrameHost();
+  if (consumers_.empty()) {
+    return;
+  }
 
   scoped_refptr<RequestMediator> mediator =
       base::MakeRefCounted<RequestMediator>();
   for (Consumer* consumer : consumers_) {
     auto request = consumer->MaybeRequestFrameTextDump(handle);
-    if (request) {
-      mediator->AddConsumerRequest(std::move(request));
+    if (!request) {
+      continue;
     }
+    mediator->AddConsumerRequest(*request);
+    requests_.push_back(std::move(request));
   }
 
-  mojo::AssociatedRemote<mojom::PageTextService> renderer_text_service;
-  render_frame_host->GetRemoteAssociatedInterfaces()->GetInterface(
-      &renderer_text_service);
+  outstanding_requests_ += mediator->MakeSelfOwnedAndDispatchRequests(
+      mediator,
+      base::BindRepeating(&PageTextObserver::OnFrameTextDumpCompleted,
+                          weak_factory_.GetWeakPtr()),
+      handle->GetRenderFrameHost());
+}
 
-  mediator->MakeSelfOwnedAndDispatchRequests(mediator,
-                                             std::move(renderer_text_service));
+bool PageTextObserver::IsOOPIF(content::RenderFrameHost* rfh) const {
+  return rfh->GetProcess()->GetID() !=
+         rfh->GetMainFrame()->GetProcess()->GetID();
+}
+
+void PageTextObserver::RenderFrameCreated(content::RenderFrameHost* rfh) {
+  if (!IsOOPIF(rfh)) {
+    return;
+  }
+
+  scoped_refptr<RequestMediator> mediator =
+      base::MakeRefCounted<RequestMediator>();
+  for (const auto& request : requests_) {
+    mediator->AddAMPRequest(*request);
+  }
+
+  outstanding_requests_ += mediator->MakeSelfOwnedAndDispatchRequests(
+      mediator,
+      base::BindRepeating(&PageTextObserver::OnFrameTextDumpCompleted,
+                          weak_factory_.GetWeakPtr()),
+      rfh);
+}
+
+void PageTextObserver::OnFrameTextDumpCompleted(
+    base::Optional<FrameTextDumpResult> frame_result) {
+  // |frame_result| will be null in the event the RFH dies, in which case we can
+  // no longer expect the request to be fulfilled, so it should not be counted
+  // as outstanding anymore.
+  outstanding_requests_--;
+
+  if (frame_result) {
+    if (!page_result_) {
+      page_result_ = std::make_unique<PageTextDumpResult>();
+    }
+    page_result_->AddFrameTextDumpResult(*frame_result);
+  }
+
+  if (!!outstanding_requests_grace_timer_ && outstanding_requests_ == 0) {
+    outstanding_requests_grace_timer_.reset();
+    DispatchResponses();
+  }
+}
+
+void PageTextObserver::DidFinishLoad(
+    content::RenderFrameHost* render_frame_host,
+    const GURL& validated_url) {
+  if (outstanding_requests_ > 0) {
+    outstanding_requests_grace_timer_ = std::make_unique<base::OneShotTimer>();
+    outstanding_requests_grace_timer_->Start(
+        FROM_HERE, features::PageTextExtractionOutstandingRequestsGracePeriod(),
+        base::BindOnce(&PageTextObserver::DispatchResponses,
+                       base::Unretained(this)));
+    return;
+  }
+  DispatchResponses();
+}
+
+void PageTextObserver::DispatchResponses() {
+  outstanding_requests_grace_timer_.reset();
+
+  if (!page_result_) {
+    return;
+  }
+
+  for (const auto& consumer_request : requests_) {
+    std::move(consumer_request->callback).Run(*page_result_);
+  }
+  requests_.clear();
+  page_result_.reset();
 }
 
 void PageTextObserver::AddConsumer(Consumer* consumer) {
diff --git a/components/optimization_guide/content/browser/page_text_observer.h b/components/optimization_guide/content/browser/page_text_observer.h
index a4ba615..0cc6c1e15 100644
--- a/components/optimization_guide/content/browser/page_text_observer.h
+++ b/components/optimization_guide/content/browser/page_text_observer.h
@@ -11,6 +11,8 @@
 
 #include "base/callback.h"
 #include "base/memory/weak_ptr.h"
+#include "base/timer/timer.h"
+#include "components/optimization_guide/content/browser/page_text_dump_result.h"
 #include "components/optimization_guide/content/mojom/page_text_service.mojom.h"
 #include "content/public/browser/web_contents_observer.h"
 #include "content/public/browser/web_contents_user_data.h"
@@ -50,7 +52,7 @@
 
     // The callback that is used to provide dumped page text.
     using TextDumpCallback =
-        base::RepeatingCallback<void(const std::u16string&)>;
+        base::OnceCallback<void(const PageTextDumpResult&)>;
     TextDumpCallback callback;
 
     // The max size of the text dump in bytes. Note that the actual size
@@ -59,6 +61,12 @@
     // pages with little text.
     uint32_t max_size = 0;
 
+    // Set when subframe text dumps should be taken on AMP subframes. A text
+    // dump of the mainframe will always also be taken. Consumer who set this
+    // should use |PageTextDumpResult::ConcatenateWithAMPHandling| on the
+    // |callback|.
+    bool dump_amp_subframes = false;
+
     // All of the |TextDumpEvent|'s that have been requested.
     std::set<mojom::TextDumpEvent> events;
   };
@@ -79,7 +87,11 @@
   virtual void RemoveConsumer(Consumer* consumer);
 
   // content::WebContentsObserver:
+  void DidStartNavigation(content::NavigationHandle* handle) override;
   void DidFinishNavigation(content::NavigationHandle* handle) override;
+  void RenderFrameCreated(content::RenderFrameHost* rfh) override;
+  void DidFinishLoad(content::RenderFrameHost* render_frame_host,
+                     const GURL& validated_url) override;
 
   PageTextObserver(const PageTextObserver&) = delete;
   PageTextObserver& operator=(const PageTextObserver&) = delete;
@@ -87,12 +99,32 @@
  protected:
   explicit PageTextObserver(content::WebContents* web_contents);
 
+  // Virtual for testing.
+  virtual bool IsOOPIF(content::RenderFrameHost* rfh) const;
+
  private:
   friend class content::WebContentsUserData<PageTextObserver>;
 
+  void OnFrameTextDumpCompleted(
+      base::Optional<FrameTextDumpResult> frame_result);
+
+  void DispatchResponses();
+
   // All registered consumers.
   std::set<Consumer*> consumers_;
 
+  // A persisted set of consumer requests.
+  std::vector<std::unique_ptr<ConsumerTextDumpRequest>> requests_;
+
+  std::unique_ptr<PageTextDumpResult> page_result_;
+
+  // |outstanding_requests_grace_timer_| is set after |DidFinishLoad| if the
+  // number of |outstanding_requests_| is > 0. When the timer fires, the
+  // |page_result_| will be finialized and dispatched to consumers (in
+  // |DispatchResponses|).
+  std::unique_ptr<base::OneShotTimer> outstanding_requests_grace_timer_;
+  size_t outstanding_requests_ = 0;
+
   base::WeakPtrFactory<PageTextObserver> weak_factory_{this};
 
   WEB_CONTENTS_USER_DATA_KEY_DECL();
diff --git a/components/optimization_guide/content/browser/page_text_observer_unittest.cc b/components/optimization_guide/content/browser/page_text_observer_unittest.cc
index 8f83e63..54ec3f8 100644
--- a/components/optimization_guide/content/browser/page_text_observer_unittest.cc
+++ b/components/optimization_guide/content/browser/page_text_observer_unittest.cc
@@ -9,6 +9,8 @@
 #include "base/bind.h"
 #include "base/callback.h"
 #include "base/optional.h"
+#include "base/stl_util.h"
+#include "base/strings/strcat.h"
 #include "base/strings/utf_string_conversions.h"
 #include "components/optimization_guide/content/mojom/page_text_service.mojom.h"
 #include "content/public/browser/navigation_handle.h"
@@ -27,6 +29,14 @@
 
 namespace {
 
+FrameTextDumpResult MakeFrameDump(mojom::TextDumpEvent event,
+                                  content::GlobalFrameRoutingId rfh_id,
+                                  bool amp_frame,
+                                  const std::u16string& contents) {
+  return FrameTextDumpResult::Initialize(event, rfh_id, amp_frame)
+      .CompleteWithContents(contents);
+}
+
 class TestConsumer : public PageTextObserver::Consumer {
  public:
   TestConsumer() = default;
@@ -35,16 +45,18 @@
   void Reset() { was_called_ = false; }
 
   void PopulateRequest(uint32_t max_size,
-                       const std::set<mojom::TextDumpEvent>& events) {
+                       const std::set<mojom::TextDumpEvent>& events,
+                       bool request_amp = false) {
     request_ = std::make_unique<PageTextObserver::ConsumerTextDumpRequest>();
     request_->max_size = max_size;
     request_->events = events;
-    request_->callback = base::BindRepeating(&TestConsumer::OnGotTextDump,
-                                             base::Unretained(this));
+    request_->callback =
+        base::BindOnce(&TestConsumer::OnGotTextDump, base::Unretained(this));
+    request_->dump_amp_subframes = request_amp;
   }
 
   void WaitForPageText() {
-    if (text_) {
+    if (result_) {
       return;
     }
 
@@ -55,7 +67,9 @@
 
   bool was_called() const { return was_called_; }
 
-  const base::Optional<std::u16string>& text() const { return text_; }
+  base::Optional<PageTextDumpResult> result() {
+    return base::OptionalFromPtr(result_.get());
+  }
 
   // PageTextObserver::Consumer:
   std::unique_ptr<PageTextObserver::ConsumerTextDumpRequest>
@@ -65,20 +79,18 @@
   }
 
  private:
-  void OnGotTextDump(const std::u16string& text) {
-    text_ = text;
+  void OnGotTextDump(const PageTextDumpResult& result) {
+    result_ = std::make_unique<PageTextDumpResult>(result);
     if (on_page_text_closure_) {
       std::move(on_page_text_closure_).Run();
     }
   }
 
   bool was_called_ = false;
-
   std::unique_ptr<PageTextObserver::ConsumerTextDumpRequest> request_;
 
   base::OnceClosure on_page_text_closure_;
-
-  base::Optional<std::u16string> text_;
+  std::unique_ptr<PageTextDumpResult> result_;
 };
 
 class FakePageTextService : public mojom::PageTextService {
@@ -138,6 +150,36 @@
       responses_;
 };
 
+class TestPageTextObserver : public PageTextObserver {
+ public:
+  explicit TestPageTextObserver(content::WebContents* web_contents)
+      : PageTextObserver(web_contents) {}
+  ~TestPageTextObserver() override = default;
+
+  void SetIsOOPIF(content::RenderFrameHost* rfh, bool is_oopif) {
+    oopif_overrides_.erase(rfh);
+    oopif_overrides_.emplace(rfh, is_oopif);
+  }
+
+  bool IsOOPIF(content::RenderFrameHost* rfh) const override {
+    auto iter = oopif_overrides_.find(rfh);
+    if (iter != oopif_overrides_.end()) {
+      return iter->second;
+    }
+    return false;
+  }
+
+  void DidFinishLoad(content::RenderFrameHost* render_frame_host,
+                     const GURL& validated_url) override {
+    // Intentionally do nothing so that subframes can be added in tests.
+  }
+
+  void CallDidFinishLoad() { PageTextObserver::DidFinishLoad(nullptr, GURL()); }
+
+ private:
+  std::map<content::RenderFrameHost*, bool> oopif_overrides_;
+};
+
 }  // namespace
 
 class PageTextObserverTest : public content::RenderViewHostTestHarness {
@@ -148,13 +190,14 @@
   // content::RenderViewHostTestHarness:
   void SetUp() override {
     content::RenderViewHostTestHarness::SetUp();
-    PageTextObserver::CreateForWebContents(web_contents());
+    observer_ = std::make_unique<TestPageTextObserver>(web_contents());
     ASSERT_TRUE(observer());
   }
 
-  PageTextObserver* observer() {
-    return PageTextObserver::FromWebContents(web_contents());
-  }
+  TestPageTextObserver* observer() const { return observer_.get(); }
+
+ private:
+  std::unique_ptr<TestPageTextObserver> observer_;
 };
 
 TEST_F(PageTextObserverTest, ConsumerCalled) {
@@ -181,6 +224,7 @@
   content::NavigationSimulator::CreateRendererInitiated(
       GURL("http://test.com/#fragment"), main_rfh())
       ->CommitSameDocument();
+  observer()->CallDidFinishLoad();
 
   EXPECT_TRUE(consumer.was_called());
 }
@@ -242,11 +286,18 @@
 
   content::NavigationSimulator::NavigateAndCommitFromBrowser(
       web_contents(), GURL("http://test.com"));
-
   EXPECT_TRUE(consumer.was_called());
 
+  observer()->CallDidFinishLoad();
   consumer.WaitForPageText();
-  EXPECT_EQ(base::ASCIIToUTF16("abc"), *consumer.text());
+
+  ASSERT_TRUE(consumer.result());
+  EXPECT_THAT(consumer.result()->frame_results(),
+              ::testing::UnorderedElementsAreArray({
+                  MakeFrameDump(mojom::TextDumpEvent::kFirstLayout,
+                                main_rfh()->GetGlobalFrameRoutingId(),
+                                /*amp_frame=*/false, base::ASCIIToUTF16("abc")),
+              }));
 
   EXPECT_THAT(
       fake_renderer_service.requests(),
@@ -279,11 +330,18 @@
 
   content::NavigationSimulator::NavigateAndCommitFromBrowser(
       web_contents(), GURL("http://test.com"));
-
   EXPECT_TRUE(consumer.was_called());
 
+  observer()->CallDidFinishLoad();
   consumer.WaitForPageText();
-  EXPECT_EQ(base::ASCIIToUTF16("abc"), *consumer.text());
+
+  ASSERT_TRUE(consumer.result());
+  EXPECT_THAT(consumer.result()->frame_results(),
+              ::testing::UnorderedElementsAreArray({
+                  MakeFrameDump(mojom::TextDumpEvent::kFirstLayout,
+                                main_rfh()->GetGlobalFrameRoutingId(),
+                                /*amp_frame=*/false, base::ASCIIToUTF16("abc")),
+              }));
 
   EXPECT_THAT(
       fake_renderer_service.requests(),
@@ -316,11 +374,19 @@
 
   content::NavigationSimulator::NavigateAndCommitFromBrowser(
       web_contents(), GURL("http://test.com"));
-
   EXPECT_TRUE(consumer.was_called());
 
+  observer()->CallDidFinishLoad();
   consumer.WaitForPageText();
-  EXPECT_EQ(base::ASCIIToUTF16("abcd"), *consumer.text());
+
+  ASSERT_TRUE(consumer.result());
+  EXPECT_THAT(
+      consumer.result()->frame_results(),
+      ::testing::UnorderedElementsAreArray({
+          MakeFrameDump(mojom::TextDumpEvent::kFirstLayout,
+                        main_rfh()->GetGlobalFrameRoutingId(),
+                        /*amp_frame=*/false, base::ASCIIToUTF16("abcd")),
+      }));
 
   EXPECT_THAT(
       fake_renderer_service.requests(),
@@ -352,11 +418,19 @@
 
   content::NavigationSimulator::NavigateAndCommitFromBrowser(
       web_contents(), GURL("http://test.com"));
-
   EXPECT_TRUE(consumer.was_called());
 
+  observer()->CallDidFinishLoad();
   consumer.WaitForPageText();
-  EXPECT_EQ(base::ASCIIToUTF16("abcd"), *consumer.text());
+
+  ASSERT_TRUE(consumer.result());
+  EXPECT_THAT(
+      consumer.result()->frame_results(),
+      ::testing::UnorderedElementsAreArray({
+          MakeFrameDump(mojom::TextDumpEvent::kFirstLayout,
+                        main_rfh()->GetGlobalFrameRoutingId(),
+                        /*amp_frame=*/false, base::ASCIIToUTF16("abcd")),
+      }));
 
   EXPECT_THAT(
       fake_renderer_service.requests(),
@@ -394,14 +468,27 @@
 
   content::NavigationSimulator::NavigateAndCommitFromBrowser(
       web_contents(), GURL("http://test.com"));
-
   EXPECT_TRUE(consumer1.was_called());
   EXPECT_TRUE(consumer2.was_called());
 
+  observer()->CallDidFinishLoad();
   consumer1.WaitForPageText();
   consumer2.WaitForPageText();
-  EXPECT_EQ(base::ASCIIToUTF16("abc"), *consumer1.text());
-  EXPECT_EQ(base::ASCIIToUTF16("abc"), *consumer2.text());
+
+  ASSERT_TRUE(consumer1.result());
+  EXPECT_THAT(consumer1.result()->frame_results(),
+              ::testing::UnorderedElementsAreArray({
+                  MakeFrameDump(mojom::TextDumpEvent::kFirstLayout,
+                                main_rfh()->GetGlobalFrameRoutingId(),
+                                /*amp_frame=*/false, base::ASCIIToUTF16("abc")),
+              }));
+  ASSERT_TRUE(consumer2.result());
+  EXPECT_THAT(consumer2.result()->frame_results(),
+              ::testing::UnorderedElementsAreArray({
+                  MakeFrameDump(mojom::TextDumpEvent::kFirstLayout,
+                                main_rfh()->GetGlobalFrameRoutingId(),
+                                /*amp_frame=*/false, base::ASCIIToUTF16("abc")),
+              }));
 
   EXPECT_THAT(
       fake_renderer_service.requests(),
@@ -440,12 +527,20 @@
 
   content::NavigationSimulator::NavigateAndCommitFromBrowser(
       web_contents(), GURL("http://test.com"));
-
   EXPECT_TRUE(consumer1.was_called());
   EXPECT_FALSE(consumer2.was_called());
 
+  observer()->CallDidFinishLoad();
   consumer1.WaitForPageText();
-  EXPECT_EQ(base::ASCIIToUTF16("abc"), *consumer1.text());
+
+  ASSERT_TRUE(consumer1.result());
+  EXPECT_THAT(consumer1.result()->frame_results(),
+              ::testing::UnorderedElementsAreArray({
+                  MakeFrameDump(mojom::TextDumpEvent::kFirstLayout,
+                                main_rfh()->GetGlobalFrameRoutingId(),
+                                /*amp_frame=*/false, base::ASCIIToUTF16("abc")),
+              }));
+  EXPECT_FALSE(consumer2.result());
 
   EXPECT_THAT(
       fake_renderer_service.requests(),
@@ -486,14 +581,24 @@
 
   content::NavigationSimulator::NavigateAndCommitFromBrowser(
       web_contents(), GURL("http://test.com"));
-
   EXPECT_TRUE(consumer1.was_called());
   EXPECT_TRUE(consumer2.was_called());
 
+  observer()->CallDidFinishLoad();
   consumer1.WaitForPageText();
   consumer2.WaitForPageText();
-  EXPECT_EQ(base::ASCIIToUTF16("abc"), *consumer1.text());
-  EXPECT_EQ(base::ASCIIToUTF16("xyz"), *consumer2.text());
+
+  ASSERT_TRUE(consumer1.result());
+  EXPECT_THAT(consumer1.result()->frame_results(),
+              ::testing::UnorderedElementsAreArray({
+                  MakeFrameDump(mojom::TextDumpEvent::kFirstLayout,
+                                main_rfh()->GetGlobalFrameRoutingId(),
+                                /*amp_frame=*/false, base::ASCIIToUTF16("abc")),
+                  MakeFrameDump(mojom::TextDumpEvent::kFinishedLoad,
+                                main_rfh()->GetGlobalFrameRoutingId(),
+                                /*amp_frame=*/false, base::ASCIIToUTF16("xyz")),
+              }));
+  EXPECT_EQ(consumer1.result(), consumer2.result());
 
   EXPECT_THAT(
       fake_renderer_service.requests(),
@@ -503,4 +608,214 @@
       }));
 }
 
+TEST_F(PageTextObserverTest, AMPRequestedOnOOPIF) {
+  TestConsumer consumer;
+  observer()->AddConsumer(&consumer);
+
+  consumer.PopulateRequest(
+      /*max_size=*/1024,
+      /*events=*/{mojom::TextDumpEvent::kFirstLayout},
+      /*request_amp=*/true);
+
+  FakePageTextService fake_renderer_service;
+  fake_renderer_service.SetRemoteResponsesForEvent(
+      mojom::TextDumpEvent::kFirstLayout, {
+                                              base::ASCIIToUTF16("abc"),
+                                              base::ASCIIToUTF16("def"),
+                                              base::nullopt,
+                                          });
+
+  blink::AssociatedInterfaceProvider* remote_interfaces =
+      main_rfh()->GetRemoteAssociatedInterfaces();
+  remote_interfaces->OverrideBinderForTesting(
+      mojom::PageTextService::Name_,
+      base::BindRepeating(&FakePageTextService::BindPendingReceiver,
+                          base::Unretained(&fake_renderer_service)));
+
+  content::NavigationSimulator::NavigateAndCommitFromBrowser(
+      web_contents(), GURL("http://test.com"));
+  EXPECT_TRUE(consumer.was_called());
+
+  // Add an OOPIF subframe.
+  content::RenderFrameHost* oopif_subframe =
+      content::RenderFrameHostTester::For(main_rfh())->AppendChild("subframe");
+  observer()->SetIsOOPIF(oopif_subframe, true);
+
+  FakePageTextService subframe_fake_renderer_service;
+  blink::AssociatedInterfaceProvider* subframe_remote_interfaces =
+      oopif_subframe->GetRemoteAssociatedInterfaces();
+  subframe_remote_interfaces->OverrideBinderForTesting(
+      mojom::PageTextService::Name_,
+      base::BindRepeating(&FakePageTextService::BindPendingReceiver,
+                          base::Unretained(&subframe_fake_renderer_service)));
+  subframe_fake_renderer_service.SetRemoteResponsesForEvent(
+      mojom::TextDumpEvent::kFinishedLoad, {
+                                               base::ASCIIToUTF16("amp"),
+                                               base::nullopt,
+                                           });
+
+  observer()->RenderFrameCreated(oopif_subframe);
+  observer()->CallDidFinishLoad();
+  consumer.WaitForPageText();
+
+  EXPECT_THAT(
+      fake_renderer_service.requests(),
+      ::testing::UnorderedElementsAreArray({
+          mojom::PageTextDumpRequest(1024U, mojom::TextDumpEvent::kFirstLayout),
+      }));
+  EXPECT_THAT(subframe_fake_renderer_service.requests(),
+              ::testing::UnorderedElementsAreArray({
+                  mojom::PageTextDumpRequest(
+                      1024U, mojom::TextDumpEvent::kFinishedLoad),
+              }));
+
+  ASSERT_TRUE(consumer.result());
+  EXPECT_THAT(
+      consumer.result()->frame_results(),
+      ::testing::UnorderedElementsAreArray({
+          MakeFrameDump(mojom::TextDumpEvent::kFinishedLoad,
+                        oopif_subframe->GetGlobalFrameRoutingId(),
+                        /*amp_frame=*/true, base::ASCIIToUTF16("amp")),
+          MakeFrameDump(mojom::TextDumpEvent::kFirstLayout,
+                        main_rfh()->GetGlobalFrameRoutingId(),
+                        /*amp_frame=*/false, base::ASCIIToUTF16("abcdef")),
+      }));
+}
+
+TEST_F(PageTextObserverTest, AMPNotRequestedOnOOPIF) {
+  TestConsumer consumer;
+  observer()->AddConsumer(&consumer);
+
+  consumer.PopulateRequest(
+      /*max_size=*/1024,
+      /*events=*/{mojom::TextDumpEvent::kFirstLayout},
+      /*request_amp=*/false);
+
+  FakePageTextService fake_renderer_service;
+  fake_renderer_service.SetRemoteResponsesForEvent(
+      mojom::TextDumpEvent::kFirstLayout, {
+                                              base::ASCIIToUTF16("abc"),
+                                              base::ASCIIToUTF16("def"),
+                                              base::nullopt,
+                                          });
+
+  blink::AssociatedInterfaceProvider* remote_interfaces =
+      main_rfh()->GetRemoteAssociatedInterfaces();
+  remote_interfaces->OverrideBinderForTesting(
+      mojom::PageTextService::Name_,
+      base::BindRepeating(&FakePageTextService::BindPendingReceiver,
+                          base::Unretained(&fake_renderer_service)));
+
+  content::NavigationSimulator::NavigateAndCommitFromBrowser(
+      web_contents(), GURL("http://test.com"));
+  EXPECT_TRUE(consumer.was_called());
+
+  // Add an OOPIF subframe.
+  content::RenderFrameHost* oopif_subframe =
+      content::RenderFrameHostTester::For(main_rfh())->AppendChild("subframe");
+  observer()->SetIsOOPIF(oopif_subframe, true);
+
+  FakePageTextService subframe_fake_renderer_service;
+  blink::AssociatedInterfaceProvider* subframe_remote_interfaces =
+      oopif_subframe->GetRemoteAssociatedInterfaces();
+  subframe_remote_interfaces->OverrideBinderForTesting(
+      mojom::PageTextService::Name_,
+      base::BindRepeating(&FakePageTextService::BindPendingReceiver,
+                          base::Unretained(&subframe_fake_renderer_service)));
+  subframe_fake_renderer_service.SetRemoteResponsesForEvent(
+      mojom::TextDumpEvent::kFinishedLoad, {
+                                               base::ASCIIToUTF16("\n"),
+                                               base::ASCIIToUTF16("amp"),
+                                               base::nullopt,
+                                           });
+
+  observer()->RenderFrameCreated(oopif_subframe);
+  observer()->CallDidFinishLoad();
+  base::RunLoop().RunUntilIdle();
+
+  EXPECT_THAT(
+      fake_renderer_service.requests(),
+      ::testing::UnorderedElementsAreArray({
+          mojom::PageTextDumpRequest(1024U, mojom::TextDumpEvent::kFirstLayout),
+      }));
+  EXPECT_TRUE(subframe_fake_renderer_service.requests().empty());
+
+  ASSERT_TRUE(consumer.result());
+  EXPECT_THAT(
+      consumer.result()->frame_results(),
+      ::testing::UnorderedElementsAreArray({
+          MakeFrameDump(mojom::TextDumpEvent::kFirstLayout,
+                        main_rfh()->GetGlobalFrameRoutingId(),
+                        /*amp_frame=*/false, base::ASCIIToUTF16("abcdef")),
+      }));
+}
+
+TEST_F(PageTextObserverTest, AMPRequestedOnNonOOPIF) {
+  TestConsumer consumer;
+  observer()->AddConsumer(&consumer);
+
+  consumer.PopulateRequest(
+      /*max_size=*/1024,
+      /*events=*/{mojom::TextDumpEvent::kFirstLayout},
+      /*request_amp=*/true);
+
+  FakePageTextService fake_renderer_service;
+  fake_renderer_service.SetRemoteResponsesForEvent(
+      mojom::TextDumpEvent::kFirstLayout, {
+                                              base::ASCIIToUTF16("abc"),
+                                              base::ASCIIToUTF16("def"),
+                                              base::nullopt,
+                                          });
+
+  blink::AssociatedInterfaceProvider* remote_interfaces =
+      main_rfh()->GetRemoteAssociatedInterfaces();
+  remote_interfaces->OverrideBinderForTesting(
+      mojom::PageTextService::Name_,
+      base::BindRepeating(&FakePageTextService::BindPendingReceiver,
+                          base::Unretained(&fake_renderer_service)));
+
+  content::NavigationSimulator::NavigateAndCommitFromBrowser(
+      web_contents(), GURL("http://test.com"));
+  EXPECT_TRUE(consumer.was_called());
+
+  // Add a non-OOPIF subframe.
+  content::RenderFrameHost* subframe =
+      content::RenderFrameHostTester::For(main_rfh())->AppendChild("subframe");
+  observer()->SetIsOOPIF(subframe, false);
+
+  FakePageTextService subframe_fake_renderer_service;
+  blink::AssociatedInterfaceProvider* subframe_remote_interfaces =
+      subframe->GetRemoteAssociatedInterfaces();
+  subframe_remote_interfaces->OverrideBinderForTesting(
+      mojom::PageTextService::Name_,
+      base::BindRepeating(&FakePageTextService::BindPendingReceiver,
+                          base::Unretained(&subframe_fake_renderer_service)));
+  subframe_fake_renderer_service.SetRemoteResponsesForEvent(
+      mojom::TextDumpEvent::kFinishedLoad, {
+                                               base::ASCIIToUTF16("\n"),
+                                               base::ASCIIToUTF16("amp"),
+                                               base::nullopt,
+                                           });
+
+  observer()->RenderFrameCreated(subframe);
+  observer()->CallDidFinishLoad();
+  base::RunLoop().RunUntilIdle();
+
+  EXPECT_THAT(
+      fake_renderer_service.requests(),
+      ::testing::UnorderedElementsAreArray({
+          mojom::PageTextDumpRequest(1024U, mojom::TextDumpEvent::kFirstLayout),
+      }));
+  EXPECT_TRUE(subframe_fake_renderer_service.requests().empty());
+
+  ASSERT_TRUE(consumer.result());
+  EXPECT_THAT(
+      consumer.result()->frame_results(),
+      ::testing::UnorderedElementsAreArray({
+          MakeFrameDump(mojom::TextDumpEvent::kFirstLayout,
+                        main_rfh()->GetGlobalFrameRoutingId(),
+                        /*amp_frame=*/false, base::ASCIIToUTF16("abcdef")),
+      }));
+}
+
 }  // namespace optimization_guide
diff --git a/components/optimization_guide/content/renderer/page_text_agent.cc b/components/optimization_guide/content/renderer/page_text_agent.cc
index ce93532..6349c52 100644
--- a/components/optimization_guide/content/renderer/page_text_agent.cc
+++ b/components/optimization_guide/content/renderer/page_text_agent.cc
@@ -7,6 +7,7 @@
 #include "content/public/renderer/render_frame.h"
 #include "mojo/public/cpp/bindings/remote.h"
 #include "third_party/blink/public/common/associated_interfaces/associated_interface_registry.h"
+#include "third_party/blink/public/web/web_frame_content_dumper.h"
 
 namespace optimization_guide {
 
@@ -30,7 +31,8 @@
 }  // namespace
 
 PageTextAgent::PageTextAgent(content::RenderFrame* frame)
-    : content::RenderFrameObserverTracker<PageTextAgent>(frame) {
+    : content::RenderFrameObserver(frame),
+      content::RenderFrameObserverTracker<PageTextAgent>(frame) {
   if (!frame) {
     // For unittesting.
     return;
@@ -49,6 +51,11 @@
 PageTextAgent::MaybeRequestTextDumpOnLayoutEvent(
     blink::WebMeaningfulLayout event,
     uint32_t* max_size) {
+  // This code path is only supported for mainframes, when there is a frame.
+  if (render_frame() && !render_frame()->IsMainFrame()) {
+    return base::NullCallback();
+  }
+
   base::Optional<mojom::TextDumpEvent> mojo_event =
       LayoutEventAsMojoEvent(event);
   if (!mojo_event) {
@@ -94,6 +101,54 @@
   consumer->OnChunksEnd();
 }
 
+void PageTextAgent::DidFinishLoad() {
+  if (!render_frame()) {
+    return;
+  }
+
+  // Only subframes should use this code path.
+  if (render_frame()->IsMainFrame()) {
+    return;
+  }
+
+  // Only AMP subframes are supported.
+  if (!is_amp_page_) {
+    return;
+  }
+
+  auto requests_iter =
+      requests_by_event_.find(mojom::TextDumpEvent::kFinishedLoad);
+  if (requests_iter == requests_by_event_.end()) {
+    return;
+  }
+
+  mojo::PendingRemote<mojom::PageTextConsumer> pending_consumer =
+      std::move(requests_iter->second.second);
+  uint32_t max_size = requests_iter->second.first->max_size;
+  requests_by_event_.erase(mojom::TextDumpEvent::kFinishedLoad);
+
+  std::u16string content = blink::WebFrameContentDumper::DumpFrameTreeAsText(
+                               render_frame()->GetWebFrame(), max_size)
+                               .Utf16();
+  OnPageTextDump(std::move(pending_consumer), content);
+}
+
+void PageTextAgent::DidStartNavigation(
+    const GURL& url,
+    base::Optional<blink::WebNavigationType> navigation_type) {
+  is_amp_page_ = false;
+  // Note that |requests_by_event_| should NOT be reset here. Requests and
+  // navigations from the browser race with each other, and the text dump
+  // request normally wins. We don't want to drop a request when its navigation
+  // is just about to start.
+}
+
+void PageTextAgent::DidObserveLoadingBehavior(
+    blink::LoadingBehaviorFlag behavior) {
+  is_amp_page_ |=
+      behavior & blink::LoadingBehaviorFlag::kLoadingBehaviorAmpDocumentLoaded;
+}
+
 void PageTextAgent::RequestPageTextDump(
     mojom::PageTextDumpRequestPtr request,
     mojo::PendingRemote<mojom::PageTextConsumer> consumer) {
diff --git a/components/optimization_guide/content/renderer/page_text_agent.h b/components/optimization_guide/content/renderer/page_text_agent.h
index 6b253851..b76e630 100644
--- a/components/optimization_guide/content/renderer/page_text_agent.h
+++ b/components/optimization_guide/content/renderer/page_text_agent.h
@@ -14,6 +14,7 @@
 #include "base/memory/weak_ptr.h"
 #include "base/optional.h"
 #include "components/optimization_guide/content/mojom/page_text_service.mojom.h"
+#include "content/public/renderer/render_frame_observer.h"
 #include "content/public/renderer/render_frame_observer_tracker.h"
 #include "mojo/public/cpp/bindings/associated_receiver_set.h"
 #include "mojo/public/cpp/bindings/pending_remote.h"
@@ -27,10 +28,11 @@
 
 // PageTextAgent is the interface between ChromeRenderFrameObserver and
 // mojom::PageTextService. It currently supports requesting and getting text
-// dumps during |content::RenderFrameObserver::DidMeaningfulLayout|, but more
-// events will be added in the future.
+// dumps during |content::RenderFrameObserver::DidMeaningfulLayout|, and
+// |DidFinishLoad| for subframes.
 class PageTextAgent
     : public mojom::PageTextService,
+      public content::RenderFrameObserver,
       public content::RenderFrameObserverTracker<PageTextAgent> {
  public:
   explicit PageTextAgent(content::RenderFrame* frame);
@@ -52,6 +54,14 @@
       mojom::PageTextDumpRequestPtr request,
       mojo::PendingRemote<mojom::PageTextConsumer> consumer) override;
 
+  // content::RenderFrameObserver:
+  void OnDestruct() override {}
+  void DidObserveLoadingBehavior(blink::LoadingBehaviorFlag behavior) override;
+  void DidStartNavigation(
+      const GURL& url,
+      base::Optional<blink::WebNavigationType> navigation_type) override;
+  void DidFinishLoad() override;
+
   PageTextAgent(const PageTextAgent&) = delete;
   PageTextAgent& operator=(const PageTextAgent&) = delete;
 
@@ -69,6 +79,9 @@
                 mojo::PendingRemote<mojom::PageTextConsumer>>;
   std::map<mojom::TextDumpEvent, RequestAndConsumer> requests_by_event_;
 
+  // Set when an AMP page is detected from loading behavior flags.
+  bool is_amp_page_ = false;
+
   mojo::AssociatedReceiverSet<mojom::PageTextService> receivers_;
 
   base::WeakPtrFactory<PageTextAgent> weak_factory_{this};
diff --git a/components/optimization_guide/content/renderer/page_text_agent_browsertest.cc b/components/optimization_guide/content/renderer/page_text_agent_browsertest.cc
new file mode 100644
index 0000000..dfe551f
--- /dev/null
+++ b/components/optimization_guide/content/renderer/page_text_agent_browsertest.cc
@@ -0,0 +1,185 @@
+// Copyright 2021 The Chromium 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/optimization_guide/content/renderer/page_text_agent.h"
+
+#include <limits>
+#include <string>
+
+#include "base/bind.h"
+#include "base/callback.h"
+#include "base/optional.h"
+#include "base/strings/strcat.h"
+#include "base/strings/utf_string_conversions.h"
+#include "content/public/renderer/render_frame.h"
+#include "content/public/renderer/render_view.h"
+#include "content/public/test/render_view_test.h"
+#include "content/public/test/test_utils.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/public/web/web_local_frame.h"
+
+namespace optimization_guide {
+
+namespace {
+
+class TestConsumer : public mojom::PageTextConsumer {
+ public:
+  TestConsumer() = default;
+  ~TestConsumer() override = default;
+
+  std::u16string text() const { return base::StrCat(chunks_); }
+  bool on_chunks_end_called() const { return on_chunks_end_called_; }
+  size_t num_chunks() const { return chunks_.size(); }
+
+  void Bind(mojo::PendingReceiver<mojom::PageTextConsumer> pending_receiver) {
+    receiver_.Bind(std::move(pending_receiver));
+  }
+
+  // mojom::PageTextConsumer:
+  void OnTextDumpChunk(const std::u16string& chunk) override {
+    ASSERT_FALSE(on_chunks_end_called_);
+    chunks_.push_back(chunk);
+  }
+
+  void OnChunksEnd() override { on_chunks_end_called_ = true; }
+
+ private:
+  mojo::Receiver<mojom::PageTextConsumer> receiver_{this};
+  std::vector<std::u16string> chunks_;
+  bool on_chunks_end_called_ = false;
+};
+
+}  // namespace
+
+class PageTextAgentRenderViewTest : public content::RenderViewTest {
+ public:
+  PageTextAgentRenderViewTest() = default;
+  ~PageTextAgentRenderViewTest() override = default;
+};
+
+TEST_F(PageTextAgentRenderViewTest, AMPSubframeFirstLayout) {
+  // Create and get a subframe.
+  LoadHTML(
+      "<html><body>"
+      "  <p>hello</p>"
+      "  <iframe name=sub srcdoc=\"<p>world</p>\"></iframe>"
+      "</body></html>");
+  content::RenderFrame* subframe = content::RenderFrame::FromWebFrame(
+      GetMainFrame()->FindFrameByName("sub")->ToWebLocalFrame());
+
+  PageTextAgent subframe_agent(subframe);
+
+  // Send the request.
+  mojo::PendingRemote<mojom::PageTextConsumer> consumer_remote;
+  TestConsumer consumer;
+  consumer.Bind(consumer_remote.InitWithNewPipeAndPassReceiver());
+
+  auto request = mojom::PageTextDumpRequest::New();
+  request->max_size = 1024;
+  request->event = mojom::TextDumpEvent::kFirstLayout;
+  subframe_agent.RequestPageTextDump(std::move(request),
+                                     std::move(consumer_remote));
+
+  // Simulate a page load.
+  subframe_agent.DidObserveLoadingBehavior(
+      blink::LoadingBehaviorFlag::kLoadingBehaviorAmpDocumentLoaded);
+  subframe_agent.DidFinishLoad();
+  base::RunLoop().RunUntilIdle();
+  EXPECT_FALSE(consumer.on_chunks_end_called());
+}
+
+TEST_F(PageTextAgentRenderViewTest, NotAMPSubframeLoadFinished) {
+  // Create and get a subframe.
+  LoadHTML(
+      "<html><body>"
+      "  <p>hello</p>"
+      "  <iframe name=sub srcdoc=\"<p>world</p>\"></iframe>"
+      "</body></html>");
+  content::RenderFrame* subframe = content::RenderFrame::FromWebFrame(
+      GetMainFrame()->FindFrameByName("sub")->ToWebLocalFrame());
+
+  PageTextAgent subframe_agent(subframe);
+
+  // Send the request.
+  mojo::PendingRemote<mojom::PageTextConsumer> consumer_remote;
+  TestConsumer consumer;
+  consumer.Bind(consumer_remote.InitWithNewPipeAndPassReceiver());
+
+  auto request = mojom::PageTextDumpRequest::New();
+  request->max_size = 1024;
+  request->event = mojom::TextDumpEvent::kFinishedLoad;
+  subframe_agent.RequestPageTextDump(std::move(request),
+                                     std::move(consumer_remote));
+
+  // Simulate a page load.
+  subframe_agent.DidObserveLoadingBehavior(
+      blink::LoadingBehaviorFlag::kLoadingBehaviorNone);
+  subframe_agent.DidFinishLoad();
+  base::RunLoop().RunUntilIdle();
+  EXPECT_FALSE(consumer.on_chunks_end_called());
+}
+
+TEST_F(PageTextAgentRenderViewTest, MainFrame) {
+  // Create and get a subframe.
+  LoadHTML(
+      "<html><body>"
+      "  <p>hello</p>"
+      "</body></html>");
+  content::RenderFrame* mainframe =
+      content::RenderFrame::FromWebFrame(GetMainFrame());
+
+  PageTextAgent subframe_agent(mainframe);
+
+  // Send the request.
+  mojo::PendingRemote<mojom::PageTextConsumer> consumer_remote;
+  TestConsumer consumer;
+  consumer.Bind(consumer_remote.InitWithNewPipeAndPassReceiver());
+
+  auto request = mojom::PageTextDumpRequest::New();
+  request->max_size = 1024;
+  request->event = mojom::TextDumpEvent::kFinishedLoad;
+  subframe_agent.RequestPageTextDump(std::move(request),
+                                     std::move(consumer_remote));
+
+  // Simulate a page load.
+  subframe_agent.DidObserveLoadingBehavior(
+      blink::LoadingBehaviorFlag::kLoadingBehaviorAmpDocumentLoaded);
+  subframe_agent.DidFinishLoad();
+  base::RunLoop().RunUntilIdle();
+  EXPECT_FALSE(consumer.on_chunks_end_called());
+}
+
+TEST_F(PageTextAgentRenderViewTest, AMPSuccessCase) {
+  // Create and get a subframe.
+  LoadHTML(
+      "<html><body>"
+      "  <p>hello</p>"
+      "  <iframe name=sub srcdoc=\"<p>world</p>\"></iframe>"
+      "</body></html>");
+  content::RenderFrame* subframe = content::RenderFrame::FromWebFrame(
+      GetMainFrame()->FindFrameByName("sub")->ToWebLocalFrame());
+
+  PageTextAgent subframe_agent(subframe);
+
+  // Send the request.
+  mojo::PendingRemote<mojom::PageTextConsumer> consumer_remote;
+  TestConsumer consumer;
+  consumer.Bind(consumer_remote.InitWithNewPipeAndPassReceiver());
+
+  auto request = mojom::PageTextDumpRequest::New();
+  request->max_size = 1024;
+  request->event = mojom::TextDumpEvent::kFinishedLoad;
+  subframe_agent.RequestPageTextDump(std::move(request),
+                                     std::move(consumer_remote));
+
+  // Simulate a page load.
+  subframe_agent.DidObserveLoadingBehavior(
+      blink::LoadingBehaviorFlag::kLoadingBehaviorAmpDocumentLoaded);
+  subframe_agent.DidFinishLoad();
+  base::RunLoop().RunUntilIdle();
+  EXPECT_TRUE(consumer.on_chunks_end_called());
+  EXPECT_EQ(base::ASCIIToUTF16("world"), consumer.text());
+}
+
+}  // namespace optimization_guide
diff --git a/components/optimization_guide/core/BUILD.gn b/components/optimization_guide/core/BUILD.gn
index 58262f7..9f6ad51 100644
--- a/components/optimization_guide/core/BUILD.gn
+++ b/components/optimization_guide/core/BUILD.gn
@@ -61,6 +61,7 @@
     "prediction_model_file.h",
     "store_update_data.cc",
     "store_update_data.h",
+    "tab_url_provider.h",
     "top_host_provider.h",
     "url_pattern_with_wildcards.cc",
     "url_pattern_with_wildcards.h",
diff --git a/components/optimization_guide/core/hints_fetcher.cc b/components/optimization_guide/core/hints_fetcher.cc
index 09cbfcc6..661cbd61 100644
--- a/components/optimization_guide/core/hints_fetcher.cc
+++ b/components/optimization_guide/core/hints_fetcher.cc
@@ -437,7 +437,7 @@
       base::Time host_valid_time = base::Time::FromDeltaSinceWindowsEpoch(
           base::TimeDelta::FromSecondsD(*value));
       host_hints_due_for_refresh =
-          (host_valid_time - features::GetHintsFetchRefreshDuration() <=
+          (host_valid_time - features::GetHostHintsFetchRefreshDuration() <=
            time_clock_->Now());
     }
     if (host_hints_due_for_refresh)
diff --git a/components/optimization_guide/core/hints_fetcher_unittest.cc b/components/optimization_guide/core/hints_fetcher_unittest.cc
index bf009ad64..1c75ee18 100644
--- a/components/optimization_guide/core/hints_fetcher_unittest.cc
+++ b/components/optimization_guide/core/hints_fetcher_unittest.cc
@@ -275,7 +275,7 @@
   // Advancing the clock so that it's still one hour before the hints need to be
   // refreshed.
   test_clock.Advance(features::StoredFetchedHintsFreshnessDuration() -
-                     features::GetHintsFetchRefreshDuration() -
+                     features::GetHostHintsFetchRefreshDuration() -
                      base::TimeDelta().FromHours(1));
 
   EXPECT_FALSE(FetchHints({"foo.com"}, {} /* urls */));
diff --git a/components/optimization_guide/core/optimization_guide_features.cc b/components/optimization_guide/core/optimization_guide_features.cc
index bdf801a8..6e52d0a 100644
--- a/components/optimization_guide/core/optimization_guide_features.cc
+++ b/components/optimization_guide/core/optimization_guide_features.cc
@@ -62,7 +62,7 @@
     "OptimizationTargetPrediction", base::FEATURE_ENABLED_BY_DEFAULT};
 
 // Enables the downloading of models.
-const base::Feature kOptimizationGuideModelDownloading{
+const base::Feature kOptimizationGuideModelDownloading {
   "OptimizationGuideModelDownloading",
 #if BUILDFLAG(BUILD_WITH_TFLITE_LIB)
       base::FEATURE_ENABLED_BY_DEFAULT
@@ -75,6 +75,18 @@
 const base::Feature kPageContentAnnotations{"PageContentAnnotations",
                                             base::FEATURE_DISABLED_BY_DEFAULT};
 
+// This feature flag does not turn off any behavior, it is only used for
+// experiment parameters.
+const base::Feature kPageTextExtraction{
+    "OptimizationGuidePageContentExtraction", base::FEATURE_ENABLED_BY_DEFAULT};
+
+// The default value here is a bit of a guess.
+// TODO(crbug/1163244): This should be tuned once metrics are available.
+base::TimeDelta PageTextExtractionOutstandingRequestsGracePeriod() {
+  return base::TimeDelta::FromMilliseconds(GetFieldTrialParamByFeatureAsInt(
+      kPageTextExtraction, "outstanding_requests_grace_period_ms", 1000));
+}
+
 size_t MaxHintsFetcherTopHostBlocklistSize() {
   // The blocklist will be limited to the most engaged hosts and will hold twice
   // (2*N) as many hosts that the HintsFetcher request hints for. The extra N
@@ -87,15 +99,6 @@
          MaxHostsForOptimizationGuideServiceHintsFetch();
 }
 
-bool ShouldBatchUpdateHintsForTopHosts() {
-  if (base::FeatureList::IsEnabled(kRemoteOptimizationGuideFetching)) {
-    return GetFieldTrialParamByFeatureAsBool(kRemoteOptimizationGuideFetching,
-                                             "batch_update_hints_for_top_hosts",
-                                             true);
-  }
-  return false;
-}
-
 size_t MaxHostsForOptimizationGuideServiceHintsFetch() {
   return GetFieldTrialParamByFeatureAsInt(
       kRemoteOptimizationGuideFetching,
@@ -219,12 +222,25 @@
   return net::GetEffectiveConnectionTypeForName(param_value);
 }
 
-base::TimeDelta GetHintsFetchRefreshDuration() {
+base::TimeDelta GetHostHintsFetchRefreshDuration() {
   return base::TimeDelta::FromHours(GetFieldTrialParamByFeatureAsInt(
       kRemoteOptimizationGuideFetching, "hints_fetch_refresh_duration_in_hours",
       72));
 }
 
+base::TimeDelta GetActiveTabsFetchRefreshDuration() {
+  return base::TimeDelta::FromHours(GetFieldTrialParamByFeatureAsInt(
+      kRemoteOptimizationGuideFetching,
+      "active_tabs_fetch_refresh_duration_in_hours", 1));
+}
+
+base::TimeDelta GetActiveTabsStalenessTolerance() {
+  // 90 days initially chosen since that's how long local history lasts for.
+  return base::TimeDelta::FromDays(GetFieldTrialParamByFeatureAsInt(
+      kRemoteOptimizationGuideFetching,
+      "active_tabs_staleness_tolerance_in_days", 90));
+}
+
 size_t MaxConcurrentPageNavigationFetches() {
   // If overridden, this needs to be large enough where we do not thrash the
   // inflight page navigations since if we approach the limit here, we will
diff --git a/components/optimization_guide/core/optimization_guide_features.h b/components/optimization_guide/core/optimization_guide_features.h
index 52aba79f..195202c 100644
--- a/components/optimization_guide/core/optimization_guide_features.h
+++ b/components/optimization_guide/core/optimization_guide_features.h
@@ -27,6 +27,11 @@
 extern const base::Feature kOptimizationTargetPrediction;
 extern const base::Feature kOptimizationGuideModelDownloading;
 extern const base::Feature kPageContentAnnotations;
+extern const base::Feature kPageTextExtraction;
+
+// The grace period duration for how long to give outstanding page text dump
+// requests to respond after DidFinishLoad.
+base::TimeDelta PageTextExtractionOutstandingRequestsGracePeriod();
 
 // The maximum number of hosts that can be stored in the
 // |kHintsFetcherTopHostBlocklist| dictionary pref when initialized. The top
@@ -35,9 +40,6 @@
 // requested until the user navigates to the host again.
 size_t MaxHintsFetcherTopHostBlocklistSize();
 
-// Whether hints for top hosts should be batch updated.
-bool ShouldBatchUpdateHintsForTopHosts();
-
 // The maximum number of hosts allowed to be requested by the client to the
 // remote Optimzation Guide Service.
 size_t MaxHostsForOptimizationGuideServiceHintsFetch();
@@ -103,8 +105,16 @@
 // Returns the duration of the time window before hints expiration during which
 // the hosts should be refreshed. Example: If the hints for a host expire at
 // time T, then they are eligible for refresh at T -
-// GetHintsFetchRefreshDuration().
-base::TimeDelta GetHintsFetchRefreshDuration();
+// GetHostHintsFetchRefreshDuration().
+base::TimeDelta GetHostHintsFetchRefreshDuration();
+
+// Returns the duration of the time window between fetches for hints for the
+// URLs opened in active tabs.
+base::TimeDelta GetActiveTabsFetchRefreshDuration();
+
+// Returns the max duration since the time a tab has to be shown to be
+// considered active for a hints refresh.
+base::TimeDelta GetActiveTabsStalenessTolerance();
 
 // Returns the max number of concurrent fetches to the remote Optimization Guide
 // Service that should be allowed.
diff --git a/components/optimization_guide/core/tab_url_provider.h b/components/optimization_guide/core/tab_url_provider.h
new file mode 100644
index 0000000..992ae75
--- /dev/null
+++ b/components/optimization_guide/core/tab_url_provider.h
@@ -0,0 +1,33 @@
+// Copyright 2021 The Chromium 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_OPTIMIZATION_GUIDE_CORE_TAB_URL_PROVIDER_H_
+#define COMPONENTS_OPTIMIZATION_GUIDE_CORE_TAB_URL_PROVIDER_H_
+
+#include <vector>
+
+#include "base/time/time.h"
+#include "url/gurl.h"
+
+namespace optimization_guide {
+
+// A class to handle querying for the tab URLs for a user.
+class TabUrlProvider {
+ public:
+  virtual ~TabUrlProvider() = default;
+
+  // Returns URLS of tabs that are considered active for the user, as
+  // represented by |profile|. Tabs are considered active if they were last
+  // shown within |duration_since_last_shown|. The returned vector will be
+  // sorted by descending time since last shown.
+  virtual const std::vector<GURL> GetUrlsOfActiveTabs(
+      const base::TimeDelta& duration_since_last_shown) = 0;
+
+ protected:
+  TabUrlProvider() = default;
+};
+
+}  // namespace optimization_guide
+
+#endif  // COMPONENTS_OPTIMIZATION_GUIDE_CORE_TAB_URL_PROVIDER_H_
diff --git a/components/policy/resources/policy_templates.json b/components/policy/resources/policy_templates.json
index aef01528..14c1263 100644
--- a/components/policy/resources/policy_templates.json
+++ b/components/policy/resources/policy_templates.json
@@ -24024,7 +24024,7 @@
 
        If the policy is enabled and set to one or more domains, no lookalike warnings pages will be shown when the user visits pages on that domain.
 
-       If the policy is disabled, not set, or set to an empty list, warnings may appear on any site the user visits.
+       If the policy is not set, or set to an empty list, warnings may appear on any site the user visits.
 
        A hostname can be allowed with a complete host match, or any domain match. For example, a URL like "https://foo.example.com/bar" may have warnings suppressed if this list includes either "foo.example.com" or "example.com".'''
 
diff --git a/components/renderer_context_menu/render_view_context_menu_base.cc b/components/renderer_context_menu/render_view_context_menu_base.cc
index 50c634c..f269127 100644
--- a/components/renderer_context_menu/render_view_context_menu_base.cc
+++ b/components/renderer_context_menu/render_view_context_menu_base.cc
@@ -301,6 +301,22 @@
     toolkit_delegate_->RebuildMenu();
 }
 
+void RenderViewContextMenuBase::RemoveSeparatorBeforeMenuItem(int command_id) {
+  int index = menu_model_.GetIndexOfCommandId(command_id);
+  // Ignore if command not found (index == -1) or if it's the first menu item.
+  if (index <= 0)
+    return;
+
+  ui::MenuModel::ItemType prev_type = menu_model_.GetTypeAt(index - 1);
+  if (prev_type != ui::MenuModel::ItemType::TYPE_SEPARATOR)
+    return;
+
+  menu_model_.RemoveItemAt(index - 1);
+
+  if (toolkit_delegate_)
+    toolkit_delegate_->RebuildMenu();
+}
+
 RenderViewHost* RenderViewContextMenuBase::GetRenderViewHost() const {
   return source_web_contents_->GetMainFrame()->GetRenderViewHost();
 }
diff --git a/components/renderer_context_menu/render_view_context_menu_base.h b/components/renderer_context_menu/render_view_context_menu_base.h
index cfe99b0b..52002b1 100644
--- a/components/renderer_context_menu/render_view_context_menu_base.h
+++ b/components/renderer_context_menu/render_view_context_menu_base.h
@@ -114,6 +114,7 @@
   void UpdateMenuIcon(int command_id, const ui::ImageModel& icon) override;
   void RemoveMenuItem(int command_id) override;
   void RemoveAdjacentSeparators() override;
+  void RemoveSeparatorBeforeMenuItem(int command_id) override;
   content::RenderViewHost* GetRenderViewHost() const override;
   content::WebContents* GetWebContents() const override;
   content::BrowserContext* GetBrowserContext() const override;
diff --git a/components/renderer_context_menu/render_view_context_menu_proxy.h b/components/renderer_context_menu/render_view_context_menu_proxy.h
index a5575ef..21045c9 100644
--- a/components/renderer_context_menu/render_view_context_menu_proxy.h
+++ b/components/renderer_context_menu/render_view_context_menu_proxy.h
@@ -108,6 +108,9 @@
   // Removes separators so that any adjacent duplicates are reduced to only 1.
   virtual void RemoveAdjacentSeparators() = 0;
 
+  // Removes separator (if any) before the specified context menu item.
+  virtual void RemoveSeparatorBeforeMenuItem(int command_id) = 0;
+
   // Add spell check service item to the context menu.
   virtual void AddSpellCheckServiceItem(bool is_checked) = 0;
 
diff --git a/components/resources/BUILD.gn b/components/resources/BUILD.gn
index a36546c..fb82ba643 100644
--- a/components/resources/BUILD.gn
+++ b/components/resources/BUILD.gn
@@ -103,6 +103,11 @@
     "credits",
     rebase_path(about_credits_file, root_build_dir),
   ]
+
+  # Defined by downstream projects.
+  if (defined(extra_third_party_notice_dirs)) {
+    args += [ "--extra-third-party-dirs=$extra_third_party_notice_dirs" ]
+  }
 }
 
 if (is_android) {
diff --git a/components/spellcheck/browser/spell_check_host_impl.cc b/components/spellcheck/browser/spell_check_host_impl.cc
index d5e6a5cc..581a48a 100644
--- a/components/spellcheck/browser/spell_check_host_impl.cc
+++ b/components/spellcheck/browser/spell_check_host_impl.cc
@@ -69,15 +69,6 @@
 }
 
 #if defined(OS_WIN)
-void SpellCheckHostImpl::GetPerLanguageSuggestions(
-    const std::u16string& word,
-    GetPerLanguageSuggestionsCallback callback) {
-  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
-
-  // This API requires Chrome-only features.
-  std::move(callback).Run(std::vector<std::vector<std::u16string>>());
-}
-
 void SpellCheckHostImpl::InitializeDictionaries(
     InitializeDictionariesCallback callback) {
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
diff --git a/components/spellcheck/browser/spell_check_host_impl.h b/components/spellcheck/browser/spell_check_host_impl.h
index cc75b45..2d128920 100644
--- a/components/spellcheck/browser/spell_check_host_impl.h
+++ b/components/spellcheck/browser/spell_check_host_impl.h
@@ -52,10 +52,6 @@
                           FillSuggestionListCallback callback) override;
 
 #if defined(OS_WIN)
-  void GetPerLanguageSuggestions(
-      const std::u16string& word,
-      GetPerLanguageSuggestionsCallback callback) override;
-
   void InitializeDictionaries(InitializeDictionariesCallback callback) override;
 #endif  // defined(OS_WIN)
 #endif  // BUILDFLAG(USE_BROWSER_SPELLCHECKER) &&
diff --git a/components/spellcheck/browser/windows_spell_checker.cc b/components/spellcheck/browser/windows_spell_checker.cc
index 1594934..4843319 100644
--- a/components/spellcheck/browser/windows_spell_checker.cc
+++ b/components/spellcheck/browser/windows_spell_checker.cc
@@ -241,8 +241,18 @@
             (action == CORRECTIVE_ACTION_GET_SUGGESTIONS ||
              action == CORRECTIVE_ACTION_REPLACE)) {
           std::vector<std::u16string> suggestions;
-          FillSuggestionList(it->first, text.substr(start_index, error_length),
-                             &suggestions);
+          if (!base::FeatureList::IsEnabled(
+                  spellcheck::kWinRetrieveSuggestionsOnlyOnDemand)) {
+            // Perform the expensive operation of retrieving suggestions for all
+            // misspelled words while performing a text check. If
+            // kWinRetrieveSuggestionsOnlyOnDemand is set, suggestions will
+            // be retrieved on demand when the context menu is brought up with a
+            // misspelled word selected, and the spellcheck results returned by
+            // this method will have empty suggestion lists.
+            FillSuggestionList(it->first,
+                               text.substr(start_index, error_length),
+                               &suggestions);
+          }
 
           result_map[std::tuple<ULONG, ULONG>(start_index, error_length)]
               .push_back(suggestions);
diff --git a/components/spellcheck/browser/windows_spell_checker_unittest.cc b/components/spellcheck/browser/windows_spell_checker_unittest.cc
index f3ef72ad..d80259b 100644
--- a/components/spellcheck/browser/windows_spell_checker_unittest.cc
+++ b/components/spellcheck/browser/windows_spell_checker_unittest.cc
@@ -5,6 +5,7 @@
 #include "components/spellcheck/browser/spellcheck_platform.h"
 
 #include <stddef.h>
+#include <ostream>
 
 #include "base/bind.h"
 #include "base/containers/contains.h"
@@ -28,11 +29,21 @@
 
 namespace {
 
+struct RequestTextCheckTestCase {
+  const char* text_to_check;
+  const char* expected_suggestion;
+};
+
+std::ostream& operator<<(std::ostream& out,
+                         const RequestTextCheckTestCase& test_case) {
+  out << "text_to_check=" << test_case.text_to_check
+      << ", expected_suggestion=" << test_case.expected_suggestion;
+  return out;
+}
+
 class WindowsSpellCheckerTest : public testing::Test {
  public:
   WindowsSpellCheckerTest() {
-    feature_list_.InitAndEnableFeature(spellcheck::kWinUseBrowserSpellChecker);
-
     // The WindowsSpellchecker object can be created even on Windows versions
     // that don't support platform spellchecking. However, the spellcheck
     // factory won't be instantiated and the result returned in the
@@ -94,6 +105,15 @@
   }
 
  protected:
+  void SetUp() override {
+    feature_list_.InitWithFeatures(
+        /*enabled_features=*/{spellcheck::kWinUseBrowserSpellChecker,
+                              spellcheck::kWinRetrieveSuggestionsOnlyOnDemand},
+        /*disabled_features=*/{});
+  }
+
+  void RunRequestTextCheckTest(const RequestTextCheckTestCase& test_case);
+
   std::unique_ptr<WindowsSpellChecker> win_spell_checker_;
 
   bool callback_finished_ = false;
@@ -109,47 +129,38 @@
       base::test::TaskEnvironment::MainThreadType::UI};
 };
 
-TEST_F(WindowsSpellCheckerTest, RequestTextCheck) {
+void WindowsSpellCheckerTest::RunRequestTextCheckTest(
+    const RequestTextCheckTestCase& test_case) {
   ASSERT_EQ(set_language_result_,
             spellcheck::WindowsVersionSupportsSpellchecker());
 
-  static const struct {
-    const char* text_to_check;
-    const char* expected_suggestion;
-  } kTestCases[] = {
-      {"absense", "absence"},    {"becomeing", "becoming"},
-      {"cieling", "ceiling"},    {"definate", "definite"},
-      {"eigth", "eight"},        {"exellent", "excellent"},
-      {"finaly", "finally"},     {"garantee", "guarantee"},
-      {"humerous", "humorous"},  {"imediately", "immediately"},
-      {"jellous", "jealous"},    {"knowlege", "knowledge"},
-      {"lenght", "length"},      {"manuever", "maneuver"},
-      {"naturaly", "naturally"}, {"ommision", "omission"},
-  };
+  const std::u16string word(base::ASCIIToUTF16(test_case.text_to_check));
 
-  for (size_t i = 0; i < base::size(kTestCases); ++i) {
-    const auto& test_case = kTestCases[i];
-    const std::u16string word(base::ASCIIToUTF16(test_case.text_to_check));
+  // Check if the suggested words occur.
+  win_spell_checker_->RequestTextCheck(
+      1, word,
+      base::BindOnce(&WindowsSpellCheckerTest::TextCheckCompletionCallback,
+                     base::Unretained(this)));
+  RunUntilResultReceived();
 
-    // Check if the suggested words occur.
-    win_spell_checker_->RequestTextCheck(
-        1, word,
-        base::BindOnce(&WindowsSpellCheckerTest::TextCheckCompletionCallback,
-                       base::Unretained(this)));
-    RunUntilResultReceived();
+  if (!spellcheck::WindowsVersionSupportsSpellchecker()) {
+    // On Windows versions that don't support platform spellchecking, the
+    // returned vector of results should be empty.
+    ASSERT_TRUE(spell_check_results_.empty());
+    return;
+  }
 
-    if (!spellcheck::WindowsVersionSupportsSpellchecker()) {
-      // On Windows versions that don't support platform spellchecking, the
-      // returned vector of results should be empty.
-      ASSERT_TRUE(spell_check_results_.empty());
-      continue;
-    }
+  ASSERT_EQ(1u, spell_check_results_.size())
+      << "RequestTextCheck: Wrong number of results";
 
-    ASSERT_EQ(1u, spell_check_results_.size())
-        << "RequestTextCheckTests case " << i << ": Wrong number of results";
-
-    const std::vector<std::u16string>& suggestions =
-        spell_check_results_.front().replacements;
+  const std::vector<std::u16string>& suggestions =
+      spell_check_results_.front().replacements;
+  if (base::FeatureList::IsEnabled(
+          spellcheck::kWinRetrieveSuggestionsOnlyOnDemand)) {
+    // RequestTextCheck should return no suggestions.
+    ASSERT_TRUE(suggestions.empty())
+        << "RequestTextCheck: No suggestions are expected";
+  } else {
     const std::u16string suggested_word(
         base::ASCIIToUTF16(test_case.expected_suggestion));
     auto position =
@@ -158,11 +169,56 @@
                        return suggestion.compare(suggested_word) == 0;
                      });
 
-    ASSERT_NE(suggestions.end(), position) << "RequestTextCheckTests case " << i
-                                           << ": Expected suggestion not found";
+    ASSERT_FALSE(position == suggestions.end())
+        << "RequestTextCheck: Expected suggestion not found";
   }
 }
 
+static const RequestTextCheckTestCase kRequestTextCheckTestCases[] = {
+    {"absense", "absence"},    {"becomeing", "becoming"},
+    {"cieling", "ceiling"},    {"definate", "definite"},
+    {"eigth", "eight"},        {"exellent", "excellent"},
+    {"finaly", "finally"},     {"garantee", "guarantee"},
+    {"humerous", "humorous"},  {"imediately", "immediately"},
+    {"jellous", "jealous"},    {"knowlege", "knowledge"},
+    {"lenght", "length"},      {"manuever", "maneuver"},
+    {"naturaly", "naturally"}, {"ommision", "omission"},
+};
+
+class WindowsSpellCheckerRequestTextCheckTest
+    : public WindowsSpellCheckerTest,
+      public testing::WithParamInterface<RequestTextCheckTestCase> {};
+
+INSTANTIATE_TEST_SUITE_P(TestCases,
+                         WindowsSpellCheckerRequestTextCheckTest,
+                         testing::ValuesIn(kRequestTextCheckTestCases));
+
+TEST_P(WindowsSpellCheckerRequestTextCheckTest, RequestTextCheck) {
+  RunRequestTextCheckTest(GetParam());
+}
+
+class WindowsSpellCheckerRequestTextCheckWithSuggestionsTest
+    : public WindowsSpellCheckerRequestTextCheckTest {
+ protected:
+  void SetUp() override {
+    // Want to maintain test coverage for requesting suggestions on call to
+    // RequestTextCheck.
+    feature_list_.InitWithFeatures(
+        /*enabled_features=*/{spellcheck::kWinUseBrowserSpellChecker},
+        /*disabled_features=*/{
+            spellcheck::kWinRetrieveSuggestionsOnlyOnDemand});
+  }
+};
+
+INSTANTIATE_TEST_SUITE_P(TestCases,
+                         WindowsSpellCheckerRequestTextCheckWithSuggestionsTest,
+                         testing::ValuesIn(kRequestTextCheckTestCases));
+
+TEST_P(WindowsSpellCheckerRequestTextCheckWithSuggestionsTest,
+       RequestTextCheck) {
+  RunRequestTextCheckTest(GetParam());
+}
+
 TEST_F(WindowsSpellCheckerTest, RetrieveSpellcheckLanguages) {
   // Test retrieval of real dictionary on system (useful for debug logging
   // other registered dictionaries).
diff --git a/components/spellcheck/common/spellcheck.mojom b/components/spellcheck/common/spellcheck.mojom
index 21bce14..5116380 100644
--- a/components/spellcheck/common/spellcheck.mojom
+++ b/components/spellcheck/common/spellcheck.mojom
@@ -75,12 +75,6 @@
   FillSuggestionList(mojo_base.mojom.String16 word) =>
       (array<mojo_base.mojom.String16> suggestions);
 
-  // Returns a list of suggestions for each spellcheck language for a given word
-  // with a platform-specific spell checker.
-  [EnableIf=is_win, Sync]
-  GetPerLanguageSuggestions(mojo_base.mojom.String16 word) =>
-      (array<array<mojo_base.mojom.String16>> suggestions);
-
   // Completes initialization of the spellcheck service by loading dictionaries.
   [EnableIf=is_win]
   InitializeDictionaries() =>
diff --git a/components/spellcheck/common/spellcheck_features.cc b/components/spellcheck/common/spellcheck_features.cc
index ac010b7..99717a6 100644
--- a/components/spellcheck/common/spellcheck_features.cc
+++ b/components/spellcheck/common/spellcheck_features.cc
@@ -31,6 +31,9 @@
 const base::Feature kWinDelaySpellcheckServiceInit{
     "WinDelaySpellcheckServiceInit", base::FEATURE_DISABLED_BY_DEFAULT};
 
+const base::Feature kWinRetrieveSuggestionsOnlyOnDemand{
+    "WinRetrieveSuggestionsOnlyOnDemand", base::FEATURE_ENABLED_BY_DEFAULT};
+
 bool WindowsVersionSupportsSpellchecker() {
   return base::win::GetVersion() > base::win::Version::WIN7 &&
          base::win::GetVersion() < base::win::Version::WIN_LAST;
diff --git a/components/spellcheck/common/spellcheck_features.h b/components/spellcheck/common/spellcheck_features.h
index 282bd74c..a40f1850 100644
--- a/components/spellcheck/common/spellcheck_features.h
+++ b/components/spellcheck/common/spellcheck_features.h
@@ -38,6 +38,12 @@
 //    --disable-sync-types="Dictionary"
 extern const base::Feature kWinDelaySpellcheckServiceInit;
 
+// When set, do not perform the expensive operation of retrieving suggestions
+// for all misspelled words while performing a text check. Instead retrieve
+// suggestions on demand when the context menu is brought up with a misspelled
+// word selected.
+extern const base::Feature kWinRetrieveSuggestionsOnlyOnDemand;
+
 bool WindowsVersionSupportsSpellchecker();
 #endif  // defined(OS_WIN)
 
diff --git a/components/spellcheck/renderer/spellcheck_provider.cc b/components/spellcheck/renderer/spellcheck_provider.cc
index 4ed5859..81f932b 100644
--- a/components/spellcheck/renderer/spellcheck_provider.cc
+++ b/components/spellcheck/renderer/spellcheck_provider.cc
@@ -270,31 +270,17 @@
 #if defined(OS_WIN) && BUILDFLAG(USE_BROWSER_SPELLCHECKER)
     base::TimeTicks suggestions_start = base::TimeTicks::Now();
 #endif  // defined(OS_WIN) && BUILDFLAG(USE_BROWSER_SPELLCHECKER)
+    // Retrieve suggestions from Hunspell. Windows platform spellchecker
+    // suggestions are retrieved in SpellingMenuObserver::InitMenu on the
+    // browser process side to avoid a blocking IPC.
     spellcheck::PerLanguageSuggestions per_language_suggestions;
     spellcheck_->SpellCheckWord(word.c_str(), kWordStart, word.size(),
                                 routing_id(), &offset, &length,
                                 &per_language_suggestions);
 
 #if defined(OS_WIN) && BUILDFLAG(USE_BROWSER_SPELLCHECKER)
-    if (spellcheck::UseBrowserSpellChecker() &&
-        spellcheck_->EnabledLanguageCount() < spellcheck_->LanguageCount()) {
-      // Also fetch suggestions from the browser process (native spellchecker).
-      // This is a synchronous Mojo call, because this method must return
-      // synchronously.
-      spellcheck::PerLanguageSuggestions browser_suggestions;
-      GetSpellCheckHost().GetPerLanguageSuggestions(word, &browser_suggestions);
-
-      per_language_suggestions.reserve(per_language_suggestions.size() +
-                                       browser_suggestions.size());
-      per_language_suggestions.insert(per_language_suggestions.end(),
-                                      browser_suggestions.begin(),
-                                      browser_suggestions.end());
-      spellcheck_renderer_metrics::RecordHybridSuggestionDuration(
-          base::TimeTicks::Now() - suggestions_start);
-    } else {
-      spellcheck_renderer_metrics::RecordHunspellSuggestionDuration(
-          base::TimeTicks::Now() - suggestions_start);
-    }
+    spellcheck_renderer_metrics::RecordHunspellSuggestionDuration(
+        base::TimeTicks::Now() - suggestions_start);
 #endif  // defined(OS_WIN) && BUILDFLAG(USE_BROWSER_SPELLCHECKER)
 
     std::vector<std::u16string> suggestions;
diff --git a/components/spellcheck/renderer/spellcheck_provider_test.cc b/components/spellcheck/renderer/spellcheck_provider_test.cc
index 4d25a89..e406f09 100644
--- a/components/spellcheck/renderer/spellcheck_provider_test.cc
+++ b/components/spellcheck/renderer/spellcheck_provider_test.cc
@@ -186,12 +186,6 @@
 }
 
 #if defined(OS_WIN)
-void TestingSpellCheckProvider::GetPerLanguageSuggestions(
-    const std::u16string& word,
-    GetPerLanguageSuggestionsCallback callback) {
-  NOTREACHED();
-}
-
 void TestingSpellCheckProvider::InitializeDictionaries(
     InitializeDictionariesCallback callback) {
   if (base::FeatureList::IsEnabled(
diff --git a/components/spellcheck/renderer/spellcheck_provider_test.h b/components/spellcheck/renderer/spellcheck_provider_test.h
index 6868c032..493cbd1 100644
--- a/components/spellcheck/renderer/spellcheck_provider_test.h
+++ b/components/spellcheck/renderer/spellcheck_provider_test.h
@@ -144,11 +144,7 @@
                      CheckSpellingCallback) override;
   void FillSuggestionList(const std::u16string&,
                           FillSuggestionListCallback) override;
-
 #if defined(OS_WIN)
-  void GetPerLanguageSuggestions(
-      const std::u16string& word,
-      GetPerLanguageSuggestionsCallback callback) override;
   void InitializeDictionaries(InitializeDictionariesCallback callback) override;
 #endif  // defined(OS_WIN)
 #endif  // BUILDFLAG(USE_BROWSER_SPELLCHECKER)
diff --git a/components/translate/content/renderer/translate_agent.h b/components/translate/content/renderer/translate_agent.h
index b58459f..c06f675 100644
--- a/components/translate/content/renderer/translate_agent.h
+++ b/components/translate/content/renderer/translate_agent.h
@@ -162,7 +162,7 @@
   std::string source_lang_;
   std::string target_lang_;
 
-  // Time when a page langauge is determined. This is used to know a duration
+  // Time when a page language is determined. This is used to know a duration
   // time from showing infobar to requesting translation.
   base::TimeTicks language_determined_time_;
 
diff --git a/components/translate/core/browser/translate_manager.cc b/components/translate/core/browser/translate_manager.cc
index aa2f22e..1f5c371 100644
--- a/components/translate/core/browser/translate_manager.cc
+++ b/components/translate/core/browser/translate_manager.cc
@@ -178,7 +178,7 @@
   // object. It should instead be passed around to code that uses it.
   InitTranslateEvent(page_language_code, target_lang, *translate_prefs);
 
-  // Logs the initial source and target langauges, as well as whether the
+  // Logs the initial source and target languages, as well as whether the
   // initial source language is in the user's content language.
   GetActiveTranslateMetricsLogger()->LogInitialSourceLanguage(
       page_language_code,
diff --git a/components/webapk/android/libs/client/src/org/chromium/components/webapk/lib/client/WebApkValidator.java b/components/webapk/android/libs/client/src/org/chromium/components/webapk/lib/client/WebApkValidator.java
index 813219b..cd21da9 100644
--- a/components/webapk/android/libs/client/src/org/chromium/components/webapk/lib/client/WebApkValidator.java
+++ b/components/webapk/android/libs/client/src/org/chromium/components/webapk/lib/client/WebApkValidator.java
@@ -211,13 +211,6 @@
     @SuppressLint("PackageManagerGetSignatures")
     private static @ValidationResult int isValidWebApkInternal(
             Context context, String webappPackageName) {
-        if (sOverrideValidationForTesting) {
-            if (DEBUG) {
-                Log.d(TAG, "WebApk validation is disabled for testing.");
-            }
-            // Always return V1_WEB_APK in this case, because we only care if it's V1 WebAPK.
-            return ValidationResult.V1_WEB_APK;
-        }
         if (sExpectedSignature == null || sCommentSignedPublicKeyBytes == null) {
             Log.wtf(TAG,
                     "WebApk validation failure - expected signature not set - "
@@ -238,6 +231,13 @@
         if (isNotWebApkQuick(packageInfo)) {
             return ValidationResult.FAILURE;
         }
+        if (sOverrideValidationForTesting) {
+            if (DEBUG) {
+                Log.d(TAG, "WebApk validation is disabled for testing.");
+            }
+            // Always return V1_WEB_APK in this case, because we only care if it's V1 WebAPK.
+            return ValidationResult.V1_WEB_APK;
+        }
         if (verifyV1WebApk(packageInfo, webappPackageName)) {
             return ValidationResult.V1_WEB_APK;
         }
diff --git a/components/webapk/android/libs/client/src/org/chromium/components/webapk/lib/client/WebApkValidatorTest.java b/components/webapk/android/libs/client/src/org/chromium/components/webapk/lib/client/WebApkValidatorTest.java
index f379955..4c8ccbe 100644
--- a/components/webapk/android/libs/client/src/org/chromium/components/webapk/lib/client/WebApkValidatorTest.java
+++ b/components/webapk/android/libs/client/src/org/chromium/components/webapk/lib/client/WebApkValidatorTest.java
@@ -78,6 +78,7 @@
     public void setUp() {
         mPackageManager = Shadows.shadowOf(RuntimeEnvironment.application.getPackageManager());
         WebApkValidator.init(EXPECTED_SIGNATURE, PUBLIC_KEY);
+        WebApkValidator.setDisableValidationForTesting(false);
     }
 
     /**
@@ -467,6 +468,51 @@
                 RuntimeEnvironment.application, "https://evil.com/manifest.json"));
     }
 
+    /**
+     * Tests when override validation is set, {@link WebApkValidator.isValidWebApk} returns
+     * true with invalid signature.
+     */
+    @Test
+    public void testIsValidWebApkWithOverridesSignature() {
+        mPackageManager.addPackage(newPackageInfoWithBrowserSignature(
+                WEBAPK_PACKAGE_NAME, new Signature(SIGNATURE_1), TEST_STARTURL, null));
+
+        assertFalse(
+                WebApkValidator.isValidWebApk(RuntimeEnvironment.application, WEBAPK_PACKAGE_NAME));
+
+        WebApkValidator.setDisableValidationForTesting(true);
+        assertTrue(
+                WebApkValidator.isValidWebApk(RuntimeEnvironment.application, WEBAPK_PACKAGE_NAME));
+    }
+
+    /**
+     * Tests when override validation is set, {@link WebApkValidator.isValidWebApk} returns false
+     * when no START_URL.
+     */
+    @Test
+    public void testIsValidWebApkOverridesReturnsFalseNoStartUrl() {
+        PackageInfo webapkPackage = newPackageInfoWithBrowserSignature(
+                WEBAPK_PACKAGE_NAME, new Signature(EXPECTED_SIGNATURE), TEST_STARTURL, null);
+        webapkPackage.applicationInfo.metaData.remove(START_URL);
+        mPackageManager.addPackage(webapkPackage);
+
+        WebApkValidator.setDisableValidationForTesting(true);
+        assertFalse(
+                WebApkValidator.isValidWebApk(RuntimeEnvironment.application, WEBAPK_PACKAGE_NAME));
+    }
+
+    /**
+     * Tests when override validation is set, {@link WebApkValidator.isValidWebApk} returns false
+     * when package is not installed.
+     */
+    @Test
+    public void testIsValidWebApkOverridesPackageNotFound() {
+        WebApkValidator.setDisableValidationForTesting(true);
+
+        assertFalse(WebApkValidator.isValidWebApk(
+                RuntimeEnvironment.application, INVALID_WEBAPK_PACKAGE_NAME));
+    }
+
     // Get the full test file path.
     private static String testFilePath(String fileName) {
         return TestDir.getTestFilePath(TEST_DATA_DIR + fileName);
diff --git a/content/browser/accessibility/browser_accessibility_android.cc b/content/browser/accessibility/browser_accessibility_android.cc
index 3a6da58..5dfee16 100644
--- a/content/browser/accessibility/browser_accessibility_android.cc
+++ b/content/browser/accessibility/browser_accessibility_android.cc
@@ -496,26 +496,22 @@
   BrowserAccessibilityManagerAndroid* manager_android =
       static_cast<BrowserAccessibilityManagerAndroid*>(manager());
   if (manager_android->prune_tree_for_screen_reader()) {
-    // Headings with text can drop their children.
+    // For some nodes, we will consider children before determining if the node
+    // is a leaf. For nodes with relevant children, we will return false here
+    // and allow the child nodes to be set as a leaf.
+
+    // Headings with text can drop their children (with exceptions).
     std::u16string name = GetInnerText();
     if (GetRole() == ax::mojom::Role::kHeading && !name.empty()) {
-      if (HasFocusableNonOptionChild()) {
-        return false;
-      } else {
-        return true;
-      }
+      return IsLeafConsideringChildren();
     }
 
-    // Focusable nodes with text should not expose their children.
+    // Focusable nodes with text can drop their children (with exceptions).
     if (HasState(ax::mojom::State::kFocusable) && !name.empty()) {
-      if (HasFocusableNonOptionChild()) {
-        return false;
-      } else {
-        return true;
-      }
+      return IsLeafConsideringChildren();
     }
 
-    // Nodes with only static text as children should not expose their children.
+    // Nodes with only static text can drop their children.
     if (HasOnlyTextChildren())
       return true;
   }
@@ -523,6 +519,47 @@
   return false;
 }
 
+bool BrowserAccessibilityAndroid::IsLeafConsideringChildren() const {
+  // This is called from IsLeaf, so don't call PlatformChildCount
+  // from within this!
+
+  // Check for any children that should be exposed and return false if found (by
+  // returning false we are saying the parent node is NOT a leaf and this child
+  // node should instead be the leaf).
+  //
+  // If a node has a child that meets any of these criteria, it is NOT a leaf:
+  //
+  //   * child is focusable, and NOT a menu option
+  //   * child is a table, cell, or row
+  //
+  for (auto it = InternalChildrenBegin(); it != InternalChildrenEnd(); ++it) {
+    BrowserAccessibility* child = it.get();
+
+    if (child->HasState(ax::mojom::State::kFocusable) &&
+        child->GetRole() != ax::mojom::Role::kMenuListOption) {
+      return false;
+    }
+
+    if (child->GetRole() == ax::mojom::Role::kTable ||
+        child->GetRole() == ax::mojom::Role::kCell ||
+        child->GetRole() == ax::mojom::Role::kRow ||
+        child->GetRole() == ax::mojom::Role::kLayoutTable ||
+        child->GetRole() == ax::mojom::Role::kLayoutTableCell ||
+        child->GetRole() == ax::mojom::Role::kLayoutTableRow) {
+      return false;
+    }
+
+    // Check nested children and return false if any meet above criteria.
+    if (!static_cast<BrowserAccessibilityAndroid*>(child)
+             ->IsLeafConsideringChildren())
+      return false;
+  }
+
+  // If no such children were found, return true signaling the parent node can
+  // be the leaf node.
+  return true;
+}
+
 std::u16string BrowserAccessibilityAndroid::GetInnerText() const {
   if (ui::IsIframe(GetRole()))
     return std::u16string();
@@ -2112,21 +2149,6 @@
   }
 }
 
-bool BrowserAccessibilityAndroid::HasFocusableNonOptionChild() const {
-  // This is called from IsLeaf, so don't call PlatformChildCount
-  // from within this!
-  for (auto it = InternalChildrenBegin(); it != InternalChildrenEnd(); ++it) {
-    BrowserAccessibility* child = it.get();
-    if (child->HasState(ax::mojom::State::kFocusable) &&
-        child->GetRole() != ax::mojom::Role::kMenuListOption)
-      return true;
-    if (static_cast<BrowserAccessibilityAndroid*>(child)
-            ->HasFocusableNonOptionChild())
-      return true;
-  }
-  return false;
-}
-
 std::u16string BrowserAccessibilityAndroid::GetTargetUrl() const {
   if (ui::IsImageOrVideo(GetRole()) || ui::IsLink(GetRole()))
     return GetString16Attribute(ax::mojom::StringAttribute::kUrl);
diff --git a/content/browser/accessibility/browser_accessibility_android.h b/content/browser/accessibility/browser_accessibility_android.h
index 26ba139..33e14156 100644
--- a/content/browser/accessibility/browser_accessibility_android.h
+++ b/content/browser/accessibility/browser_accessibility_android.h
@@ -79,7 +79,6 @@
 
   bool HasAriaCurrent() const;
 
-  bool HasFocusableNonOptionChild() const;
   bool HasNonEmptyValue() const;
 
   bool HasCharacterLocations() const;
@@ -88,6 +87,7 @@
   const char* GetClassName() const;
   bool IsChildOfLeaf() const override;
   bool IsLeaf() const override;
+  bool IsLeafConsideringChildren() const;
   std::u16string GetInnerText() const override;
   std::u16string GetValueForControl() const override;
   std::u16string GetHint() const;
diff --git a/content/browser/accessibility/browser_accessibility_manager_unittest.cc b/content/browser/accessibility/browser_accessibility_manager_unittest.cc
index 13f73f2a..cf6baa4 100644
--- a/content/browser/accessibility/browser_accessibility_manager_unittest.cc
+++ b/content/browser/accessibility/browser_accessibility_manager_unittest.cc
@@ -1366,9 +1366,7 @@
 
   ui::AXNodeData parent_childtree;
   parent_childtree.id = 3;
-  parent_childtree.AddStringAttribute(
-      ax::mojom::StringAttribute::kChildTreeId,
-      child_update.tree_data.tree_id.ToString());
+  parent_childtree.AddChildTreeId(child_update.tree_data.tree_id);
   parent_childtree.SetName("parent_childtree");
   parent_childtree.relative_bounds.bounds = gfx::RectF(100, 100, 100, 100);
 
diff --git a/content/browser/accessibility/browser_accessibility_manager_win_unittest.cc b/content/browser/accessibility/browser_accessibility_manager_win_unittest.cc
index d3d1ed4..1af95d0 100644
--- a/content/browser/accessibility/browser_accessibility_manager_win_unittest.cc
+++ b/content/browser/accessibility/browser_accessibility_manager_win_unittest.cc
@@ -114,9 +114,7 @@
   ui::AXNodeData parent_tree_root;
   parent_tree_root.id = 1;
   parent_tree_root.role = ax::mojom::Role::kRootWebArea;
-  parent_tree_root.AddStringAttribute(
-      ax::mojom::StringAttribute::kChildTreeId,
-      child_tree_update.tree_data.tree_id.ToString());
+  parent_tree_root.AddChildTreeId(child_tree_update.tree_data.tree_id);
   ui::AXTreeUpdate parent_tree_update = MakeAXTreeUpdate(parent_tree_root);
 
   child_tree_update.tree_data.parent_tree_id =
diff --git a/content/browser/accessibility/browser_accessibility_unittest.cc b/content/browser/accessibility/browser_accessibility_unittest.cc
index 49061e3..ec4f6ba 100644
--- a/content/browser/accessibility/browser_accessibility_unittest.cc
+++ b/content/browser/accessibility/browser_accessibility_unittest.cc
@@ -128,8 +128,7 @@
   parent_tree_update.nodes[3].id = 4;
 
   parent_tree_update.nodes[4].id = 5;
-  parent_tree_update.nodes[4].AddStringAttribute(
-      ax::mojom::StringAttribute::kChildTreeId, child_tree_id.ToString());
+  parent_tree_update.nodes[4].AddChildTreeId(child_tree_id);
 
   parent_tree_update.nodes[5].id = 6;
   parent_tree_update.nodes[5].child_ids = {9};
@@ -857,8 +856,7 @@
 
   parent_tree_update.nodes[0].id = 1;
   parent_tree_update.nodes[0].role = ax::mojom::Role::kPortal;
-  parent_tree_update.nodes[0].AddStringAttribute(
-      ax::mojom::StringAttribute::kChildTreeId, child_tree_id.ToString());
+  parent_tree_update.nodes[0].AddChildTreeId(child_tree_id);
 
   ui::AXTreeUpdate child_tree_update;
   child_tree_update.tree_data.tree_id = child_tree_id;
diff --git a/content/browser/accessibility/dump_accessibility_tree_browsertest.cc b/content/browser/accessibility/dump_accessibility_tree_browsertest.cc
index 58efe94..8b275c7 100644
--- a/content/browser/accessibility/dump_accessibility_tree_browsertest.cc
+++ b/content/browser/accessibility/dump_accessibility_tree_browsertest.cc
@@ -2721,6 +2721,11 @@
   RunHtmlTest(AccessibilitySvgG_TestFile);
 }
 
+IN_PROC_BROWSER_TEST_P(DumpAccessibilityTreeTest,
+                       AccessibilityTabindexExposeChildren) {
+  RunHtmlTest(FILE_PATH_LITERAL("tabindex-expose-children.html"));
+}
+
 IN_PROC_BROWSER_TEST_P(DumpAccessibilityTreeTest, AccessibilityTableRowAdd) {
   RunHtmlTest(FILE_PATH_LITERAL("table-row-add.html"));
 }
diff --git a/content/browser/devtools/protocol/page_handler.cc b/content/browser/devtools/protocol/page_handler.cc
index 7d59a04..554b129 100644
--- a/content/browser/devtools/protocol/page_handler.cc
+++ b/content/browser/devtools/protocol/page_handler.cc
@@ -505,15 +505,10 @@
   params.frame_tree_node_id = frame_tree_node->frame_tree_node_id();
   frame_tree_node->navigator().controller().LoadURLWithParams(params);
 
-  base::UnguessableToken frame_token = frame_tree_node->devtools_frame_token();
-  auto navigate_callback = navigate_callbacks_.find(frame_token);
-  if (navigate_callback != navigate_callbacks_.end()) {
-    std::string error_string = net::ErrorToString(net::ERR_ABORTED);
-    navigate_callback->second->sendSuccess(out_frame_id, Maybe<std::string>(),
-                                           Maybe<std::string>(error_string));
-  }
   if (frame_tree_node->navigation_request()) {
-    navigate_callbacks_[frame_token] = std::move(callback);
+    navigate_callbacks_[frame_tree_node->navigation_request()
+                            ->devtools_navigation_token()] =
+        std::move(callback);
   } else {
     callback->sendSuccess(out_frame_id, Maybe<std::string>(),
                           Maybe<std::string>());
@@ -521,20 +516,29 @@
 }
 
 void PageHandler::NavigationReset(NavigationRequest* navigation_request) {
-  auto navigate_callback = navigate_callbacks_.find(
-      navigation_request->frame_tree_node()->devtools_frame_token());
+  auto navigate_callback =
+      navigate_callbacks_.find(navigation_request->devtools_navigation_token());
   if (navigate_callback == navigate_callbacks_.end())
     return;
   std::string frame_id =
       navigation_request->frame_tree_node()->devtools_frame_token().ToString();
-  bool success = navigation_request->GetNetErrorCode() == net::OK;
-  std::string error_string =
-      net::ErrorToString(navigation_request->GetNetErrorCode());
-  navigate_callback->second->sendSuccess(
-      frame_id,
-      Maybe<std::string>(
-          navigation_request->devtools_navigation_token().ToString()),
-      success ? Maybe<std::string>() : Maybe<std::string>(error_string));
+  // A new NavigationRequest may have been created before |navigation_request|
+  // started, in which case it is not marked as aborted. We report this as an
+  // abort to DevTools anyway.
+  if (!navigation_request->IsNavigationStarted()) {
+    navigate_callback->second->sendSuccess(
+        frame_id, Maybe<std::string>(),
+        Maybe<std::string>(net::ErrorToString(net::ERR_ABORTED)));
+  } else {
+    bool success = navigation_request->GetNetErrorCode() == net::OK;
+    std::string error_string =
+        net::ErrorToString(navigation_request->GetNetErrorCode());
+    navigate_callback->second->sendSuccess(
+        frame_id,
+        Maybe<std::string>(
+            navigation_request->devtools_navigation_token().ToString()),
+        success ? Maybe<std::string>() : Maybe<std::string>(error_string));
+  }
   navigate_callbacks_.erase(navigate_callback);
 }
 
diff --git a/content/browser/devtools/protocol/page_handler.h b/content/browser/devtools/protocol/page_handler.h
index 789e05c..be667cb7 100644
--- a/content/browser/devtools/protocol/page_handler.h
+++ b/content/browser/devtools/protocol/page_handler.h
@@ -244,6 +244,7 @@
   base::ScopedObservation<RenderWidgetHost, RenderWidgetHostObserver>
       observation_{this};
   JavaScriptDialogCallback pending_dialog_;
+  // Maps DevTools navigation tokens to pending NavigateCallbacks.
   base::flat_map<base::UnguessableToken, std::unique_ptr<NavigateCallback>>
       navigate_callbacks_;
   base::flat_set<download::DownloadItem*> pending_downloads_;
diff --git a/content/browser/prerender/prerender_browsertest.cc b/content/browser/prerender/prerender_browsertest.cc
index c6f727c6..b8afe0946 100644
--- a/content/browser/prerender/prerender_browsertest.cc
+++ b/content/browser/prerender/prerender_browsertest.cc
@@ -397,10 +397,19 @@
   base::Lock lock_;
 };
 
+// TODO(crbug.com/1189150): Some MPArch fail on android-asan and therefore
+// disabled.
+#if defined(ADDRESS_SANITIZER)
+INSTANTIATE_TEST_SUITE_P(All,
+                         PrerenderBrowserTest,
+                         testing::Values(kWebContents),
+                         ToString);
+#else
 INSTANTIATE_TEST_SUITE_P(All,
                          PrerenderBrowserTest,
                          testing::Values(kWebContents, kMPArch),
                          ToString);
+#endif
 
 IN_PROC_BROWSER_TEST_P(PrerenderBrowserTest, LinkRelPrerender) {
   const GURL kInitialUrl = GetUrl("/prerender/add_prerender.html");
diff --git a/content/browser/renderer_host/render_frame_host_manager.cc b/content/browser/renderer_host/render_frame_host_manager.cc
index cd71394..54eaff5 100644
--- a/content/browser/renderer_host/render_frame_host_manager.cc
+++ b/content/browser/renderer_host/render_frame_host_manager.cc
@@ -388,7 +388,7 @@
                            is_same_document_navigation,
                            clear_proxies_on_commit);
 
-  // Make sure any dynamic changes to this frame's sandbox flags and feature
+  // Make sure any dynamic changes to this frame's sandbox flags and permissions
   // policy that were made prior to navigation take effect.  This should only
   // happen for cross-document navigations.
   if (!is_same_document_navigation)
diff --git a/content/browser/security_exploit_browsertest.cc b/content/browser/security_exploit_browsertest.cc
index 2ba54e4f..2ff6de6 100644
--- a/content/browser/security_exploit_browsertest.cc
+++ b/content/browser/security_exploit_browsertest.cc
@@ -1535,7 +1535,7 @@
 
 // Test that the browser correctly reports a bad message when a child frame
 // attempts to navigate with a Trust Tokens redemption operation associated with
-// the navigation, but its parent lacks the trust-token-redemption Feature
+// the navigation, but its parent lacks the trust-token-redemption Permissions
 // Policy feature.
 IN_PROC_BROWSER_TEST_F(
     SecurityExploitBrowserTestWithTrustTokensEnabled,
@@ -1581,8 +1581,8 @@
 
 // Test that the browser correctly reports a bad message when a child frame
 // attempts to navigate with a Trust Tokens signing operation associated with
-// the navigation, but its parent lacks the trust-token-redemption (sic) Feature
-// Policy feature.
+// the navigation, but its parent lacks the trust-token-redemption (sic)
+// Permissions Policy feature.
 IN_PROC_BROWSER_TEST_F(
     SecurityExploitBrowserTestWithTrustTokensEnabled,
     BrowserForbidsTrustTokenSigningWithoutPermissionsPolicy) {
diff --git a/content/browser/utility_sandbox_delegate_win.cc b/content/browser/utility_sandbox_delegate_win.cc
index d0923bf..3147ebd 100644
--- a/content/browser/utility_sandbox_delegate_win.cc
+++ b/content/browser/utility_sandbox_delegate_win.cc
@@ -62,6 +62,19 @@
 
 // Sets the sandbox policy for the network service process.
 bool NetworkPreSpawnTarget(sandbox::TargetPolicy* policy) {
+  if (base::FeatureList::IsEnabled(
+          sandbox::policy::features::kNetworkServiceSandboxLPAC)) {
+    // LPAC sandbox is enabled, so do not use a restricted token.
+    if (sandbox::SBOX_ALL_OK !=
+        policy->SetTokenLevel(sandbox::USER_UNPROTECTED,
+                              sandbox::USER_UNPROTECTED)) {
+      return false;
+    }
+    // All other app container policies are set in
+    // SandboxWin::StartSandboxedProcess.
+    return true;
+  }
+
   // USER_LIMITED is as tight as this sandbox can be, because
   // DNS running in-process is blocked by USER_RESTRICTED and
   // below as it can't connect to the service.
@@ -115,6 +128,13 @@
 
 bool UtilitySandboxedProcessLauncherDelegate::GetAppContainerId(
     std::string* appcontainer_id) {
+  if (sandbox_type_ == sandbox::policy::SandboxType::kNetwork) {
+    DCHECK(base::FeatureList::IsEnabled(
+        sandbox::policy::features::kNetworkServiceSandboxLPAC));
+    *appcontainer_id = base::WideToUTF8(cmd_line_.GetProgram().value());
+    return true;
+  }
+
   if ((sandbox_type_ == sandbox::policy::SandboxType::kXrCompositing &&
        base::FeatureList::IsEnabled(sandbox::policy::features::kXRSandbox)) ||
       sandbox_type_ == sandbox::policy::SandboxType::kMediaFoundationCdm) {
@@ -137,6 +157,11 @@
       // Default policy is disabled for MF Cdm process to allow the application
       // of specific LPAC sandbox policies.
       return true;
+    case sandbox::policy::SandboxType::kNetwork:
+      // If LPAC is enabled for network sandbox then LPAC-specific policy is set
+      // elsewhere.
+      return base::FeatureList::IsEnabled(
+          sandbox::policy::features::kNetworkServiceSandboxLPAC);
     default:
       return false;
   }
@@ -149,10 +174,8 @@
 
 bool UtilitySandboxedProcessLauncherDelegate::PreSpawnTarget(
     sandbox::TargetPolicy* policy) {
-  if (sandbox_type_ == sandbox::policy::SandboxType::kNetwork) {
-    if (!NetworkPreSpawnTarget(policy))
-      return false;
-  }
+  if (sandbox_type_ == sandbox::policy::SandboxType::kNetwork)
+    return NetworkPreSpawnTarget(policy);
 
   if (sandbox_type_ == sandbox::policy::SandboxType::kAudio) {
     if (!AudioPreSpawnTarget(policy))
diff --git a/content/browser/webrtc/webrtc_image_capture_browsertest.cc b/content/browser/webrtc/webrtc_image_capture_browsertest.cc
index ce7f83f..6b32e9d 100644
--- a/content/browser/webrtc/webrtc_image_capture_browsertest.cc
+++ b/content/browser/webrtc/webrtc_image_capture_browsertest.cc
@@ -23,15 +23,16 @@
 
 namespace content {
 
-// Disable FocusDistance test which fails with Logitec cameras.
+// Disable FocusDistance test which fails with Logitech cameras.
 // TODO(crbug.com/957020): renable these tests when we have a way to detect
 // which device is connected and hence avoid running it if the camera is
 // Logitech.
 #define MAYBE_ManipulateFocusDistance DISABLED_ManipulateFocusDistance
 
-#if defined(OS_ANDROID)
 // TODO(crbug.com/793859): Re-enable test on Android as soon as the cause for
 // the bug is understood and fixed.
+// TODO(crbug.com/1187247): Flaky on Linux/Windows.
+#if defined(OS_ANDROID) || defined(OS_LINUX) || defined(OS_WIN)
 #define MAYBE_ManipulatePan DISABLED_ManipulatePan
 #define MAYBE_ManipulateZoom DISABLED_ManipulateZoom
 #else
@@ -205,12 +206,24 @@
       RunImageCaptureTestCase("testCreateAndGetPhotoSettingsSucceeds()"));
 }
 
-IN_PROC_BROWSER_TEST_P(WebRtcImageCaptureSucceedsBrowserTest, TakePhoto) {
+// TODO(crbug.com/1187247): Flaky on Linux/Windows.
+#if defined(OS_LINUX) || defined(OS_WIN)
+#define MAYBE_TakePhoto DISABLED_TakePhoto
+#else
+#define MAYBE_TakePhoto TakePhoto
+#endif
+IN_PROC_BROWSER_TEST_P(WebRtcImageCaptureSucceedsBrowserTest, MAYBE_TakePhoto) {
   embedded_test_server()->StartAcceptingConnections();
   ASSERT_TRUE(RunImageCaptureTestCase("testCreateAndTakePhotoSucceeds()"));
 }
 
-IN_PROC_BROWSER_TEST_P(WebRtcImageCaptureSucceedsBrowserTest, GrabFrame) {
+// TODO(crbug.com/1187247): Flaky on Linux/Windows.
+#if defined(OS_LINUX) || defined(OS_WIN)
+#define MAYBE_GrabFrame DISABLED_GrabFrame
+#else
+#define MAYBE_GrabFrame GrabFrame
+#endif
+IN_PROC_BROWSER_TEST_P(WebRtcImageCaptureSucceedsBrowserTest, MAYBE_GrabFrame) {
   embedded_test_server()->StartAcceptingConnections();
   ASSERT_TRUE(RunImageCaptureTestCase("testCreateAndGrabFrameSucceeds()"));
 }
diff --git a/content/public/android/java/src/org/chromium/content/browser/LauncherThread.java b/content/public/android/java/src/org/chromium/content/browser/LauncherThread.java
index 8f9488b1..353e2797 100644
--- a/content/public/android/java/src/org/chromium/content/browser/LauncherThread.java
+++ b/content/public/android/java/src/org/chromium/content/browser/LauncherThread.java
@@ -20,7 +20,7 @@
     private static final JavaHandlerThread sThread =
             new JavaHandlerThread("Chrome_ProcessLauncherThread", Process.THREAD_PRIORITY_DEFAULT);
     private static final Handler sThreadHandler;
-    // Can be overritten in tests.
+    // Can be overwritten in tests.
     private static Handler sHandler;
     static {
         sThread.maybeStart();
diff --git a/content/public/browser/content_browser_client.cc b/content/public/browser/content_browser_client.cc
index 290e65b..fb89f8a 100644
--- a/content/public/browser/content_browser_client.cc
+++ b/content/public/browser/content_browser_client.cc
@@ -9,7 +9,7 @@
 // declarations instead of including more headers. If that is infeasible, adjust
 // the limit. For more info, see
 // https://chromium.googlesource.com/chromium/src/+/HEAD/docs/wmax_tokens.md
-#pragma clang max_tokens_here 853000
+#pragma clang max_tokens_here 850000
 
 #include <utility>
 
diff --git a/content/public/common/content_features.cc b/content/public/common/content_features.cc
index 967f7dd..60085dc0 100644
--- a/content/public/common/content_features.cc
+++ b/content/public/common/content_features.cc
@@ -972,7 +972,7 @@
                                        base::FEATURE_DISABLED_BY_DEFAULT};
 
 const base::Feature kMacV2GPUSandbox{"MacV2GPUSandbox",
-                                     base::FEATURE_DISABLED_BY_DEFAULT};
+                                     base::FEATURE_ENABLED_BY_DEFAULT};
 
 // Enables retrying to obtain list of available cameras on Macbooks after
 // restarting the video capture service if a previous attempt delivered zero
diff --git a/content/renderer/render_thread_impl.cc b/content/renderer/render_thread_impl.cc
index cac1c32..950b869 100644
--- a/content/renderer/render_thread_impl.cc
+++ b/content/renderer/render_thread_impl.cc
@@ -41,6 +41,7 @@
 #include "base/threading/thread_restrictions.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "base/trace_event/memory_dump_manager.h"
+#include "base/trace_event/memory_pressure_level_proto.h"
 #include "base/trace_event/trace_event.h"
 #include "base/trace_event/typed_macros.h"
 #include "base/values.h"
@@ -90,7 +91,6 @@
 #include "content/renderer/variations_render_thread_observer.h"
 #include "content/renderer/worker/embedded_shared_worker_stub.h"
 #include "content/renderer/worker/worker_thread_registry.h"
-#include "components/viz/common/features.h"
 #include "device/gamepad/public/cpp/gamepads.h"
 #include "gin/public/debug.h"
 #include "gpu/GLES2/gl2extchromium.h"
@@ -1740,8 +1740,9 @@
                 auto* event =
                     ctx.event<perfetto::protos::pbzero::ChromeTrackEvent>();
                 auto* data = event->set_chrome_memory_pressure_notification();
-                data->set_level(base::MemoryPressureListener::LevelAsTraceEnum(
-                    memory_pressure_level));
+                data->set_level(
+                    base::trace_event::MemoryPressureLevelToTraceEnum(
+                        memory_pressure_level));
               });
   if (blink_platform_impl_)
     blink::WebMemoryPressureListener::OnMemoryPressure(memory_pressure_level);
diff --git a/content/services/isolated_xr_device/xr_runtime_provider.cc b/content/services/isolated_xr_device/xr_runtime_provider.cc
index 9bf67133..5c14def 100644
--- a/content/services/isolated_xr_device/xr_runtime_provider.cc
+++ b/content/services/isolated_xr_device/xr_runtime_provider.cc
@@ -220,4 +220,4 @@
     : device_service_host_(std::move(device_service_host)),
       io_task_runner_(std::move(io_task_runner)) {}
 
-IsolatedXRRuntimeProvider::~IsolatedXRRuntimeProvider() = default;
+IsolatedXRRuntimeProvider::~IsolatedXRRuntimeProvider() = default;
\ No newline at end of file
diff --git a/content/services/isolated_xr_device/xr_runtime_provider.h b/content/services/isolated_xr_device/xr_runtime_provider.h
index 3810e07..b29ecc7 100644
--- a/content/services/isolated_xr_device/xr_runtime_provider.h
+++ b/content/services/isolated_xr_device/xr_runtime_provider.h
@@ -54,8 +54,20 @@
       scoped_refptr<base::SingleThreadTaskRunner> task_runner);
 
   bool should_check_openxr_ = false;
-  std::unique_ptr<device::OpenXrDevice> openxr_device_;
+
+  // We need to make sure openxr_device is destroyed before openxr_statics
+  // because there might be an active render-loop thread that is owned by
+  // openxr_device that has an active xr session,
+  // which get into a race condition if:
+  // - openxr_statics is destroyed due to a tab closing (or refresh),
+  //     triggering xrDestroyInstance on the XrInstance owned by OpenXRStatics
+  // - At the same time, the session is being ended on the renderloop thread,
+  //     and xrDestroySession could be running at the same time.
+  // Note that: Destructors for nonstatic member objects are called in the
+  // reverse order in which they appear in the class declaration.
   std::unique_ptr<device::OpenXrStatics> openxr_statics_;
+  std::unique_ptr<device::OpenXrDevice> openxr_device_;
+
   std::unique_ptr<viz::Gpu> viz_gpu_;
 #endif
 
diff --git a/content/test/BUILD.gn b/content/test/BUILD.gn
index c5cb6e0..2df239a93 100644
--- a/content/test/BUILD.gn
+++ b/content/test/BUILD.gn
@@ -665,7 +665,7 @@
   group("performance_web_engine_test_suite") {
     testonly = true
     data = [
-      "//content/test/gpu/run_telemetry_test_fuchsia.py",
+      "//content/test/gpu/run_telemetry_benchmark_fuchsia.py",
       "//content/test/gpu/",
     ]
     data_deps = [
diff --git a/content/test/data/accessibility/html/tabindex-expose-children-expected-android.txt b/content/test/data/accessibility/html/tabindex-expose-children-expected-android.txt
new file mode 100644
index 0000000..b40129f
--- /dev/null
+++ b/content/test/data/accessibility/html/tabindex-expose-children-expected-android.txt
@@ -0,0 +1,10 @@
+android.webkit.WebView focusable focused scrollable
+++android.view.View
+++++android.view.View
+++++++android.view.View name='1.'
+++++++android.view.View name='2.'
+++android.view.View focusable name='3. 4.'
+++++android.view.View
+++++++android.view.View
+++++++++android.view.View name='3.'
+++++++++android.view.View name='4.'
\ No newline at end of file
diff --git a/content/test/data/accessibility/html/tabindex-expose-children-expected-auralinux.txt b/content/test/data/accessibility/html/tabindex-expose-children-expected-auralinux.txt
new file mode 100644
index 0000000..11267851
--- /dev/null
+++ b/content/test/data/accessibility/html/tabindex-expose-children-expected-auralinux.txt
@@ -0,0 +1,14 @@
+[document web]
+++[section]
+++++[section]
+++++++[section] name='1.'
+++++++++[static] name='1.'
+++++++[section] name='2.'
+++++++++[static] name='2.'
+++[section] name='3. 4.'
+++++[section]
+++++++[section]
+++++++++[section] name='3.'
+++++++++++[static] name='3.'
+++++++++[section] name='4.'
+++++++++++[static] name='4.'
\ No newline at end of file
diff --git a/content/test/data/accessibility/html/tabindex-expose-children-expected-blink.txt b/content/test/data/accessibility/html/tabindex-expose-children-expected-blink.txt
new file mode 100644
index 0000000..f3d129d
--- /dev/null
+++ b/content/test/data/accessibility/html/tabindex-expose-children-expected-blink.txt
@@ -0,0 +1,23 @@
+rootWebArea
+++genericContainer ignored
+++++genericContainer ignored
+++++++genericContainer ignored
+++++++++layoutTable
+++++++++++genericContainer ignored
+++++++++++++layoutTableRow
+++++++++++++++layoutTableCell name='1.'
+++++++++++++++++staticText name='1.'
+++++++++++++++++++inlineTextBox name='1.'
+++++++++++++++layoutTableCell name='2.'
+++++++++++++++++staticText name='2.'
+++++++++++++++++++inlineTextBox name='2.'
+++++++genericContainer name='3. 4.'
+++++++++layoutTable
+++++++++++genericContainer ignored
+++++++++++++layoutTableRow
+++++++++++++++layoutTableCell name='3.'
+++++++++++++++++staticText name='3.'
+++++++++++++++++++inlineTextBox name='3.'
+++++++++++++++layoutTableCell name='4.'
+++++++++++++++++staticText name='4.'
+++++++++++++++++++inlineTextBox name='4.'
\ No newline at end of file
diff --git a/content/test/data/accessibility/html/tabindex-expose-children-expected-mac.txt b/content/test/data/accessibility/html/tabindex-expose-children-expected-mac.txt
new file mode 100644
index 0000000..e0fb06d7
--- /dev/null
+++ b/content/test/data/accessibility/html/tabindex-expose-children-expected-mac.txt
@@ -0,0 +1,14 @@
+AXWebArea
+++AXGroup
+++++AXGroup
+++++++AXGroup AXTitle='1.'
+++++++++AXStaticText AXValue='1.'
+++++++AXGroup AXTitle='2.'
+++++++++AXStaticText AXValue='2.'
+++AXGroup AXDescription='3. 4.'
+++++AXGroup
+++++++AXGroup
+++++++++AXGroup AXTitle='3.'
+++++++++++AXStaticText AXValue='3.'
+++++++++AXGroup AXTitle='4.'
+++++++++++AXStaticText AXValue='4.'
\ No newline at end of file
diff --git a/content/test/data/accessibility/html/tabindex-expose-children-expected-uia-win.txt b/content/test/data/accessibility/html/tabindex-expose-children-expected-uia-win.txt
new file mode 100644
index 0000000..824ee840
--- /dev/null
+++ b/content/test/data/accessibility/html/tabindex-expose-children-expected-uia-win.txt
@@ -0,0 +1,14 @@
+Document
+++Table Grid.ColumnCount=2 Grid.RowCount=1
+++++DataItem IsControlElement=false
+++++++DataItem Name='1.' GridItem.Column=0 GridItem.ColumnSpan=1 GridItem.Row=0 GridItem.RowSpan=1
+++++++++Text Name='1.' IsControlElement=false
+++++++DataItem Name='2.' GridItem.Column=1 GridItem.ColumnSpan=1 GridItem.Row=0 GridItem.RowSpan=1
+++++++++Text Name='2.' IsControlElement=false
+++Group Name='3. 4.'
+++++Table Grid.ColumnCount=2 Grid.RowCount=1
+++++++DataItem IsControlElement=false
+++++++++DataItem Name='3.' GridItem.Column=0 GridItem.ColumnSpan=1 GridItem.Row=0 GridItem.RowSpan=1
+++++++++++Text Name='3.' IsControlElement=false
+++++++++DataItem Name='4.' GridItem.Column=1 GridItem.ColumnSpan=1 GridItem.Row=0 GridItem.RowSpan=1
+++++++++++Text Name='4.' IsControlElement=false
\ No newline at end of file
diff --git a/content/test/data/accessibility/html/tabindex-expose-children-expected-win.txt b/content/test/data/accessibility/html/tabindex-expose-children-expected-win.txt
new file mode 100644
index 0000000..361d236
--- /dev/null
+++ b/content/test/data/accessibility/html/tabindex-expose-children-expected-win.txt
@@ -0,0 +1,14 @@
+ROLE_SYSTEM_DOCUMENT READONLY FOCUSABLE
+++ROLE_SYSTEM_TABLE layout-guess:true
+++++ROLE_SYSTEM_ROW
+++++++ROLE_SYSTEM_CELL name='1.'
+++++++++ROLE_SYSTEM_STATICTEXT name='1.'
+++++++ROLE_SYSTEM_CELL name='2.'
+++++++++ROLE_SYSTEM_STATICTEXT name='2.'
+++IA2_ROLE_SECTION name='3. 4.' FOCUSABLE
+++++ROLE_SYSTEM_TABLE layout-guess:true
+++++++ROLE_SYSTEM_ROW
+++++++++ROLE_SYSTEM_CELL name='3.'
+++++++++++ROLE_SYSTEM_STATICTEXT name='3.'
+++++++++ROLE_SYSTEM_CELL name='4.'
+++++++++++ROLE_SYSTEM_STATICTEXT name='4.'
\ No newline at end of file
diff --git a/content/test/data/accessibility/html/tabindex-expose-children.html b/content/test/data/accessibility/html/tabindex-expose-children.html
new file mode 100644
index 0000000..6fab16c
--- /dev/null
+++ b/content/test/data/accessibility/html/tabindex-expose-children.html
@@ -0,0 +1,21 @@
+<html>
+  <body>
+    <div>
+      <table>
+        <tr>
+          <td>1.</td>
+          <td>2.</td>
+        </tr>
+      </table>
+    </div>
+
+    <div tabindex="0">
+      <table>
+        <tr>
+          <td>3.</td>
+          <td>4.</td>
+        </tr>
+      </table>
+    </div>
+  </body>
+</html>
diff --git a/content/test/data/gpu/vc/webgpu_video.js b/content/test/data/gpu/vc/webgpu_video.js
index e7d45769c..c697a19 100644
--- a/content/test/data/gpu/vc/webgpu_video.js
+++ b/content/test/data/gpu/vc/webgpu_video.js
@@ -339,32 +339,42 @@
     ],
   });
 
-  // The frameId is increased by one every time oneFrame() is called.
-  var frameId = 0;
+  // videos #0-#3 : 30 fps.
+  // videos #3-#15: 15 fps.
+  // videos #16+: 7 fps.
+  // Not every video frame is ready in rAF callback. Only draw videos that
+  // are ready.
+  var videoIsReady = new Array(videos.length);
 
-  // The videos are displayed at different frame rate with lower indices in
-  // videos updated faster than hight indices.
-  // For videos #0-#3, they are to be displayed at 30 fps.
-  // For videos #3-#15, they are to be displayed at 15 fps.
-  // For videos #16+, they are to be displayed at 7.5 fps.
-  // Since oneFrame() is called at 30 fps, the video textures #0-#3 are copied
-  // every frame, #4 -#15 are copied every other frame and #16+ are copied
-  // every 4 frames.
-  function GetNumOfVideosToCopyForCurrentFrame(frameId) {
-    switch (frameId % 4) {
-      case 0:
-        return videos.length;
-      case 1:
-      case 3:
-        return Math.min(4, videos.length);
-      case 2:
-        return Math.min(16, videos.length);
-      default:
-        console.error('Something wrong with frameId % 4');
-    }
+  function UpdateIsVideoReady(video) {
+    videoIsReady[video.id] = true;
+    video.requestVideoFrameCallback(function () {
+      UpdateIsVideoReady(video);
+    });
   }
 
-  const oneFrame = () => {
+  for (const video of videos) {
+    video.requestVideoFrameCallback(function () {
+      UpdateIsVideoReady(video);
+    });
+  }
+
+  // If rAF is running at 60 fps, skip every other frame so the demo is
+  // running at 30 fps.
+  // 30 fps is 33 milliseconds/frame. To prevent skipping frames accidentally
+  // when rAF is running near 30fps with small delta, use 32 ms instead of 33 ms
+  // for comparison.
+  const frameTime30Fps = 32;
+  let lastTimestamp = performance.now();
+
+  const oneFrame = (timestamp) => {
+    const elapsed = timestamp - lastTimestamp;
+    if (elapsed < frameTime30Fps) {
+      window.requestAnimationFrame(oneFrame);
+      return;
+    }
+    lastTimestamp = timestamp;
+
     const swapChainTexture = swapChain.getCurrentTexture();
     renderPassDescriptor.colorAttachments[0].attachment = swapChainTexture
       .createView();
@@ -376,24 +386,21 @@
     passEncoder.setPipeline(pipeline);
     passEncoder.setVertexBuffer(0, verticesBuffer);
 
-    // These videos are displayed at different fps. Not every video needs to be
-    // updated in this frame. The videos at lower indices are updated faster
-    // (higher fps) than videos at higher indices. See comments of
-    // GetNumOfVideosToCopyForCurrentFrame() for how the videos with different
-    // fps are arranged.
-    const numVideosToCopy = GetNumOfVideosToCopyForCurrentFrame(frameId);
-
-    Promise.all(videos.slice(0, numVideosToCopy).
-      map(video => createImageBitmap(video))).then((videoFrames) => {
-        for (let i = 0; i < numVideosToCopy; ++i) {
-          device.queue.copyImageBitmapToTexture(
-            { imageBitmap: videoFrames[i], origin: { x: 0, y: 0 } },
-            { texture: videoTextures[i] },
-            {
-              width: videos[i].videoWidth, height: videos[i].videoHeight,
-              depthOrArrayLayers: 1
-            }
-          );
+    Promise.all(videos.map(video =>
+      (videoIsReady[video.id] ? createImageBitmap(video) : null))).
+      then((videoFrames) => {
+        for (let i = 0; i < videos.length; ++i) {
+          if (videoFrames[i] != undefined) {
+            device.queue.copyImageBitmapToTexture(
+              { imageBitmap: videoFrames[i], origin: { x: 0, y: 0 } },
+              { texture: videoTextures[i] },
+              {
+                width: videos[i].videoWidth, height: videos[i].videoHeight,
+                depthOrArrayLayers: 1
+              }
+            );
+            videoIsReady[i] = false;
+          }
         }
 
         for (let i = 0; i < videos.length; ++i) {
@@ -411,33 +418,31 @@
         passEncoder.endPass();
         device.queue.submit([commandEncoder.finish()]);
 
-        frameId++;
+        window.requestAnimationFrame(oneFrame);
       });
   }
 
-  const oneFrameWithImportTextureApi = () => {
+  const oneFrameWithImportTextureApi = (timestamp) => {
+    const elapsed = timestamp - lastTimestamp;
+    if (elapsed < frameTime30Fps) {
+      window.requestAnimationFrame(oneFrame);
+      return;
+    }
+    lastTimestamp = timestamp;
+
+    for (let i = 0; i < videos.length; ++i) {
+      if (videoIsReady[i]) {
+        videoTextures[i].destroy();
+        videoTextures[i] = device.experimentalImportTexture(
+          videos[i], GPUTextureUsage.SAMPLED);
+        videoIsReady[i] = false;
+      }
+    }
+
     const swapChainTexture = swapChain.getCurrentTexture();
     renderPassDescriptor.colorAttachments[0].attachment = swapChainTexture
       .createView();
 
-    // These videos are displayed at different fps. Not every video needs to be
-    // updated in this frame. The videos at lower indices are updated faster
-    // than videos at higher indices. See GetNumOfVideosToCopyForCurrentFrame()
-    // for how the videos with different fps are arranged.
-    const numVideosToCopy = GetNumOfVideosToCopyForCurrentFrame(frameId);
-
-    // Destroy the textures after submit to promptly recycle resources.
-    // The textures not being imported here this time are not to be destroyed.
-    // We still need to render those textures for this frame.
-    for (let i = 0; i < numVideosToCopy; ++i) {
-      videoTextures[i].destroy();
-    }
-
-    for (let i = 0; i < numVideosToCopy; ++i) {
-      videoTextures[i] = device.experimentalImportTexture(
-        videos[i], GPUTextureUsage.SAMPLED);
-    }
-
     const commandEncoder = device.createCommandEncoder();
     const passEncoder =
       commandEncoder.beginRenderPass(renderPassDescriptor);
@@ -472,13 +477,12 @@
     passEncoder.endPass();
     device.queue.submit([commandEncoder.finish()]);
 
-    frameId++;
+    window.requestAnimationFrame(oneFrameWithImportTextureApi);
   }
 
-  // Call oneFrame() every 33 milliseconds to simulate 30 fps.
   if (useImportTextureApi) {
-    setInterval(oneFrameWithImportTextureApi, 33);
+    window.requestAnimationFrame(oneFrameWithImportTextureApi);
   } else {
-    setInterval(oneFrame, 33);
+    window.requestAnimationFrame(oneFrame);
   }
 }
diff --git a/content/test/data/gpu/vc/webgpu_videos_mxn.html b/content/test/data/gpu/vc/webgpu_videos_mxn.html
index eab8b37d..3ed98dd 100644
--- a/content/test/data/gpu/vc/webgpu_videos_mxn.html
+++ b/content/test/data/gpu/vc/webgpu_videos_mxn.html
@@ -130,7 +130,7 @@
       const videoCount = videoRows * videoColumns;
       for (let i = 0; i < videoCount + 1; i++) {
         const video = document.createElement('video');
-        video.id = 'video_' + i;
+        video.id = i;
         video.loop = true;
         video.autoplay = true;
         video.muted = true;
diff --git a/content/test/gpu/gpu_tests/test_expectations/webgl2_conformance_expectations.txt b/content/test/gpu/gpu_tests/test_expectations/webgl2_conformance_expectations.txt
index 3e44f1d8..ca72ac9e 100644
--- a/content/test/gpu/gpu_tests/test_expectations/webgl2_conformance_expectations.txt
+++ b/content/test/gpu/gpu_tests/test_expectations/webgl2_conformance_expectations.txt
@@ -120,8 +120,6 @@
 crbug.com/angleproject/4807 [ win angle-d3d11 passthrough ] conformance2/glsl3/switch-case.html [ Failure ]
 crbug.com/1081973 conformance/buffers/buffer-data-and-buffer-sub-data.html [ Failure ]
 crbug.com/1082533 [ mac intel ] conformance/textures/misc/texture-copying-and-deletion.html [ Failure ]
-crbug.com/1082533 [ win angle-vulkan passthrough ] conformance/textures/misc/texture-copying-and-deletion.html [ Failure ]
-crbug.com/1018028 [ win angle-vulkan ] conformance/rendering/bind-framebuffer-flush-bug.html [ Failure ]
 crbug.com/angleproject/5038 conformance/extensions/ext-color-buffer-half-float.html [ Skip ]
 # TODO(crbug.com/1136231): Uncomment suppression for s3tc-and-rgtc.html below
 # under crbug.com/963205 once these two failures are fixed.
@@ -133,7 +131,6 @@
 
 # Failing on some platforms.
 crbug.com/1175371 [ android ] conformance/extensions/khr-parallel-shader-compile.html [ Failure ]
-crbug.com/1175371 [ chromeos ] conformance/extensions/khr-parallel-shader-compile.html [ Failure ]
 crbug.com/1175371 [ linux ] conformance/extensions/khr-parallel-shader-compile.html [ Failure ]
 crbug.com/1175371 [ mac ] conformance/extensions/khr-parallel-shader-compile.html [ Failure ]
 crbug.com/1175371 [ win ] conformance/extensions/khr-parallel-shader-compile.html [ Failure ]
@@ -143,23 +140,12 @@
 
 crbug.com/953120 conformance/programs/program-handling.html [ Failure ]
 
-crbug.com/949249 [ win passthrough angle-opengl nvidia ] conformance2/extensions/ovr_multiview2.html [ Failure ]
 crbug.com/949249 [ win passthrough angle-d3d11 ] conformance2/extensions/ovr_multiview2.html [ Failure ]
 crbug.com/949249 [ linux passthrough angle-opengl ] conformance2/extensions/ovr_multiview2.html [ Failure ]
 
-# Failing new test
-crbug.com/874620 [ linux nvidia ] conformance2/glsl3/const-struct-from-array-as-function-parameter.html [ Failure ]
-crbug.com/874620 [ angle-opengl win nvidia ] conformance2/glsl3/const-struct-from-array-as-function-parameter.html [ Failure ]
-
 # Too slow (take about one hour to run)
 crbug.com/619403 deqp/functional/gles3/builtinprecision/* [ Skip ]
 
-# Failing on NVIDIA OpenGL, but fixed in latest driver
-# TODO(http://crbug.com/887241): Upgrade the drivers on the bots.
-crbug.com/772651 [ linux nvidia ] conformance/glsl/bugs/vector-scalar-arithmetic-inside-loop.html [ Failure ]
-crbug.com/772651 [ android nvidia ] conformance/glsl/bugs/vector-scalar-arithmetic-inside-loop.html [ Failure ]
-crbug.com/772651 [ nvidia ] conformance/glsl/bugs/vector-scalar-arithmetic-inside-loop-complex.html [ Failure ]
-
 # This test needs to be rewritten to measure its expected
 # performance; it's currently too flaky even on release bots.
 crbug.com/735483 conformance/rendering/texture-switch-performance.html [ Skip ]
@@ -169,33 +155,16 @@
 # TODO(crbug.com/979444): once this is passing on the passthrough
 # command decoder on Android, simplify this expectation to just not
 # declare any OS.
-crbug.com/979444 [ chromeos ] conformance/textures/misc/texture-corner-case-videos.html [ RetryOnFailure ]
 crbug.com/979444 [ linux ] conformance/textures/misc/texture-corner-case-videos.html [ RetryOnFailure ]
 crbug.com/979444 [ mac no-asan no-passthrough ] conformance/textures/misc/texture-corner-case-videos.html [ RetryOnFailure ]
 crbug.com/979444 [ win ] conformance/textures/misc/texture-corner-case-videos.html [ RetryOnFailure ]
 crbug.com/979444 [ android angle-disabled ] conformance/textures/misc/texture-corner-case-videos.html [ RetryOnFailure ]
 
-# Nvidia bugs fixed in latest driver
-# TODO(http://crbug.com/887241): Upgrade the drivers on the bots.
-crbug.com/798117 [ angle-vulkan win nvidia ] conformance/glsl/bugs/assign-to-swizzled-twice-in-function.html [ Failure ]
-crbug.com/798117 [ linux nvidia ] conformance/glsl/bugs/assign-to-swizzled-twice-in-function.html [ Failure ]
-crbug.com/792210 [ nvidia ] conformance/glsl/bugs/in-parameter-passed-as-inout-argument-and-global.html [ Failure ]
-
 # Flaky timeouts on Intel Windows, Linux and Mac
 crbug.com/1085222 [ win10 intel-0x3e92 ] deqp/functional/gles3/shaderoperator/binary_operator_* [ Failure ]
-crbug.com/1085222 [ win10 intel-0x3e92 ] deqp/functional/gles3/shaderoperator/unary_operator_01.html [ Failure ]
-crbug.com/1085222 [ win10 intel-0x3e92 ] deqp/functional/gles3/shaderoperator/unary_operator_02.html [ Failure ]
 crbug.com/1085222 [ win10 intel-0x5912 ] deqp/functional/gles3/shaderoperator/binary_operator_* [ Failure ]
 crbug.com/1085222 [ win10 intel-0x5912 ] deqp/functional/gles3/shaderoperator/unary_operator_01.html [ Failure ]
 crbug.com/1085222 [ win10 intel-0x5912 ] deqp/functional/gles3/shaderoperator/unary_operator_02.html [ Failure ]
-crbug.com/1085222 [ linux intel-0x3e92 ] deqp/functional/gles3/shaderoperator/binary_operator_* [ RetryOnFailure ]
-crbug.com/1085222 [ linux intel-0x3e92 ] deqp/functional/gles3/shadermatrix/sub_* [ RetryOnFailure ]
-crbug.com/1085222 [ linux intel-0x3e92 ] deqp/functional/gles3/shadermatrix/mul_* [ RetryOnFailure ]
-crbug.com/1085222 [ linux intel-0x3e92 ] deqp/functional/gles3/shadermatrix/add_* [ RetryOnFailure ]
-crbug.com/1085222 [ linux intel-0x5912 ] deqp/functional/gles3/shaderoperator/binary_operator_* [ RetryOnFailure ]
-crbug.com/1085222 [ linux intel-0x5912 ] deqp/functional/gles3/shadermatrix/sub_* [ RetryOnFailure ]
-crbug.com/1085222 [ linux intel-0x5912 ] deqp/functional/gles3/shadermatrix/mul_* [ RetryOnFailure ]
-crbug.com/1085222 [ linux intel-0x5912 ] deqp/functional/gles3/shadermatrix/add_* [ RetryOnFailure ]
 crbug.com/1085222 [ catalina intel-0xa2e ] deqp/functional/gles3/shaderoperator/binary_operator_* [ RetryOnFailure ]
 crbug.com/1085222 [ catalina intel-0xa2e ] deqp/functional/gles3/shaderoperator/unary_operator_* [ RetryOnFailure ]
 
@@ -208,72 +177,22 @@
 crbug.com/1127867 [ win angle-d3d11 ] conformance2/textures/image_data/tex-3d-rgb565-rgb-unsigned_byte.html [ RetryOnFailure ]
 
 # Intel flaky issues
-crbug.com/1122744 [ win angle-d3d11 intel ] conformance/textures/misc/texparameter-test.html [ RetryOnFailure ]
-crbug.com/912579 [ angle-opengl win passthrough intel ] conformance2/rendering/out-of-bounds-index-buffers-after-copying.html [ RetryOnFailure ]
 crbug.com/1114780 [ win intel ] deqp/functional/gles3/multisample.html [ Skip ]
-crbug.com/1127867 [ win angle-d3d11 intel ] conformance2/textures/image_data/tex-3d-r16f-red-half_float.html [ RetryOnFailure ]
 # Flakily times out on driver 26.20.100.8141.
-crbug.com/1093482 [ win10 intel-0x3e92 ] deqp/functional/gles3/shadermatrix/div_dynamic.html [ RetryOnFailure ]
 crbug.com/1093482 [ win10 intel-0x5912 ] deqp/functional/gles3/shadermatrix/div_dynamic.html [ RetryOnFailure ]
 crbug.com/1093482 [ win10 intel-0x3e92 ] deqp/functional/gles3/shadermatrix/sub_dynamic.html [ RetryOnFailure ]
-crbug.com/1093482 [ win10 intel-0x5912 ] deqp/functional/gles3/shadermatrix/sub_dynamic.html [ RetryOnFailure ]
-
-# Intel failed issues
-crbug.com/angleproject/2994 [ angle-opengl win passthrough intel ] conformance2/textures/misc/copy-texture-image-same-texture.html [ Failure ]
-crbug.com/1042246 [ angle-opengl win passthrough intel ] conformance2/extensions/ext-texture-norm16.html [ Skip ]
-
-# Intel driver issues
-crbug.com/795030 [ win intel angle-opengl passthrough intel_lt_27.20.100.8280 ] deqp/functional/gles3/shadercommonfunction.html [ Failure ]
-crbug.com/602688 [ win intel angle-opengl passthrough intel_lt_26.20.100.7870 ] conformance2/glsl3/vector-dynamic-indexing.html [ Failure ]
-crbug.com/1082565 [ win intel angle-opengl passthrough intel_lt_26.20.100.8141 ] conformance/canvas/webgl-to-2d-canvas.html [ Failure ]
-
-# This case causes no-over-optimization-on-uniform-array fail.
-crbug.com/884210 [ angle-opengl win passthrough intel ] conformance/ogles/GL/gl_FragCoord/gl_FragCoord_001_to_003.html [ Skip ]
-
-crbug.com/602688 [ angle-opengl win passthrough intel intel_lt_25.20.100.6577 ] conformance/glsl/constructors/glsl-construct-mat2.html [ Failure ]
-crbug.com/602688 [ angle-opengl win passthrough intel intel_lt_25.20.100.6577 ] conformance2/textures/misc/texture-npot.html [ Failure ]
-crbug.com/854100 [ angle-opengl win passthrough intel intel_lt_25.20.100.6577 ] conformance/glsl/variables/gl-pointcoord.html [ Failure ]
-crbug.com/957631 [ angle-opengl win passthrough intel intel_lt_25.20.100.6577 ] conformance2/rendering/element-index-uint.html [ Failure ]
-crbug.com/angleproject/2880 [ angle-opengl win passthrough intel intel_lt_25.20.100.6577 ] deqp/functional/gles3/shaderbuiltinvar.html [ Failure ]
-
-# This is an OpenGL driver bug on Intel platform and it is fixed in
-# Intel Driver 25.20.100.6444.
-# Case no-over-optimization-on-uniform-array-08 always fail if run
-# case gl-min-uniforms first.
-# Temporarily skip these two cases now.
-crbug.com/953243 [ angle-opengl win passthrough intel intel_lt_25.20.100.6444 ] conformance/limits/gl-min-uniforms.html [ Skip ]
-crbug.com/953243 [ angle-opengl win passthrough intel intel_lt_25.20.100.6444 ] conformance/uniforms/no-over-optimization-on-uniform-array-08.html [ Skip ]
-
-# This is an OpenGL driver bug on Intel platform and it is fixed in
-# Intel Driver 25.20.100.6444.
-# Case no-over-optimization-on-uniform-array-09 always fail if run
-# case biuDepthRange_001_to_002 first.
-# Temporarily skip these two cases now because this issue blocks
-# WEBGL_video_texture implementation.
-crbug.com/907195 [ angle-opengl win passthrough intel intel_lt_25.20.100.6444 ] conformance/ogles/GL/biuDepthRange/biuDepthRange_001_to_002.html [ Skip ]
-crbug.com/907195 [ angle-opengl win passthrough intel intel_lt_25.20.100.6444 ] conformance/uniforms/no-over-optimization-on-uniform-array-09.html [ Skip ]
-
-# TODO(crbug.com/1000354): Remove when angle workaround is applied to win angle-opengles
-crbug.com/1000354 [ win angle-opengles ] conformance2/extensions/ext-texture-norm16.html [ Skip ]
 
 # Windows only.
 crbug.com/736926 [ win ] conformance2/textures/svg_image/tex-2d-rgb565-rgb-unsigned_short_5_6_5.html [ RetryOnFailure ]
-crbug.com/angleproject/3033 [ win no-passthrough ] conformance2/textures/misc/generate-mipmap-with-large-base-level.html [ Failure ]
 crbug.com/angleproject/1465 [ win angle-d3d11 ] conformance2/glsl3/tricky-loop-conditions.html [ Failure ]
 
 # Win / NVidia
-crbug.com/679639 [ angle-d3d11 win nvidia ] conformance2/rendering/draw-with-integer-texture-base-level.html [ Failure ]
-# Flake seen once.
-crbug.com/691951 [ angle-d3d11 win nvidia ] deqp/functional/gles3/shaderoperator/binary_operator_11.html [ RetryOnFailure ]
-crbug.com/735464 [ angle-d3d11 win nvidia ] deqp/functional/gles3/textureshadow/* [ RetryOnFailure ]
-crbug.com/907544 [ angle-vulkan win passthrough nvidia ] conformance/uniforms/uniform-default-values.html [ RetryOnFailure ]
 # Related to either upgrade to Windows 10 1909 or driver 451.48
 crbug.com/1106078 [ angle-d3d11 win nvidia-0x1cb3 ] deqp/functional/gles3/shadertexturefunction/texturesize.html [ Failure ]
 crbug.com/1106076 [ angle-d3d11 win nvidia-0x1cb3 ] conformance2/textures/misc/tex-3d-mipmap-levels-intel-bug.html [ Failure ]
 crbug.com/1106076 [ angle-d3d11 win nvidia-0x1cb3 ] conformance2/textures/misc/tex-mipmap-levels.html [ Failure ]
 
 # Win / NVIDIA Quadro P400 / D3D11 flaky failures
-crbug.com/680754 [ angle-d3d11 win nvidia-0x1cb3 ] deqp/data/gles3/shaders/functions.html [ Failure ]
 crbug.com/921052 [ angle-d3d11 win nvidia-0x1cb3 ] deqp/functional/gles3/framebufferblit/depth_stencil.html [ Failure ]
 crbug.com/1054795 [ angle-d3d11 win7 nvidia-0x1cb3 ] deqp/functional/gles3/transformfeedback/basic_types_interleaved_triangles.html [ Failure ]
 crbug.com/1054795 [ angle-d3d11 win7 nvidia-0x1cb3 ] deqp/functional/gles3/transformfeedback/basic_types_separate_triangles.html [ Failure ]
@@ -281,36 +200,16 @@
 crbug.com/1054795 [ angle-d3d11 win7 nvidia-0x1cb3 ] deqp/functional/gles3/transformfeedback/random_separate_triangles.html [ Failure ]
 crbug.com/1054795 [ angle-d3d11 win7 nvidia-0x1cb3 ] deqp/functional/gles3/transformfeedback/interpolation_flat.html [ Failure ]
 crbug.com/966143 [ angle-d3d11 win7 nvidia-0x1cb3 ] conformance/attribs/gl-vertex-attrib-unconsumed-out-of-bounds.html [ RetryOnFailure ]
-crbug.com/966143 [ angle-d3d11 win7 nvidia-0x1cb3 ] conformance/attribs/gl-vertex-attrib-zero-issues.html [ RetryOnFailure ]
-crbug.com/728670 [ angle-d3d11 win nvidia-0x1cb3 ] conformance/extensions/oes-texture-half-float-with-video.html [ RetryOnFailure ]
 crbug.com/728670 [ angle-d3d11 win nvidia-0x1cb3 ] conformance/textures/image_bitmap_from_video/tex-2d-rgba-rgba-unsigned_short_5_5_5_1.html [ RetryOnFailure ]
 crbug.com/728670 [ angle-d3d11 win nvidia-0x1cb3 ] conformance/textures/image_bitmap_from_video/tex-2d-rgba-rgba-unsigned_short_4_4_4_4.html [ RetryOnFailure ]
 crbug.com/728670 [ angle-d3d11 win nvidia-0x1cb3 ] conformance2/textures/video/* [ RetryOnFailure ]
 crbug.com/728670 [ angle-d3d11 win nvidia-0x1cb3 ] conformance2/textures/image_bitmap_from_video/* [ RetryOnFailure ]
 
 # Win / NVIDIA GeForce GTX 1660 / D3D11 flaky failures
-crbug.com/angleproject/4377 [ angle-d3d11 win nvidia-0x2184 ] conformance/attribs/gl-vertex-attrib-unconsumed-out-of-bounds.html [ RetryOnFailure ]
-crbug.com/1057635 [ angle-d3d11 win nvidia-0x2184 ] deqp/functional/gles3/framebufferblit/depth_stencil.html [ RetryOnFailure ]
 crbug.com/1073613 [ angle-d3d11 win nvidia-0x2184 ] conformance2/textures/misc/tex-3d-mipmap-levels-intel-bug.html [ Failure ]
 crbug.com/1073613 [ angle-d3d11 win nvidia-0x2184 ] conformance2/textures/misc/tex-mipmap-levels.html [ Failure ]
 crbug.com/1073613 [ angle-d3d11 win nvidia-0x2184 ] deqp/functional/gles3/shadertexturefunction/texturesize.html [ Failure ]
 
-# WIN / OpenGL / NVIDIA failures
-# crbug.com/963205 [ win nvidia angle-opengl passthrough ] conformance/extensions/s3tc-and-rgtc.html [ RetryOnFailure ]
-crbug.com/963205 [ win nvidia angle-opengl passthrough ] conformance/extensions/webgl-compressed-texture-s3tc-srgb.html [ RetryOnFailure ]
-crbug.com/715001 [ angle-opengl win nvidia                    ] conformance/limits/gl-max-texture-dimensions.html [ Failure ]
-crbug.com/703779 [ angle-opengl win nvidia                    ] conformance/textures/misc/texture-size.html [ Failure ]
-crbug.com/921055 [ angle-opengl win passthrough nvidia-0x1cb3 ] conformance2/textures/image_bitmap_from_image_data/tex-2d-rgb9_e5-rgb-float.html [ RetryOnFailure ]
-crbug.com/905003 [ angle-opengl win passthrough nvidia        ] conformance2/textures/misc/integer-cubemap-specification-order-bug.html [ Failure ]
-crbug.com/887578 [ angle-opengl win passthrough nvidia        ] deqp/data/gles3/shaders/conversions.html [ RetryOnFailure ]
-crbug.com/965648 [ angle-opengl win passthrough nvidia-0x1cb3 ] deqp/functional/gles3/shaderstruct.html [ RetryOnFailure ]
-crbug.com/822733 [ angle-opengl win nvidia-0x1cb3             ] deqp/functional/gles3/transformfeedback/* [ RetryOnFailure ]
-crbug.com/1060024 [ angle-opengl win passthrough nvidia ] deqp/functional/gles3/shaderloop_do_while.html [ RetryOnFailure ]
-crbug.com/1060024 [ angle-opengl win passthrough nvidia ] deqp/functional/gles3/shaderloop_for.html [ RetryOnFailure ]
-crbug.com/1060024 [ angle-opengl win passthrough nvidia ] deqp/functional/gles3/shaderloop_while.html [ RetryOnFailure ]
-crbug.com/angleproject/4555 [ win angle-opengl passthrough nvidia ] conformance/misc/type-conversion-test.html [ Skip ]
-
-
 # Win / AMD
 
 # Recently many tests have become flaky on this configuration, returning
@@ -325,24 +224,10 @@
 crbug.com/844483 [ angle-d3d11 win amd ] conformance2/textures/image_bitmap_from_canvas/tex-2d-rgb9_e5-rgb-half_float.html [ Skip ]
 crbug.com/844483 [ angle-d3d11 win amd ] deqp/functional/gles3/texturefiltering/2d_array_combinations_05.html [ Skip ]
 crbug.com/483282 [ angle-d3d11 win amd ] conformance2/rendering/blitframebuffer-stencil-only.html [ Failure ]
-crbug.com/828984 [ angle-d3d11 win amd ] deqp/functional/gles3/draw/draw_arrays_instanced.html [ RetryOnFailure ]
-crbug.com/828984 [ angle-d3d11 win amd ] deqp/functional/gles3/draw/draw_elements.html [ RetryOnFailure ]
-crbug.com/828984 [ angle-d3d11 win amd ] deqp/functional/gles3/draw/draw_range_elements.html [ RetryOnFailure ]
-crbug.com/828984 [ angle-d3d11 win amd ] deqp/functional/gles3/draw/random.html [ RetryOnFailure ]
-crbug.com/828984 [ angle-d3d11 win amd ] deqp/functional/gles3/samplerobject.html [ RetryOnFailure ]
-crbug.com/828984 [ angle-d3d11 win amd ] deqp/functional/gles3/textureshadow/2d_array_nearest_mipmap_linear_less.html [ RetryOnFailure ]
-crbug.com/828984 [ angle-d3d11 win amd ] conformance/glsl/bugs/logic-inside-block-without-braces.html [ RetryOnFailure ]
-crbug.com/828984 [ angle-d3d11 win amd ] conformance/glsl/functions/glsl-function-mod-float.html [ RetryOnFailure ]
 crbug.com/828984 [ angle-d3d11 win amd ] conformance/renderbuffers/depth-renderbuffer-initialization.html [ RetryOnFailure ]
-crbug.com/828984 [ angle-d3d11 win amd ] conformance/renderbuffers/renderbuffer-initialization.html [ RetryOnFailure ]
-crbug.com/828984 [ angle-d3d11 win amd ] conformance2/glsl3/vector-dynamic-indexing.html [ RetryOnFailure ]
 crbug.com/828984 [ angle-d3d11 win amd ] conformance2/textures/canvas_sub_rectangle/tex-2d-rgb9_e5-rgb-half_float.html [ RetryOnFailure ]
 crbug.com/828984 [ angle-d3d11 win amd ] conformance2/textures/canvas_sub_rectangle/tex-2d-rgb9_e5-rgb-float.html [ RetryOnFailure ]
-crbug.com/828984 [ angle-d3d11 win amd ] conformance2/textures/canvas_sub_rectangle/tex-2d-rgb32f-rgb-float.html [ RetryOnFailure ]
 crbug.com/844483 [ angle-d3d11 win amd ] conformance2/textures/canvas_sub_rectangle/tex-2d-srgb8_alpha8-rgba-unsigned_byte.html [ RetryOnFailure ]
-crbug.com/828984 [ angle-d3d11 win amd ] conformance2/textures/misc/copy-texture-image-webgl-specific.html [ RetryOnFailure ]
-crbug.com/878780 [ win amd ] conformance2/textures/webgl_canvas/* [ RetryOnFailure ]
-crbug.com/992457 [ win amd ] conformance2/textures/canvas_sub_rectangle/tex-2d-srgb8-rgb-unsigned_byte.html [ RetryOnFailure ]
 
 # Recent AMD drivers seem to have a regression with 3D textures.
 # (Some of these tests seem to occasionally pass unexpectedly.)
@@ -357,16 +242,10 @@
 crbug.com/angleproject/2424 [ angle-d3d11 win amd ] deqp/functional/gles3/texturespecification/basic_texsubimage3d_* [ Failure ]
 crbug.com/angleproject/2424 [ angle-d3d11 win amd ] deqp/functional/gles3/texturespecification/teximage3d_pbo_3d* [ Failure ]
 crbug.com/angleproject/2424 [ angle-d3d11 win amd ] deqp/functional/gles3/texturespecification/teximage3d_unpack_params.html [ Failure ]
-crbug.com/angleproject/2424 [ angle-d3d11 win amd ] deqp/functional/gles3/texturespecification/texsubimage3d_unpack_params.html [ Failure ]
 
 # AMD texture handling has become flaky.
 crbug.com/1110951 [ angle-d3d11 win amd ] conformance/textures/misc/texture-sub-image-cube-maps.html [ RetryOnFailure ]
 
-# Have seen this time out. Think it may be because it's currently
-# the first test that runs in the shard, and the browser might not
-# be coming up correctly.
-crbug.com/687374 [ win amd-0x6613 ] deqp/functional/gles3/multisample.html [ RetryOnFailure ]
-
 # Failing on AMD RX 5500 XT
 crbug.com/1152597 [ win amd-0x7340 angle-d3d11 ] conformance/renderbuffers/framebuffer-state-restoration.html [ Failure ]
 crbug.com/1152599 [ win amd-0x7340 angle-d3d11 ] conformance/rendering/polygon-offset.html [ Failure ]
@@ -382,7 +261,6 @@
 # D3D11 / AMD RX 5500 XT / Passthrough command decoder
 crbug.com/1159539 [ win amd-0x7340 angle-d3d11 passthrough ] deqp/functional/gles3/fbodepthbuffer.html [ RetryOnFailure ]
 crbug.com/1159539 [ win amd-0x7340 angle-d3d11 passthrough ] deqp/functional/gles3/fborender/resize_03.html [ RetryOnFailure ]
-crbug.com/1159539 [ win amd-0x7340 angle-d3d11 passthrough ] deqp/functional/gles3/fborender/recreate_color_00.html [ RetryOnFailure ]
 crbug.com/1159539 [ win amd-0x7340 angle-d3d11 passthrough ] deqp/functional/gles3/fborender/recreate_color_01.html [ RetryOnFailure ]
 crbug.com/1159539 [ win amd-0x7340 angle-d3d11 passthrough ] deqp/functional/gles3/fborender/recreate_color_04.html [ RetryOnFailure ]
 crbug.com/1159539 [ win amd-0x7340 angle-d3d11 passthrough ] deqp/functional/gles3/fborender/recreate_color_05.html [ RetryOnFailure ]
@@ -390,10 +268,6 @@
 # Flaky on AMD Win7
 crbug.com/989050 [ angle-d3d11 win7 amd ] conformance2/textures/misc/tex-unpack-params-imagedata.html [ RetryOnFailure ]
 
-# Passthrough command decoder / OpenGL / Windows
-crbug.com/835364 [ angle-opengl win passthrough nvidia ] deqp/functional/gles3/fbocompleteness.html [ Failure ]
-crbug.com/835364 [ angle-opengl win passthrough ] conformance/renderbuffers/depth-renderbuffer-initialization.html [ RetryOnFailure ]
-
 # Passthrough command decoder / Linux / OpenGL / NVIDIA
 crbug.com/766918 [ angle-opengl linux passthrough nvidia ] conformance2/textures/image_bitmap_from_video/* [ RetryOnFailure ]
 crbug.com/965648 [ angle-opengl linux passthrough nvidia ] deqp/functional/gles3/shaderstruct.html [ RetryOnFailure ]
@@ -408,28 +282,11 @@
 # Flakes heavily on many OpenGL configurations
 crbug.com/832238 [ mac angle-disabled ] conformance2/transform_feedback/too-small-buffers.html [ Failure ]
 
-# Regressions in 10.12.4.
-crbug.com/705865 [ sierra intel ] conformance2/textures/misc/tex-base-level-bug.html [ Failure ]
-crbug.com/705865 [ sierra intel no-passthrough ] conformance2/textures/misc/tex-mipmap-levels.html [ Failure ]
-
-# Regressions in 10.13
-crbug.com/774826 [ highsierra intel-0xa2e no-passthrough ] deqp/functional/gles3/fbocolorbuffer/tex2d_00.html [ Failure ]
-crbug.com/774826 [ mojave intel-0xa2e no-passthrough ] deqp/functional/gles3/fbocolorbuffer/tex2d_00.html [ Failure ]
-crbug.com/774826 [ highsierra intel-0xa2e no-passthrough ] deqp/functional/gles3/fboinvalidate/format_00.html [ Failure ]
-crbug.com/774826 [ mojave intel-0xa2e no-passthrough ] deqp/functional/gles3/fboinvalidate/format_00.html [ Failure ]
-crbug.com/774826 [ highsierra intel-0xa2e no-passthrough ] deqp/functional/gles3/framebufferblit/default_framebuffer_05.html [ Failure ]
-crbug.com/774826 [ mojave intel-0xa2e no-passthrough ] deqp/functional/gles3/framebufferblit/default_framebuffer_05.html [ Failure ]
-crbug.com/774827 [ highsierra nvidia-0xfe9 ] conformance2/glsl3/array-assign.html [ Failure ]
-crbug.com/774827 [ mojave nvidia-0xfe9 ] conformance2/glsl3/array-assign.html [ Failure ]
-crbug.com/774827 [ highsierra nvidia-0xfe9 ] deqp/functional/gles3/fborender/resize_03.html [ Failure ]
+# Regressed in 10.13, still fails in 10.14
 crbug.com/774827 [ mojave nvidia-0xfe9 ] deqp/functional/gles3/fborender/resize_03.html [ Failure ]
-crbug.com/774827 [ highsierra nvidia-0xfe9 ] deqp/functional/gles3/shaderindexing/mat_00.html [ Failure ]
 crbug.com/774827 [ mojave nvidia-0xfe9 ] deqp/functional/gles3/shaderindexing/mat_00.html [ Failure ]
-crbug.com/774827 [ highsierra nvidia-0xfe9 ] deqp/functional/gles3/shaderindexing/mat_02.html [ Failure ]
 crbug.com/774827 [ mojave nvidia-0xfe9 ] deqp/functional/gles3/shaderindexing/mat_02.html [ Failure ]
-crbug.com/774827 [ highsierra nvidia-0xfe9 ] deqp/functional/gles3/texturespecification/teximage2d_pbo_cube_00.html [ Failure ]
 crbug.com/774827 [ mojave nvidia-0xfe9 ] deqp/functional/gles3/texturespecification/teximage2d_pbo_cube_00.html [ Failure ]
-crbug.com/905003 [ sierra intel-0xa2e ] conformance2/textures/misc/integer-cubemap-specification-order-bug.html [ Failure ]
 
 # Fails on multiple GPU types.
 crbug.com/844311 [ mac ] conformance/glsl/misc/fragcolor-fragdata-invariant.html [ Failure ]
@@ -447,19 +304,11 @@
 crbug.com/728271 [ mac nvidia-0xfe9 ] deqp/functional/gles3/shaderindexing/tmp.html [ Failure ]
 crbug.com/641209 [ mac nvidia-0xfe9 ] deqp/functional/gles3/fbomultisample* [ Failure ]
 crbug.com/483282 [ mac nvidia-0xfe9 ] deqp/functional/gles3/framebufferblit/default_framebuffer_04.html [ Failure ]
-crbug.com/483282 [ mac nvidia-0xfe9 ] conformance/attribs/gl-disabled-vertex-attrib.html [ Failure ]
-crbug.com/483282 [ mac nvidia-0xfe9 ] conformance/extensions/webgl-compressed-texture-size-limit.html [ RetryOnFailure ]
 crbug.com/996344 [ mac nvidia-0xfe9 ] conformance/glsl/bugs/vector-scalar-arithmetic-inside-loop.html [ Failure ]
-crbug.com/483282 [ mac nvidia-0xfe9 ] conformance/programs/gl-bind-attrib-location-long-names-test.html [ Failure ]
-crbug.com/483282 [ mac nvidia-0xfe9 ] conformance/programs/gl-bind-attrib-location-test.html [ Failure ]
-crbug.com/483282 [ mac nvidia-0xfe9 no-passthrough ] conformance2/glsl3/loops-with-side-effects.html [ Failure ]
-crbug.com/981122 [ mac nvidia-0xfe9 ] conformance2/rendering/framebuffer-completeness-draw-framebuffer.html [ RetryOnFailure ]
 crbug.com/996344 [ mac nvidia-0xfe9 ] conformance2/textures/misc/tex-base-level-bug.html [ Failure ]
 crbug.com/483282 [ angle-disabled mac nvidia-0xfe9 ] conformance2/textures/misc/tex-input-validation.html [ Failure ]
 crbug.com/996344 [ mac nvidia-0xfe9 no-passthrough ] conformance2/textures/misc/tex-mipmap-levels.html [ Failure ]
-crbug.com/682834 [ mac nvidia-0xfe9 ] conformance2/textures/image_bitmap_from_video/tex-2d-rgba16f-rgba-half_float.html [ RetryOnFailure ]
 crbug.com/784817 [ mac nvidia-0xfe9 ] conformance/glsl/bugs/init-array-with-loop.html [ Failure ]
-crbug.com/716652 [ sierra nvidia-0xfe9 ] deqp/functional/gles3/draw/random.html [ Failure ]
 crbug.com/483282 [ mac nvidia-0xfe9 ] deqp/functional/gles3/framebufferblit/conversion_04.html [ Failure ]
 crbug.com/483282 [ mac nvidia-0xfe9 ] deqp/functional/gles3/framebufferblit/conversion_07.html [ Failure ]
 crbug.com/483282 [ mac nvidia-0xfe9 no-passthrough ] deqp/functional/gles3/framebufferblit/conversion_08.html [ Failure ]
@@ -473,23 +322,6 @@
 crbug.com/483282 [ mac nvidia-0xfe9 ] deqp/functional/gles3/framebufferblit/conversion_32.html [ Failure ]
 crbug.com/483282 [ mac nvidia-0xfe9 ] deqp/functional/gles3/framebufferblit/conversion_34.html [ Failure ]
 crbug.com/483282 [ mac nvidia-0xfe9 ] deqp/functional/gles3/pixelbufferobject.html [ Failure ]
-crbug.com/483282 [ mac nvidia-0xfe9 ] deqp/functional/gles3/negativevertexarrayapi.html [ Failure ]
-crbug.com/483282 [ mac nvidia-0xfe9 ] deqp/functional/gles3/shaderindexing/varying.html [ Failure ]
-crbug.com/614174 [ mac nvidia-0xfe9 ] deqp/functional/gles3/texturespecification/teximage2d_pbo_2d_00.html [ Failure ]
-crbug.com/614174 [ mac nvidia-0xfe9 ] deqp/functional/gles3/texturespecification/teximage2d_pbo_2d_01.html [ Failure ]
-crbug.com/614174 [ mac nvidia-0xfe9 ] deqp/functional/gles3/texturespecification/texsubimage2d_pbo_2d_00.html [ Failure ]
-crbug.com/614174 [ mac nvidia-0xfe9 ] deqp/functional/gles3/texturespecification/texsubimage2d_pbo_2d_01.html [ Failure ]
-crbug.com/614174 [ mac nvidia-0xfe9 ] deqp/functional/gles3/texturespecification/texsubimage2d_pbo_cube_00.html [ Failure ]
-crbug.com/614174 [ mac nvidia-0xfe9 ] deqp/functional/gles3/texturespecification/texsubimage2d_pbo_cube_01.html [ Failure ]
-crbug.com/614174 [ mac nvidia-0xfe9 ] deqp/functional/gles3/texturespecification/texsubimage2d_pbo_cube_02.html [ Failure ]
-crbug.com/614174 [ mac nvidia-0xfe9 ] deqp/functional/gles3/texturespecification/texsubimage2d_pbo_cube_03.html [ Failure ]
-crbug.com/614174 [ mac nvidia-0xfe9 ] deqp/functional/gles3/texturespecification/texsubimage2d_pbo_cube_04.html [ Failure ]
-crbug.com/614174 [ mac nvidia-0xfe9 ] deqp/functional/gles3/texturespecification/teximage3d_pbo_2d_array_00.html [ Failure ]
-crbug.com/614174 [ mac nvidia-0xfe9 ] deqp/functional/gles3/texturespecification/teximage3d_pbo_2d_array_01.html [ Failure ]
-crbug.com/614174 [ mac nvidia-0xfe9 ] deqp/functional/gles3/texturespecification/teximage3d_pbo_3d_00.html [ Failure ]
-crbug.com/614174 [ mac nvidia-0xfe9 ] deqp/functional/gles3/texturespecification/teximage3d_pbo_3d_01.html [ Failure ]
-crbug.com/614174 [ mac nvidia-0xfe9 ] deqp/functional/gles3/texturespecification/texsubimage3d_pbo_3d_00.html [ Failure ]
-crbug.com/614174 [ mac nvidia-0xfe9 ] deqp/functional/gles3/texturespecification/texsubimage3d_pbo_3d_01.html [ Failure ]
 crbug.com/483282 [ mac nvidia-0xfe9 ] deqp/functional/gles3/fragmentoutput/array.fixed.html [ Failure ]
 crbug.com/483282 [ mac nvidia-0xfe9 ] deqp/functional/gles3/fragmentoutput/basic.fixed.html [ Failure ]
 crbug.com/483282 [ mac nvidia-0xfe9 ] deqp/functional/gles3/fragmentoutput/random_00.html [ Failure ]
@@ -501,42 +333,24 @@
 crbug.com/483282 [ mac nvidia-0xfe9 ] deqp/functional/gles3/fbocolorbuffer/tex3d_05.html [ Failure ]
 crbug.com/483282 [ mac nvidia-0xfe9 ] deqp/functional/gles3/fbocolorbuffer/texcube_05.html [ Failure ]
 crbug.com/483282 [ mac nvidia-0xfe9 ] deqp/functional/gles3/fbocolorbuffer/blend.html [ Failure ]
-crbug.com/483282 [ mac nvidia-0xfe9 ] deqp/functional/gles3/draw/draw_arrays.html [ Failure ]
-crbug.com/483282 [ mac nvidia-0xfe9 ] deqp/functional/gles3/draw/draw_arrays_instanced.html [ Failure ]
-crbug.com/483282 [ mac nvidia-0xfe9 ] deqp/functional/gles3/draw/draw_elements.html [ Failure ]
-crbug.com/483282 [ mac nvidia-0xfe9 ] deqp/functional/gles3/draw/draw_elements_instanced.html [ Failure ]
-crbug.com/483282 [ mac nvidia-0xfe9 ] deqp/functional/gles3/draw/draw_range_elements.html [ Failure ]
 crbug.com/483282 [ mac nvidia-0xfe9 no-passthrough ] deqp/functional/gles3/fboinvalidate/format_02.html [ Failure ]
 crbug.com/483282 [ mac nvidia-0xfe9 ] deqp/functional/gles3/negativeshaderapi.html [ Failure ]
-crbug.com/483282 [ mac nvidia-0xfe9 ] deqp/functional/gles3/vertexarrays/multiple_attributes.output.html [ RetryOnFailure ]
-crbug.com/984588 [ mac nvidia-0xfe9 ] deqp/functional/gles3/framebufferblit/conversion_06.html [ Failure ]
-crbug.com/984588 [ mac nvidia-0xfe9 ] deqp/functional/gles3/framebufferblit/conversion_21.html [ Failure ]
-crbug.com/984588 [ mac nvidia-0xfe9 ] deqp/functional/gles3/framebufferblit/conversion_26.html [ Failure ]
 crbug.com/654187 [ mac nvidia-0xfe9 ] deqp/functional/gles3/framebufferblit/conversion_28.html [ Failure ]
 crbug.com/654187 [ mac nvidia-0xfe9 ] deqp/functional/gles3/framebufferblit/conversion_30.html [ Failure ]
 crbug.com/654187 [ mac nvidia-0xfe9 no-passthrough ] deqp/functional/gles3/framebufferblit/conversion_31.html [ Failure ]
 crbug.com/654187 [ mac nvidia-0xfe9 ] deqp/functional/gles3/framebufferblit/conversion_33.html [ Failure ]
 crbug.com/795052 [ mac nvidia-0xfe9 ] conformance2/uniforms/draw-with-uniform-blocks.html [ Failure ]
-crbug.com/911772 [ mac nvidia-0xfe9 ] conformance2/glsl3/compound-assignment-type-combination.html [ RetryOnFailure ]
-crbug.com/911772 [ mac nvidia-0xfe9 ] conformance2/query/occlusion-query.html [ RetryOnFailure ]
-crbug.com/911772 [ mac nvidia-0xfe9 ] conformance2/state/gl-object-get-calls.html [ RetryOnFailure ]
-crbug.com/911772 [ mac nvidia-0xfe9 ] conformance2/textures/canvas/tex-3d-r16f-red-half_float.html [ RetryOnFailure ]
 crbug.com/929398 [ mac nvidia-0xfe9 no-passthrough ] conformance2/glsl3/tricky-loop-conditions.html [ Failure ]
-crbug.com/952282 [ mac nvidia-0xfe9 ] conformance2/rendering/framebuffer-unsupported.html [ RetryOnFailure ]
-crbug.com/965209 [ mac nvidia-0xfe9 ] conformance2/samplers/multi-context-sampler-test.html [ RetryOnFailure ]
 crbug.com/756537 [ mac nvidia ] deqp/functional/gles3/shaderoperator/* [ Failure ]
-crbug.com/907599 [ mac nvidia debug ] conformance/textures/video/tex-2d-rgb-rgb-unsigned_short_5_6_5.html [ RetryOnFailure ]
-crbug.com/1022831 [ mac nvidia-0xfe9 ] deqp/functional/gles3/vertexarrays/single_attribute.usage.stream_draw.html [ RetryOnFailure ]
-crbug.com/1022831 [ mac nvidia-0xfe9 ] deqp/functional/gles3/vertexarrays/single_attribute.usage.dynamic_copy.html [ RetryOnFailure ]
-crbug.com/1022831 [ mac nvidia-0xfe9 ] deqp/functional/gles3/shaderloop_while.html [ RetryOnFailure ]
 crbug.com/1027534 [ mac nvidia-0xfe9 ] conformance2/rendering/uniform-block-buffer-size.html [ Failure ]
-crbug.com/1027776 [ mac nvidia-0xfe9 ] deqp/data/gles3/shaders/conversions.html [ Failure ]
 crbug.com/1027776 [ mac nvidia-0xfe9 ] conformance2/rendering/framebuffer-texture-level1.html [ Failure ]
 crbug.com/1037650 [ mac nvidia-0xfe9 ] conformance2/textures/canvas_sub_rectangle/* [ RetryOnFailure ]
 crbug.com/1073538 [ mac nvidia-0xfe9 ] deqp/functional/gles3/shadermatrix/div_dynamic.html [ RetryOnFailure ]
 crbug.com/1086194 [ mac nvidia-0xfe9 ] conformance/textures/canvas_sub_rectangle/* [ RetryOnFailure ]
 crbug.com/1136231 [ mac nvidia-0xfe9 ] conformance/extensions/s3tc-and-rgtc.html [ Failure ]
 
+crbug.com/772651 [ mac nvidia ] conformance/glsl/bugs/vector-scalar-arithmetic-inside-loop-complex.html [ Failure ]
+
 # Mac AMD
 # AMD Radeon HD 8870M (1002:6821)
 # TODO(kbr): uncomment the following two exepectations after test
@@ -580,15 +394,11 @@
 crbug.com/1027605 [ mac amd-0x6821 no-passthrough ] deqp/functional/gles3/transformfeedback/random_separate_lines.html [ RetryOnFailure ]
 crbug.com/1027605 [ mac amd-0x6821 no-passthrough ] deqp/functional/gles3/transformfeedback/random_separate_points.html [ RetryOnFailure ]
 crbug.com/1027605 [ mac amd-0x6821 no-passthrough ] deqp/functional/gles3/transformfeedback/random_separate_triangles.html [ RetryOnFailure ]
-crbug.com/751254 [ mac amd ] deqp/functional/gles3/shaderindexing/mat_00.html [ RetryOnFailure ]
 crbug.com/636648 [ mac amd ] deqp/functional/gles3/shaderindexing/mat_01.html [ RetryOnFailure ]
-crbug.com/644360 [ mac amd ] deqp/functional/gles3/shaderindexing/mat_02.html [ RetryOnFailure ]
 crbug.com/659871 [ mac amd ] deqp/functional/gles3/shaderindexing/tmp.html [ RetryOnFailure ]
 
 crbug.com/642822 [ mac amd ] conformance2/rendering/clipping-wide-points.html [ Failure ]
 
-crbug.com/1005593 [ mac amd ] deqp/functional/gles3/shaderstruct.html [ RetryOnFailure ]
-crbug.com/1026588 [ mac amd ] deqp/functional/gles3/shaderoperator/sequence.html [ RetryOnFailure ]
 
 # Mac Intel
 crbug.com/886970 [ mac intel-0xa2e ] conformance/rendering/canvas-alpha-bug.html [ Failure ]
@@ -604,73 +414,30 @@
 crbug.com/658724 [ mac intel ] deqp/functional/gles3/framebufferblit/rect_03.html [ Failure ]
 crbug.com/658724 [ mac intel ] deqp/functional/gles3/framebufferblit/rect_04.html [ Failure ]
 crbug.com/658657 [ mac intel ] deqp/functional/gles3/texturespecification/random_teximage2d_2d.html [ Failure ]
-crbug.com/1026588 [ mac intel ] deqp/functional/gles3/shaderoperator/sequence.html [ RetryOnFailure ]
-crbug.com/905004 [ mac intel ] deqp/functional/gles3/shadertexturefunction/texturelod.html [ Failure ]
-crbug.com/905004 [ mac intel ] deqp/functional/gles3/shadertexturefunction/texturegrad.html [ Failure ]
-crbug.com/905004 [ mac intel ] deqp/functional/gles3/shadertexturefunction/textureprojgrad.html [ Failure ]
-crbug.com/665197 [ mac intel-0xa2e ] conformance2/textures/image_bitmap_from_image_bitmap/tex-2d-rgb8ui-rgb_integer-unsigned_byte.html [ Failure ]
-crbug.com/665197 [ mac intel-0xa2e ] conformance2/textures/image_bitmap_from_image_bitmap/tex-2d-rgba8ui-rgba_integer-unsigned_byte.html [ Failure ]
-crbug.com/665197 [ mac intel-0xa2e ] conformance2/textures/image_bitmap_from_image_bitmap/tex-2d-r8ui-red_integer-unsigned_byte.html [ Failure ]
-crbug.com/665197 [ mac intel-0xa2e ] conformance2/textures/image_bitmap_from_image_bitmap/tex-2d-rg8ui-rg_integer-unsigned_byte.html [ Failure ]
-crbug.com/665197 [ mac intel-0xa2e ] conformance2/textures/svg_image/tex-2d-rgb8ui-rgb_integer-unsigned_byte.html [ Failure ]
-crbug.com/665197 [ mac intel-0xa2e ] conformance2/textures/svg_image/tex-2d-rgba8ui-rgba_integer-unsigned_byte.html [ Failure ]
-crbug.com/665197 [ mac intel-0xa2e ] conformance2/textures/svg_image/tex-2d-r8ui-red_integer-unsigned_byte.html [ Failure ]
-crbug.com/665197 [ mac intel-0xa2e ] conformance2/textures/svg_image/tex-2d-rg8ui-rg_integer-unsigned_byte.html [ Failure ]
-crbug.com/665197 [ mac intel-0xa2e ] conformance2/textures/canvas/tex-2d-rgb8ui-rgb_integer-unsigned_byte.html [ Failure ]
 crbug.com/665197 [ mac intel-0xa2e ] conformance2/textures/canvas/tex-2d-rgba8ui-rgba_integer-unsigned_byte.html [ Failure ]
-crbug.com/665197 [ mac intel-0xa2e ] conformance2/textures/canvas/tex-2d-r8ui-red_integer-unsigned_byte.html [ Failure ]
 crbug.com/665197 [ mac intel-0xa2e ] conformance2/textures/canvas/tex-2d-rg8ui-rg_integer-unsigned_byte.html [ Failure ]
-crbug.com/665197 [ mac intel-0xa2e ] conformance2/textures/image_bitmap_from_image/tex-2d-rgb8ui-rgb_integer-unsigned_byte.html [ Failure ]
-crbug.com/665197 [ mac intel-0xa2e ] conformance2/textures/image_bitmap_from_image/tex-2d-rgba8ui-rgba_integer-unsigned_byte.html [ Failure ]
-crbug.com/665197 [ mac intel-0xa2e ] conformance2/textures/image_bitmap_from_image/tex-2d-r8ui-red_integer-unsigned_byte.html [ Failure ]
-crbug.com/665197 [ mac intel-0xa2e ] conformance2/textures/image_bitmap_from_image/tex-2d-rg8ui-rg_integer-unsigned_byte.html [ Failure ]
-crbug.com/665197 [ mac intel-0xa2e ] conformance2/textures/image_bitmap_from_blob/tex-2d-rgb8ui-rgb_integer-unsigned_byte.html [ Failure ]
-crbug.com/665197 [ mac intel-0xa2e ] conformance2/textures/image_bitmap_from_blob/tex-2d-rgba8ui-rgba_integer-unsigned_byte.html [ Failure ]
-crbug.com/665197 [ mac intel-0xa2e ] conformance2/textures/image_bitmap_from_blob/tex-2d-r8ui-red_integer-unsigned_byte.html [ Failure ]
-crbug.com/665197 [ mac intel-0xa2e ] conformance2/textures/image_bitmap_from_blob/tex-2d-rg8ui-rg_integer-unsigned_byte.html [ Failure ]
 crbug.com/665197 [ mac intel-0xa2e ] conformance2/textures/image_bitmap_from_video/tex-2d-rgb8ui-rgb_integer-unsigned_byte.html [ Failure ]
 crbug.com/665197 [ mac intel-0xa2e ] conformance2/textures/image_bitmap_from_video/tex-2d-rgba8ui-rgba_integer-unsigned_byte.html [ Failure ]
 crbug.com/665197 [ mac intel-0xa2e ] conformance2/textures/image_bitmap_from_video/tex-2d-r8ui-red_integer-unsigned_byte.html [ Failure ]
 crbug.com/665197 [ mac intel-0xa2e ] conformance2/textures/image_bitmap_from_video/tex-2d-rg8ui-rg_integer-unsigned_byte.html [ Failure ]
-crbug.com/665197 [ mac intel-0xa2e ] conformance2/textures/image/tex-2d-rgb8ui-rgb_integer-unsigned_byte.html [ Failure ]
-crbug.com/665197 [ mac intel-0xa2e ] conformance2/textures/image/tex-2d-rgba8ui-rgba_integer-unsigned_byte.html [ Failure ]
-crbug.com/665197 [ mac intel-0xa2e ] conformance2/textures/image/tex-2d-r8ui-red_integer-unsigned_byte.html [ Failure ]
-crbug.com/665197 [ mac intel-0xa2e ] conformance2/textures/image/tex-2d-rg8ui-rg_integer-unsigned_byte.html [ Failure ]
-crbug.com/665197 [ mac intel-0xa2e ] conformance2/textures/webgl_canvas/tex-2d-rgb8ui-rgb_integer-unsigned_byte.html [ Failure ]
 crbug.com/665197 [ mac intel-0xa2e ] conformance2/textures/webgl_canvas/tex-2d-rgba8ui-rgba_integer-unsigned_byte.html [ Failure ]
 crbug.com/665197 [ mac intel-0xa2e ] conformance2/textures/webgl_canvas/tex-2d-r8ui-red_integer-unsigned_byte.html [ Failure ]
 crbug.com/665197 [ mac intel-0xa2e ] conformance2/textures/webgl_canvas/tex-2d-rg8ui-rg_integer-unsigned_byte.html [ Failure ]
-crbug.com/665197 [ mac intel-0xa2e ] conformance2/textures/image_bitmap_from_image_data/tex-2d-rgb8ui-rgb_integer-unsigned_byte.html [ Failure ]
-crbug.com/665197 [ mac intel-0xa2e ] conformance2/textures/image_bitmap_from_image_data/tex-2d-rgba8ui-rgba_integer-unsigned_byte.html [ Failure ]
-crbug.com/665197 [ mac intel-0xa2e ] conformance2/textures/image_bitmap_from_image_data/tex-2d-r8ui-red_integer-unsigned_byte.html [ Failure ]
-crbug.com/665197 [ mac intel-0xa2e ] conformance2/textures/image_bitmap_from_image_data/tex-2d-rg8ui-rg_integer-unsigned_byte.html [ Failure ]
 crbug.com/665197 [ mac intel-0xa2e ] conformance2/textures/video/tex-2d-rgb8ui-rgb_integer-unsigned_byte.html [ Failure ]
 crbug.com/665197 [ mac intel-0xa2e ] conformance2/textures/video/tex-2d-rgba8ui-rgba_integer-unsigned_byte.html [ Failure ]
 crbug.com/665197 [ mac intel-0xa2e ] conformance2/textures/video/tex-2d-r8ui-red_integer-unsigned_byte.html [ Failure ]
 crbug.com/665197 [ mac intel-0xa2e ] conformance2/textures/video/tex-2d-rg8ui-rg_integer-unsigned_byte.html [ Failure ]
-crbug.com/665197 [ mac intel-0xa2e ] conformance2/textures/canvas_sub_rectangle/tex-2d-rgb8ui-rgb_integer-unsigned_byte.html [ Failure ]
 crbug.com/665197 [ mac intel-0xa2e ] conformance2/textures/canvas_sub_rectangle/tex-2d-rgba8ui-rgba_integer-unsigned_byte.html [ Failure ]
 crbug.com/665197 [ mac intel-0xa2e ] conformance2/textures/canvas_sub_rectangle/tex-2d-r8ui-red_integer-unsigned_byte.html [ Failure ]
 crbug.com/665197 [ mac intel-0xa2e ] conformance2/textures/canvas_sub_rectangle/tex-2d-rg8ui-rg_integer-unsigned_byte.html [ Failure ]
-crbug.com/665197 [ mac intel-0xa2e ] conformance2/textures/image_data/tex-2d-rgb8ui-rgb_integer-unsigned_byte.html [ Failure ]
-crbug.com/665197 [ mac intel-0xa2e ] conformance2/textures/image_data/tex-2d-rgba8ui-rgba_integer-unsigned_byte.html [ Failure ]
-crbug.com/665197 [ mac intel-0xa2e ] conformance2/textures/image_data/tex-2d-r8ui-red_integer-unsigned_byte.html [ Failure ]
-crbug.com/665197 [ mac intel-0xa2e ] conformance2/textures/image_data/tex-2d-rg8ui-rg_integer-unsigned_byte.html [ Failure ]
-crbug.com/665197 [ mac intel-0xa2e ] conformance2/textures/image_bitmap_from_canvas/tex-2d-rgb8ui-rgb_integer-unsigned_byte.html [ Failure ]
 crbug.com/665197 [ mac intel-0xa2e ] conformance2/textures/image_bitmap_from_canvas/tex-2d-rgba8ui-rgba_integer-unsigned_byte.html [ Failure ]
-crbug.com/665197 [ mac intel-0xa2e ] conformance2/textures/image_bitmap_from_canvas/tex-2d-r8ui-red_integer-unsigned_byte.html [ Failure ]
 crbug.com/665197 [ mac intel-0xa2e ] conformance2/textures/image_bitmap_from_canvas/tex-2d-rg8ui-rg_integer-unsigned_byte.html [ Failure ]
-crbug.com/658930 [ mac intel ] conformance2/textures/misc/integer-cubemap-texture-sampling.html [ Failure ]
 crbug.com/782317 [ mac intel ] conformance/rendering/rendering-stencil-large-viewport.html [ Failure ]
 crbug.com/1039465 [ mac intel ] conformance/textures/canvas/tex-2d-rgba-rgba-unsigned_short_4_4_4_4.html [ RetryOnFailure ]
 crbug.com/1018028 [ mac intel ] conformance/rendering/bind-framebuffer-flush-bug.html [ Failure ]
 crbug.com/1082455 [ mac intel ] conformance2/renderbuffers/multisampled-depth-renderbuffer-initialization.html [ Failure ]
 crbug.com/1144207 [ mac intel-0x3e9b angle-disabled ] conformance/extensions/webgl-multi-draw.html [ Failure ]
 
-# Mac Intel No-Passthrough
-crbug.com/1042011 [ mac intel-0xa2e no-passthrough ] conformance2/textures/canvas_sub_rectangle/tex-3d-r11f_g11f_b10f-rgb-half_float.html [ RetryOnFailure ]
-
-
 # Mac Passthrough
 crbug.com/angleproject/5223 [ mac passthrough ] conformance2/textures/misc/tex-mipmap-levels.html [ Failure ]
 
@@ -684,7 +451,6 @@
 crbug.com/982294 [ mac passthrough amd ] deqp/functional/gles3/transformfeedback/random_interleaved_points.html [ Failure ]
 crbug.com/982294 [ mac passthrough amd ] deqp/functional/gles3/transformfeedback/random_interleaved_triangles.html [ Failure ]
 
-
 # Mac Passthrough / NVIDIA
 crbug.com/982294 [ mac passthrough nvidia ] conformance/renderbuffers/framebuffer-object-attachment.html [ Failure ]
 crbug.com/982294 [ mac passthrough nvidia ] conformance/textures/misc/tex-input-validation.html [ Failure ]
@@ -729,14 +495,6 @@
 crbug.com/1092734 [ mac passthrough intel ] conformance2/textures/webgl_canvas/tex-2d-rgba4-rgba-unsigned_short_4_4_4_4.html [ Failure ]
 crbug.com/1092734 [ mac passthrough intel ] conformance2/textures/webgl_canvas/tex-2d-rgba8-rgba-unsigned_byte.html [ Failure ]
 
-# Mac ASAN flakes
-crbug.com/1059760 [ mac asan ] conformance/textures/image_bitmap_from_video/* [ RetryOnFailure ]
-crbug.com/1059760 [ mac asan no-passthrough ] conformance/textures/misc/texture-corner-case-videos.html [ RetryOnFailure ]
-crbug.com/1059760 [ mac asan ] conformance/textures/video/* [ RetryOnFailure ]
-crbug.com/1059760 [ mac asan ] conformance2/textures/image_bitmap_from_video/* [ RetryOnFailure ]
-crbug.com/1059760 [ mac asan ] conformance2/textures/misc/tex-image-with-bad-args-from-dom-elements.html [ RetryOnFailure ]
-crbug.com/1059760 [ mac asan ] conformance2/textures/video/* [ RetryOnFailure ]
-
 # Mac ASAN slow tests
 crbug.com/1113404 [ mac asan ] deqp/functional/gles3/multisample.html [ Skip ]
 
@@ -777,25 +535,16 @@
 crbug.com/1130112 [ mac apple-apple-m1 ] deqp/functional/gles3/texturefiltering/cube_combinations_04.html [ Failure ]
 crbug.com/1130112 [ mac apple-apple-m1 ] deqp/functional/gles3/texturefiltering/cube_sizes_03.html [ Failure ]
 crbug.com/1130112 [ mac apple-apple-m1 ] deqp/functional/gles3/texturefiltering/cube_sizes_04.html [ Failure ]
-crbug.com/1130114 [ mac apple-apple-m1 ] conformance2/extensions/ext-color-buffer-float.html [ Failure ]
-crbug.com/1130117 [ mac apple-apple-m1 ] deqp/functional/gles3/fbomultisample.2_samples.html [ Failure ]
-crbug.com/1130117 [ mac apple-apple-m1 ] deqp/functional/gles3/fbomultisample.4_samples.html [ Failure ]
 crbug.com/1130117 [ mac apple-apple-m1 no-asan ] deqp/functional/gles3/multisample.html [ Failure ]
 crbug.com/1130118 [ mac apple-apple-m1 ] conformance2/rendering/blitframebuffer-filter-srgb.html [ Failure ]
 crbug.com/1130118 [ mac apple-apple-m1 ] deqp/functional/gles3/framebufferblit/rect_03.html [ Failure ]
 crbug.com/1130118 [ mac apple-apple-m1 ] deqp/functional/gles3/framebufferblit/rect_04.html [ Failure ]
-crbug.com/1130118 [ mac apple-apple-m1 ] deqp/functional/gles3/framebufferblit/default_framebuffer_00.html [ Failure ]
 crbug.com/1130119 [ mac apple-apple-m1 ] conformance2/glsl3/vector-dynamic-indexing.html [ Failure ]
 crbug.com/1130119 [ mac apple-apple-m1 ] conformance2/textures/misc/tex-base-level-bug.html [ Failure ]
-crbug.com/1130119 [ mac apple-apple-m1 ] conformance2/textures/webgl_canvas/tex-3d-r32f-red-float.html [ RetryOnFailure ]
 crbug.com/1130119 [ mac apple-apple-m1 ] conformance2/rendering/framebuffer-completeness-unaffected.html [ Failure ]
 crbug.com/1130119 [ mac apple-apple-m1 passthrough angle-opengl ] conformance2/rendering/framebuffer-render-to-layer.html [ Failure ]
 crbug.com/1130119 [ mac apple-apple-m1 ] deqp/functional/gles3/fbocompleteness.html [ Failure ]
-crbug.com/1130119 [ mac apple-apple-m1 ] deqp/functional/gles3/fbodepthbuffer.html [ Failure ]
 crbug.com/1130119 [ mac apple-apple-m1 ] deqp/functional/gles3/shadertexturefunction/texturegrad.html [ Failure ]
-crbug.com/1130119 [ mac apple-apple-m1 ] deqp/functional/gles3/textureshadow/cube_linear_mipmap_linear_greater.html [ RetryOnFailure ]
-crbug.com/1130119 [ mac apple-apple-m1 ] deqp/functional/gles3/textureshadow/cube_linear_mipmap_linear_less.html [ RetryOnFailure ]
-crbug.com/1130119 [ mac apple-apple-m1 ] deqp/functional/gles3/texturespecification/texstorage2d_format_size.html [ RetryOnFailure ]
 crbug.com/1130703 [ mac apple-apple-m1 no-passthrough ] conformance/textures/misc/texture-copying-and-deletion.html [ Failure ]
 crbug.com/1130703 [ mac apple-apple-m1 no-passthrough ] conformance/textures/misc/texture-copying-feedback-loops.html [ Failure ]
 crbug.com/1130708 [ mac apple-apple-m1 no-passthrough ] conformance2/textures/canvas/tex-2d-rgb5_a1-rgba-unsigned_byte.html [ Failure ]
@@ -818,55 +567,17 @@
 
 # Mesa issues
 # Driver tag doesn't work on the passthrough command decoder because the detected driver version is ANGLE's version.
-crbug.com/1127180 [ linux intel no-passthrough mesa_ge_20.1 ] deqp/functional/gles3/shaderindexing/tmp.html [ Failure ]
-crbug.com/1127180 [ linux intel no-passthrough mesa_ge_20.1 ] deqp/data/gles3/shaders/functions.html [ Failure ]
-crbug.com/1084864 [ linux intel angle-opengl passthrough ] conformance2/rendering/blitframebuffer-filter-outofbounds.html [ Failure ]
 crbug.com/1081978 [ linux intel angle-opengl passthrough ] conformance2/textures/misc/tex-3d-size-limit.html [ Failure ]
 crbug.com/angleproject/4242 [ linux intel angle-opengl passthrough ] conformance2/glsl3/matrix-row-major-dynamic-indexing.html [ Failure ]
 crbug.com/angleproject/4242 [ linux intel no-passthrough mesa_lt_19.1 ] conformance2/glsl3/matrix-row-major-dynamic-indexing.html [ Failure ]
 
-# Linux only.
-crbug.com/905006 [ linux nvidia ] conformance2/glsl3/vector-dynamic-indexing-nv-driver-bug.html [ Failure ]
-
 # Linux NVIDIA
-# This test is flaky both with and without ANGLE.
-crbug.com/709351 [ linux nvidia ] conformance2/glsl3/vector-dynamic-indexing-swizzled-lvalue.html [ Failure ]
 crbug.com/905003 [ linux nvidia ] conformance2/textures/misc/integer-cubemap-specification-order-bug.html [ Failure ]
-crbug.com/680278 [ angle-opengl linux nvidia ] conformance2/rendering/framebuffer-texture-level1.html [ Failure ]
 
-# Observed flaky on Swarmed bots. Some of these were directly
-# observed, some not. We can't afford any flakes on the tryservers
-# so mark them all flaky.
-crbug.com/780706 [ linux nvidia-0x1cb3 ] deqp/functional/gles3/transformfeedback/array_interleaved_lines.html [ RetryOnFailure ]
-crbug.com/780706 [ linux nvidia-0x1cb3 ] deqp/functional/gles3/transformfeedback/array_interleaved_points.html [ RetryOnFailure ]
-crbug.com/780706 [ linux nvidia-0x1cb3 ] deqp/functional/gles3/transformfeedback/array_interleaved_triangles.html [ RetryOnFailure ]
-crbug.com/780706 [ linux nvidia-0x1cb3 ] deqp/functional/gles3/transformfeedback/array_separate_lines.html [ RetryOnFailure ]
-crbug.com/780706 [ linux nvidia-0x1cb3 ] deqp/functional/gles3/transformfeedback/array_separate_points.html [ RetryOnFailure ]
-crbug.com/780706 [ linux nvidia-0x1cb3 ] deqp/functional/gles3/transformfeedback/array_separate_triangles.html [ RetryOnFailure ]
-crbug.com/780706 [ linux nvidia-0x1cb3 ] deqp/functional/gles3/transformfeedback/basic_types_interleaved_lines.html [ RetryOnFailure ]
-crbug.com/780706 [ linux nvidia-0x1cb3 ] deqp/functional/gles3/transformfeedback/basic_types_interleaved_points.html [ RetryOnFailure ]
-crbug.com/780706 [ linux nvidia-0x1cb3 ] deqp/functional/gles3/transformfeedback/basic_types_interleaved_triangles.html [ RetryOnFailure ]
-crbug.com/780706 [ linux nvidia-0x1cb3 ] deqp/functional/gles3/transformfeedback/basic_types_separate_lines.html [ RetryOnFailure ]
-crbug.com/780706 [ linux nvidia-0x1cb3 ] deqp/functional/gles3/transformfeedback/basic_types_separate_points.html [ RetryOnFailure ]
-crbug.com/780706 [ linux nvidia-0x1cb3 ] deqp/functional/gles3/transformfeedback/basic_types_separate_triangles.html [ RetryOnFailure ]
-crbug.com/780706 [ linux nvidia-0x1cb3 ] deqp/functional/gles3/transformfeedback/interpolation_centroid.html [ RetryOnFailure ]
-crbug.com/780706 [ linux nvidia-0x1cb3 ] deqp/functional/gles3/transformfeedback/interpolation_flat.html [ RetryOnFailure ]
-crbug.com/780706 [ linux nvidia-0x1cb3 ] deqp/functional/gles3/transformfeedback/interpolation_smooth.html [ RetryOnFailure ]
-crbug.com/780706 [ linux nvidia-0x1cb3 ] deqp/functional/gles3/transformfeedback/point_size.html [ RetryOnFailure ]
-crbug.com/780706 [ linux nvidia-0x1cb3 ] deqp/functional/gles3/transformfeedback/position.html [ RetryOnFailure ]
-crbug.com/780706 [ linux nvidia-0x1cb3 ] deqp/functional/gles3/transformfeedback/random_interleaved_lines.html [ RetryOnFailure ]
-crbug.com/780706 [ linux nvidia-0x1cb3 ] deqp/functional/gles3/transformfeedback/random_interleaved_points.html [ RetryOnFailure ]
-crbug.com/780706 [ linux nvidia-0x1cb3 ] deqp/functional/gles3/transformfeedback/random_interleaved_triangles.html [ RetryOnFailure ]
-crbug.com/780706 [ linux nvidia-0x1cb3 ] deqp/functional/gles3/transformfeedback/random_separate_lines.html [ RetryOnFailure ]
-crbug.com/780706 [ linux nvidia-0x1cb3 ] deqp/functional/gles3/transformfeedback/random_separate_points.html [ RetryOnFailure ]
-crbug.com/780706 [ linux nvidia-0x1cb3 ] deqp/functional/gles3/transformfeedback/random_separate_triangles.html [ RetryOnFailure ]
 crbug.com/906846 [ linux nvidia ] conformance2/rendering/canvas-resizing-with-pbo-bound.html [ RetryOnFailure ]
 
 # Linux NVIDIA Quadro P400, OpenGL backend
-crbug.com/715001 [ linux nvidia-0x1cb3 ] conformance/limits/gl-max-texture-dimensions.html [ Failure ]
-crbug.com/703779 [ angle-opengl linux nvidia-0x1cb3 ] conformance/textures/misc/texture-size.html [ Failure ]
 crbug.com/703779 [ angle-opengl linux nvidia-0x1cb3 ] conformance/extensions/webgl-compressed-texture-size-limit.html [ Failure ]
-crbug.com/703779 [ angle-opengl linux nvidia-0x1cb3 ] conformance/textures/misc/texture-size-limit.html [ Failure ]
 crbug.com/703779 [ angle-opengl linux nvidia-0x1cb3 ] deqp/functional/gles3/fbocompleteness.html [ Failure ]
 
 # Linux / OpenGL / NVIDIA failures
@@ -876,49 +587,10 @@
 crbug.com/1115314 [ linux nvidia-0x2184 angle-opengl passthrough ] deqp/functional/gles3/fbocompleteness.html [ Failure ]
 
 # Linux AMD only.
-# It looks like AMD shader compiler rejects many valid ES3 semantics.
-crbug.com/766776 [ linux amd ] conformance2/attribs/gl-vertex-attrib-normalized-int.html [ Failure ]
-crbug.com/981070 [ linux amd ] conformance2/glsl3/matrix-row-major.html [ Failure ]
-crbug.com/709351 [ linux amd ] conformance2/glsl3/vector-dynamic-indexing-swizzled-lvalue.html [ Failure ]
-crbug.com/483282 [ linux amd ] deqp/data/gles3/shaders/conversions.html [ Failure ]
 crbug.com/483282 [ linux amd ] deqp/data/gles3/shaders/arrays.html [ Skip ]
 crbug.com/483282 [ linux amd ] deqp/data/gles3/shaders/qualification_order.html [ Skip ]
-crbug.com/483282 [ linux amd ] deqp/functional/gles3/samplerobject.html [ Failure ]
-crbug.com/483282 [ linux amd ] deqp/functional/gles3/shaderprecision_int.html [ Failure ]
-crbug.com/694877 [ linux amd ] deqp/functional/gles3/vertexarrays/single_attribute.first.html [ Failure ]
-crbug.com/483282 [ linux amd ] deqp/functional/gles3/negativetextureapi.html [ Failure ]
 crbug.com/483282 [ linux amd ] deqp/functional/gles3/transformfeedback/array_separate* [ Failure ]
-crbug.com/634525 [ linux amd ] conformance2/rendering/blitframebuffer-filter-srgb.html [ Failure ]
-crbug.com/662644 [ linux amd ] conformance2/rendering/blitframebuffer-outside-readbuffer.html [ Failure ]
-crbug.com/483282 [ linux amd ] conformance2/textures/misc/tex-mipmap-levels.html [ Failure ]
-crbug.com/899754 [ linux amd ] conformance2/vertex_arrays/vertex-array-object-and-disabled-attributes.html [ Failure ]
-crbug.com/911216 [ linux no-passthrough amd ] conformance2/textures/misc/copy-texture-image-same-texture.html [ Failure ]
-crbug.com/483282 [ linux amd ] deqp/functional/gles3/texturespecification/teximage2d_depth_pbo.html [ Failure ]
-crbug.com/483282 [ linux amd ] deqp/functional/gles3/draw/random.html [ Failure ]
-crbug.com/483282 [ linux amd ] deqp/functional/gles3/fbomultisample* [ Failure ]
-crbug.com/483282 [ linux amd ] deqp/functional/gles3/framebufferblit/conversion_07.html [ Failure ]
-crbug.com/658832 [ linux amd ] deqp/functional/gles3/framebufferblit/default_framebuffer_00.html [ Failure ]
 crbug.com/483282 [ linux amd ] conformance2/glsl3/vector-dynamic-indexing.html [ Failure ]
-crbug.com/483282 [ angle-disabled linux amd ] conformance2/reading/read-pixels-pack-parameters.html [ Failure ]
-crbug.com/483282 [ angle-disabled linux amd ] conformance2/textures/misc/tex-unpack-params.html [ Failure ]
-crbug.com/662644 [ linux amd ] conformance2/rendering/clipping-wide-points.html [ Failure ]
-
-# TODO(kbr): re-enable after next conformance roll. crbug.com/736499
-# crbug.com/633022 [ linux amd ] conformance2/extensions/ext-color-buffer-float.html [ Failure ]
-crbug.com/655147 [ linux no-passthrough amd ] conformance2/rendering/blitframebuffer-filter-outofbounds.html [ Failure ]
-crbug.com/705865 [ linux amd ] conformance2/textures/misc/tex-base-level-bug.html [ Failure ]
-crbug.com/705865 [ linux amd ] conformance2/textures/image/tex-2d-r11f_g11f_b10f-rgb-float.html [ Failure ]
-
-# Uniform buffer related failures
-crbug.com/483282 [ linux amd ] deqp/functional/gles3/uniformbuffers/random.html [ Failure ]
-crbug.com/angleproject/5014 [ linux amd ] conformance2/uniforms/uniform-blocks-with-arrays.html [ Failure ]
-crbug.com/809595 [ angle-disabled linux amd ] conformance2/uniforms/simple-buffer-change.html [ Failure ]
-
-# Linux AMD R7 240
-crbug.com/696345 [ angle-disabled linux amd-0x6613 ] conformance2/transform_feedback/switching-objects.html [ Failure ]
-crbug.com/913301 [ linux amd-0x6613 ] conformance2/textures/misc/generate-mipmap-with-large-base-level.html [ Failure ]
-crbug.com/809237 [ linux amd-0x6613 ] conformance2/uniforms/incompatible-texture-type-for-sampler.html [ Skip ]
-crbug.com/1018028 [ linux amd-0x6613 ] conformance/rendering/bind-framebuffer-flush-bug.html [ Failure ]
 
 # Linux AMD RX 5500 XT
 crbug.com/1147232 [ linux amd-0x7340 no-passthrough ] conformance/textures/misc/texture-size-limit.html [ Skip ]
@@ -937,18 +609,12 @@
 [ android qualcomm ] WebglExtension_WEBGL_compressed_texture_s3tc [ Skip ]
 [ android qualcomm ] WebglExtension_WEBGL_compressed_texture_s3tc_srgb [ Skip ]
 
-# Was too difficult to suppress these failures on individual bots.
-crbug.com/1165751 [ android-lollipop ] conformance/glsl/bugs/vector-matrix-constructor-scalarization.html [ Failure ]
-crbug.com/1165751 [ android-marshmallow ] conformance/glsl/bugs/vector-matrix-constructor-scalarization.html [ Failure ]
-crbug.com/1165751 [ android-nougat ] conformance/glsl/bugs/vector-matrix-constructor-scalarization.html [ Failure ]
-
 # Video tests are flaky. Sometimes the video is black.
 crbug.com/948894 [ android ] conformance/textures/video/* [ RetryOnFailure ]
 crbug.com/948894 [ android ] conformance/textures/image_bitmap_from_video/* [ RetryOnFailure ]
 crbug.com/948894 [ android ] conformance2/textures/video/* [ RetryOnFailure ]
 crbug.com/948894 [ android ] conformance2/textures/image_bitmap_from_video/* [ RetryOnFailure ]
 
-crbug.com/1081973 [ android ] conformance2/textures/misc/compressed-tex-image.html [ Failure ]
 crbug.com/angleproject/4417 [ android ] conformance2/rendering/framebuffer-render-to-layer.html [ Failure ]
 
 # Video uploads to some texture formats new in WebGL 2.0 are
@@ -974,33 +640,17 @@
 crbug.com/1008535 [ android android-pixel-2 passthrough ] conformance2/textures/misc/tex-image-with-bad-args-from-dom-elements.html [ Skip ]
 crbug.com/1000354 [ android android-pixel-2 passthrough ] conformance2/reading/read-pixels-from-fbo-test.html [ Failure ]
 crbug.com/951628 [ android android-pixel-2 passthrough ] conformance/rendering/blending.html [ Failure ]
-crbug.com/981216 [ android android-pixel-2 ] deqp/functional/gles3/fbocolorbuffer/tex2d_01.html [ RetryOnFailure ]
-crbug.com/1022410 [ android qualcomm ] conformance2/transform_feedback/switching-objects.html [ RetryOnFailure ]
 crbug.com/1027125 [ android qualcomm ] deqp/functional/gles3/negativetextureapi.html [ Failure ]
 crbug.com/1092734 [ android android-pixel-2 passthrough ] conformance2/textures/webgl_canvas/tex-2d-rgb565-rgb-unsigned_short_5_6_5.html [ Failure ]
 crbug.com/1092734 [ android android-pixel-2 passthrough ] conformance2/textures/webgl_canvas/tex-2d-rgb565-rgb-unsigned_byte.html [ Failure ]
-crbug.com/1126631 [ android android-pixel-2 passthrough ] conformance2/textures/webgl_canvas/tex-2d-rgba8-rgba-unsigned_byte.html [ RetryOnFailure ]
 crbug.com/1126631 [ android android-pixel-2 passthrough ] conformance2/textures/webgl_canvas/tex-3d-r11f_g11f_b10f-rgb-half_float.html [ RetryOnFailure ]
-crbug.com/1126631 [ android android-pixel-2 passthrough ] conformance2/textures/webgl_canvas/tex-2d-rgb9_e5-rgb-half_float.html [ RetryOnFailure ]
-crbug.com/1126631 [ android android-pixel-2 passthrough ] conformance2/textures/webgl_canvas/tex-2d-r11f_g11f_b10f-rgb-unsigned_int_10f_11f_11f_rev.html [ RetryOnFailure ]
-crbug.com/1126631 [ android android-pixel-2 passthrough ] conformance/textures/webgl_canvas/tex-2d-luminance-luminance-unsigned_byte.html [ RetryOnFailure ]
 crbug.com/1143323 [ android android-pixel-2 passthrough ] conformance/rendering/draw-arrays-out-of-bounds.html [ Failure ]
-crbug.com/1154706 [ android android-pixel-2 no-passthrough ] conformance/ogles/GL/clamp/clamp_001_to_006.html [ RetryOnFailure ]
 
 
 # This test is failing on Android Pixel 2 and 3 (Qualcomm)
 # Seems to be an OpenGL ES bug.
 crbug.com/695742 [ android qualcomm angle-disabled ] deqp/functional/gles3/multisample.html [ RetryOnFailure ]
 
-# This test is flaky but can fail three times in a row so it must be
-# marked as Fail instead of Flaky.
-crbug.com/946177 [ android qualcomm ] deqp/functional/gles3/shaderbuiltinvar.html [ Failure ]
-
-# Nvidia (Shield TV) failures
-crbug.com/906752 [ android nvidia ] conformance2/glsl3/array-complex-indexing.html [ Failure ]
-crbug.com/874620 [ android nvidia ] conformance2/glsl3/const-struct-from-array-as-function-parameter.html [ Failure ]
-crbug.com/905006 [ android nvidia ] conformance2/glsl3/vector-dynamic-indexing-nv-driver-bug.html [ Failure ]
-
 # Pixel 4
 crbug.com/1175226 [ android android-pixel-4 angle-opengles passthrough ] conformance/rendering/blending.html [ Failure ]
 crbug.com/1175229 [ android android-pixel-4 angle-opengles passthrough ] conformance2/textures/webgl_canvas/tex-2d-rgb565-rgb-unsigned_byte.html [ Failure ]
@@ -1027,24 +677,13 @@
 # command decoder, simplify the RetryOnFailure expectation at the top
 # of this file to just not declare any OS.
 crbug.com/979444 [ android angle-opengles ] conformance/textures/misc/texture-corner-case-videos.html [ Failure ]
-crbug.com/906724 [ android angle-opengles ] conformance/textures/video/tex-2d-luminance_alpha-luminance_alpha-unsigned_byte.html [ Failure ]
-crbug.com/906724 [ android angle-opengles ] conformance/textures/video/tex-2d-luminance-luminance-unsigned_byte.html [ Failure ]
 
 crbug.com/angleproject/3684 [ android angle-opengles ] conformance2/renderbuffers/multisample-with-full-sample-counts.html [ Failure ]
-crbug.com/967410 [ android angle-opengles ] conformance2/textures/video/tex-2d-rgb16f-rgb-half_float.html [ Failure ]
-crbug.com/967410 [ android angle-opengles ] conformance2/textures/video/tex-2d-rgb9_e5-rgb-half_float.html [ Failure ]
-crbug.com/967410 [ android angle-opengles ] conformance2/textures/video/tex-2d-srgb8-rgb-unsigned_byte.html [ Failure ]
-crbug.com/967410 [ android angle-opengles ] conformance2/textures/video/tex-2d-srgb8_alpha8-rgba-unsigned_byte.html [ Failure ]
 crbug.com/967410 [ android angle-opengles ] conformance2/textures/misc/npot-video-sizing.html [ Failure ]
 crbug.com/angleproject/3685 [ android angle-opengles ] conformance2/transform_feedback/simultaneous_binding.html [ Failure ]
 crbug.com/angleproject/3686 [ android angle-opengles ] deqp/functional/gles3/multisample.html [ Failure ]
-crbug.com/angleproject/3753 [ android angle-opengles ] conformance/textures/misc/texture-upload-size.html [ Failure ]
 
 crbug.com/1037958 [ mac amd-0x679e ] conformance2/transform_feedback/switching-objects.html [ RetryOnFailure ]
-crbug.com/1103370 [ android angle-opengles no-passthrough qualcomm ] deqp/functional/gles3/fborender/recreate_color_03.html [ RetryOnFailure ]
-
-# Too flaky on vaidating command decoder to mark RetryOnFailure.
-crbug.com/1082003 [ android angle-opengles no-passthrough qualcomm ] deqp/functional/gles3/fragdepth.html [ Failure ]
 
 # Conflicting expectations to test that the
 # "Expectations have no collisions" unittest works.
diff --git a/extensions/browser/event_router.h b/extensions/browser/event_router.h
index a289fc4..073e11d 100644
--- a/extensions/browser/event_router.h
+++ b/extensions/browser/event_router.h
@@ -164,8 +164,8 @@
   EventListenerMap& listeners() { return listeners_; }
 
   // Registers an observer to be notified when an event listener for
-  // |event_name| is added or removed. There can currently be only one observer
-  // for each distinct |event_name|.
+  // |event_name| is added or removed. There can currently be multiple
+  // observers for each distinct |event_name|.
   void RegisterObserver(Observer* observer, const std::string& event_name);
 
   // Unregisters an observer from all events.
diff --git a/extensions/common/api/bluetooth_socket.idl b/extensions/common/api/bluetooth_socket.idl
index cb95c02..2d98e92 100644
--- a/extensions/common/api/bluetooth_socket.idl
+++ b/extensions/common/api/bluetooth_socket.idl
@@ -195,16 +195,16 @@
     // Creates a Bluetooth socket.
     // |properties| : The socket properties (optional).
     // |callback| : Called when the socket has been created.
-    static void create(optional SocketProperties properties,
-                       CreateCallback callback);
+    [supportsPromises] static void create(optional SocketProperties properties,
+                                          CreateCallback callback);
 
     // Updates the socket properties.
     // |socketId| : The socket identifier.
     // |properties| : The properties to update.
     // |callback| : Called when the properties are updated.
-    static void update(long socketId,
-                       SocketProperties properties,
-                       optional UpdateCallback callback);
+    [supportsPromises] static void update(long socketId,
+                                          SocketProperties properties,
+                                          optional UpdateCallback callback);
 
     // Enables or disables a connected socket from receiving messages from its
     // peer, or a listening socket from accepting new connections. The default
@@ -216,29 +216,32 @@
     // connections are accepted until its backlog is full then additional
     // connection requests are refused. <code>onAccept</code> events are raised
     // only when the socket is un-paused.
-    static void setPaused(long socketId,
-                          boolean paused,
-                          optional SetPausedCallback callback);
+    [supportsPromises] static void setPaused(
+        long socketId,
+        boolean paused,
+        optional SetPausedCallback callback);
 
     // Listen for connections using the RFCOMM protocol.
     // |socketId| : The socket identifier.
     // |uuid| : Service UUID to listen on.
     // |options| : Optional additional options for the service.
     // |callback| : Called when listen operation completes.
-    static void listenUsingRfcomm(long socketId,
-                                  DOMString uuid,
-                                  optional ListenOptions options,
-                                  ListenCallback callback);
+    [supportsPromises] static void listenUsingRfcomm(
+        long socketId,
+        DOMString uuid,
+        optional ListenOptions options,
+        ListenCallback callback);
 
     // Listen for connections using the L2CAP protocol.
     // |socketId| : The socket identifier.
     // |uuid| : Service UUID to listen on.
     // |options| : Optional additional options for the service.
     // |callback| : Called when listen operation completes.
-    static void listenUsingL2cap(long socketId,
-                                 DOMString uuid,
-                                 optional ListenOptions options,
-                                 ListenCallback callback);
+    [supportsPromises] static void listenUsingL2cap(
+        long socketId,
+        DOMString uuid,
+        optional ListenOptions options,
+        ListenCallback callback);
 
     // Connects the socket to a remote Bluetooth device. When the
     // <code>connect</code> operation completes successfully,
@@ -251,16 +254,17 @@
     // |address| : The address of the Bluetooth device.
     // |uuid| : The UUID of the service to connect to.
     // |callback| : Called when the connect attempt is complete.
-    static void connect(long socketId,
-                        DOMString address,
-                        DOMString uuid,
-                        ConnectCallback callback);
+    [supportsPromises] static void connect(long socketId,
+                                           DOMString address,
+                                           DOMString uuid,
+                                           ConnectCallback callback);
 
     // Disconnects the socket. The socket identifier remains valid.
     // |socketId| : The socket identifier.
     // |callback| : Called when the disconnect attempt is complete.
-    static void disconnect(long socketId,
-                           optional DisconnectCallback callback);
+    [supportsPromises] static void disconnect(
+        long socketId,
+        optional DisconnectCallback callback);
 
     // Disconnects and destroys the socket. Each socket created should be
     // closed after use. The socket id is no longer valid as soon at the
@@ -268,26 +272,26 @@
     // when the callback is invoked.
     // |socketId| : The socket identifier.
     // |callback| : Called when the <code>close</code> operation completes.
-    static void close(long socketId,
-                      optional CloseCallback callback);
+    [supportsPromises] static void close(long socketId,
+                                         optional CloseCallback callback);
 
     // Sends data on the given Bluetooth socket.
     // |socketId| : The socket identifier.
     // |data| : The data to send.
     // |callback| : Called with the number of bytes sent.
-    static void send(long socketId,
-                     ArrayBuffer data,
-                     optional SendCallback callback);
+    [supportsPromises] static void send(long socketId,
+                                        ArrayBuffer data,
+                                        optional SendCallback callback);
 
     // Retrieves the state of the given socket.
     // |socketId| : The socket identifier.
     // |callback| : Called when the socket state is available.
-    static void getInfo(long socketId,
-                        GetInfoCallback callback);
+    [supportsPromises] static void getInfo(long socketId,
+                                           GetInfoCallback callback);
 
     // Retrieves the list of currently opened sockets owned by the application.
     // |callback| : Called when the list of sockets is available.
-    static void getSockets(GetSocketsCallback callback);
+    [supportsPromises] static void getSockets(GetSocketsCallback callback);
   };
 
   interface Events {
diff --git a/extensions/common/api/cast_channel.idl b/extensions/common/api/cast_channel.idl
index 6fd3035b..9b80a4d 100644
--- a/extensions/common/api/cast_channel.idl
+++ b/extensions/common/api/cast_channel.idl
@@ -176,25 +176,25 @@
     // callback will be invoked with a ChannelInfo with channel.readyState ==
     // 'closed', channel.errorState will be set to the error condition, and
     // onError will be fired with error details.
-    static void open(ConnectInfo connectInfo,
-                     ChannelInfoCallback callback);
+    [supportsPromises] static void open(ConnectInfo connectInfo,
+                                        ChannelInfoCallback callback);
 
     // Sends a message on the channel and invokes callback with the resulting
     // channel status.  The channel must be in readyState == 'open'.  If
     // unsuccessful, channel.readyState will be set to 'closed',
     // channel.errorState will be set to the error condition, and onError will
     // be fired with error details.
-    static void send(ChannelInfo channel,
-                     MessageInfo message,
-                     ChannelInfoCallback callback);
+    [supportsPromises] static void send(ChannelInfo channel,
+                                        MessageInfo message,
+                                        ChannelInfoCallback callback);
 
     // Requests that the channel be closed and invokes callback with the
     // resulting channel status.  The channel must be in readyState == 'open' or
     // 'connecting'.  If successful, onClose will be fired with readyState ==
     // 'closed'.  If unsuccessful, channel.readyState will be set to 'closed',
     // and channel.errorState will be set to the error condition.
-    static void close(ChannelInfo channel,
-                      ChannelInfoCallback callback);
+    [supportsPromises] static void close(ChannelInfo channel,
+                                         ChannelInfoCallback callback);
   };
 
   // Events on the channel.
diff --git a/extensions/common/api/cec_private.idl b/extensions/common/api/cec_private.idl
index b32874e..4c3af5f 100644
--- a/extensions/common/api/cec_private.idl
+++ b/extensions/common/api/cec_private.idl
@@ -46,7 +46,8 @@
     //
     // |callback| will be run as soon as all displays have been requested to
     // change their power state.
-    static void sendStandBy(optional ChangePowerStateCallback callback);
+    [supportsPromises] static void sendStandBy(
+        optional ChangePowerStateCallback callback);
 
     // Attempt to announce this device as the active input source towards all
     // HDMI CEC enabled displays connected, waking them from standby if
@@ -54,10 +55,11 @@
     //
     // |callback| will be run as soon as all displays have been requested to
     // change their power state.
-    static void sendWakeUp(optional ChangePowerStateCallback callback);
+    [supportsPromises] static void sendWakeUp(
+        optional ChangePowerStateCallback callback);
 
     // Queries all HDMI CEC capable displays for their current power state.
-    static void queryDisplayCecPowerState(
+    [supportsPromises] static void queryDisplayCecPowerState(
         DisplayCecPowerStateCallback callback);
   };
 };
diff --git a/extensions/common/api/clipboard.idl b/extensions/common/api/clipboard.idl
index 55b3bb8..382da46c 100644
--- a/extensions/common/api/clipboard.idl
+++ b/extensions/common/api/clipboard.idl
@@ -46,9 +46,10 @@
     // The callback is called with <code>chrome.runtime.lastError</code>
     // set to error code if there is an error.
     // Requires clipboard and clipboardWrite permissions.
-    static void setImageData(ArrayBuffer imageData,
-                             ImageType type,
-                             optional AdditionalDataItem[] additionalItems,
-                             optional SetImageDataCallback callback);
+    [supportsPromises] static void setImageData(
+        ArrayBuffer imageData,
+        ImageType type,
+        optional AdditionalDataItem[] additionalItems,
+        optional SetImageDataCallback callback);
   };
 };
diff --git a/extensions/common/api/declarative_net_request.idl b/extensions/common/api/declarative_net_request.idl
index 4eb0487..babe792 100644
--- a/extensions/common/api/declarative_net_request.idl
+++ b/extensions/common/api/declarative_net_request.idl
@@ -471,13 +471,14 @@
     // to the rule set. This can happen for multiple reasons, such as invalid
     // rule format, duplicate rule ID, rule count limit exceeded, internal
     // errors, and others.
-    static void updateDynamicRules(UpdateRuleOptions options,
-                                   optional EmptyCallback callback);
+    [supportsPromises] static void updateDynamicRules(
+        UpdateRuleOptions options,
+        optional EmptyCallback callback);
 
     // Returns the current set of dynamic rules for the extension.
     // |callback|: Called with the set of dynamic rules. An error might be
     // raised in case of transient internal errors.
-    static void getDynamicRules(GetRulesCallback callback);
+    [supportsPromises] static void getDynamicRules(GetRulesCallback callback);
 
     // Modifies the current set of session scoped rules for the extension.
     // The rules with IDs listed in <code>options.removeRuleIds</code> are first
@@ -495,12 +496,13 @@
     // an error, $(ref:runtime.lastError) will be set and no change will be made
     // to the rule set. This can happen for multiple reasons, such as invalid
     // rule format, duplicate rule ID, rule count limit exceeded, and others.
-    static void updateSessionRules(UpdateRuleOptions options,
-                                   optional EmptyCallback callback);
+    [supportsPromises] static void updateSessionRules(
+        UpdateRuleOptions options,
+        optional EmptyCallback callback);
 
     // Returns the current set of session scoped rules for the extension.
     // |callback|: Called with the set of session scoped rules.
-    static void getSessionRules(GetRulesCallback callback);
+    [supportsPromises] static void getSessionRules(GetRulesCallback callback);
 
     // Updates the set of enabled static rulesets for the extension. The
     // rulesets with IDs listed in <code>options.disableRulesetIds</code> are
@@ -514,13 +516,15 @@
     // $(ref:runtime.lastError) will be set and no change will be made to set of
     // enabled rulesets. This can happen for multiple reasons, such as invalid
     // ruleset IDs, rule count limit exceeded, or internal errors.
-    static void updateEnabledRulesets(UpdateRulesetOptions options,
-                                      optional EmptyCallback callback);
+    [supportsPromises] static void updateEnabledRulesets(
+        UpdateRulesetOptions options,
+        optional EmptyCallback callback);
 
     // Returns the ids for the current set of enabled static rulesets.
     // |callback|: Called with a list of ids, where each id corresponds to an
     // enabled static $(ref:Ruleset).
-    static void getEnabledRulesets(GetEnabledRulesetsCallback callback);
+    [supportsPromises] static void getEnabledRulesets(
+        GetEnabledRulesetsCallback callback);
 
     // Returns all rules matched for the extension. Callers can optionally
     // filter the list of matched rules by specifying a |filter|. This method is
@@ -535,13 +539,14 @@
     // case of an error, $(ref:runtime.lastError) will be set and no rules will
     // be returned. This can happen for multiple reasons, such as insufficient
     // permissions, or exceeding the quota.
-    static void getMatchedRules(optional MatchedRulesFilter filter,
-                                GetMatchedRulesCallback callback);
+    [supportsPromises] static void getMatchedRules(
+        optional MatchedRulesFilter filter,
+        GetMatchedRulesCallback callback);
 
     // Configures if the action count for tabs should be displayed as the
     // extension action's badge text and provides a way for that action count to
     // be incremented.
-    static void setExtensionActionOptions(
+    [supportsPromises] static void setExtensionActionOptions(
         ExtensionActionOptions options,
         optional EmptyCallback callback);
 
@@ -550,12 +555,13 @@
     // |regexOptions|: The regular expression to check.
     // |callback|: Called with details consisting of whether the regular
     // expression is supported and the reason if not.
-    static void isRegexSupported(RegexOptions regexOptions,
-                                 IsRegexSupportedCallback callback);
+    [supportsPromises] static void isRegexSupported(
+        RegexOptions regexOptions,
+        IsRegexSupportedCallback callback);
 
     // Returns the number of static rules an extension can enable before the
     // <a href="#global-rule-limit">global static rule limit</a> is reached.
-    static void getAvailableStaticRuleCount(
+    [supportsPromises] static void getAvailableStaticRuleCount(
         GetAvailableStaticRuleCountCallback callback);
   };
 
diff --git a/fuchsia/engine/context_provider_impl.cc b/fuchsia/engine/context_provider_impl.cc
index a62bb75..5c8dc52 100644
--- a/fuchsia/engine/context_provider_impl.cc
+++ b/fuchsia/engine/context_provider_impl.cc
@@ -171,6 +171,7 @@
       switches::kMaxDecodedImageSizeMb,
       switches::kRendererProcessLimit,
       switches::kUseCmdDecoder,
+      switches::kVModule,
       switches::kVulkanHeapMemoryLimitMb,
       switches::kVulkanSyncCpuMemoryLimitMb,
       switches::kWebglAntialiasingMode,
diff --git a/gpu/command_buffer/service/scheduler.cc b/gpu/command_buffer/service/scheduler.cc
index d5001368..4730d63 100644
--- a/gpu/command_buffer/service/scheduler.cc
+++ b/gpu/command_buffer/service/scheduler.cc
@@ -5,6 +5,7 @@
 #include "gpu/command_buffer/service/scheduler.h"
 
 #include <algorithm>
+#include <vector>
 
 #include "base/bind.h"
 #include "base/callback.h"
@@ -13,6 +14,7 @@
 #include "base/metrics/histogram_macros.h"
 #include "base/single_thread_task_runner.h"
 #include "base/stl_util.h"
+#include "base/time/time.h"
 #include "base/timer/elapsed_timer.h"
 #include "base/trace_event/trace_event.h"
 #include "base/trace_event/traced_value.h"
@@ -185,6 +187,20 @@
   return order_num;
 }
 
+base::TimeDelta Scheduler::Sequence::FrontTaskWaitingDependencyDelta() {
+  DCHECK(!tasks_.empty());
+  if (tasks_.front().first_dependency_added.is_null()) {
+    // didn't wait for dependencies.
+    return base::TimeDelta();
+  }
+  return tasks_.front().running_ready - tasks_.front().first_dependency_added;
+}
+
+base::TimeDelta Scheduler::Sequence::FrontTaskSchedulingDelay() {
+  DCHECK(!tasks_.empty());
+  return base::TimeTicks::Now() - tasks_.front().running_ready;
+}
+
 uint32_t Scheduler::Sequence::BeginTask(base::OnceClosure* closure) {
   DCHECK(closure);
   DCHECK(!tasks_.empty());
@@ -204,6 +220,14 @@
   running_state_ = IDLE;
 }
 
+void Scheduler::Sequence::SetLastTaskFirstDependencyTimeIfNeeded() {
+  DCHECK(!tasks_.empty());
+  if (tasks_.back().first_dependency_added.is_null()) {
+    // Fence are always added for the last task (which should always exists).
+    tasks_.back().first_dependency_added = base::TimeTicks::Now();
+  }
+}
+
 void Scheduler::Sequence::AddWaitFence(const SyncToken& sync_token,
                                        uint32_t order_num,
                                        SequenceId release_sequence_id) {
@@ -233,6 +257,17 @@
     SchedulingPriority wait_priority = it->second;
     wait_fences_.erase(it);
 
+    for (auto& task : tasks_) {
+      if (order_num == task.order_num) {
+        // The fence applies to this task, bump the readiness timestamp
+        task.running_ready = base::TimeTicks::Now();
+        break;
+      } else if (order_num < task.order_num) {
+        // Updated all task related to this fence.
+        break;
+      }
+    }
+
     Sequence* release_sequence = scheduler_->GetSequence(release_sequence_id);
     if (release_sequence)
       release_sequence->RemoveWaitingPriority(wait_priority);
@@ -424,6 +459,7 @@
                            sync_token, order_num, release_sequence_id,
                            sequence_id))) {
       sequence->AddWaitFence(sync_token, order_num, release_sequence_id);
+      sequence->SetLastTaskFirstDependencyTimeIfNeeded();
     }
   }
 
@@ -544,6 +580,18 @@
   Sequence* sequence = GetSequence(state.sequence_id);
   DCHECK(sequence);
 
+  UMA_HISTOGRAM_CUSTOM_MICROSECONDS_TIMES(
+      "GPU.Scheduler.TaskDependencyTime",
+      sequence->FrontTaskWaitingDependencyDelta(),
+      base::TimeDelta::FromMicroseconds(10), base::TimeDelta::FromSeconds(30),
+      100);
+
+  UMA_HISTOGRAM_CUSTOM_MICROSECONDS_TIMES(
+      "GPU.Scheduler.TaskSchedulingDelayTime",
+      sequence->FrontTaskSchedulingDelay(),
+      base::TimeDelta::FromMicroseconds(10), base::TimeDelta::FromSeconds(30),
+      100);
+
   base::OnceClosure closure;
   uint32_t order_num = sequence->BeginTask(&closure);
   DCHECK_EQ(order_num, state.order_num);
diff --git a/gpu/command_buffer/service/scheduler.h b/gpu/command_buffer/service/scheduler.h
index c47f6f5..9df2986 100644
--- a/gpu/command_buffer/service/scheduler.h
+++ b/gpu/command_buffer/service/scheduler.h
@@ -17,6 +17,7 @@
 #include "base/memory/weak_ptr.h"
 #include "base/synchronization/lock.h"
 #include "base/threading/thread_checker.h"
+#include "base/time/time.h"
 #include "gpu/command_buffer/common/command_buffer_id.h"
 #include "gpu/command_buffer/common/scheduling_priority.h"
 #include "gpu/command_buffer/common/sync_token.h"
@@ -169,6 +170,14 @@
     // Update cached scheduling priority while running.
     void UpdateRunningPriority();
 
+    // The time delta it took for the front task's dependencies to be completed.
+    base::TimeDelta FrontTaskWaitingDependencyDelta();
+
+    // The delay between when the front task was ready to run (no more
+    // dependencies) and now. This is used when the task is actually started to
+    // check for low scheduling delays.
+    base::TimeDelta FrontTaskSchedulingDelay();
+
     // Returns the next order number and closure. Sets running state to RUNNING.
     uint32_t BeginTask(base::OnceClosure* closure);
 
@@ -183,6 +192,10 @@
     // in between |BeginTask| and |FinishTask|.
     void ContinueTask(base::OnceClosure closure);
 
+    // Sets the first dependency added time on the last task if it wasn't
+    // already set, no-op otherwise.
+    void SetLastTaskFirstDependencyTimeIfNeeded();
+
     // Add a sync token fence that this sequence should wait on.
     void AddWaitFence(const SyncToken& sync_token,
                       uint32_t order_num,
@@ -235,6 +248,13 @@
 
       base::OnceClosure closure;
       uint32_t order_num;
+
+      // TODO(b/181148082): add measuring callback to return values to task
+      // owner
+      // Note: this time is only correct once the last fence has been removed,
+      // as it is updated for all fences.
+      base::TimeTicks running_ready = base::TimeTicks::Now();
+      base::TimeTicks first_dependency_added;
     };
 
     // Description of Stream priority propagation: Each Stream has an initial
diff --git a/gpu/gles2_conform_support/generate_gles2_conform_tests.py b/gpu/gles2_conform_support/generate_gles2_conform_tests.py
index b6b49a1..632864f 100755
--- a/gpu/gles2_conform_support/generate_gles2_conform_tests.py
+++ b/gpu/gles2_conform_support/generate_gles2_conform_tests.py
@@ -30,17 +30,17 @@
   out_file.write("""
 #include "gpu/gles2_conform_support/gles2_conform_test.h"
 #include "testing/gtest/include/gtest/gtest.h"
-""")
+""".encode("utf8"))
 
   for test in tests:
-    out_file.write("""
+    out_file.write(("""
 TEST(GLES2ConformTest, %(name)s) {
   EXPECT_TRUE(RunGLES2ConformTest("%(path)s"));
 }
 """ % {
         "name": re.sub(r'[^A-Za-z0-9]', '_', test),
         "path": test,
-      })
+      }).encode("utf8"))
 
 
 def main(argv):
@@ -52,7 +52,7 @@
     out_dir = '.'
 
   out_filename = os.path.join(out_dir, 'gles2_conform_test_autogen.cc')
-  with open(out_filename, 'wb') as out_file:
+  with open(out_filename, 'w') as out_file:
     GenerateTests(out_file)
 
   return 0
diff --git a/gpu/gles2_conform_support/generate_gles2_embedded_data.py b/gpu/gles2_conform_support/generate_gles2_embedded_data.py
index 809b24a5..d8eb08f 100755
--- a/gpu/gles2_conform_support/generate_gles2_embedded_data.py
+++ b/gpu/gles2_conform_support/generate_gles2_embedded_data.py
@@ -5,6 +5,8 @@
 
 """generates files to embed the gles2 conformance test data in executable."""
 
+from __future__ import print_function
+
 import os
 import sys
 
@@ -35,23 +37,23 @@
       self.files_data_c = open(os.path.join(base_dir, "FilesDATA.c"), "wb")
       self.files_toc_c = open(os.path.join(base_dir, "FilesTOC.c"), "wb")
 
-      self.files_data_h.write("#ifndef FilesDATA_h\n\n")
-      self.files_data_h.write("#define FilesDATA_h\n\n");
+      self.files_data_h.write(b"#ifndef FilesDATA_h\n\n")
+      self.files_data_h.write(b"#define FilesDATA_h\n\n");
 
-      self.files_data_c.write("#include \"FilesDATA.h\"\n\n")
+      self.files_data_c.write(b"#include \"FilesDATA.h\"\n\n")
 
-      self.files_toc_c.write("#include \"FilesTOC.h\"\n\n");
-      self.files_toc_c.write("struct GTFVectorFileEntry tempFiles;\n\n");
-      self.files_toc_c.write("struct FileEntry files[] = {\n");
+      self.files_toc_c.write(b"#include \"FilesTOC.h\"\n\n");
+      self.files_toc_c.write(b"struct GTFVectorFileEntry tempFiles;\n\n");
+      self.files_toc_c.write(b"struct FileEntry files[] = {\n");
 
     self.AddFiles(scan_dir)
 
     if self.base_dir != None:
-      self.files_toc_c.write("\n};\n\n");
+      self.files_toc_c.write(b"\n};\n\n");
       self.files_toc_c.write(
-        "int numFileEntrys = sizeof(files) / sizeof(struct FileEntry);\n");
+        b"int numFileEntrys = sizeof(files) / sizeof(struct FileEntry);\n");
 
-      self.files_data_h.write("\n\n#endif  // FilesDATA_h\n");
+      self.files_data_h.write(b"\n\n#endif  // FilesDATA_h\n");
 
       self.files_data_c.close()
       self.files_data_h.close()
@@ -70,14 +72,16 @@
           sub_dirs.append(full_path)
       elif ext in GenerateEmbeddedFiles.extensions_to_include:
         if self.base_dir == None:
-          print full_path.replace("\\", "/")
+          print(full_path.replace("\\", "/"))
         else:
           self.count += 1
           name = "_FILE_%s_%d" % (ext.upper(), self.count)
           name = name.replace(".", "_")
 
-          self.files_data_h.write("extern const char %s[];\n" % name)
-          self.files_data_c.write("const char %s[] = \n" % name)
+          self.files_data_h.write(
+              ("extern const char %s[];\n" % name).encode("utf8"))
+          self.files_data_c.write(
+              ("const char %s[] = \n" % name).encode("utf8"))
 
           data = open(full_path, "r")
           lines = data.readlines();
@@ -89,11 +93,11 @@
             line = line.replace("\\", "\\\\")
             line = line.replace("\"", "\\\"")
 
-            self.files_data_c.write('"%s\\n"\n' % line)
+            self.files_data_c.write(('"%s\\n"\n' % line).encode("utf8"))
 
-          self.files_data_c.write(";\n")
-          self.files_toc_c.write("\t{ \"%s\", %s, 0 },\n" % (
-              base_path.replace("\\", "/"), name))
+          self.files_data_c.write(b";\n")
+          self.files_toc_c.write(("\t{ \"%s\", %s, 0 },\n" % (
+              base_path.replace("\\", "/"), name)).encode("utf8"))
 
     for sub_dir in sub_dirs:
       self.AddFiles(sub_dir)
diff --git a/infra/config/generated/commit-queue.cfg b/infra/config/generated/commit-queue.cfg
index 0f616146..3d7963b 100644
--- a/infra/config/generated/commit-queue.cfg
+++ b/infra/config/generated/commit-queue.cfg
@@ -508,6 +508,7 @@
         location_regexp: ".+/[+]/third_party/blink/web_tests/wpt_internal/webgpu/.+"
         location_regexp: ".+/[+]/third_party/blink/web_tests/WebGPUExpectations"
         location_regexp: ".+/[+]/third_party/dawn/.+"
+        location_regexp: ".+/[+]/third_party/webgpu-cts/.+"
         location_regexp: ".+/[+]/tools/clang/scripts/update.py"
         location_regexp: ".+/[+]/ui/gl/features.gni"
         location_regexp_exclude: ".+/[+]/docs/.+"
@@ -522,6 +523,7 @@
         location_regexp: ".+/[+]/third_party/blink/web_tests/wpt_internal/webgpu/.+"
         location_regexp: ".+/[+]/third_party/blink/web_tests/WebGPUExpectations"
         location_regexp: ".+/[+]/third_party/dawn/.+"
+        location_regexp: ".+/[+]/third_party/webgpu-cts/.+"
         location_regexp: ".+/[+]/tools/clang/scripts/update.py"
         location_regexp: ".+/[+]/ui/gl/features.gni"
         location_regexp_exclude: ".+/[+]/docs/.+"
@@ -544,6 +546,7 @@
         location_regexp: ".+/[+]/third_party/blink/web_tests/wpt_internal/webgpu/.+"
         location_regexp: ".+/[+]/third_party/blink/web_tests/WebGPUExpectations"
         location_regexp: ".+/[+]/third_party/dawn/.+"
+        location_regexp: ".+/[+]/third_party/webgpu-cts/.+"
         location_regexp: ".+/[+]/tools/clang/scripts/update.py"
         location_regexp: ".+/[+]/ui/gl/features.gni"
         location_regexp_exclude: ".+/[+]/docs/.+"
@@ -558,6 +561,7 @@
         location_regexp: ".+/[+]/third_party/blink/web_tests/wpt_internal/webgpu/.+"
         location_regexp: ".+/[+]/third_party/blink/web_tests/WebGPUExpectations"
         location_regexp: ".+/[+]/third_party/dawn/.+"
+        location_regexp: ".+/[+]/third_party/webgpu-cts/.+"
         location_regexp: ".+/[+]/tools/clang/scripts/update.py"
         location_regexp: ".+/[+]/ui/gl/features.gni"
         location_regexp_exclude: ".+/[+]/docs/.+"
diff --git a/infra/config/generated/cq-builders.md b/infra/config/generated/cq-builders.md
index 41b2637..7d7b4ac3 100644
--- a/infra/config/generated/cq-builders.md
+++ b/infra/config/generated/cq-builders.md
@@ -183,6 +183,7 @@
   * [`//third_party/blink/web_tests/wpt_internal/webgpu/.+`](https://cs.chromium.org/chromium/src/third_party/blink/web_tests/wpt_internal/webgpu/)
   * [`//third_party/blink/web_tests/WebGPUExpectations`](https://cs.chromium.org/chromium/src/third_party/blink/web_tests/WebGPUExpectations)
   * [`//third_party/dawn/.+`](https://cs.chromium.org/chromium/src/third_party/dawn/)
+  * [`//third_party/webgpu-cts/.+`](https://cs.chromium.org/chromium/src/third_party/webgpu-cts/)
   * [`//tools/clang/scripts/update.py`](https://cs.chromium.org/search?q=package:%5Echromium$+file:tools/clang/scripts/update.py)
   * [`//ui/gl/features.gni`](https://cs.chromium.org/search?q=package:%5Echromium$+file:ui/gl/features.gni)
 
@@ -196,6 +197,7 @@
   * [`//third_party/blink/web_tests/wpt_internal/webgpu/.+`](https://cs.chromium.org/chromium/src/third_party/blink/web_tests/wpt_internal/webgpu/)
   * [`//third_party/blink/web_tests/WebGPUExpectations`](https://cs.chromium.org/chromium/src/third_party/blink/web_tests/WebGPUExpectations)
   * [`//third_party/dawn/.+`](https://cs.chromium.org/chromium/src/third_party/dawn/)
+  * [`//third_party/webgpu-cts/.+`](https://cs.chromium.org/chromium/src/third_party/webgpu-cts/)
   * [`//tools/clang/scripts/update.py`](https://cs.chromium.org/search?q=package:%5Echromium$+file:tools/clang/scripts/update.py)
   * [`//ui/gl/features.gni`](https://cs.chromium.org/search?q=package:%5Echromium$+file:ui/gl/features.gni)
 
@@ -209,6 +211,7 @@
   * [`//third_party/blink/web_tests/wpt_internal/webgpu/.+`](https://cs.chromium.org/chromium/src/third_party/blink/web_tests/wpt_internal/webgpu/)
   * [`//third_party/blink/web_tests/WebGPUExpectations`](https://cs.chromium.org/chromium/src/third_party/blink/web_tests/WebGPUExpectations)
   * [`//third_party/dawn/.+`](https://cs.chromium.org/chromium/src/third_party/dawn/)
+  * [`//third_party/webgpu-cts/.+`](https://cs.chromium.org/chromium/src/third_party/webgpu-cts/)
   * [`//tools/clang/scripts/update.py`](https://cs.chromium.org/search?q=package:%5Echromium$+file:tools/clang/scripts/update.py)
   * [`//ui/gl/features.gni`](https://cs.chromium.org/search?q=package:%5Echromium$+file:ui/gl/features.gni)
 
@@ -222,6 +225,7 @@
   * [`//third_party/blink/web_tests/wpt_internal/webgpu/.+`](https://cs.chromium.org/chromium/src/third_party/blink/web_tests/wpt_internal/webgpu/)
   * [`//third_party/blink/web_tests/WebGPUExpectations`](https://cs.chromium.org/chromium/src/third_party/blink/web_tests/WebGPUExpectations)
   * [`//third_party/dawn/.+`](https://cs.chromium.org/chromium/src/third_party/dawn/)
+  * [`//third_party/webgpu-cts/.+`](https://cs.chromium.org/chromium/src/third_party/webgpu-cts/)
   * [`//tools/clang/scripts/update.py`](https://cs.chromium.org/search?q=package:%5Echromium$+file:tools/clang/scripts/update.py)
   * [`//ui/gl/features.gni`](https://cs.chromium.org/search?q=package:%5Echromium$+file:ui/gl/features.gni)
 
diff --git a/infra/config/subprojects/chromium/try.star b/infra/config/subprojects/chromium/try.star
index 9583e43..ad167fe 100644
--- a/infra/config/subprojects/chromium/try.star
+++ b/infra/config/subprojects/chromium/try.star
@@ -814,6 +814,7 @@
             ".+/[+]/third_party/blink/web_tests/wpt_internal/webgpu/.+",
             ".+/[+]/third_party/blink/web_tests/WebGPUExpectations",
             ".+/[+]/third_party/dawn/.+",
+            ".+/[+]/third_party/webgpu-cts/.+",
             ".+/[+]/tools/clang/scripts/update.py",
             ".+/[+]/ui/gl/features.gni",
         ],
@@ -834,6 +835,7 @@
             ".+/[+]/third_party/blink/web_tests/wpt_internal/webgpu/.+",
             ".+/[+]/third_party/blink/web_tests/WebGPUExpectations",
             ".+/[+]/third_party/dawn/.+",
+            ".+/[+]/third_party/webgpu-cts/.+",
             ".+/[+]/tools/clang/scripts/update.py",
             ".+/[+]/ui/gl/features.gni",
         ],
@@ -854,6 +856,7 @@
             ".+/[+]/third_party/blink/web_tests/wpt_internal/webgpu/.+",
             ".+/[+]/third_party/blink/web_tests/WebGPUExpectations",
             ".+/[+]/third_party/dawn/.+",
+            ".+/[+]/third_party/webgpu-cts/.+",
             ".+/[+]/tools/clang/scripts/update.py",
             ".+/[+]/ui/gl/features.gni",
         ],
@@ -874,6 +877,7 @@
             ".+/[+]/third_party/blink/web_tests/wpt_internal/webgpu/.+",
             ".+/[+]/third_party/blink/web_tests/WebGPUExpectations",
             ".+/[+]/third_party/dawn/.+",
+            ".+/[+]/third_party/webgpu-cts/.+",
             ".+/[+]/tools/clang/scripts/update.py",
             ".+/[+]/ui/gl/features.gni",
         ],
diff --git a/ios/build/bots/chromium.webrtc.fyi/WebRTC Chromium FYI ios-device.json b/ios/build/bots/chromium.webrtc.fyi/WebRTC Chromium FYI ios-device.json
index 4378efb..fee62969 100644
--- a/ios/build/bots/chromium.webrtc.fyi/WebRTC Chromium FYI ios-device.json
+++ b/ios/build/bots/chromium.webrtc.fyi/WebRTC Chromium FYI ios-device.json
@@ -5,6 +5,7 @@
   ],
   "xcode build version": "12d4e",
   "gn_args": [
+    "enable_run_ios_unittests_with_xctest=true",
     "goma_dir=\"$(goma_dir)\"",
     "ios_enable_code_signing=false",
     "ios_set_attributes_for_xcode_project_generation=false",
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 944f3a474..d63f36dc 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
@@ -4,6 +4,7 @@
   ],
   "xcode build version": "12d4e",
   "gn_args": [
+    "enable_run_ios_unittests_with_xctest=true",
     "goma_dir=\"$(goma_dir)\"",
     "ios_enable_code_signing=false",
     "ios_set_attributes_for_xcode_project_generation=false",
diff --git a/ios/chrome/app/BUILD.gn b/ios/chrome/app/BUILD.gn
index be6bfcf7..a7d178f 100644
--- a/ios/chrome/app/BUILD.gn
+++ b/ios/chrome/app/BUILD.gn
@@ -163,6 +163,18 @@
   ]
 }
 
+source_set("safe_mode_app_state_agent") {
+  configs += [ "//build/config/compiler:enable_arc" ]
+  sources = [
+    "safe_mode_app_state_agent.h",
+    "safe_mode_app_state_agent.mm",
+  ]
+  deps = [
+    "//base",
+    "//ios/chrome/app/application_delegate:app_state_header",
+  ]
+}
+
 source_set("app_internal") {
   configs += [ "//build/config/compiler:enable_arc" ]
   sources = [
@@ -186,6 +198,7 @@
     ":blocking_scene_commands",
     ":content_suggestions_scheduler_app_state_agent",
     ":mode",
+    ":safe_mode_app_state_agent",
     ":tests_hook",
     "//base",
     "//build:branding_buildflags",
diff --git a/ios/chrome/app/application_delegate/BUILD.gn b/ios/chrome/app/application_delegate/BUILD.gn
index 712b732d..8e405d9 100644
--- a/ios/chrome/app/application_delegate/BUILD.gn
+++ b/ios/chrome/app/application_delegate/BUILD.gn
@@ -47,6 +47,7 @@
     "//ios/chrome/app",
     "//ios/chrome/app:app_internal",
     "//ios/chrome/app:mode",
+    "//ios/chrome/app:safe_mode_app_state_agent",
     "//ios/chrome/app/intents",
     "//ios/chrome/app/spotlight",
     "//ios/chrome/app/startup",
@@ -140,6 +141,7 @@
   sources = [
     "app_state.h",
     "app_state_agent.h",
+    "app_state_observer.h",
   ]
   public_deps = [
     "//ios/chrome/browser/ui/main:scene_state_observer",
diff --git a/ios/chrome/app/application_delegate/app_state.h b/ios/chrome/app/application_delegate/app_state.h
index 4f15d69..6648be7 100644
--- a/ios/chrome/app/application_delegate/app_state.h
+++ b/ios/chrome/app/application_delegate/app_state.h
@@ -10,6 +10,7 @@
 #include <memory>
 
 #import "ios/chrome/app/application_delegate/app_state_agent.h"
+#import "ios/chrome/app/application_delegate/app_state_observer.h"
 #import "ios/chrome/browser/ui/main/scene_state_observer.h"
 #import "ios/chrome/browser/ui/scoped_ui_blocker/ui_blocker_manager.h"
 
@@ -30,26 +31,6 @@
 class TimeTicks;
 }
 
-@protocol AppStateObserver <NSObject>
-
-@optional
-
-// Called when a scene is connected.
-// On iOS 12, called when the mainSceneState is set.
-- (void)appState:(AppState*)appState sceneConnected:(SceneState*)sceneState;
-
-// Called when the first scene initializes its UI.
-- (void)appState:(AppState*)appState
-    firstSceneHasInitializedUI:(SceneState*)sceneState;
-
-// Called after the app exits safe mode.
-- (void)appStateDidExitSafeMode:(AppState*)appState;
-
-// Called when |AppState.lastTappedWindow| changes.
-- (void)appState:(AppState*)appState lastTappedWindowChanged:(UIWindow*)window;
-
-@end
-
 // Represents the application state and responds to application state changes
 // and system events.
 @interface AppState : NSObject <UIBlockerManager, SceneStateObserver>
@@ -109,6 +90,9 @@
 // Timestamp of when a scene was last becoming active. Can be null.
 @property(nonatomic, assign) base::TimeTicks lastTimeInForeground;
 
+// The initialization stage the app is currently at.
+@property(nonatomic, readonly) InitStage initStage;
+
 // Saves the launchOptions to be used from -newTabFromLaunchOptions. If the
 // application is in background, initialize the browser to basic. If not, launch
 // the browser.
@@ -177,6 +161,10 @@
 // This automatically sets the app state on the |agent|.
 - (void)addAgent:(id<AppStateAgent>)agent;
 
+// Queue the transition to the next app initialization stage. Will stop
+// transitioning when the Final stage is reached.
+- (void)queueTransitionToNextInitStage;
+
 @end
 
 #endif  // IOS_CHROME_APP_APPLICATION_DELEGATE_APP_STATE_H_
diff --git a/ios/chrome/app/application_delegate/app_state.mm b/ios/chrome/app/application_delegate/app_state.mm
index 1d448b4..e98fbda 100644
--- a/ios/chrome/app/application_delegate/app_state.mm
+++ b/ios/chrome/app/application_delegate/app_state.mm
@@ -154,6 +154,13 @@
 // Agents attached to this app state.
 @property(nonatomic, strong) NSMutableArray<id<AppStateAgent>>* agents;
 
+// Operational blocks to be run by the local runloop that are queued by the
+// init stage agents.
+@property(nonatomic, strong) NSMutableArray* stageQueue;
+
+// Redefined internaly as readwrite.
+@property(nonatomic, assign, readwrite) InitStage initStage;
+
 @end
 
 @implementation AppState
@@ -189,6 +196,8 @@
                  object:nil];
       }
     }
+
+    _stageQueue = [NSMutableArray new];
   }
   return self;
 }
@@ -552,6 +561,13 @@
   [agent setAppState:self];
 }
 
+- (void)queueTransitionToNextInitStage {
+  __weak AppState* weakSelf = self;
+  [_stageQueue addObject:(^{
+                 [weakSelf advanceToNextInitStage];
+               })];
+}
+
 #pragma mark - Multiwindow-related
 
 - (SceneState*)foregroundActiveScene {
@@ -602,6 +618,8 @@
 
 #pragma mark - SafeModeCoordinatorDelegate Implementation
 
+// TODO(crbug.com/1178809): Handle this with an app state agent when
+// transitioning out of safe mode.
 - (void)coordinatorDidExitSafeMode:(nonnull SafeModeCoordinator*)coordinator {
   [self stopSafeMode];
   [_browserLauncher startUpBrowserToStage:INITIALIZATION_STAGE_FOREGROUND];
@@ -648,6 +666,9 @@
   _userInteracted = YES;
   [self saveLaunchDetailsToDefaults];
 
+  [self startInitStages];
+
+  // TODO(crbug.com/1178809): Move this logic to the safe mode agent.
   if ([SafeModeCoordinator shouldStart]) {
     self.inSafeMode = YES;
     if (!base::ios::IsMultiwindowSupported()) {
@@ -658,6 +679,9 @@
     return;
   }
 
+  // TODO(crbug.com/1178809): Move the logic below to a Final Init Stage agent
+  // to make sure that the logic is only executed when getting out of safe mode.
+
   // Don't add code here. Add it in MainController's
   // -startUpBrowserForegroundInitialization.
   DCHECK([self.startupInformation isColdStart]);
@@ -689,6 +713,41 @@
   [[PreviousSessionInfo sharedInstance] beginRecordingCurrentSession];
 }
 
+// Advances to the next init stage.
+- (void)advanceToNextInitStage {
+  // The app is done with all init stages, nothing to do.
+  if (self.initStage == InitStageFinal)
+    return;
+
+  InitStage newInitStage = static_cast<InitStage>(self.initStage + 1);
+
+  [self.observers appState:self willTransitionToInitStage:newInitStage];
+  self.initStage = newInitStage;
+  [self.observers appState:self didTransitionToInitStage:self.initStage];
+}
+
+// Starts the transition through init stages.
+- (void)startInitStages {
+  __weak AppState* weakSelf = self;
+  [_stageQueue addObject:(^{
+                 [weakSelf.observers appState:weakSelf
+                     willTransitionToInitStage:InitStageStart];
+                 [weakSelf.observers appState:weakSelf
+                     didTransitionToInitStage:InitStageStart];
+               })];
+  [self executeInitStageQueue];
+}
+
+// Executes the blocks in the init stage queue until the queue is empty. This
+// will also execute the new blocks that are queued on the fly.
+- (void)executeInitStageQueue {
+  while ([_stageQueue count] > 0) {
+    void (^stageBlock)() = [_stageQueue objectAtIndex:0];
+    stageBlock();
+    [_stageQueue removeObjectAtIndex:0];
+  }
+}
+
 #pragma mark - UIBlockerManager
 
 - (void)incrementBlockingUICounterForTarget:(id<UIBlockerTarget>)target {
diff --git a/ios/chrome/app/application_delegate/app_state_observer.h b/ios/chrome/app/application_delegate/app_state_observer.h
new file mode 100644
index 0000000..f3b326e
--- /dev/null
+++ b/ios/chrome/app/application_delegate/app_state_observer.h
@@ -0,0 +1,56 @@
+// Copyright 2021 The Chromium 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_APP_APPLICATION_DELEGATE_APP_STATE_OBSERVER_H_
+#define IOS_CHROME_APP_APPLICATION_DELEGATE_APP_STATE_OBSERVER_H_
+
+@class AppState;
+@class UIWindow;
+@class SceneState;
+
+// App initialization stages. The app will go sequentially in-order through each
+// stage each time the app is launched. This enum might expand in the future but
+// the start and last stages will always keep the same label and relative
+// position.
+typedef NS_ENUM(NSUInteger, InitStage) {
+  // The first stage when starting the initialization. The label and value of
+  // this enum item should not change.
+  InitStageStart = 0,
+  // The app is in safe mode.
+  InitStageSafeMode,
+  // The final stage before being done with initialization. The label and
+  // relative position (always last) of this enum item should not change.
+  // The value may change when inserting enum items between Start and Final.
+  InitStageFinal
+};
+
+@protocol AppStateObserver <NSObject>
+
+@optional
+
+// Called when a scene is connected.
+// On iOS 12, called when the mainSceneState is set.
+- (void)appState:(AppState*)appState sceneConnected:(SceneState*)sceneState;
+
+// Called when the first scene initializes its UI.
+- (void)appState:(AppState*)appState
+    firstSceneHasInitializedUI:(SceneState*)sceneState;
+
+// Called after the app exits safe mode.
+- (void)appStateDidExitSafeMode:(AppState*)appState;
+
+// Called when |AppState.lastTappedWindow| changes.
+- (void)appState:(AppState*)appState lastTappedWindowChanged:(UIWindow*)window;
+
+// Called when the app is about to transition to |initStage|. The init stage of
+// the app at that moment is still |initStage| - 1.
+- (void)appState:(AppState*)appState
+    willTransitionToInitStage:(InitStage)initStage;
+
+// Called right after the app is transitioned to the |initStage|.
+- (void)appState:(AppState*)appState
+    didTransitionToInitStage:(InitStage)initStage;
+
+@end
+
+#endif  // IOS_CHROME_APP_APPLICATION_DELEGATE_APP_STATE_OBSERVER_H_
diff --git a/ios/chrome/app/application_delegate/app_state_unittest.mm b/ios/chrome/app/application_delegate/app_state_unittest.mm
index 95dd9e4..ed68254a 100644
--- a/ios/chrome/app/application_delegate/app_state_unittest.mm
+++ b/ios/chrome/app/application_delegate/app_state_unittest.mm
@@ -11,6 +11,7 @@
 #import "base/ios/ios_util.h"
 #import "base/test/task_environment.h"
 #import "ios/chrome/app/app_startup_parameters.h"
+#import "ios/chrome/app/application_delegate/app_state_observer.h"
 #import "ios/chrome/app/application_delegate/app_state_testing.h"
 #import "ios/chrome/app/application_delegate/browser_launcher.h"
 #import "ios/chrome/app/application_delegate/fake_startup_information.h"
@@ -21,6 +22,8 @@
 #import "ios/chrome/app/application_delegate/tab_switching.h"
 #import "ios/chrome/app/application_delegate/user_activity_handler.h"
 #import "ios/chrome/app/main_application_delegate.h"
+#import "ios/chrome/app/safe_mode_app_state_agent.h"
+#include "ios/chrome/app/safe_mode_app_state_agent.h"
 #include "ios/chrome/browser/browser_state/test_chrome_browser_state.h"
 #include "ios/chrome/browser/chrome_url_constants.h"
 #import "ios/chrome/browser/device_sharing/device_sharing_manager.h"
@@ -61,8 +64,11 @@
 
 // Exposes private safe mode start/stop methods.
 @interface AppState (Private)
+@property(nonatomic, strong) SafeModeCoordinator* safeModeCoordinator;
+
 - (void)startSafeMode;
 - (void)stopSafeMode;
+- (void)coordinatorDidExitSafeMode:(SafeModeCoordinator*)coordinator;
 @end
 
 #pragma mark - Class definition.
@@ -134,6 +140,7 @@
 class AppStateTest : public BlockCleanupTest {
  protected:
   AppStateTest() {
+    // Init mocks.
     browser_launcher_mock_ =
         [OCMockObject mockForProtocol:@protocol(BrowserLauncher)];
     startup_information_mock_ =
@@ -143,6 +150,8 @@
     main_application_delegate_ =
         [OCMockObject mockForClass:[MainApplicationDelegate class]];
     window_ = [OCMockObject mockForClass:[UIWindow class]];
+    app_state_observer_mock_ =
+        [OCMockObject mockForProtocol:@protocol(AppStateObserver)];
 
     interface_provider_ = [[StubBrowserInterfaceProvider alloc] init];
   }
@@ -289,6 +298,7 @@
   id getConnectionInformationMock() { return connection_information_mock_; }
   id getApplicationDelegateMock() { return main_application_delegate_; }
   id getWindowMock() { return window_; }
+  id getAppStateObserverMock() { return app_state_observer_mock_; }
   StubBrowserInterfaceProvider* getInterfaceProvider() {
     return interface_provider_;
   }
@@ -306,6 +316,7 @@
   id startup_information_mock_;
   id main_application_delegate_;
   id window_;
+  id app_state_observer_mock_;
   StubBrowserInterfaceProvider* interface_provider_;
   ScenesBlock connected_scenes_swizzle_block_;
   DecisionBlock safe_mode_swizzle_block_;
@@ -354,6 +365,10 @@
   // Test.
   EXPECT_TRUE(result);
   EXPECT_OCMOCK_VERIFY(browserLauncherMock);
+
+  // Verify the launch stage is still at the starting point when the app is in
+  // background.
+  EXPECT_EQ(InitStageStart, appState.initStage);
 }
 
 // Tests that if the application is active and Safe Mode should be activated
@@ -377,15 +392,44 @@
   [[windowMock expect] makeKeyAndVisible];
 
   AppState* appState = getAppStateWithMock();
+  ASSERT_FALSE([appState isInSafeMode]);
+
+  id appStateObserverMock = getAppStateObserverMock();
+  // Expected app state observer calls when starting the init stages.
+  [[appStateObserverMock expect] appState:appState
+                willTransitionToInitStage:InitStageStart];
+  [[appStateObserverMock expect] appState:appState
+                 didTransitionToInitStage:InitStageStart];
+  // Expected app state observer calls when transitioning from Start to Safe
+  // Mode.
+  [[appStateObserverMock expect] appState:appState
+                willTransitionToInitStage:InitStageSafeMode];
+  [[appStateObserverMock expect] appState:appState
+                 didTransitionToInitStage:InitStageSafeMode];
+  // Expected app state observer calls when transitioning from Safe Mode to
+  // Final.
+  [[appStateObserverMock expect] appState:appState
+                willTransitionToInitStage:InitStageFinal];
+  [[appStateObserverMock expect] appState:appState
+                 didTransitionToInitStage:InitStageFinal];
+  [appState addObserver:appStateObserverMock];
+
+  [appState addAgent:[[SafeModeAppAgent alloc] init]];
 
   id browserLauncherMock = getBrowserLauncherMock();
   BrowserInitializationStageType stageBasic = INITIALIZATION_STAGE_BASIC;
   [[browserLauncherMock expect] startUpBrowserToStage:stageBasic];
   [[browserLauncherMock expect] setLaunchOptions:launchOptions];
 
-  swizzleSafeModeShouldStart(YES);
+  // Expected calls on AppState#coordinatorDidExitSafeMode.
+  [[appStateObserverMock expect] appStateDidExitSafeMode:appState];
+  [[browserLauncherMock expect]
+      startUpBrowserToStage:INITIALIZATION_STAGE_FOREGROUND];
+  id applicationDelegateMock = getApplicationDelegateMock();
+  [[applicationDelegateMock expect]
+      applicationDidBecomeActive:[UIApplication sharedApplication]];
 
-  ASSERT_FALSE([appState isInSafeMode]);
+  swizzleSafeModeShouldStart(YES);
 
   appState.mainSceneState.activationLevel =
       SceneActivationLevelForegroundActive;
@@ -395,18 +439,25 @@
                                                  stateBackground:NO];
 
   if (base::ios::IsMultiwindowSupported()) {
-    [appState startSafeMode];
+    // Start the safe mode by transitioning the scene to foreground again after
+    // #requiresHandlingAfterLaunchWithOptions which starts the safe mode.
+    appState.mainSceneState.activationLevel =
+        SceneActivationLevelForegroundActive;
   }
 
-  // Test.
   EXPECT_TRUE(result);
   EXPECT_TRUE([appState isInSafeMode]);
-  EXPECT_OCMOCK_VERIFY(browserLauncherMock);
-  EXPECT_OCMOCK_VERIFY(windowMock);
 
-  if (base::ios::IsMultiwindowSupported()) {
-    [appState stopSafeMode];
-  }
+  // Stop safe mode.
+  [appState coordinatorDidExitSafeMode:appState.safeModeCoordinator];
+
+  // Verify that the dependencies are called properly during the app journey.
+  EXPECT_OCMOCK_VERIFY(windowMock);
+  EXPECT_OCMOCK_VERIFY(browserLauncherMock);
+  EXPECT_OCMOCK_VERIFY(appStateObserverMock);
+  EXPECT_OCMOCK_VERIFY(applicationDelegateMock);
+
+  EXPECT_EQ(InitStageFinal, appState.initStage);
 }
 
 // Tests that if the application is active
@@ -423,6 +474,33 @@
   [[[getWindowMock() stub] andReturn:nil] rootViewController];
 
   AppState* appState = getAppStateWithMock();
+  ASSERT_FALSE([appState isInSafeMode]);
+
+  id appStateObserverMock = getAppStateObserverMock();
+  // Expected app state observer calls when starting the init stages.
+  [[appStateObserverMock expect] appState:appState
+                willTransitionToInitStage:InitStageStart];
+  [[appStateObserverMock expect] appState:appState
+                 didTransitionToInitStage:InitStageStart];
+  // Expected app state observer calls when transitioning from Start to Safe
+  // Mode.
+  [[appStateObserverMock expect] appState:appState
+                willTransitionToInitStage:InitStageSafeMode];
+  [[appStateObserverMock expect] appState:appState
+                 didTransitionToInitStage:InitStageSafeMode];
+  // Expected app state observer calls when transitioning from Safe Mode to
+  // Final.
+  [[appStateObserverMock expect] appState:appState
+                willTransitionToInitStage:InitStageFinal];
+  [[appStateObserverMock expect] appState:appState
+                 didTransitionToInitStage:InitStageFinal];
+  [appState addObserver:appStateObserverMock];
+
+  [appState addAgent:[[SafeModeAppAgent alloc] init]];
+
+  id applicationDelegateMock = getApplicationDelegateMock();
+  [[applicationDelegateMock expect]
+      applicationDidBecomeActive:[UIApplication sharedApplication]];
 
   id browserLauncherMock = getBrowserLauncherMock();
   BrowserInitializationStageType stageBasic = INITIALIZATION_STAGE_BASIC;
@@ -440,7 +518,11 @@
 
   // Test.
   EXPECT_TRUE(result);
+  EXPECT_EQ(InitStageFinal, appState.initStage);
+
+  // Verify that the dependencies were called properly.
   EXPECT_OCMOCK_VERIFY(browserLauncherMock);
+  EXPECT_OCMOCK_VERIFY(appStateObserverMock);
 }
 
 using AppStateNoFixtureTest = PlatformTest;
@@ -802,6 +884,8 @@
   [[window expect] makeKeyAndVisible];
   AppState* appState = getAppStateWithRealWindow(window);
 
+  [appState addAgent:[[SafeModeAppAgent alloc] init]];
+
   // Starting safe mode will call makeKeyAndVisible on the window.
   [[window expect] makeKeyAndVisible];
   appState.mainSceneState.activationLevel =
@@ -809,13 +893,18 @@
   appState.mainSceneState.window = window;
 
   // Actions.
-  [getAppStateWithMock() applicationWillEnterForeground:application
-                                        metricsMediator:metricsMediator
-                                           memoryHelper:memoryHelper];
+  [appState applicationWillEnterForeground:application
+                           metricsMediator:metricsMediator
+                              memoryHelper:memoryHelper];
 
   // Tests.
   EXPECT_OCMOCK_VERIFY(window);
-  EXPECT_TRUE([getAppStateWithMock() isInSafeMode]);
+
+  // Verify that the app is still in safe mode after initializing the UI when
+  // entering foreground from background.
+  EXPECT_TRUE([appState isInSafeMode]);
+
+  EXPECT_EQ(InitStageFinal, appState.initStage);
 }
 
 // Tests that -applicationDidEnterBackground calls the metrics mediator.
diff --git a/ios/chrome/app/main_controller.mm b/ios/chrome/app/main_controller.mm
index 6c029f1..fd15a82 100644
--- a/ios/chrome/app/main_controller.mm
+++ b/ios/chrome/app/main_controller.mm
@@ -34,6 +34,7 @@
 #import "ios/chrome/app/content_suggestions_scheduler_app_state_agent.h"
 #import "ios/chrome/app/deferred_initialization_runner.h"
 #import "ios/chrome/app/memory_monitor.h"
+#import "ios/chrome/app/safe_mode_app_state_agent.h"
 #import "ios/chrome/app/spotlight/spotlight_manager.h"
 #include "ios/chrome/app/startup/chrome_app_startup_parameters.h"
 #include "ios/chrome/app/startup/chrome_main_starter.h"
@@ -616,6 +617,7 @@
   [appState addAgent:[[AppMetricsAppStateAgent alloc] init]];
   [appState addAgent:[[ContentSuggestionsSchedulerAppAgent alloc] init]];
   [appState addAgent:[[IncognitoUsageAppStateAgent alloc] init]];
+  [appState addAgent:[[SafeModeAppAgent alloc] init]];
 
   // Create the window accessibility agent only when multuple windows are
   // possible.
diff --git a/ios/chrome/app/safe_mode_app_state_agent.h b/ios/chrome/app/safe_mode_app_state_agent.h
new file mode 100644
index 0000000..1aafd462
--- /dev/null
+++ b/ios/chrome/app/safe_mode_app_state_agent.h
@@ -0,0 +1,17 @@
+// Copyright 2021 The Chromium 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_APP_SAFE_MODE_APP_STATE_AGENT_H_
+#define IOS_CHROME_APP_SAFE_MODE_APP_STATE_AGENT_H_
+
+#import "ios/chrome/app/application_delegate/app_state_agent.h"
+
+@class AppState;
+
+// App state agent that triggers the safe mode stage when needed.
+@interface SafeModeAppAgent : NSObject <AppStateAgent>
+
+@end
+
+#endif  // IOS_CHROME_APP_SAFE_MODE_APP_STATE_AGENT_H_
diff --git a/ios/chrome/app/safe_mode_app_state_agent.mm b/ios/chrome/app/safe_mode_app_state_agent.mm
new file mode 100644
index 0000000..26d802a
--- /dev/null
+++ b/ios/chrome/app/safe_mode_app_state_agent.mm
@@ -0,0 +1,48 @@
+// Copyright 2021 The Chromium 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/app/safe_mode_app_state_agent.h"
+
+#include "base/check.h"
+#import "ios/chrome/app/application_delegate/app_state.h"
+#import "ios/chrome/app/application_delegate/app_state_observer.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+@interface SafeModeAppAgent () <AppStateObserver>
+
+// The app state for the app.
+@property(nonatomic, weak, readonly) AppState* appState;
+
+@end
+
+@implementation SafeModeAppAgent
+
+#pragma mark - AppStateAgent
+
+- (void)setAppState:(AppState*)appState {
+  // This should only be called once!
+  DCHECK(!_appState);
+
+  _appState = appState;
+  [appState addObserver:self];
+}
+
+#pragma mark - AppStateObserver
+
+- (void)appState:(AppState*)appState
+    didTransitionToInitStage:(InitStage)initStage {
+  if (initStage == InitStageStart) {
+    // TODO(crbug.com/1178809): Move this responsibility to a specific agent.
+    // Handle the transition out of the Start stage.
+    [self.appState queueTransitionToNextInitStage];
+  } else if (initStage == InitStageSafeMode) {
+    // TODO(crbug.com/1178809): Trigger safe mode from here.
+    [self.appState queueTransitionToNextInitStage];
+  }
+}
+
+@end
diff --git a/ios/chrome/browser/resources/Settings.bundle/Experimental.plist b/ios/chrome/browser/resources/Settings.bundle/Experimental.plist
index 8f996bd..aa69029 100644
--- a/ios/chrome/browser/resources/Settings.bundle/Experimental.plist
+++ b/ios/chrome/browser/resources/Settings.bundle/Experimental.plist
@@ -24,6 +24,16 @@
 		</dict>
 		<dict>
 			<key>Type</key>
+			<string>PSToggleSwitchSpecifier</string>
+			<key>Title</key>
+			<string>Reset Notice Card</string>
+			<key>Key</key>
+			<string>ResetNoticeCard</string>
+			<key>DefaultValue</key>
+			<false/>
+		</dict>
+		<dict>
+			<key>Type</key>
 			<string>PSGroupSpecifier</string>
 			<key>Title</key>
 			<string>UI Settings</string>
diff --git a/ios/chrome/browser/system_flags.h b/ios/chrome/browser/system_flags.h
index 4a1fe78..9db3af3c 100644
--- a/ios/chrome/browser/system_flags.h
+++ b/ios/chrome/browser/system_flags.h
@@ -47,6 +47,11 @@
 // Returns the URL for the alternative Discover Feed server.
 std::string getAlternateDiscoverFeedServerURL();
 
+// Returns true if the prefs for the notice card views count and clicks count
+// should be reset to zero on feed start.
+// TODO(crbug.com/1189232): Remove after launch.
+bool ShouldResetNoticeCardOnFeedStart();
+
 // Whether memory debugging tools are enabled.
 bool IsMemoryDebuggingEnabled();
 
diff --git a/ios/chrome/browser/system_flags.mm b/ios/chrome/browser/system_flags.mm
index 044733e..ef17d31d 100644
--- a/ios/chrome/browser/system_flags.mm
+++ b/ios/chrome/browser/system_flags.mm
@@ -89,6 +89,11 @@
   return base::SysNSStringToUTF8(alternateServerURL);
 }
 
+bool ShouldResetNoticeCardOnFeedStart() {
+  return [[NSUserDefaults standardUserDefaults]
+      boolForKey:@"ResetNoticeCard"];
+}
+
 bool IsMemoryDebuggingEnabled() {
 // Always return true for Chromium builds, but check the user default for
 // official builds because memory debugging should never be enabled on stable.
diff --git a/ios/chrome/browser/ui/content_suggestions/content_suggestions_header_view_controller.mm b/ios/chrome/browser/ui/content_suggestions/content_suggestions_header_view_controller.mm
index b20f2588..9d6c1ba 100644
--- a/ios/chrome/browser/ui/content_suggestions/content_suggestions_header_view_controller.mm
+++ b/ios/chrome/browser/ui/content_suggestions/content_suggestions_header_view_controller.mm
@@ -421,8 +421,8 @@
       [self.identityDiscButton.trailingAnchor
           constraintEqualToAnchor:self.view.safeAreaLayoutGuide.trailingAnchor
                          constant:-ntp_home::kIdentityAvatarMargin],
-      [self.identityDiscButton.centerYAnchor
-          constraintEqualToAnchor:self.logoVendor.view.centerYAnchor],
+      [self.identityDiscButton.topAnchor
+          constraintEqualToAnchor:self.logoVendor.view.topAnchor],
     ];
     self.identityDiscLogoPortraitConstraints = @[
       [self.identityDiscButton.heightAnchor
@@ -430,8 +430,8 @@
       [self.identityDiscButton.widthAnchor constraintEqualToConstant:dimension],
       [self.identityDiscButton.trailingAnchor
           constraintEqualToAnchor:self.fakeOmnibox.trailingAnchor],
-      [self.identityDiscButton.centerYAnchor
-          constraintEqualToAnchor:self.logoVendor.view.centerYAnchor],
+      [self.identityDiscButton.topAnchor
+          constraintEqualToAnchor:self.logoVendor.view.topAnchor],
     ];
     [self updateIdentityDiscLogoConstraints];
   }
diff --git a/ios/chrome/browser/ui/infobars/translate_infobar_egtest.mm b/ios/chrome/browser/ui/infobars/translate_infobar_egtest.mm
index c5c574c..801c8af 100644
--- a/ios/chrome/browser/ui/infobars/translate_infobar_egtest.mm
+++ b/ios/chrome/browser/ui/infobars/translate_infobar_egtest.mm
@@ -718,7 +718,7 @@
 }
 
 // Tests that the target language can be changed. TODO(crbug.com/1046629):
-// implement test for changing source langauge.
+// implement test for changing source language.
 - (void)testInfobarChangeTargetLanguage {
   // TODO(crbug.com/1116012): This test is failing flaky on iOS14.
   if (@available(iOS 14, *)) {
diff --git a/ios/components/security_interstitials/OWNERS b/ios/components/security_interstitials/OWNERS
index 233a0ea..78f00191 100644
--- a/ios/components/security_interstitials/OWNERS
+++ b/ios/components/security_interstitials/OWNERS
@@ -1,2 +1,3 @@
 ajuma@chromium.org
 livvielin@chromium.org
+meacer@chromium.org
diff --git a/ios/google_internal/frameworks/chrome_internal_dynamic_framework.arm64.zip.sha1 b/ios/google_internal/frameworks/chrome_internal_dynamic_framework.arm64.zip.sha1
index 14024df..36158ff 100644
--- a/ios/google_internal/frameworks/chrome_internal_dynamic_framework.arm64.zip.sha1
+++ b/ios/google_internal/frameworks/chrome_internal_dynamic_framework.arm64.zip.sha1
@@ -1 +1 @@
-70e77cb35c58de6693356fcf9a6f384a50da3ef5
\ No newline at end of file
+9b438e95b482b83c7ae096b5afd1721b1a50758a
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_internal_dynamic_framework.x64.zip.sha1 b/ios/google_internal/frameworks/chrome_internal_dynamic_framework.x64.zip.sha1
index dcfe9cda..126acbe 100644
--- a/ios/google_internal/frameworks/chrome_internal_dynamic_framework.x64.zip.sha1
+++ b/ios/google_internal/frameworks/chrome_internal_dynamic_framework.x64.zip.sha1
@@ -1 +1 @@
-e3f24663c0957236353f1e1dad5c7f4417eae5be
\ No newline at end of file
+f8da1f28be68712961abba6e8f26c62607d0e34a
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.arm64.zip.sha1 b/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.arm64.zip.sha1
index 3d4b58f..31c69d2 100644
--- a/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.arm64.zip.sha1
+++ b/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.arm64.zip.sha1
@@ -1 +1 @@
-2f8661337a074bfc9fd5f1932e273d647e388b2b
\ No newline at end of file
+848872ce8642d00623bde9a516d659181e3121ab
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.x64.zip.sha1 b/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.x64.zip.sha1
index e463560..1d039db0 100644
--- a/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.x64.zip.sha1
+++ b/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.x64.zip.sha1
@@ -1 +1 @@
-bd6a44349addc2c07d9b452e021d9fd005208f2c
\ No newline at end of file
+0718b675d3af2162abe6a469aee4df9df5ee81f5
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.arm64.zip.sha1 b/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.arm64.zip.sha1
index ecdf669..28a2256 100644
--- a/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.arm64.zip.sha1
+++ b/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.arm64.zip.sha1
@@ -1 +1 @@
-1f4c7e89852343e0d0633613d5bb250c7adceab6
\ No newline at end of file
+6bc6cf8dfda89bf0ee1e866911f5a7e6dbf2063c
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.x64.zip.sha1 b/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.x64.zip.sha1
index 0ee3986d..3c9cc78 100644
--- a/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.x64.zip.sha1
+++ b/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.x64.zip.sha1
@@ -1 +1 @@
-8e08472a0c9b205a411fde56afff21c791dda162
\ No newline at end of file
+5394113d19e7177373d044afc9d37bc041436b21
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/remoting_internal_dynamic_framework.arm64.zip.sha1 b/ios/google_internal/frameworks/remoting_internal_dynamic_framework.arm64.zip.sha1
index 64ac7c5..2cbbe0ac7 100644
--- a/ios/google_internal/frameworks/remoting_internal_dynamic_framework.arm64.zip.sha1
+++ b/ios/google_internal/frameworks/remoting_internal_dynamic_framework.arm64.zip.sha1
@@ -1 +1 @@
-b96084a447b21f54b645407e032011d3df25f485
\ No newline at end of file
+93806748177178ac91a660014863ea507c536e43
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/remoting_internal_dynamic_framework.x64.zip.sha1 b/ios/google_internal/frameworks/remoting_internal_dynamic_framework.x64.zip.sha1
index a6afdc18..7fd99b2 100644
--- a/ios/google_internal/frameworks/remoting_internal_dynamic_framework.x64.zip.sha1
+++ b/ios/google_internal/frameworks/remoting_internal_dynamic_framework.x64.zip.sha1
@@ -1 +1 @@
-4ef14f914340fbddee158dcd2dbefbe6e42db3f1
\ No newline at end of file
+941d9df1c0e3c3d296062316d72435f79c38677d
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.arm64.zip.sha1 b/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.arm64.zip.sha1
index a9b8647..8226b15 100644
--- a/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.arm64.zip.sha1
+++ b/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.arm64.zip.sha1
@@ -1 +1 @@
-14857a6a127c0b6d2782ea3dd9505c97abd6d25f
\ No newline at end of file
+c4b6718178c8783b838229beea2a66b042d514e4
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.x64.zip.sha1 b/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.x64.zip.sha1
index cefb490..5ba7298 100644
--- a/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.x64.zip.sha1
+++ b/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.x64.zip.sha1
@@ -1 +1 @@
-a788983e472722474a7faada8dfd16c73f3ed886
\ No newline at end of file
+55b923d64c82bd6bcfecb41efe3b2d6208f6bef4
\ No newline at end of file
diff --git a/ios/web_view/internal/translate/cwv_translation_controller_internal.h b/ios/web_view/internal/translate/cwv_translation_controller_internal.h
index 0788063..2f09c20 100644
--- a/ios/web_view/internal/translate/cwv_translation_controller_internal.h
+++ b/ios/web_view/internal/translate/cwv_translation_controller_internal.h
@@ -34,7 +34,7 @@
 // Called to keep this class informed of the current translate progress.
 // |step| the state of current translation.
 // |sourceLanguage| the source language associated with the current |step|.
-// |targetLanguage| the target langauge associated with the current |step|.
+// |targetLanguage| the target language associated with the current |step|.
 // |errorType| the error, if any for the current |step|.
 // |triggeredFromMenu| should be true if this was a result from user action.
 - (void)updateTranslateStep:(translate::TranslateStep)step
diff --git a/net/http2/platform/impl/http2_bug_tracker_impl.h b/net/http2/platform/impl/http2_bug_tracker_impl.h
index 8ee3bd0..ed3fb8d 100644
--- a/net/http2/platform/impl/http2_bug_tracker_impl.h
+++ b/net/http2/platform/impl/http2_bug_tracker_impl.h
@@ -9,6 +9,10 @@
 
 #define HTTP2_BUG_IMPL LOG(DFATAL)
 #define HTTP2_BUG_IF_IMPL LOG_IF(DFATAL, (condition))
+
+#define HTTP2_BUG_V2_IMPL(bug_id) LOG(DFATAL)
+#define HTTP2_BUG_IF_V2_IMPL(bug_id, condition) LOG_IF(DFATAL, (condition))
+
 #define FLAGS_http2_always_log_bugs_for_tests_IMPL (true)
 
 #endif  // NET_HTTP2_PLATFORM_IMPL_HTTP2_BUG_TRACKER_IMPL_H_
diff --git a/net/quic/mock_decrypter.cc b/net/quic/mock_decrypter.cc
index 51350fd..6950369a 100644
--- a/net/quic/mock_decrypter.cc
+++ b/net/quic/mock_decrypter.cc
@@ -52,12 +52,12 @@
 }
 
 bool MockDecrypter::SetPreliminaryKey(absl::string_view key) {
-  QUIC_BUG << "Should not be called";
+  LOG(DFATAL) << "Should not be called";
   return false;
 }
 
 bool MockDecrypter::SetDiversificationNonce(const DiversificationNonce& nonce) {
-  QUIC_BUG << "Should not be called";
+  LOG(DFATAL) << "Should not be called";
   return true;
 }
 
diff --git a/net/quic/platform/impl/quic_bug_tracker_impl.h b/net/quic/platform/impl/quic_bug_tracker_impl.h
index ac6f5a02..d421e66 100644
--- a/net/quic/platform/impl/quic_bug_tracker_impl.h
+++ b/net/quic/platform/impl/quic_bug_tracker_impl.h
@@ -6,10 +6,10 @@
 
 #include "net/third_party/quiche/src/quic/platform/api/quic_logging.h"
 
-#define QUIC_BUG_IMPL QUIC_LOG(DFATAL)
-#define QUIC_BUG_IF_IMPL(condition) QUIC_LOG_IF(DFATAL, condition)
-#define QUIC_PEER_BUG_IMPL QUIC_LOG(ERROR)
-#define QUIC_PEER_BUG_IF_IMPL(condition) QUIC_LOG_IF(ERROR, condition)
+#define QUIC_BUG_IMPL(bug_id) QUIC_LOG(DFATAL)
+#define QUIC_BUG_IF_IMPL(bug_id, condition) QUIC_LOG_IF(DFATAL, condition)
+#define QUIC_PEER_BUG_IMPL(bug_id) QUIC_LOG(ERROR)
+#define QUIC_PEER_BUG_IF_IMPL(bug_id, condition) QUIC_LOG_IF(ERROR, condition)
 
 #define QUIC_BUG_V2_IMPL(bug_id) QUIC_LOG(DFATAL)
 #define QUIC_BUG_IF_V2_IMPL(bug_id, condition) QUIC_LOG_IF(DFATAL, condition)
diff --git a/net/quic/platform/impl/quic_linux_socket_utils.h b/net/quic/platform/impl/quic_linux_socket_utils.h
index 7f6e7f16..a5ac07ff 100644
--- a/net/quic/platform/impl/quic_linux_socket_utils.h
+++ b/net/quic/platform/impl/quic_linux_socket_utils.h
@@ -227,8 +227,9 @@
 
       return WriteResult(WRITE_STATUS_OK, mhdr->num_bytes_sent(rc));
     } else if (rc == 0) {
-      QUIC_BUG << "sendmmsg returned 0, returning WRITE_STATUS_ERROR. errno: "
-               << errno;
+      LOG(DFATAL)
+          << "sendmmsg returned 0, returning WRITE_STATUS_ERROR. errno: "
+          << errno;
       errno = EIO;
     }
 
diff --git a/net/quic/platform/impl/quic_socket_address_impl.cc b/net/quic/platform/impl/quic_socket_address_impl.cc
index 9b7a6708..50668ab 100644
--- a/net/quic/platform/impl/quic_socket_address_impl.cc
+++ b/net/quic/platform/impl/quic_socket_address_impl.cc
@@ -75,7 +75,7 @@
 }
 
 QuicSocketAddressImpl QuicSocketAddressImpl::Normalized() const {
-  QUIC_BUG << "QuicSocketAddressImpl::Normalized() is not implemented.";
+  LOG(DFATAL) << "QuicSocketAddressImpl::Normalized() is not implemented.";
   return QuicSocketAddressImpl();
 }
 
diff --git a/net/quic/platform/impl/quic_socket_utils.cc b/net/quic/platform/impl/quic_socket_utils.cc
index 97f3b85..2922dcd 100644
--- a/net/quic/platform/impl/quic_socket_utils.cc
+++ b/net/quic/platform/impl/quic_socket_utils.cc
@@ -237,8 +237,8 @@
   }
 
   if (hdr.msg_flags & MSG_CTRUNC) {
-    QUIC_BUG << "Incorrectly set control length: " << hdr.msg_controllen
-             << ", expected " << sizeof(cbuf);
+    LOG(DFATAL) << "Incorrectly set control length: " << hdr.msg_controllen
+                << ", expected " << sizeof(cbuf);
     return -1;
   }
 
@@ -368,7 +368,7 @@
     in6_pktinfo* pktinfo = static_cast<in6_pktinfo*>(cmsg_data);
     memcpy(&pktinfo->ipi6_addr, address_str.c_str(), address_str.length());
   } else {
-    QUIC_BUG << "Unrecognized IPAddress";
+    LOG(DFATAL) << "Unrecognized IPAddress";
   }
 }
 
diff --git a/net/quic/quic_test_packet_maker.cc b/net/quic/quic_test_packet_maker.cc
index 17c26bf..4f23bdc 100644
--- a/net/quic/quic_test_packet_maker.cc
+++ b/net/quic/quic_test_packet_maker.cc
@@ -1111,7 +1111,7 @@
 QuicTestPacketMaker::MakeStatelessResetPacket() {
   auto connection_id = quic::test::TestConnectionId();
   return quic::QuicFramer::BuildIetfStatelessResetPacket(
-      connection_id,
+      connection_id, quic::QuicFramer::GetMinStatelessResetPacketLength(),
       quic::QuicUtils::GenerateStatelessResetToken(connection_id));
 }
 
@@ -1143,7 +1143,7 @@
       long_header_type_ = quic::INVALID_PACKET_TYPE;
       break;
     default:
-      QUIC_BUG << quic::EncryptionLevelToString(level);
+      LOG(DFATAL) << quic::EncryptionLevelToString(level);
       long_header_type_ = quic::INVALID_PACKET_TYPE;
   }
 }
diff --git a/net/spdy/platform/impl/spdy_bug_tracker_impl.h b/net/spdy/platform/impl/spdy_bug_tracker_impl.h
index 2d4bab9..931e1052 100644
--- a/net/spdy/platform/impl/spdy_bug_tracker_impl.h
+++ b/net/spdy/platform/impl/spdy_bug_tracker_impl.h
@@ -7,8 +7,12 @@
 
 #include "base/logging.h"
 
-#define SPDY_BUG_IMPL LOG(DFATAL)
-#define SPDY_BUG_IF_IMPL(condition) LOG_IF(DFATAL, (condition))
+#define SPDY_BUG_IMPL(bug_id) LOG(DFATAL)
+#define SPDY_BUG_IF_IMPL(bug_id, condition) LOG_IF(DFATAL, (condition))
+
+#define SPDY_BUG_V2_IMPL(bug_id) LOG(DFATAL)
+#define SPDY_BUG_IF_V2_IMPL(bug_id, condition) LOG_IF(DFATAL, (condition))
+
 #define FLAGS_spdy_always_log_bugs_for_tests_impl (true)
 
 #endif  // NET_SPDY_PLATFORM_IMPL_SPDY_BUG_TRACKER_IMPL_H_
diff --git a/net/third_party/quiche/BUILD.gn b/net/third_party/quiche/BUILD.gn
index 3f84328..4985dce8 100644
--- a/net/third_party/quiche/BUILD.gn
+++ b/net/third_party/quiche/BUILD.gn
@@ -123,8 +123,6 @@
       "src/http2/hpack/decoder/hpack_whole_entry_listener.cc",
       "src/http2/hpack/decoder/hpack_whole_entry_listener.h",
       "src/http2/hpack/hpack_static_table_entries.inc",
-      "src/http2/hpack/hpack_string.cc",
-      "src/http2/hpack/hpack_string.h",
       "src/http2/hpack/http2_hpack_constants.cc",
       "src/http2/hpack/http2_hpack_constants.h",
       "src/http2/hpack/huffman/hpack_huffman_decoder.cc",
@@ -360,6 +358,8 @@
       "src/quic/core/http/spdy_server_push_utils.h",
       "src/quic/core/http/spdy_utils.cc",
       "src/quic/core/http/spdy_utils.h",
+      "src/quic/core/http/web_transport_http3.cc",
+      "src/quic/core/http/web_transport_http3.h",
       "src/quic/core/legacy_quic_stream_id_manager.cc",
       "src/quic/core/legacy_quic_stream_id_manager.h",
       "src/quic/core/packet_number_indexed_queue.h",
@@ -953,6 +953,8 @@
     "src/quic/test_tools/quic_stream_sequencer_peer.h",
     "src/quic/test_tools/quic_sustained_bandwidth_recorder_peer.cc",
     "src/quic/test_tools/quic_sustained_bandwidth_recorder_peer.h",
+    "src/quic/test_tools/quic_test_backend.cc",
+    "src/quic/test_tools/quic_test_backend.h",
     "src/quic/test_tools/quic_test_utils.cc",
     "src/quic/test_tools/quic_test_utils.h",
     "src/quic/test_tools/quic_time_wait_list_manager_peer.cc",
@@ -1228,7 +1230,6 @@
     "src/http2/hpack/decoder/hpack_string_collector.h",
     "src/http2/hpack/decoder/hpack_string_decoder_test.cc",
     "src/http2/hpack/decoder/hpack_whole_entry_buffer_test.cc",
-    "src/http2/hpack/hpack_string_test.cc",
     "src/http2/hpack/http2_hpack_constants_test.cc",
     "src/http2/hpack/huffman/hpack_huffman_decoder_test.cc",
     "src/http2/hpack/huffman/hpack_huffman_encoder_test.cc",
diff --git a/net/tools/epoll_server/platform/impl/epoll_bug_impl.h b/net/tools/epoll_server/platform/impl/epoll_bug_impl.h
index 696a960..ec84dab 100644
--- a/net/tools/epoll_server/platform/impl/epoll_bug_impl.h
+++ b/net/tools/epoll_server/platform/impl/epoll_bug_impl.h
@@ -7,6 +7,7 @@
 
 #include "net/tools/epoll_server/platform/impl/epoll_logging_impl.h"
 
-#define EPOLL_BUG_IMPL EPOLL_LOG_IMPL(DFATAL)
+#define EPOLL_BUG_IMPL(bug_id) EPOLL_LOG_IMPL(DFATAL)
+#define EPOLL_BUG_V2_IMPL(bug_id) EPOLL_LOG_IMPL(DFATAL)
 
 #endif  // NET_TOOLS_EPOLL_SERVER_PLATFORM_IMPL_EPOLL_BUG_IMPL_H_
diff --git a/remoting/host/curtain_mode_linux.cc b/remoting/host/curtain_mode_linux.cc
index 5c09359..cf815a5 100644
--- a/remoting/host/curtain_mode_linux.cc
+++ b/remoting/host/curtain_mode_linux.cc
@@ -50,14 +50,14 @@
   // Try to identify a virtual session. Since there's no way to tell from the
   // vendor string, we check for known virtual input devices.
   // TODO(rmsousa): Find a similar way to determine that the *output* is secure.
-  x11::Connection connection;
-  if (!connection.xinput().present()) {
+  x11::Connection* connection = x11::Connection::Get();
+  if (!connection->xinput().present()) {
     // If XInput is not available, assume it is not a virtual session.
     LOG(ERROR) << "X Input extension not available";
     return false;
   }
 
-  auto devices = connection.xinput().ListInputDevices().Sync();
+  auto devices = connection->xinput().ListInputDevices().Sync();
   if (!devices) {
     LOG(ERROR) << "ListInputDevices failed";
     return false;
diff --git a/remoting/host/desktop_resizer_x11.cc b/remoting/host/desktop_resizer_x11.cc
index 5b4036d..fe10ee9f 100644
--- a/remoting/host/desktop_resizer_x11.cc
+++ b/remoting/host/desktop_resizer_x11.cc
@@ -106,8 +106,9 @@
 }
 
 DesktopResizerX11::DesktopResizerX11()
-    : randr_(&connection_.randr()),
-      screen_(&connection_.default_screen()),
+    : connection_(x11::Connection::Get()),
+      randr_(&connection_->randr()),
+      screen_(&connection_->default_screen()),
       root_(screen_->root),
       exact_resize_(base::CommandLine::ForCurrentProcess()->HasSwitch(
           "server-supports-exact-resize")) {
@@ -127,11 +128,11 @@
   // Process pending events so that the connection setup data is updated
   // with the correct display metrics.
   if (has_randr_)
-    connection_.DispatchAll();
+    connection_->DispatchAll();
 
   ScreenResolution result(
-      webrtc::DesktopSize(connection_.default_screen().width_in_pixels,
-                          connection_.default_screen().height_in_pixels),
+      webrtc::DesktopSize(connection_->default_screen().width_in_pixels,
+                          connection_->default_screen().height_in_pixels),
       webrtc::DesktopVector(kDefaultDPI, kDefaultDPI));
   return result;
 }
@@ -177,11 +178,11 @@
   // error, for example if xrandr has been used to add a mode with the same
   // name as our temporary mode, or to remove the "client resolution" mode. We
   // don't want to terminate the process if this happens.
-  x11::ScopedIgnoreErrors ignore_errors(&connection_);
+  x11::ScopedIgnoreErrors ignore_errors(connection_);
 
   // Grab the X server while we're changing the display resolution. This ensures
   // that the display configuration doesn't change under our feet.
-  ScopedXGrabServer grabber(&connection_);
+  ScopedXGrabServer grabber(connection_);
 
   if (exact_resize_)
     SetResolutionNewMode(resolution);
diff --git a/remoting/host/desktop_resizer_x11.h b/remoting/host/desktop_resizer_x11.h
index 5982353..a684408 100644
--- a/remoting/host/desktop_resizer_x11.h
+++ b/remoting/host/desktop_resizer_x11.h
@@ -75,7 +75,7 @@
   // its resolution.
   void SwitchToMode(const char* name);
 
-  x11::Connection connection_;
+  x11::Connection* connection_;
   x11::RandR* const randr_ = nullptr;
   const x11::Screen* const screen_ = nullptr;
   x11::Window root_;
diff --git a/remoting/host/input_monitor/BUILD.gn b/remoting/host/input_monitor/BUILD.gn
index 84e6ea623..42f4503 100644
--- a/remoting/host/input_monitor/BUILD.gn
+++ b/remoting/host/input_monitor/BUILD.gn
@@ -39,6 +39,8 @@
   if (use_x11 && (!is_chromeos_ash || use_ozone)) {
     sources += [
       "local_hotkey_input_monitor_x11.cc",
+      "local_input_monitor_x11_common.cc",
+      "local_input_monitor_x11_common.h",
       "local_keyboard_input_monitor_x11.cc",
       "local_mouse_input_monitor_x11.cc",
     ]
diff --git a/remoting/host/input_monitor/local_hotkey_input_monitor_x11.cc b/remoting/host/input_monitor/local_hotkey_input_monitor_x11.cc
index 7d47d3e4..f51fff5a4 100644
--- a/remoting/host/input_monitor/local_hotkey_input_monitor_x11.cc
+++ b/remoting/host/input_monitor/local_hotkey_input_monitor_x11.cc
@@ -18,7 +18,7 @@
 #include "base/macros.h"
 #include "base/sequence_checker.h"
 #include "base/single_thread_task_runner.h"
-#include "ui/events/devices/x11/xinput_util.h"
+#include "remoting/host/input_monitor/local_input_monitor_x11_common.h"
 #include "ui/gfx/x/connection.h"
 #include "ui/gfx/x/event.h"
 #include "ui/gfx/x/future.h"
@@ -80,7 +80,7 @@
     // True when Ctrl is pressed.
     bool ctrl_pressed_ = false;
 
-    std::unique_ptr<x11::Connection> connection_;
+    x11::Connection* connection_ = nullptr;
 
     DISALLOW_COPY_AND_ASSIGN(Core);
   };
@@ -132,29 +132,24 @@
                                base::BindOnce(&Core::StopOnInputThread, this));
 }
 
-LocalHotkeyInputMonitorX11::Core::~Core() {
-  DCHECK(!connection_);
-}
+LocalHotkeyInputMonitorX11::Core::~Core() = default;
 
 void LocalHotkeyInputMonitorX11::Core::StartOnInputThread() {
   DCHECK(input_task_runner_->BelongsToCurrentThread());
   DCHECK(!connection_);
 
-  // TODO(jamiewalch): We should pass the connection in.
-  connection_ = std::make_unique<x11::Connection>();
+  connection_ = x11::Connection::Get();
   connection_->AddEventObserver(this);
 
   if (!connection_->xinput().present()) {
-    LOG(ERROR) << "X Record extension not available.";
+    LOG(ERROR) << "X Input extension not available.";
     return;
   }
   // Let the server know the client XInput version.
   connection_->xinput().XIQueryVersion(
       {x11::Input::major_version, x11::Input::minor_version});
 
-  x11::Input::XIEventMask mask{};
-  ui::SetXinputMask(&mask, x11::Input::RawDeviceEvent::RawKeyPress);
-  ui::SetXinputMask(&mask, x11::Input::RawDeviceEvent::RawKeyRelease);
+  auto mask = CommonXIEventMaskForRootWindow();
   connection_->xinput().XISelectEvents(
       {connection_->default_root(),
        {{x11::Input::DeviceId::AllMaster, {mask}}}});
@@ -173,7 +168,6 @@
 void LocalHotkeyInputMonitorX11::Core::StopOnInputThread() {
   DCHECK(input_task_runner_->BelongsToCurrentThread());
   controller_.reset();
-  connection_.reset();
 }
 
 void LocalHotkeyInputMonitorX11::Core::OnConnectionData() {
@@ -193,8 +187,10 @@
   // selected them.
   if (!raw)
     return;
-  DCHECK(raw->opcode == x11::Input::RawDeviceEvent::RawKeyPress ||
-         raw->opcode == x11::Input::RawDeviceEvent::RawKeyRelease);
+  if (raw->opcode != x11::Input::RawDeviceEvent::RawKeyPress &&
+      raw->opcode != x11::Input::RawDeviceEvent::RawKeyRelease) {
+    return;
+  }
 
   const bool down = raw->opcode == x11::Input::RawDeviceEvent::RawKeyPress;
   const auto key_sym =
diff --git a/remoting/host/input_monitor/local_input_monitor_x11_common.cc b/remoting/host/input_monitor/local_input_monitor_x11_common.cc
new file mode 100644
index 0000000..82809f4c
--- /dev/null
+++ b/remoting/host/input_monitor/local_input_monitor_x11_common.cc
@@ -0,0 +1,17 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "remoting/host/input_monitor/local_input_monitor_x11_common.h"
+
+namespace remoting {
+
+x11::Input::XIEventMask CommonXIEventMaskForRootWindow() {
+  x11::Input::XIEventMask mask{};
+  ui::SetXinputMask(&mask, x11::Input::RawDeviceEvent::RawKeyPress);
+  ui::SetXinputMask(&mask, x11::Input::RawDeviceEvent::RawKeyRelease);
+  ui::SetXinputMask(&mask, x11::Input::RawDeviceEvent::RawMotion);
+  return mask;
+}
+
+}  // namespace remoting
diff --git a/remoting/host/input_monitor/local_input_monitor_x11_common.h b/remoting/host/input_monitor/local_input_monitor_x11_common.h
new file mode 100644
index 0000000..a28d12f8
--- /dev/null
+++ b/remoting/host/input_monitor/local_input_monitor_x11_common.h
@@ -0,0 +1,20 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef REMOTING_HOST_INPUT_MONITOR_LOCAL_INPUT_MONITOR_X11_COMMON_H_
+#define REMOTING_HOST_INPUT_MONITOR_LOCAL_INPUT_MONITOR_X11_COMMON_H_
+
+#include "ui/events/devices/x11/xinput_util.h"
+#include "ui/gfx/x/xinput.h"
+
+namespace remoting {
+
+// Returns the input event mask used by all the X11 local input monitors.
+// Since the mask is set for the root window, each input monitor must use
+// the same mask.
+x11::Input::XIEventMask CommonXIEventMaskForRootWindow();
+
+}  // namespace remoting
+
+#endif  // REMOTING_HOST_INPUT_MONITOR_LOCAL_INPUT_MONITOR_X11_COMMON_H_
diff --git a/remoting/host/input_monitor/local_mouse_input_monitor_x11.cc b/remoting/host/input_monitor/local_mouse_input_monitor_x11.cc
index 280ef6ee..46ae482 100644
--- a/remoting/host/input_monitor/local_mouse_input_monitor_x11.cc
+++ b/remoting/host/input_monitor/local_mouse_input_monitor_x11.cc
@@ -18,8 +18,8 @@
 #include "base/macros.h"
 #include "base/sequence_checker.h"
 #include "base/single_thread_task_runner.h"
+#include "remoting/host/input_monitor/local_input_monitor_x11_common.h"
 #include "third_party/webrtc/modules/desktop_capture/desktop_geometry.h"
-#include "ui/events/devices/x11/xinput_util.h"
 #include "ui/events/event.h"
 #include "ui/gfx/x/connection.h"
 #include "ui/gfx/x/event.h"
@@ -77,7 +77,7 @@
     // Controls watching X events.
     std::unique_ptr<base::FileDescriptorWatcher::Controller> controller_;
 
-    std::unique_ptr<x11::Connection> connection_;
+    x11::Connection* connection_ = nullptr;
 
     DISALLOW_COPY_AND_ASSIGN(Core);
   };
@@ -128,28 +128,24 @@
                                base::BindOnce(&Core::StopOnInputThread, this));
 }
 
-LocalMouseInputMonitorX11::Core::~Core() {
-  DCHECK(!connection_);
-}
+LocalMouseInputMonitorX11::Core::~Core() = default;
 
 void LocalMouseInputMonitorX11::Core::StartOnInputThread() {
   DCHECK(input_task_runner_->BelongsToCurrentThread());
   DCHECK(!connection_);
 
-  // TODO(jamiewalch): We should pass the connection in.
-  connection_ = std::make_unique<x11::Connection>();
+  connection_ = x11::Connection::Get();
   connection_->AddEventObserver(this);
 
   if (!connection_->xinput().present()) {
-    LOG(ERROR) << "X Record extension not available.";
+    LOG(ERROR) << "X Input extension not available.";
     return;
   }
   // Let the server know the client XInput version.
   connection_->xinput().XIQueryVersion(
       {x11::Input::major_version, x11::Input::minor_version});
 
-  x11::Input::XIEventMask mask{};
-  ui::SetXinputMask(&mask, x11::Input::RawDeviceEvent::RawMotion);
+  auto mask = CommonXIEventMaskForRootWindow();
   connection_->xinput().XISelectEvents(
       {connection_->default_root(),
        {{x11::Input::DeviceId::AllMaster, {mask}}}});
@@ -168,7 +164,6 @@
 void LocalMouseInputMonitorX11::Core::StopOnInputThread() {
   DCHECK(input_task_runner_->BelongsToCurrentThread());
   controller_.reset();
-  connection_.reset();
 }
 
 void LocalMouseInputMonitorX11::Core::OnConnectionData() {
@@ -184,7 +179,8 @@
   // selected them.
   if (!raw)
     return;
-  DCHECK(raw->opcode == x11::Input::RawDeviceEvent::RawMotion);
+  if (raw->opcode != x11::Input::RawDeviceEvent::RawMotion)
+    return;
 
   connection_->QueryPointer({connection_->default_root()})
       .OnResponse(base::BindOnce(
diff --git a/remoting/host/installer/linux/build_deb.py b/remoting/host/installer/linux/build_deb.py
index 3be7c421..62c9e965 100644
--- a/remoting/host/installer/linux/build_deb.py
+++ b/remoting/host/installer/linux/build_deb.py
@@ -14,7 +14,7 @@
   proc = subprocess.Popen([build_deb_script] + sys.argv[1:],
                           stdout=subprocess.PIPE)
   out, _ = proc.communicate()
-  sys.stdout.write(out.strip())
+  sys.stdout.write(out.decode('utf8').strip())
   return proc.returncode
 
 
diff --git a/sandbox/policy/features.cc b/sandbox/policy/features.cc
index 255bab5..8ba5f50 100644
--- a/sandbox/policy/features.cc
+++ b/sandbox/policy/features.cc
@@ -35,6 +35,11 @@
 
 // Enables GPU Low Privilege AppContainer when combined with kGpuAppContainer.
 const base::Feature kGpuLPAC{"GpuLPAC", base::FEATURE_ENABLED_BY_DEFAULT};
+
+// Use LPAC for network sandbox instead of restricted token. Relies on
+// NetworkServiceSandbox being also enabled.
+const base::Feature kNetworkServiceSandboxLPAC{
+    "NetworkServiceSandboxLPAC", base::FEATURE_DISABLED_BY_DEFAULT};
 #endif  // defined(OS_WIN)
 
 #if !defined(OS_ANDROID)
diff --git a/sandbox/policy/features.h b/sandbox/policy/features.h
index d26e0d1..6de7bbd6 100644
--- a/sandbox/policy/features.h
+++ b/sandbox/policy/features.h
@@ -26,6 +26,7 @@
 SANDBOX_POLICY_EXPORT extern const base::Feature kWinSboxDisableExtensionPoints;
 SANDBOX_POLICY_EXPORT extern const base::Feature kGpuAppContainer;
 SANDBOX_POLICY_EXPORT extern const base::Feature kGpuLPAC;
+SANDBOX_POLICY_EXPORT extern const base::Feature kNetworkServiceSandboxLPAC;
 #endif  // defined(OS_WIN)
 
 #if !defined(OS_ANDROID)
diff --git a/sandbox/policy/win/sandbox_win.cc b/sandbox/policy/win/sandbox_win.cc
index 8a1639d..3f826df 100644
--- a/sandbox/policy/win/sandbox_win.cc
+++ b/sandbox/policy/win/sandbox_win.cc
@@ -631,6 +631,9 @@
     case SandboxType::kMediaFoundationCdm:
       sandbox_base_name = std::string("cr.sb.cdm");
       break;
+    case SandboxType::kNetwork:
+      sandbox_base_name = std::string("cr.sb.net");
+      break;
     default:
       DCHECK(0);
   }
@@ -651,9 +654,13 @@
                                     SandboxType sandbox_type) {
   if (sandbox_type != SandboxType::kMediaFoundationCdm &&
       sandbox_type != SandboxType::kGpu &&
-      sandbox_type != SandboxType::kXrCompositing)
+      sandbox_type != SandboxType::kXrCompositing &&
+      sandbox_type != SandboxType::kNetwork)
     return SBOX_ERROR_UNSUPPORTED;
 
+  DCHECK(sandbox_type != SandboxType::kNetwork ||
+         base::FeatureList::IsEnabled(features::kNetworkServiceSandboxLPAC));
+
   if (sandbox_type == SandboxType::kGpu &&
       !profile->AddImpersonationCapability(L"chromeInstallFiles")) {
     DLOG(ERROR) << "AppContainerProfile::AddImpersonationCapability("
@@ -738,6 +745,18 @@
     profile->SetEnableLowPrivilegeAppContainer(true);
   }
 
+  // Enable LPAC for Network service.
+  if (sandbox_type == SandboxType::kNetwork) {
+    profile->AddCapability(
+        sandbox::WellKnownCapabilities::kPrivateNetworkClientServer);
+    profile->AddCapability(sandbox::WellKnownCapabilities::kInternetClient);
+    profile->AddCapability(
+        sandbox::WellKnownCapabilities::kEnterpriseAuthentication);
+    profile->AddCapability(L"lpacIdentityServices");
+    profile->AddCapability(L"lpacCryptoServices");
+    profile->SetEnableLowPrivilegeAppContainer(true);
+  }
+
   if (sandbox_type == SandboxType::kMediaFoundationCdm)
     profile->SetEnableLowPrivilegeAppContainer(true);
 
@@ -900,9 +919,13 @@
   if (sandbox_type == SandboxType::kMediaFoundationCdm)
     return true;
 
-  if (sandbox_type != SandboxType::kGpu)
-    return false;
-  return base::FeatureList::IsEnabled(features::kGpuAppContainer);
+  if (sandbox_type == SandboxType::kGpu)
+    return base::FeatureList::IsEnabled(features::kGpuAppContainer);
+
+  if (sandbox_type == SandboxType::kNetwork)
+    return base::FeatureList::IsEnabled(features::kNetworkServiceSandboxLPAC);
+
+  return false;
 }
 
 // static
diff --git a/services/device/generic_sensor/sensor_provider_impl.cc b/services/device/generic_sensor/sensor_provider_impl.cc
index 98428659..4b7f64f 100644
--- a/services/device/generic_sensor/sensor_provider_impl.cc
+++ b/services/device/generic_sensor/sensor_provider_impl.cc
@@ -9,7 +9,6 @@
 #include "base/bind.h"
 #include "base/feature_list.h"
 #include "base/memory/ptr_util.h"
-#include "base/threading/thread_task_runner_handle.h"
 #include "mojo/public/cpp/bindings/pending_remote.h"
 #include "services/device/generic_sensor/platform_sensor_provider.h"
 #include "services/device/generic_sensor/sensor_impl.h"
diff --git a/services/tracing/public/cpp/perfetto/track_event_thread_local_event_sink.cc b/services/tracing/public/cpp/perfetto/track_event_thread_local_event_sink.cc
index 3ed89e3..b0460b3 100644
--- a/services/tracing/public/cpp/perfetto/track_event_thread_local_event_sink.cc
+++ b/services/tracing/public/cpp/perfetto/track_event_thread_local_event_sink.cc
@@ -897,7 +897,7 @@
 
   CounterDescriptor* counter = track_descriptor->set_counter();
   if (counter_type != CounterDescriptor::COUNTER_UNSPECIFIED) {
-    counter->set_type(CounterDescriptor::COUNTER_THREAD_TIME_NS);
+    counter->set_type(counter_type);
   }
   if (unit_multiplier) {
     counter->set_unit_multiplier(unit_multiplier);
diff --git a/testing/buildbot/chromium.android.fyi.json b/testing/buildbot/chromium.android.fyi.json
index ad7a217d..ae0701c 100644
--- a/testing/buildbot/chromium.android.fyi.json
+++ b/testing/buildbot/chromium.android.fyi.json
@@ -533,7 +533,8 @@
         "args": [
           "--gs-results-bucket=chromium-result-details",
           "--recover-devices",
-          "--avd-config=../../tools/android/avd/proto/generic_android30.textpb"
+          "--avd-config=../../tools/android/avd/proto/generic_android30.textpb",
+          "--gtest_filter=-HarfBuzzShaperTest.EmojiPercentage"
         ],
         "merge": {
           "args": [
@@ -1377,7 +1378,8 @@
         "args": [
           "--gs-results-bucket=chromium-result-details",
           "--recover-devices",
-          "--avd-config=../../tools/android/avd/proto/generic_android30.textpb"
+          "--avd-config=../../tools/android/avd/proto/generic_android30.textpb",
+          "--gtest_filter=-FontUniqueNameLookupTest.TestMatchPostScriptNameTtc"
         ],
         "merge": {
           "args": [
diff --git a/testing/buildbot/gn_isolate_map.pyl b/testing/buildbot/gn_isolate_map.pyl
index 4d0980f3..a849004 100644
--- a/testing/buildbot/gn_isolate_map.pyl
+++ b/testing/buildbot/gn_isolate_map.pyl
@@ -1451,7 +1451,7 @@
   },
   "performance_web_engine_test_suite": {
     "args": [
-      "../../content/test/run_gpu_integration_test_fuchsia.py",
+      "../../content/test/gpu/run_telemetry_benchmark_fuchsia.py",
     ],
     "label": "//content/test:performance_web_engine_test_suite",
     "script": "//testing/scripts/run_performance_tests.py",
diff --git a/testing/buildbot/test_suite_exceptions.pyl b/testing/buildbot/test_suite_exceptions.pyl
index 9caaa76f..a688cba 100644
--- a/testing/buildbot/test_suite_exceptions.pyl
+++ b/testing/buildbot/test_suite_exceptions.pyl
@@ -179,6 +179,12 @@
   },
   'blink_platform_unittests': {
     'modifications': {
+      # TODO(crbug.com/1189336): Remove the filter once the issue is fixed.
+      'android-11-x86-fyi-rel': {
+        'args': [
+          '--gtest_filter=-HarfBuzzShaperTest.EmojiPercentage',
+        ],
+      },
       # TODO(crbug.com/1108121): Remove this filter
       'android-nougat-arm64-rel': {
         'args': [
@@ -1258,6 +1264,12 @@
           'shards': 2,
         },
       },
+      # TODO(crbug.com/1189344): Remove the filter once the issue is fixed.
+      'android-11-x86-fyi-rel': {
+        'args': [
+          '--gtest_filter=-FontUniqueNameLookupTest.TestMatchPostScriptNameTtc',
+        ],
+      },
     },
   },
   'context_lost_passthrough_tests': {
diff --git a/testing/scripts/run_android_wpt.py b/testing/scripts/run_android_wpt.py
index 99815b9..b6f2fcb 100755
--- a/testing/scripts/run_android_wpt.py
+++ b/testing/scripts/run_android_wpt.py
@@ -119,9 +119,6 @@
     # Here we add all of the arguments required to run WPT tests on Android.
     rest_args.extend([self.options.wpt_path])
 
-    # TODO(crbug.com/1166741): We should be running WPT under Python 3.
-    rest_args.extend(["--py2"])
-
     # vpython has packages needed by wpt, so force it to skip the setup
     rest_args.extend(["--venv=../../", "--skip-venv-setup"])
 
diff --git a/testing/scripts/run_wpt_tests.py b/testing/scripts/run_wpt_tests.py
index 88ce325..c762579 100755
--- a/testing/scripts/run_wpt_tests.py
+++ b/testing/scripts/run_wpt_tests.py
@@ -73,8 +73,6 @@
             WPT_BINARY,
             "--venv=" + SRC_DIR,
             "--skip-venv-setup",
-            # TODO(crbug.com/1166741): We should be running WPT under Python 3.
-            "--py3",
             "run",
             "chrome"
         ] + self.options.test_list + [
diff --git a/testing/variations/fieldtrial_testing_config.json b/testing/variations/fieldtrial_testing_config.json
index 20481cf..a3e960e 100644
--- a/testing/variations/fieldtrial_testing_config.json
+++ b/testing/variations/fieldtrial_testing_config.json
@@ -2631,6 +2631,21 @@
             ]
         }
     ],
+    "DisableCryptAuthV1DeviceSync": [
+        {
+            "platforms": [
+                "chromeos"
+            ],
+            "experiments": [
+                {
+                    "name": "Enabled",
+                    "enable_features": [
+                        "DisableCryptAuthV1DeviceSync"
+                    ]
+                }
+            ]
+        }
+    ],
     "DisableGles2ForOopR": [
         {
             "platforms": [
diff --git a/third_party/blink/common/features.cc b/third_party/blink/common/features.cc
index 47363b6..0687092d 100644
--- a/third_party/blink/common/features.cc
+++ b/third_party/blink/common/features.cc
@@ -888,5 +888,8 @@
     kBackgroundTracingPerformanceMark_AllowList{
         &kBackgroundTracingPerformanceMark, "allow_list", ""};
 
+const base::Feature kCLSM91Improvements{"CLSM91Improvements",
+                                        base::FEATURE_ENABLED_BY_DEFAULT};
+
 }  // namespace features
 }  // namespace blink
diff --git a/third_party/blink/public/common/features.h b/third_party/blink/public/common/features.h
index 57468e1..e439a1a7 100644
--- a/third_party/blink/public/common/features.h
+++ b/third_party/blink/public/common/features.h
@@ -367,6 +367,8 @@
 BLINK_COMMON_EXPORT extern const base::FeatureParam<std::string>
     kBackgroundTracingPerformanceMark_AllowList;
 
+BLINK_COMMON_EXPORT extern const base::Feature kCLSM91Improvements;
+
 }  // namespace features
 }  // namespace blink
 
diff --git a/third_party/blink/public/common/permissions_policy/permissions_policy.h b/third_party/blink/public/common/permissions_policy/permissions_policy.h
index 05daf7fe..68caa8a 100644
--- a/third_party/blink/public/common/permissions_policy/permissions_policy.h
+++ b/third_party/blink/public/common/permissions_policy/permissions_policy.h
@@ -129,10 +129,10 @@
 
 class BLINK_COMMON_EXPORT PermissionsPolicy {
  public:
-  // Represents a collection of origins which make up an allowlist in a feature
-  // policy. This collection may be set to match every origin (corresponding to
-  // the "*" syntax in the policy string, in which case the Contains() method
-  // will always return true.
+  // Represents a collection of origins which make up an allowlist in a
+  // permissions policy. This collection may be set to match every origin
+  // (corresponding to the "*" syntax in the policy string, in which case the
+  // Contains() method will always return true.
   class BLINK_COMMON_EXPORT Allowlist final {
    public:
     Allowlist();
diff --git a/third_party/blink/public/mojom/BUILD.gn b/third_party/blink/public/mojom/BUILD.gn
index 75c53038..5864a823 100644
--- a/third_party/blink/public/mojom/BUILD.gn
+++ b/third_party/blink/public/mojom/BUILD.gn
@@ -170,6 +170,7 @@
     "service_worker/service_worker_stream_handle.mojom",
     "site_engagement/site_engagement.mojom",
     "sms/webotp_service.mojom",
+    "speculation_rules/speculation_rules.mojom",
     "speech/speech_recognition_error.mojom",
     "speech/speech_recognition_error_code.mojom",
     "speech/speech_recognition_grammar.mojom",
diff --git a/third_party/blink/public/mojom/speculation_rules/OWNERS b/third_party/blink/public/mojom/speculation_rules/OWNERS
new file mode 100644
index 0000000..263f96d
--- /dev/null
+++ b/third_party/blink/public/mojom/speculation_rules/OWNERS
@@ -0,0 +1,4 @@
+file://third_party/blink/renderer/core/speculation_rules/OWNERS
+
+per-file *.mojom=set noparent
+per-file *.mojom=file://ipc/SECURITY_OWNERS
diff --git a/third_party/blink/public/mojom/speculation_rules/README.md b/third_party/blink/public/mojom/speculation_rules/README.md
new file mode 100644
index 0000000..a65212f
--- /dev/null
+++ b/third_party/blink/public/mojom/speculation_rules/README.md
@@ -0,0 +1 @@
+See `third_party/blink/renderer/core/speculation_rules/`.
diff --git a/third_party/blink/public/mojom/speculation_rules/speculation_rules.mojom b/third_party/blink/public/mojom/speculation_rules/speculation_rules.mojom
new file mode 100644
index 0000000..a37f2d8
--- /dev/null
+++ b/third_party/blink/public/mojom/speculation_rules/speculation_rules.mojom
@@ -0,0 +1,51 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+module blink.mojom;
+
+import "url/mojom/url.mojom";
+
+// See also third_party/blink/renderer/core/speculation_rules/README.md.
+
+// Browser process interface for receiving information about proposed
+// speculation from the renderer process.
+//
+// At the moment these updates are not incremental -- the renderer updates the
+// entire set of speculation candidates at once. This is probably fine as long
+// as it's occasional (i.e., the renderer buffers these updates somewhat) and
+// the set of candidates is not unmanageably large. If so we may need to
+// re-evaluate.
+interface SpeculationHost {
+  // Pushes a new set of speculation candidates, which replaces any previously
+  // sent.
+  //
+  // [[[ WARNING ]]]
+  // Not yet implemented in the browser process.
+  // Security reviewer approval for this has not yet been granted.
+  // The CL which lands a browser process implementation of this should rename
+  // this and seek ipc/SECURITY_OWNERS approval.
+  UpdateSpeculationCandidates_PendingSecurityReview(
+      array<SpeculationCandidate> candidates);
+};
+
+// The action that is proposed.
+enum SpeculationAction {
+  kPrefetch,
+  kPrefetchWithSubresources,
+};
+
+// A single candidate: a URL, an action, and any associated metadata that
+// might be needed to make a decision.
+struct SpeculationCandidate {
+  // The URL which is eligible for some action.
+  url.mojom.Url url;
+
+  // The action which is proposed for that URL.
+  SpeculationAction action = kPrefetch;
+
+  // If true, cross-origin requests associated with this speculation must be
+  // made in a manner which anonymizes the client IP. If this is not possible,
+  // this candidate must be discarded.
+  bool requires_anonymous_client_ip_when_cross_origin = false;
+};
diff --git a/third_party/blink/renderer/bindings/core/v8/binding_security.cc b/third_party/blink/renderer/bindings/core/v8/binding_security.cc
index f53cd6a..44e99dda 100644
--- a/third_party/blink/renderer/bindings/core/v8/binding_security.cc
+++ b/third_party/blink/renderer/bindings/core/v8/binding_security.cc
@@ -146,7 +146,7 @@
                    : WebFeature::kDocumentDomainBlockedCrossOriginAccess);
   }
   if (!can_access) {
-    // Ensure that if we got a cluster mismatch that it was due to a feature
+    // Ensure that if we got a cluster mismatch that it was due to a permissions
     // policy being enabled and not a logic bug.
     if (detail == SecurityOrigin::AccessResultDomainDetail::
                       kDomainNotRelevantAgentClusterMismatch) {
diff --git a/third_party/blink/renderer/build/scripts/templates/policy_helper.cc.tmpl b/third_party/blink/renderer/build/scripts/templates/policy_helper.cc.tmpl
index 7e99ab6..5ee61d5 100644
--- a/third_party/blink/renderer/build/scripts/templates/policy_helper.cc.tmpl
+++ b/third_party/blink/renderer/build/scripts/templates/policy_helper.cc.tmpl
@@ -27,9 +27,10 @@
 // add their feature names to this map unconditionally, as the trial token could
 // be added after the HTTP header needs to be parsed. This also means that
 // top-level documents which simply want to embed another page which uses an
-// origin trial feature, without using the feature themselves, can use feature
-// policy to allow use of the feature in subframes (The framed document will
-// still require a valid origin trial token to use the feature in this scenario).
+// origin trial feature, without using the feature themselves, can use
+// permissions policy to allow use of the feature in subframes (The framed
+// document will still require a valid origin trial token to use the feature in
+// this scenario).
 const FeatureNameMap& GetDefaultFeatureNameMap() {
   DEFINE_STATIC_LOCAL(FeatureNameMap, default_feature_name_map, ());
   if (default_feature_name_map.IsEmpty()) {
diff --git a/third_party/blink/renderer/core/OWNERS b/third_party/blink/renderer/core/OWNERS
index c9bdfac..400137a 100644
--- a/third_party/blink/renderer/core/OWNERS
+++ b/third_party/blink/renderer/core/OWNERS
@@ -49,7 +49,7 @@
 kinuko@chromium.org
 kojii@chromium.org
 kouhei@chromium.org
-masonfreed@chromium.org
+masonf@chromium.org
 mkwst@chromium.org
 mstensho@chromium.org
 nhiroki@chromium.org
diff --git a/third_party/blink/renderer/core/dom/OWNERS b/third_party/blink/renderer/core/dom/OWNERS
index 9f2610d..7a0ec122 100644
--- a/third_party/blink/renderer/core/dom/OWNERS
+++ b/third_party/blink/renderer/core/dom/OWNERS
@@ -1,4 +1,4 @@
-masonfreed@chromium.org
+masonf@chromium.org
 
 per-file *_mojom_traits*.*=set noparent
 per-file *_mojom_traits*.*=file://ipc/SECURITY_OWNERS
diff --git a/third_party/blink/renderer/core/dom/README.md b/third_party/blink/renderer/core/dom/README.md
index c6e4107..beb2f56 100644
--- a/third_party/blink/renderer/core/dom/README.md
+++ b/third_party/blink/renderer/core/dom/README.md
@@ -730,7 +730,7 @@
 Since `Node::UpdateDistributionForFlatTreeTraversal` can take O(N) in the worst
 case (_even if the distribution flag is clean!_), you should be careful not to
 call it in hot code paths. If you are not sure, please contact
-dom-dev@chromium.org, or add masonfreed@chromium.org to reviewers.
+dom-dev@chromium.org, or add masonf@chromium.org to reviewers.
 
 Once Blink removes Shadow DOM v0 in the future, you don't need to call
 `Node::UpdateDistributionForFlatTreeTraversal` before using `FlatTreeTraversal`
diff --git a/third_party/blink/renderer/core/execution_context/window_agent_factory.cc b/third_party/blink/renderer/core/execution_context/window_agent_factory.cc
index f49519d..1f10e47 100644
--- a/third_party/blink/renderer/core/execution_context/window_agent_factory.cc
+++ b/third_party/blink/renderer/core/execution_context/window_agent_factory.cc
@@ -16,11 +16,10 @@
 WindowAgentFactory::WindowAgentFactory() = default;
 
 WindowAgent* WindowAgentFactory::GetAgentForOrigin(
-    bool has_potential_universal_access_privilege,
     v8::Isolate* isolate,
     const SecurityOrigin* origin,
     bool is_origin_agent_cluster) {
-  if (has_potential_universal_access_privilege) {
+  if (origin->IsGrantedUniversalAccess()) {
     if (!universal_access_agent_) {
       universal_access_agent_ = MakeGarbageCollected<WindowAgent>(isolate);
     }
diff --git a/third_party/blink/renderer/core/execution_context/window_agent_factory.h b/third_party/blink/renderer/core/execution_context/window_agent_factory.h
index 33d750e..b62c64a 100644
--- a/third_party/blink/renderer/core/execution_context/window_agent_factory.h
+++ b/third_party/blink/renderer/core/execution_context/window_agent_factory.h
@@ -44,15 +44,7 @@
   // If |is_origin_agent_cluster| is true though, then the same instance will
   // only return the same instance for an exact match (scheme, host, port) to
   // |origin|.
-  //
-  // Set |has_potential_universal_access_privilege| if an agent may be able to
-  // access all other agents synchronously.
-  // I.e. pass true to if either:
-  //   * --disable-web-security is set,
-  //   * --run-web-tests is set,
-  //   * or, the Blink instance is running for Android WebView.
-  WindowAgent* GetAgentForOrigin(bool has_potential_universal_access_privilege,
-                                 v8::Isolate* isolate,
+  WindowAgent* GetAgentForOrigin(v8::Isolate* isolate,
                                  const SecurityOrigin* origin,
                                  bool is_origin_agent_cluster);
 
diff --git a/third_party/blink/renderer/core/frame/local_frame.cc b/third_party/blink/renderer/core/frame/local_frame.cc
index 65f3e30..ffcc217 100644
--- a/third_party/blink/renderer/core/frame/local_frame.cc
+++ b/third_party/blink/renderer/core/frame/local_frame.cc
@@ -1829,7 +1829,9 @@
     String destination_domain = network_utils::GetDomainAndRegistry(
         destination_url.Host(), network_utils::kIncludePrivateRegistries);
     if (!target_domain.IsEmpty() && !destination_domain.IsEmpty() &&
-        target_domain == destination_domain) {
+        target_domain == destination_domain &&
+        target_frame.GetSecurityContext()->GetSecurityOrigin()->Protocol() ==
+            destination_url.Protocol()) {
       return true;
     }
 
diff --git a/third_party/blink/renderer/core/html/forms/OWNERS b/third_party/blink/renderer/core/html/forms/OWNERS
index e69f365..b80d6277 100644
--- a/third_party/blink/renderer/core/html/forms/OWNERS
+++ b/third_party/blink/renderer/core/html/forms/OWNERS
@@ -1,3 +1,3 @@
 keishi@chromium.org
 tkent@chromium.org
-masonfreed@chromium.org
+masonf@chromium.org
diff --git a/third_party/blink/renderer/core/html/parser/OWNERS b/third_party/blink/renderer/core/html/parser/OWNERS
index c0b826f5..506b856 100644
--- a/third_party/blink/renderer/core/html/parser/OWNERS
+++ b/third_party/blink/renderer/core/html/parser/OWNERS
@@ -1,4 +1,4 @@
 csharrison@chromium.org
 kouhei@chromium.org
-masonfreed@chromium.org
+masonf@chromium.org
 yoavweiss@chromium.org
diff --git a/third_party/blink/renderer/core/input/touch_event_manager.h b/third_party/blink/renderer/core/input/touch_event_manager.h
index 46deb25..dcba6d3 100644
--- a/third_party/blink/renderer/core/input/touch_event_manager.h
+++ b/third_party/blink/renderer/core/input/touch_event_manager.h
@@ -128,13 +128,13 @@
   // that only horizontal scrolling is blocked.
   bool should_enforce_vertical_scroll_ = false;
   // When set to a value, the effective touch-action is sent to the browser
-  // after all 'touchstart' handlers have been invoked. This is used by feature
-  // policy to enforce specific directions of scroll in spite of scroll-blocking
-  // events being prevent defaulted. When multiple pointer down events occur
-  // during the same touch sequence, the value of effective touch action which
-  // is sent to the browser after handling each dispatched 'touchstart' is the
-  // intersection of all the previously calculated effective touch action values
-  // during the sequence.
+  // after all 'touchstart' handlers have been invoked. This is used by
+  // permissions policy to enforce specific directions of scroll in spite of
+  // scroll-blocking events being prevent defaulted. When multiple pointer down
+  // events occur during the same touch sequence, the value of effective touch
+  // action which is sent to the browser after handling each dispatched
+  // 'touchstart' is the intersection of all the previously calculated effective
+  // touch action values during the sequence.
   base::Optional<TouchAction> delayed_effective_touch_action_;
 };
 
diff --git a/third_party/blink/renderer/core/inspector/inspector_trace_events.cc b/third_party/blink/renderer/core/inspector/inspector_trace_events.cc
index 3debd5c..62d33f8 100644
--- a/third_party/blink/renderer/core/inspector/inspector_trace_events.cc
+++ b/third_party/blink/renderer/core/inspector/inspector_trace_events.cc
@@ -263,12 +263,11 @@
 
 void SetNodeInfo(perfetto::TracedDictionary& dict,
                  Node* node,
-                 const char* id_field_name,
-                 const char* name_field_name = nullptr) {
-  dict.Add(perfetto::StaticString{id_field_name},
-           IdentifiersFactory::IntIdForNode(node));
-  if (name_field_name)
-    dict.Add(perfetto::DynamicString{name_field_name}, node->DebugName());
+                 perfetto::StaticString id_field_name,
+                 perfetto::StaticString name_field_name = nullptr) {
+  dict.Add(id_field_name, IdentifiersFactory::IntIdForNode(node));
+  if (name_field_name.value)
+    dict.Add(name_field_name, node->DebugName());
 }
 
 const char* PseudoTypeToString(CSSSelector::PseudoType pseudo_type) {
@@ -674,10 +673,11 @@
   array.Append(quad.P4().Y());
 }
 
-static void SetGeneratingNodeInfo(perfetto::TracedDictionary& dict,
-                                  const LayoutObject* layout_object,
-                                  const char* id_field_name,
-                                  const char* name_field_name = nullptr) {
+static void SetGeneratingNodeInfo(
+    perfetto::TracedDictionary& dict,
+    const LayoutObject* layout_object,
+    perfetto::StaticString id_field_name,
+    perfetto::StaticString name_field_name = nullptr) {
   Node* node = nullptr;
   for (; layout_object && !node; layout_object = layout_object->Parent())
     node = layout_object->GeneratingNode();
diff --git a/third_party/blink/renderer/core/layout/layout_shift_tracker.cc b/third_party/blink/renderer/core/layout/layout_shift_tracker.cc
index 99f3220..3b10e544 100644
--- a/third_party/blink/renderer/core/layout/layout_shift_tracker.cc
+++ b/third_party/blink/renderer/core/layout/layout_shift_tracker.cc
@@ -7,6 +7,7 @@
 #include "cc/layers/heads_up_display_layer.h"
 #include "cc/layers/picture_layer.h"
 #include "cc/trees/layer_tree_host.h"
+#include "third_party/blink/public/common/features.h"
 #include "third_party/blink/public/common/input/web_pointer_event.h"
 #include "third_party/blink/renderer/core/dom/dom_node_ids.h"
 #include "third_party/blink/renderer/core/frame/local_dom_window.h"
@@ -142,6 +143,8 @@
       // SVGImage::DataChanged.
       is_active_(
           !frame_view->GetFrame().GetChromeClient().IsSVGImageChromeClient()),
+      enable_m91_improvements_(
+          base::FeatureList::IsEnabled(features::kCLSM91Improvements)),
       score_(0.0),
       weighted_score_(0.0),
       timer_(frame_view->GetFrame().GetTaskRunner(TaskType::kInternalDefault),
@@ -172,11 +175,13 @@
       return false;
     if (object.IsBR())
       return false;
-    if (To<LayoutText>(object).ContainsOnlyWhitespaceOrNbsp() ==
-        OnlyWhitespaceOrNbsp::kYes)
-      return false;
-    if (object.StyleRef().GetFont().ShouldSkipDrawing())
-      return false;
+    if (enable_m91_improvements_) {
+      if (To<LayoutText>(object).ContainsOnlyWhitespaceOrNbsp() ==
+          OnlyWhitespaceOrNbsp::kYes)
+        return false;
+      if (object.StyleRef().GetFont().ShouldSkipDrawing())
+        return false;
+    }
     return true;
   }
 
@@ -211,7 +216,7 @@
       return false;
   }
 
-  if (box.IsLayoutBlock()) {
+  if (enable_m91_improvements_ && box.IsLayoutBlock()) {
     // Just check the simplest case. For more complex cases, we should suggest
     // the developer to use visibility:hidden.
     if (To<LayoutBlock>(box).FirstChild())
@@ -241,22 +246,25 @@
   float threshold_physical_px =
       kMovementThreshold * object.StyleRef().EffectiveZoom();
 
-  // Check shift of starting point, including 2d-translation and scroll deltas.
-  if (EqualWithinMovementThreshold(old_starting_point, new_starting_point,
-                                   threshold_physical_px))
-    return;
+  if (enable_m91_improvements_) {
+    // Check shift of starting point, including 2d-translation and scroll
+    // deltas.
+    if (EqualWithinMovementThreshold(old_starting_point, new_starting_point,
+                                     threshold_physical_px))
+      return;
 
-  // Check shift of 2d-translation-indifferent starting point.
-  if (!translation_delta.IsZero() &&
-      EqualWithinMovementThreshold(old_starting_point + translation_delta,
-                                   new_starting_point, threshold_physical_px))
-    return;
+    // Check shift of 2d-translation-indifferent starting point.
+    if (!translation_delta.IsZero() &&
+        EqualWithinMovementThreshold(old_starting_point + translation_delta,
+                                     new_starting_point, threshold_physical_px))
+      return;
 
-  // Check shift of scroll-indifferent starting point.
-  if (!scroll_delta.IsZero() &&
-      EqualWithinMovementThreshold(old_starting_point + scroll_delta,
-                                   new_starting_point, threshold_physical_px))
-    return;
+    // Check shift of scroll-indifferent starting point.
+    if (!scroll_delta.IsZero() &&
+        EqualWithinMovementThreshold(old_starting_point + scroll_delta,
+                                     new_starting_point, threshold_physical_px))
+      return;
+  }
 
   // Check shift of 2d-translation-and-scroll-indifferent starting point.
   FloatSize translation_and_scroll_delta = scroll_delta + translation_delta;
@@ -285,8 +293,9 @@
       property_tree_state.Transform(), root_state.Transform());
   // TODO(crbug.com/1187979): Shift by |scroll_delta| to keep backward
   // compatibility in https://crrev.com/c/2754969. See the bug for details.
-  FloatPoint old_starting_point_in_root =
-      transform.MapPoint(old_starting_point + scroll_delta);
+  FloatPoint old_starting_point_in_root = transform.MapPoint(
+      old_starting_point +
+      (enable_m91_improvements_ ? scroll_delta : translation_and_scroll_delta));
   FloatPoint new_starting_point_in_root =
       transform.MapPoint(new_starting_point);
 
@@ -295,10 +304,22 @@
                                    threshold_physical_px))
     return;
 
+  if (enable_m91_improvements_) {
+    DCHECK(frame_scroll_delta_.IsZero());
+  } else if (EqualWithinMovementThreshold(
+                 old_starting_point_in_root + frame_scroll_delta_,
+                 new_starting_point_in_root, threshold_physical_px)) {
+    // TODO(skobes): Checking frame_scroll_delta_ is an imperfect solution to
+    // allowing counterscrolled layout shifts. Ideally, we would map old_rect
+    // to viewport coordinates using the previous frame's scroll tree.
+    return;
+  }
+
   FloatRect old_rect_in_root(old_rect);
   // TODO(crbug.com/1187979): Shift by |scroll_delta| to keep backward
   // compatibility in https://crrev.com/c/2754969. See the bug for details.
-  old_rect_in_root.Move(scroll_delta);
+  old_rect_in_root.Move(
+      enable_m91_improvements_ ? scroll_delta : translation_and_scroll_delta);
   transform.MapRect(old_rect_in_root);
   FloatRect new_rect_in_root(new_rect);
   transform.MapRect(new_rect_in_root);
@@ -312,7 +333,8 @@
 
   // If the object moved from or to out of view, ignore the shift if it's in
   // the inline direction only.
-  if (visible_old_rect.IsEmpty() || visible_new_rect.IsEmpty()) {
+  if (enable_m91_improvements_ &&
+      (visible_old_rect.IsEmpty() || visible_new_rect.IsEmpty())) {
     FloatPoint old_inline_direction_indifferent_starting_point_in_root =
         old_starting_point_in_root;
     if (object.IsHorizontalWritingMode()) {
@@ -539,6 +561,7 @@
   // Reset accumulated state.
   region_.Reset();
   frame_max_distance_ = 0.0;
+  frame_scroll_delta_ = ScrollOffset();
   attributions_.fill(Attribution());
 }
 
@@ -654,7 +677,11 @@
   frame.Client()->DidObserveInputForLayoutShiftTracking(timestamp);
 }
 
-void LayoutShiftTracker::NotifyScroll(mojom::blink::ScrollType scroll_type) {
+void LayoutShiftTracker::NotifyScroll(mojom::blink::ScrollType scroll_type,
+                                      ScrollOffset delta) {
+  if (!enable_m91_improvements_)
+    frame_scroll_delta_ += delta;
+
   // Only set observed_input_or_scroll_ for user-initiated scrolls, and not
   // other scrolls such as hash fragment navigations.
   if (scroll_type == mojom::blink::ScrollType::kUser ||
diff --git a/third_party/blink/renderer/core/layout/layout_shift_tracker.h b/third_party/blink/renderer/core/layout/layout_shift_tracker.h
index cf164d8..b48bef4 100644
--- a/third_party/blink/renderer/core/layout/layout_shift_tracker.h
+++ b/third_party/blink/renderer/core/layout/layout_shift_tracker.h
@@ -73,7 +73,7 @@
 
   void NotifyPrePaintFinished();
   void NotifyInput(const WebInputEvent&);
-  void NotifyScroll(mojom::blink::ScrollType);
+  void NotifyScroll(mojom::blink::ScrollType, ScrollOffset delta);
   void NotifyViewportSizeChanged();
   void NotifyFindInPageInput();
   void NotifyChangeEvent();
@@ -183,6 +183,7 @@
 
   Member<LocalFrameView> frame_view_;
   bool is_active_;
+  bool enable_m91_improvements_;
 
   // The document cumulative layout shift (DCLS) score for this LocalFrame,
   // unweighted, with move distance applied.
@@ -226,6 +227,10 @@
   // frames.
   float overall_max_distance_;
 
+  // Sum of all scroll deltas that occurred in the current animation frame.
+  // TODO(wangxianzhu): Remove when enabling CLSM91Improvements permanently.
+  ScrollOffset frame_scroll_delta_;
+
   // Whether either a user input or document scroll have been observed during
   // the session. (This is only tracked so UkmPageLoadMetricsObserver to report
   // LayoutInstability.CumulativeShiftScore.MainFrame.BeforeInputOrScroll. It's
diff --git a/third_party/blink/renderer/core/layout/ng/custom/css_layout_definition.cc b/third_party/blink/renderer/core/layout/ng/custom/css_layout_definition.cc
index d939dae..6788b79 100644
--- a/third_party/blink/renderer/core/layout/ng/custom/css_layout_definition.cc
+++ b/third_party/blink/renderer/core/layout/ng/custom/css_layout_definition.cc
@@ -222,7 +222,7 @@
     const NGBlockNode& node,
     const LogicalSize& border_box_size,
     const NGBoxStrut& border_scrollbar_padding,
-    const LayoutUnit child_percentage_resolution_block_size_for_min_max,
+    const LayoutUnit child_available_block_size,
     CustomLayoutScope* custom_layout_scope,
     IntrinsicSizesResultOptions** intrinsic_sizes_result_options,
     bool* child_depends_on_percentage_block_size) {
@@ -276,8 +276,7 @@
     // The queue may mutate (re-allocating the vector) while running a task.
     for (wtf_size_t index = 0; index < queue.size(); ++index) {
       auto task = queue[index];
-      task->Run(space, node.Style(),
-                child_percentage_resolution_block_size_for_min_max,
+      task->Run(space, node.Style(), child_available_block_size,
                 child_depends_on_percentage_block_size);
     }
     queue.clear();
diff --git a/third_party/blink/renderer/core/layout/ng/custom/css_layout_definition.h b/third_party/blink/renderer/core/layout/ng/custom/css_layout_definition.h
index 5596cc1..976fabd 100644
--- a/third_party/blink/renderer/core/layout/ng/custom/css_layout_definition.h
+++ b/third_party/blink/renderer/core/layout/ng/custom/css_layout_definition.h
@@ -67,16 +67,15 @@
 
     // Runs the web developer defined intrinsicSizes, returns true if everything
     // succeeded. It populates the IntrinsicSizesResultOptions dictionary.
-    bool IntrinsicSizes(
-        const NGConstraintSpace&,
-        const Document&,
-        const NGBlockNode&,
-        const LogicalSize& border_box_size,
-        const NGBoxStrut& border_scrollbar_padding,
-        const LayoutUnit child_percentage_resolution_block_size_for_min_max,
-        CustomLayoutScope*,
-        IntrinsicSizesResultOptions**,
-        bool* child_depends_on_percentage_block_size);
+    bool IntrinsicSizes(const NGConstraintSpace&,
+                        const Document&,
+                        const NGBlockNode&,
+                        const LogicalSize& border_box_size,
+                        const NGBoxStrut& border_scrollbar_padding,
+                        const LayoutUnit child_available_block_size,
+                        CustomLayoutScope*,
+                        IntrinsicSizesResultOptions**,
+                        bool* child_depends_on_percentage_block_size);
 
     void Trace(Visitor*) const;
 
diff --git a/third_party/blink/renderer/core/layout/ng/custom/custom_layout_work_task.cc b/third_party/blink/renderer/core/layout/ng/custom/custom_layout_work_task.cc
index 0dd3687b..6748d3b 100644
--- a/third_party/blink/renderer/core/layout/ng/custom/custom_layout_work_task.cc
+++ b/third_party/blink/renderer/core/layout/ng/custom/custom_layout_work_task.cc
@@ -47,18 +47,16 @@
   visitor->Trace(options_);
 }
 
-void CustomLayoutWorkTask::Run(
-    const NGConstraintSpace& parent_space,
-    const ComputedStyle& parent_style,
-    const LayoutUnit child_percentage_resolution_block_size_for_min_max,
-    bool* child_depends_on_percentage_block_size) {
+void CustomLayoutWorkTask::Run(const NGConstraintSpace& parent_space,
+                               const ComputedStyle& parent_style,
+                               const LayoutUnit child_available_block_size,
+                               bool* child_depends_on_percentage_block_size) {
   DCHECK(token_->IsValid());
   NGLayoutInputNode child = child_->GetLayoutNode();
 
   if (type_ == CustomLayoutWorkTask::TaskType::kIntrinsicSizes) {
-    RunIntrinsicSizesTask(parent_style,
-                          child_percentage_resolution_block_size_for_min_max,
-                          child, child_depends_on_percentage_block_size);
+    RunIntrinsicSizesTask(parent_style, child_available_block_size, child,
+                          child_depends_on_percentage_block_size);
   } else {
     DCHECK_EQ(type_, CustomLayoutWorkTask::TaskType::kLayoutFragment);
     RunLayoutFragmentTask(parent_space, parent_style, child);
@@ -148,13 +146,13 @@
 
 void CustomLayoutWorkTask::RunIntrinsicSizesTask(
     const ComputedStyle& parent_style,
-    const LayoutUnit child_percentage_resolution_block_size_for_min_max,
+    const LayoutUnit child_available_block_size,
     NGLayoutInputNode child,
     bool* child_depends_on_percentage_block_size) {
   DCHECK_EQ(type_, CustomLayoutWorkTask::TaskType::kIntrinsicSizes);
   DCHECK(resolver_);
 
-  MinMaxSizesInput input(child_percentage_resolution_block_size_for_min_max);
+  MinMaxSizesInput input(child_available_block_size);
   MinMaxSizesResult result = ComputeMinAndMaxContentContribution(
       parent_style, To<NGBlockNode>(child), input);
   resolver_->Resolve(MakeGarbageCollected<CustomIntrinsicSizes>(
diff --git a/third_party/blink/renderer/core/layout/ng/custom/custom_layout_work_task.h b/third_party/blink/renderer/core/layout/ng/custom/custom_layout_work_task.h
index de7610a..eef26c8 100644
--- a/third_party/blink/renderer/core/layout/ng/custom/custom_layout_work_task.h
+++ b/third_party/blink/renderer/core/layout/ng/custom/custom_layout_work_task.h
@@ -50,7 +50,7 @@
   // Runs this work task.
   void Run(const NGConstraintSpace& parent_space,
            const ComputedStyle& parent_style,
-           const LayoutUnit child_percentage_resolution_block_size_for_min_max,
+           const LayoutUnit child_available_block_size,
            bool* child_depends_on_percentage_block_size = nullptr);
 
  private:
@@ -64,11 +64,10 @@
   void RunLayoutFragmentTask(const NGConstraintSpace& parent_space,
                              const ComputedStyle& parent_style,
                              NGLayoutInputNode child);
-  void RunIntrinsicSizesTask(
-      const ComputedStyle& parent_style,
-      const LayoutUnit child_percentage_resolution_block_size_for_min_max,
-      NGLayoutInputNode child,
-      bool* child_depends_on_percentage_block_size);
+  void RunIntrinsicSizesTask(const ComputedStyle& parent_style,
+                             const LayoutUnit child_available_block_size,
+                             NGLayoutInputNode child,
+                             bool* child_depends_on_percentage_block_size);
 };
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/layout/ng/mathml/ng_math_fraction_layout_algorithm.cc b/third_party/blink/renderer/core/layout/ng/mathml/ng_math_fraction_layout_algorithm.cc
index ce7d99d..64c2c989 100644
--- a/third_party/blink/renderer/core/layout/ng/mathml/ng_math_fraction_layout_algorithm.cc
+++ b/third_party/blink/renderer/core/layout/ng/mathml/ng_math_fraction_layout_algorithm.cc
@@ -281,10 +281,10 @@
        child = child.NextSibling()) {
     if (child.IsOutOfFlowPositioned())
       continue;
-    auto child_result = ComputeMinAndMaxContentContribution(
-        Style(), To<NGBlockNode>(child), child_input);
-    NGBoxStrut margins = ComputeMinMaxMargins(Style(), child);
-    child_result.sizes += margins.InlineSum();
+
+    const auto child_result = ComputeMinAndMaxContentContributionForMathChild(
+        Style(), ConstraintSpace(), To<NGBlockNode>(child),
+        child_input.percentage_resolution_block_size);
 
     sizes.Encompass(child_result.sizes);
     depends_on_percentage_block_size |=
diff --git a/third_party/blink/renderer/core/layout/ng/mathml/ng_math_layout_utils.cc b/third_party/blink/renderer/core/layout/ng/mathml/ng_math_layout_utils.cc
index 46c2de49..7a499c3 100644
--- a/third_party/blink/renderer/core/layout/ng/mathml/ng_math_layout_utils.cc
+++ b/third_party/blink/renderer/core/layout/ng/mathml/ng_math_layout_utils.cc
@@ -38,6 +38,22 @@
   return space_builder.ToConstraintSpace();
 }
 
+MinMaxSizesResult ComputeMinAndMaxContentContributionForMathChild(
+    const ComputedStyle& parent_style,
+    const NGConstraintSpace& parent_space,
+    const NGBlockNode& child,
+    LayoutUnit child_available_block_size) {
+  DCHECK(child.CreatesNewFormattingContext());
+
+  MinMaxSizesInput input(child_available_block_size);
+  auto result = ComputeMinAndMaxContentContribution(parent_style, child, input);
+
+  // Add margins directly here.
+  result.sizes += ComputeMinMaxMargins(parent_style, child).InlineSum();
+
+  return result;
+}
+
 NGLayoutInputNode FirstChildInFlow(const NGBlockNode& node) {
   NGLayoutInputNode child = node.FirstChild();
   while (child && child.IsOutOfFlowPositioned())
diff --git a/third_party/blink/renderer/core/layout/ng/mathml/ng_math_layout_utils.h b/third_party/blink/renderer/core/layout/ng/mathml/ng_math_layout_utils.h
index 26de1169..bd1604d0 100644
--- a/third_party/blink/renderer/core/layout/ng/mathml/ng_math_layout_utils.h
+++ b/third_party/blink/renderer/core/layout/ng/mathml/ng_math_layout_utils.h
@@ -12,6 +12,7 @@
 
 struct LogicalSize;
 struct MinMaxSizes;
+struct MinMaxSizesResult;
 class NGBlockNode;
 class NGConstraintSpace;
 class NGLayoutInputNode;
@@ -24,6 +25,12 @@
     const NGConstraintSpace& parent_constraint_space,
     const NGLayoutInputNode&);
 
+MinMaxSizesResult ComputeMinAndMaxContentContributionForMathChild(
+    const ComputedStyle& parent_style,
+    const NGConstraintSpace& parent_constraint_space,
+    const NGBlockNode& child,
+    LayoutUnit child_available_block_size);
+
 NGLayoutInputNode FirstChildInFlow(const NGBlockNode&);
 NGLayoutInputNode NextSiblingInFlow(const NGBlockNode&);
 
diff --git a/third_party/blink/renderer/core/layout/ng/mathml/ng_math_padded_layout_algorithm.cc b/third_party/blink/renderer/core/layout/ng/mathml/ng_math_padded_layout_algorithm.cc
index a695c14..352ce2bd 100644
--- a/third_party/blink/renderer/core/layout/ng/mathml/ng_math_padded_layout_algorithm.cc
+++ b/third_party/blink/renderer/core/layout/ng/mathml/ng_math_padded_layout_algorithm.cc
@@ -124,10 +124,9 @@
   NGBlockNode content = nullptr;
   GatherChildren(&content);
 
-  MinMaxSizesResult content_result =
-      ComputeMinAndMaxContentContribution(Style(), content, input);
-  NGBoxStrut content_margins = ComputeMinMaxMargins(Style(), content);
-  content_result.sizes += content_margins.InlineSum();
+  const auto content_result = ComputeMinAndMaxContentContributionForMathChild(
+      Style(), ConstraintSpace(), content,
+      input.percentage_resolution_block_size);
 
   bool depends_on_percentage_block_size =
       content_result.depends_on_percentage_block_size;
diff --git a/third_party/blink/renderer/core/layout/ng/mathml/ng_math_radical_layout_algorithm.cc b/third_party/blink/renderer/core/layout/ng/mathml/ng_math_radical_layout_algorithm.cc
index 94ac80db..6d5e13b 100644
--- a/third_party/blink/renderer/core/layout/ng/mathml/ng_math_radical_layout_algorithm.cc
+++ b/third_party/blink/renderer/core/layout/ng/mathml/ng_math_radical_layout_algorithm.cc
@@ -204,15 +204,16 @@
   MinMaxSizes sizes;
   bool depends_on_percentage_block_size = false;
   if (index) {
-    auto horizontal = GetRadicalHorizontalParameters(Style());
+    const auto horizontal = GetRadicalHorizontalParameters(Style());
     sizes += horizontal.kern_before_degree.ClampNegativeToZero();
-    MinMaxSizesResult index_result =
-        ComputeMinAndMaxContentContribution(Style(), index, input);
-    NGBoxStrut index_margins = ComputeMinMaxMargins(Style(), index);
-    index_result.sizes += index_margins.InlineSum();
+
+    const auto index_result = ComputeMinAndMaxContentContributionForMathChild(
+        Style(), ConstraintSpace(), index,
+        input.percentage_resolution_block_size);
     depends_on_percentage_block_size |=
         index_result.depends_on_percentage_block_size;
     sizes += index_result.sizes;
+
     // kern_after_degree decreases the inline size, but is capped by the index
     // content inline size.
     sizes.min_size +=
@@ -225,10 +226,9 @@
       sizes += GetMinMaxSizesForVerticalStretchyOperator(Style(),
                                                          kSquareRootCharacter);
     }
-    MinMaxSizesResult base_result =
-        ComputeMinAndMaxContentContribution(Style(), base, input);
-    NGBoxStrut base_margins = ComputeMinMaxMargins(Style(), base);
-    base_result.sizes += base_margins.InlineSum();
+    const auto base_result = ComputeMinAndMaxContentContributionForMathChild(
+        Style(), ConstraintSpace(), base,
+        input.percentage_resolution_block_size);
     depends_on_percentage_block_size |=
         base_result.depends_on_percentage_block_size;
     sizes += base_result.sizes;
diff --git a/third_party/blink/renderer/core/layout/ng/mathml/ng_math_row_layout_algorithm.cc b/third_party/blink/renderer/core/layout/ng/mathml/ng_math_row_layout_algorithm.cc
index 6684a0f..42052bc 100644
--- a/third_party/blink/renderer/core/layout/ng/mathml/ng_math_row_layout_algorithm.cc
+++ b/third_party/blink/renderer/core/layout/ng/mathml/ng_math_row_layout_algorithm.cc
@@ -166,11 +166,9 @@
        child = child.NextSibling()) {
     if (child.IsOutOfFlowPositioned())
       continue;
-    MinMaxSizesResult child_result = ComputeMinAndMaxContentContribution(
-        Style(), To<NGBlockNode>(child), child_input);
-    NGBoxStrut child_margins = ComputeMinMaxMargins(Style(), child);
-    child_result.sizes += child_margins.InlineSum();
-
+    const auto child_result = ComputeMinAndMaxContentContributionForMathChild(
+        Style(), ConstraintSpace(), To<NGBlockNode>(child),
+        child_input.percentage_resolution_block_size);
     sizes += child_result.sizes;
 
     LayoutUnit lspace, rspace;
diff --git a/third_party/blink/renderer/core/layout/ng/mathml/ng_math_scripts_layout_algorithm.cc b/third_party/blink/renderer/core/layout/ng/mathml/ng_math_scripts_layout_algorithm.cc
index 54a1782..84ffef60 100644
--- a/third_party/blink/renderer/core/layout/ng/mathml/ng_math_scripts_layout_algorithm.cc
+++ b/third_party/blink/renderer/core/layout/ng/mathml/ng_math_scripts_layout_algorithm.cc
@@ -426,9 +426,9 @@
   // TODO(layout-dev): Determine the italic-correction without calling layout
   // within ComputeMinMaxSizes, (or setup in an interoperable constraint-space).
   LayoutUnit base_italic_correction;
-  MinMaxSizesResult base_result =
-      ComputeMinAndMaxContentContribution(Style(), base, child_input);
-  base_result.sizes += ComputeMinMaxMargins(Style(), base).InlineSum();
+  const auto base_result = ComputeMinAndMaxContentContributionForMathChild(
+      Style(), ConstraintSpace(), base,
+      child_input.percentage_resolution_block_size);
 
   sizes = base_result.sizes;
   depends_on_percentage_block_size |=
@@ -440,13 +440,13 @@
     case MathScriptType::kUnder:
     case MathScriptType::kOver:
     case MathScriptType::kSuper: {
-      NGBlockNode sub = sub_sup_pairs[0].sub;
-      NGBlockNode sup = sub_sup_pairs[0].sup;
-      auto first_post_script = sub ? sub : sup;
-      auto first_post_script_result = ComputeMinAndMaxContentContribution(
-          Style(), first_post_script, child_input);
-      first_post_script_result.sizes +=
-          ComputeMinMaxMargins(Style(), first_post_script).InlineSum();
+      const NGBlockNode sub = sub_sup_pairs[0].sub;
+      const NGBlockNode sup = sub_sup_pairs[0].sup;
+      const auto first_post_script = sub ? sub : sup;
+      const auto first_post_script_result =
+          ComputeMinAndMaxContentContributionForMathChild(
+              Style(), ConstraintSpace(), first_post_script,
+              child_input.percentage_resolution_block_size);
 
       sizes += first_post_script_result.sizes;
       if (sub)
@@ -462,21 +462,21 @@
       MinMaxSizes sub_sup_pair_size;
       unsigned index = 0;
       do {
-        auto sub = sub_sup_pairs[index].sub;
+        const auto sub = sub_sup_pairs[index].sub;
         if (!sub)
           continue;
-        auto sub_result =
-            ComputeMinAndMaxContentContribution(Style(), sub, child_input);
-        sub_result.sizes += ComputeMinMaxMargins(Style(), sub).InlineSum();
+        auto sub_result = ComputeMinAndMaxContentContributionForMathChild(
+            Style(), ConstraintSpace(), sub,
+            child_input.percentage_resolution_block_size);
         sub_result.sizes -= base_italic_correction;
         sub_sup_pair_size.Encompass(sub_result.sizes);
 
-        auto sup = sub_sup_pairs[index].sup;
+        const auto sup = sub_sup_pairs[index].sup;
         if (!sup)
           continue;
-        auto sup_result =
-            ComputeMinAndMaxContentContribution(Style(), sup, child_input);
-        sup_result.sizes += ComputeMinMaxMargins(Style(), sup).InlineSum();
+        const auto sup_result = ComputeMinAndMaxContentContributionForMathChild(
+            Style(), ConstraintSpace(), sup,
+            child_input.percentage_resolution_block_size);
         sub_sup_pair_size.Encompass(sup_result.sizes);
 
         sizes += sub_sup_pair_size;
diff --git a/third_party/blink/renderer/core/layout/ng/mathml/ng_math_under_over_layout_algorithm.cc b/third_party/blink/renderer/core/layout/ng/mathml/ng_math_under_over_layout_algorithm.cc
index 539a1f1..fb4cc7e 100644
--- a/third_party/blink/renderer/core/layout/ng/mathml/ng_math_under_over_layout_algorithm.cc
+++ b/third_party/blink/renderer/core/layout/ng/mathml/ng_math_under_over_layout_algorithm.cc
@@ -341,10 +341,9 @@
     if (child.IsOutOfFlowPositioned())
       continue;
     // TODO(crbug.com/1125136): take into account italic correction.
-    auto child_result = ComputeMinAndMaxContentContribution(
-        Style(), To<NGBlockNode>(child), child_input);
-    NGBoxStrut margins = ComputeMinMaxMargins(Style(), child);
-    child_result.sizes += margins.InlineSum();
+    const auto child_result = ComputeMinAndMaxContentContributionForMathChild(
+        Style(), ConstraintSpace(), To<NGBlockNode>(child),
+        child_input.percentage_resolution_block_size);
 
     sizes.Encompass(child_result.sizes);
     depends_on_percentage_block_size |=
diff --git a/third_party/blink/renderer/core/loader/document_loader.cc b/third_party/blink/renderer/core/loader/document_loader.cc
index 014deb716..99e49862 100644
--- a/third_party/blink/renderer/core/loader/document_loader.cc
+++ b/third_party/blink/renderer/core/loader/document_loader.cc
@@ -738,7 +738,8 @@
   // We want to allow same-document text fragment navigations if they're coming
   // from the browser. Do this only on a standard navigation so that we don't
   // clobber the token when this is called from e.g. history.replaceState.
-  if (type == WebFrameLoadType::kStandard) {
+  if (type == WebFrameLoadType::kStandard ||
+      same_document_navigation_source == kSameDocumentNavigationDefault) {
     has_text_fragment_token_ =
         TextFragmentAnchor::GenerateNewTokenForSameDocument(
             new_url.FragmentIdentifier(), type, is_content_initiated,
@@ -1823,13 +1824,7 @@
 WindowAgent* GetWindowAgentForOrigin(LocalFrame* frame,
                                      SecurityOrigin* origin,
                                      bool is_origin_keyed) {
-  // TODO(keishi): Also check if AllowUniversalAccessFromFileURLs might
-  // dynamically change.
-  bool has_potential_universal_access_privilege =
-      !frame->GetSettings()->GetWebSecurityEnabled() ||
-      frame->GetSettings()->GetAllowUniversalAccessFromFileURLs();
   return frame->window_agent_factory().GetAgentForOrigin(
-      has_potential_universal_access_privilege,
       V8PerIsolateData::MainThreadIsolate(), origin, is_origin_keyed);
 }
 
diff --git a/third_party/blink/renderer/core/loader/frame_fetch_context.cc b/third_party/blink/renderer/core/loader/frame_fetch_context.cc
index 9102a14..f766ecf 100644
--- a/third_party/blink/renderer/core/loader/frame_fetch_context.cc
+++ b/third_party/blink/renderer/core/loader/frame_fetch_context.cc
@@ -444,7 +444,7 @@
   if (!AllowScriptFromSourceWithoutNotifying(request.Url()))
     return;
 
-  // When the runtime flag "FeaturePolicyForClientHints" is enabled, feature
+  // When the runtime flag "FeaturePolicyForClientHints" is enabled, permissions
   // policy is used to enable hints for all subresources, based on the policy of
   // the requesting document, and the origin of the resource.
   const PermissionsPolicy* policy =
diff --git a/third_party/blink/renderer/core/page/context_menu_controller.cc b/third_party/blink/renderer/core/page/context_menu_controller.cc
index ee24dee9..37839a2 100644
--- a/third_party/blink/renderer/core/page/context_menu_controller.cc
+++ b/third_party/blink/renderer/core/page/context_menu_controller.cc
@@ -634,30 +634,39 @@
     // spelling marker on the word instead of spellchecking it.
     std::pair<String, String> misspelled_word_and_description =
         spell_checker.SelectMisspellingAsync();
-    data.misspelled_word =
-        WebString::FromUTF8(misspelled_word_and_description.first.Utf8())
-            .Utf16();
-    const String& description = misspelled_word_and_description.second;
-    if (description.length()) {
-      Vector<String> suggestions;
-      description.Split('\n', suggestions);
-      WebVector<std::u16string> web_suggestions(suggestions.size());
-      std::transform(suggestions.begin(), suggestions.end(),
-                     web_suggestions.begin(), [](const String& s) {
-                       return WebString::FromUTF8(s.Utf8()).Utf16();
-                     });
-      data.dictionary_suggestions = web_suggestions.ReleaseVector();
-    } else if (spell_checker.GetTextCheckerClient()) {
-      size_t misspelled_offset, misspelled_length;
-      WebVector<WebString> web_suggestions;
-      spell_checker.GetTextCheckerClient()->CheckSpelling(
-          WebString::FromUTF16(data.misspelled_word), misspelled_offset,
-          misspelled_length, &web_suggestions);
-      WebVector<std::u16string> suggestions(web_suggestions.size());
-      std::transform(web_suggestions.begin(), web_suggestions.end(),
-                     suggestions.begin(),
-                     [](const WebString& s) { return s.Utf16(); });
-      data.dictionary_suggestions = suggestions.ReleaseVector();
+    const String& misspelled_word = misspelled_word_and_description.first;
+    if (misspelled_word.length()) {
+      data.misspelled_word =
+          WebString::FromUTF8(misspelled_word.Utf8()).Utf16();
+      const String& description = misspelled_word_and_description.second;
+      if (description.length()) {
+        // Suggestions were cached for the misspelled word (won't be true for
+        // Hunspell, or Windows platform spellcheck if the
+        // kWinRetrieveSuggestionsOnlyOnDemand feature flag is set).
+        Vector<String> suggestions;
+        description.Split('\n', suggestions);
+        WebVector<std::u16string> web_suggestions(suggestions.size());
+        std::transform(suggestions.begin(), suggestions.end(),
+                       web_suggestions.begin(), [](const String& s) {
+                         return WebString::FromUTF8(s.Utf8()).Utf16();
+                       });
+        data.dictionary_suggestions = web_suggestions.ReleaseVector();
+      } else if (spell_checker.GetTextCheckerClient()) {
+        // No suggestions cached for the misspelled word. Retrieve suggestions
+        // for it (Windows platform spellchecker will do this later from
+        // SpellingMenuObserver::InitMenu on the browser process side to avoid a
+        // blocking IPC here).
+        size_t misspelled_offset, misspelled_length;
+        WebVector<WebString> web_suggestions;
+        spell_checker.GetTextCheckerClient()->CheckSpelling(
+            WebString::FromUTF16(data.misspelled_word), misspelled_offset,
+            misspelled_length, &web_suggestions);
+        WebVector<std::u16string> suggestions(web_suggestions.size());
+        std::transform(web_suggestions.begin(), web_suggestions.end(),
+                       suggestions.begin(),
+                       [](const WebString& s) { return s.Utf16(); });
+        data.dictionary_suggestions = suggestions.ReleaseVector();
+      }
     }
   }
 
diff --git a/third_party/blink/renderer/core/permissions_policy/layout_animations_policy.h b/third_party/blink/renderer/core/permissions_policy/layout_animations_policy.h
index 4ed80b1..77e0b1f9 100644
--- a/third_party/blink/renderer/core/permissions_policy/layout_animations_policy.h
+++ b/third_party/blink/renderer/core/permissions_policy/layout_animations_policy.h
@@ -14,13 +14,13 @@
 class CSSProperty;
 class ExecutionContext;
 
-// Helper methods for for 'layout-animations' (kLayoutAnimations) feature
+// Helper methods for for 'layout-animations' (kLayoutAnimations) permissions
 // policy.
 class LayoutAnimationsPolicy {
   DISALLOW_NEW();
 
  public:
-  // Returns a set of the CSS properties which are affected by the feature
+  // Returns a set of the CSS properties which are affected by the permissions
   // policy 'layout-animations'.
   static const HashSet<const CSSProperty*>& AffectedCSSProperties();
 
diff --git a/third_party/blink/renderer/core/permissions_policy/permissions_policy_test.cc b/third_party/blink/renderer/core/permissions_policy/permissions_policy_test.cc
index ee34bd9..ae4ba8b0 100644
--- a/third_party/blink/renderer/core/permissions_policy/permissions_policy_test.cc
+++ b/third_party/blink/renderer/core/permissions_policy/permissions_policy_test.cc
@@ -649,7 +649,7 @@
   PolicyParserMessageBuffer permissions_policy_logger("Permissions Policy: ");
 
   // 'geolocation' in permissions policy has a invalid allowlist item, which
-  // results in an empty allowlist, which is equivalent to 'none' in feature
+  // results in an empty allowlist, which is equivalent to "()" in permissions
   // policy syntax.
   CheckParsedPolicy(
       PermissionsPolicyParser::ParseHeader(
diff --git a/third_party/blink/renderer/core/scroll/scrollable_area.cc b/third_party/blink/renderer/core/scroll/scrollable_area.cc
index c9914bbf..6576988 100644
--- a/third_party/blink/renderer/core/scroll/scrollable_area.cc
+++ b/third_party/blink/renderer/core/scroll/scrollable_area.cc
@@ -436,7 +436,7 @@
 
   if (offset_changed && GetLayoutBox() && GetLayoutBox()->GetFrameView()) {
     GetLayoutBox()->GetFrameView()->GetLayoutShiftTracker().NotifyScroll(
-        scroll_type);
+        scroll_type, delta);
   }
 
   GetScrollAnimator().SetCurrentOffset(offset);
diff --git a/third_party/blink/renderer/core/speculation_rules/document_speculation_rules.cc b/third_party/blink/renderer/core/speculation_rules/document_speculation_rules.cc
index 320598e3..770c2a9 100644
--- a/third_party/blink/renderer/core/speculation_rules/document_speculation_rules.cc
+++ b/third_party/blink/renderer/core/speculation_rules/document_speculation_rules.cc
@@ -3,6 +3,9 @@
 // found in the LICENSE file.
 
 #include "third_party/blink/renderer/core/speculation_rules/document_speculation_rules.h"
+#include "third_party/blink/public/common/browser_interface_broker_proxy.h"
+#include "third_party/blink/renderer/core/execution_context/execution_context.h"
+#include "third_party/blink/renderer/platform/wtf/vector.h"
 
 namespace blink {
 
@@ -27,15 +30,74 @@
 }
 
 DocumentSpeculationRules::DocumentSpeculationRules(Document& document)
-    : Supplement(document) {}
+    : Supplement(document), host_(document.GetExecutionContext()) {}
 
 void DocumentSpeculationRules::AddRuleSet(SpeculationRuleSet* rule_set) {
   rule_sets_.push_back(rule_set);
+
+  if (!has_pending_update_) {
+    has_pending_update_ = true;
+    auto task_runner =
+        GetSupplementable()->GetExecutionContext()->GetTaskRunner(
+            TaskType::kIdleTask);
+    task_runner->PostTask(
+        base::Location::Current(),
+        WTF::Bind(&DocumentSpeculationRules::UpdateSpeculationCandidates,
+                  WrapWeakPersistent(this)));
+  }
 }
 
 void DocumentSpeculationRules::Trace(Visitor* visitor) const {
   Supplement::Trace(visitor);
   visitor->Trace(rule_sets_);
+  visitor->Trace(host_);
+}
+
+mojom::blink::SpeculationHost* DocumentSpeculationRules::GetHost() {
+  if (!host_.is_bound()) {
+    auto* execution_context = GetSupplementable()->GetExecutionContext();
+    if (!execution_context)
+      return nullptr;
+    execution_context->GetBrowserInterfaceBroker().GetInterface(
+        host_.BindNewPipeAndPassReceiver(
+            execution_context->GetTaskRunner(TaskType::kInternalDefault)));
+  }
+  return host_.get();
+}
+
+void DocumentSpeculationRules::UpdateSpeculationCandidates() {
+  has_pending_update_ = false;
+
+  mojom::blink::SpeculationHost* host = GetHost();
+  if (!host)
+    return;
+
+  Vector<mojom::blink::SpeculationCandidatePtr> candidates;
+  auto push_candidates = [&candidates](
+                             mojom::blink::SpeculationAction action,
+                             const HeapVector<Member<SpeculationRule>>& rules) {
+    for (SpeculationRule* rule : rules) {
+      mojom::blink::SpeculationCandidate candidate;
+      candidate.action = action;
+      candidate.requires_anonymous_client_ip_when_cross_origin =
+          rule->requires_anonymous_client_ip_when_cross_origin();
+      for (const KURL& url : rule->urls()) {
+        candidate.url = url;
+        candidates.push_back(
+            mojom::blink::SpeculationCandidate::New(candidate));
+      }
+    }
+  };
+
+  for (SpeculationRuleSet* rule_set : rule_sets_) {
+    push_candidates(mojom::blink::SpeculationAction::kPrefetch,
+                    rule_set->prefetch_rules());
+    push_candidates(mojom::blink::SpeculationAction::kPrefetchWithSubresources,
+                    rule_set->prefetch_with_subresources_rules());
+  }
+
+  host->UpdateSpeculationCandidates_PendingSecurityReview(
+      std::move(candidates));
 }
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/speculation_rules/document_speculation_rules.h b/third_party/blink/renderer/core/speculation_rules/document_speculation_rules.h
index 60f861a..d4b4b19e 100644
--- a/third_party/blink/renderer/core/speculation_rules/document_speculation_rules.h
+++ b/third_party/blink/renderer/core/speculation_rules/document_speculation_rules.h
@@ -5,17 +5,21 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_CORE_SPECULATION_RULES_DOCUMENT_SPECULATION_RULES_H_
 #define THIRD_PARTY_BLINK_RENDERER_CORE_SPECULATION_RULES_DOCUMENT_SPECULATION_RULES_H_
 
+#include "third_party/blink/public/mojom/speculation_rules/speculation_rules.mojom-blink.h"
 #include "third_party/blink/renderer/core/core_export.h"
 #include "third_party/blink/renderer/core/dom/document.h"
 #include "third_party/blink/renderer/core/speculation_rules/speculation_rule_set.h"
 #include "third_party/blink/renderer/platform/heap/collection_support/heap_vector.h"
 #include "third_party/blink/renderer/platform/heap/garbage_collected.h"
 #include "third_party/blink/renderer/platform/heap/heap.h"
+#include "third_party/blink/renderer/platform/mojo/heap_mojo_remote.h"
 #include "third_party/blink/renderer/platform/supplementable.h"
 
 namespace blink {
 
 // This corresponds to the document's list of speculation rule sets.
+//
+// Updates are pushed asynchronously.
 class CORE_EXPORT DocumentSpeculationRules
     : public GarbageCollected<DocumentSpeculationRules>,
       public Supplement<Document> {
@@ -37,7 +41,16 @@
   void Trace(Visitor*) const override;
 
  private:
+  // Retrieves a valid proxy to the speculation host in the browser.
+  // May be null if the execution context does not exist.
+  mojom::blink::SpeculationHost* GetHost();
+
+  // Pushes the current speculation candidates to the browser, immediately.
+  void UpdateSpeculationCandidates();
+
   HeapVector<Member<SpeculationRuleSet>> rule_sets_;
+  HeapMojoRemote<mojom::blink::SpeculationHost> host_;
+  bool has_pending_update_ = false;
 };
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/speculation_rules/speculation_rule_set_test.cc b/third_party/blink/renderer/core/speculation_rules/speculation_rule_set_test.cc
index fc14dd9..80bbaca 100644
--- a/third_party/blink/renderer/core/speculation_rules/speculation_rule_set_test.cc
+++ b/third_party/blink/renderer/core/speculation_rules/speculation_rule_set_test.cc
@@ -1,7 +1,14 @@
 #include "third_party/blink/renderer/core/speculation_rules/speculation_rule_set.h"
 
+#include "base/bind.h"
+#include "mojo/public/cpp/bindings/pending_receiver.h"
+#include "mojo/public/cpp/bindings/receiver.h"
+#include "mojo/public/cpp/system/message_pipe.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/public/common/browser_interface_broker_proxy.h"
+#include "third_party/blink/public/mojom/speculation_rules/speculation_rules.mojom-blink.h"
+#include "third_party/blink/renderer/core/frame/local_dom_window.h"
 #include "third_party/blink/renderer/core/frame/local_frame.h"
 #include "third_party/blink/renderer/core/frame/settings.h"
 #include "third_party/blink/renderer/core/html/html_head_element.h"
@@ -9,6 +16,7 @@
 #include "third_party/blink/renderer/core/speculation_rules/document_speculation_rules.h"
 #include "third_party/blink/renderer/core/testing/dummy_page_holder.h"
 #include "third_party/blink/renderer/platform/testing/runtime_enabled_features_test_helpers.h"
+#include "third_party/blink/renderer/platform/wtf/vector.h"
 
 namespace blink {
 namespace {
@@ -228,5 +236,92 @@
               ElementsAre(MatchesListOfURLs("https://example.com/foo")));
 }
 
+class StubSpeculationHost : public mojom::blink::SpeculationHost {
+ public:
+  using Candidates = Vector<mojom::blink::SpeculationCandidatePtr>;
+
+  const Candidates& candidates() const { return candidates_; }
+  void SetDoneClosure(base::OnceClosure done) {
+    done_closure_ = std::move(done);
+  }
+
+  void BindUnsafe(mojo::ScopedMessagePipeHandle handle) {
+    Bind(mojo::PendingReceiver<SpeculationHost>(std::move(handle)));
+  }
+
+  void Bind(mojo::PendingReceiver<SpeculationHost> receiver) {
+    receiver_.Bind(std::move(receiver));
+    receiver_.set_disconnect_handler(base::BindOnce(
+        &StubSpeculationHost::OnConnectionLost, base::Unretained(this)));
+  }
+
+  void UpdateSpeculationCandidates_PendingSecurityReview(
+      Candidates candidates) override {
+    candidates_ = std::move(candidates);
+    if (done_closure_)
+      std::move(done_closure_).Run();
+  }
+
+  void OnConnectionLost() {
+    if (done_closure_)
+      std::move(done_closure_).Run();
+  }
+
+ private:
+  mojo::Receiver<SpeculationHost> receiver_{this};
+  Vector<mojom::blink::SpeculationCandidatePtr> candidates_;
+  base::OnceClosure done_closure_;
+};
+
+TEST_F(SpeculationRuleSetTest, PropagatesToBrowser) {
+  // A <script> with a case-insensitive type match should be propagated to the
+  // browser via Mojo.
+  // TODO(jbroman): Should we need to enable script? Should that be bypassed?
+  DummyPageHolder page_holder;
+  page_holder.GetFrame().GetSettings()->SetScriptEnabled(true);
+
+  StubSpeculationHost speculation_host;
+  page_holder.GetFrame()
+      .DomWindow()
+      ->GetBrowserInterfaceBroker()
+      .SetBinderForTesting(
+          mojom::blink::SpeculationHost::Name_,
+          WTF::BindRepeating(&StubSpeculationHost::BindUnsafe,
+                             WTF::Unretained(&speculation_host)));
+
+  base::RunLoop run_loop;
+  speculation_host.SetDoneClosure(run_loop.QuitClosure());
+
+  Document& document = page_holder.GetDocument();
+  HTMLScriptElement* script =
+      MakeGarbageCollected<HTMLScriptElement>(document, CreateElementFlags());
+  script->setAttribute(html_names::kTypeAttr, "SpEcUlAtIoNrUlEs");
+  script->setText(
+      R"({"prefetch_with_subresources": [
+           {"source": "list",
+            "urls": ["https://example.com/foo", "https://example.com/bar"],
+            "requires": ["anonymous-client-ip-when-cross-origin"]}
+         ]})");
+  document.head()->appendChild(script);
+  run_loop.Run();
+
+  const auto& candidates = speculation_host.candidates();
+  ASSERT_EQ(candidates.size(), 2u);
+  {
+    const auto& candidate = candidates[0];
+    EXPECT_EQ(candidate->action,
+              mojom::blink::SpeculationAction::kPrefetchWithSubresources);
+    EXPECT_EQ(candidate->url, "https://example.com/foo");
+    EXPECT_TRUE(candidate->requires_anonymous_client_ip_when_cross_origin);
+  }
+  {
+    const auto& candidate = candidates[1];
+    EXPECT_EQ(candidate->action,
+              mojom::blink::SpeculationAction::kPrefetchWithSubresources);
+    EXPECT_EQ(candidate->url, "https://example.com/bar");
+    EXPECT_TRUE(candidate->requires_anonymous_client_ip_when_cross_origin);
+  }
+}
+
 }  // namespace
 }  // namespace blink
diff --git a/third_party/blink/renderer/modules/accessibility/ax_object.cc b/third_party/blink/renderer/modules/accessibility/ax_object.cc
index 7a0d03c1..7b00990 100644
--- a/third_party/blink/renderer/modules/accessibility/ax_object.cc
+++ b/third_party/blink/renderer/modules/accessibility/ax_object.cc
@@ -1230,9 +1230,7 @@
       if (child_token && !(IsDetached() || ChildCountIncludingIgnored())) {
         ui::AXTreeID child_tree_id =
             ui::AXTreeID::FromToken(child_token.value());
-        node_data->AddStringAttribute(
-            ax::mojom::blink::StringAttribute::kChildTreeId,
-            child_tree_id.ToString());
+        node_data->AddChildTreeId(child_tree_id);
       }
     }
   }
diff --git a/third_party/blink/renderer/platform/runtime_enabled_features.json5 b/third_party/blink/renderer/platform/runtime_enabled_features.json5
index c9ba35b..970fb52 100644
--- a/third_party/blink/renderer/platform/runtime_enabled_features.json5
+++ b/third_party/blink/renderer/platform/runtime_enabled_features.json5
@@ -633,7 +633,7 @@
     },
     {
       name: "CSSPseudoDir",
-      status: "stable",
+      status: "experimental",
     },
     {
       name: "CSSPseudoIs",
diff --git a/third_party/blink/web_tests/css3/filters/backdrop-filter-clip-radius-zoom.html b/third_party/blink/web_tests/css3/filters/backdrop-filter-clip-radius-zoom.html
index 7cd3464..b694e342 100644
--- a/third_party/blink/web_tests/css3/filters/backdrop-filter-clip-radius-zoom.html
+++ b/third_party/blink/web_tests/css3/filters/backdrop-filter-clip-radius-zoom.html
@@ -1,6 +1,6 @@
 <!DOCTYPE html>
 <title>Backdrop filter clipping radius with zoom</title>
-<link rel="author" title="Mason Freed" href="mailto:masonfreed@chromium.org">
+<link rel="author" href="mailto:masonf@chromium.org">
 <link rel="help" href="https://drafts.fxtf.org/filter-effects-2/#BackdropFilterProperty">
 
 <!--You should see two completely filled black rectangles with rounded corners.-->
diff --git a/third_party/blink/web_tests/css3/filters/backdrop-filter-clip-rect-zoom.html b/third_party/blink/web_tests/css3/filters/backdrop-filter-clip-rect-zoom.html
index 645198b..f51eef0 100644
--- a/third_party/blink/web_tests/css3/filters/backdrop-filter-clip-rect-zoom.html
+++ b/third_party/blink/web_tests/css3/filters/backdrop-filter-clip-rect-zoom.html
@@ -2,7 +2,7 @@
 <html style="zoom:5">
 <meta charset="utf-8">
 <title>backdrop-filter: Clip the filter at border box of element</title>
-<link rel="author" title="Mason Freed" href="mailto:masonfreed@chromium.org">
+<link rel="author" href="mailto:masonf@chromium.org">
 <link rel="help" href="https://drafts.fxtf.org/filter-effects-2/#BackdropFilterProperty">
 <link rel="match"  href="backdrop-filter-clip-rect-zoom-ref.html">
 
diff --git a/third_party/blink/web_tests/css3/filters/backdrop-filter-edge-clipping-2.html b/third_party/blink/web_tests/css3/filters/backdrop-filter-edge-clipping-2.html
index dea3aeb..12fc3b7 100644
--- a/third_party/blink/web_tests/css3/filters/backdrop-filter-edge-clipping-2.html
+++ b/third_party/blink/web_tests/css3/filters/backdrop-filter-edge-clipping-2.html
@@ -1,7 +1,7 @@
 <!DOCTYPE html>
 <meta charset="utf-8">
 <title>backdrop-filter: Filter input is at element bounds</title>
-<link rel="author" title="Mason Freed" href="mailto:masonfreed@chromium.org">
+<link rel="author" href="mailto:masonf@chromium.org">
 <link rel="help" href="https://drafts.fxtf.org/filter-effects-2/#BackdropFilterProperty">
 <link rel="match"  href="backdrop-filter-edge-clipping-2-ref.html">
 
diff --git a/third_party/blink/web_tests/css3/filters/backdrop-filter-plus-mask-large.html b/third_party/blink/web_tests/css3/filters/backdrop-filter-plus-mask-large.html
index 2706b3d..03c0194 100644
--- a/third_party/blink/web_tests/css3/filters/backdrop-filter-plus-mask-large.html
+++ b/third_party/blink/web_tests/css3/filters/backdrop-filter-plus-mask-large.html
@@ -1,7 +1,7 @@
 <!DOCTYPE html>
 <meta charset="utf-8">
 <title>backdrop-filter: Correctly apply masks/clips to backdrop-filter content</title>
-<link rel="author" title="Mason Freed" href="mailto:masonfreed@chromium.org">
+<link rel="author" href="mailto:masonf@chromium.org">
 <link rel="help" href="https://drafts.fxtf.org/filter-effects-2/#BackdropFilterProperty">
 <link rel="match"  href="backdrop-filter-plus-mask-ref.html">
 
diff --git a/third_party/blink/web_tests/css3/filters/backdrop-filter-plus-mask.html b/third_party/blink/web_tests/css3/filters/backdrop-filter-plus-mask.html
index 9909d66f..f0d5359 100644
--- a/third_party/blink/web_tests/css3/filters/backdrop-filter-plus-mask.html
+++ b/third_party/blink/web_tests/css3/filters/backdrop-filter-plus-mask.html
@@ -1,7 +1,7 @@
 <!DOCTYPE html>
 <meta charset="utf-8">
 <title>backdrop-filter: Correctly apply masks/clips to backdrop-filter content</title>
-<link rel="author" title="Mason Freed" href="mailto:masonfreed@chromium.org">
+<link rel="author" href="mailto:masonf@chromium.org">
 <link rel="help" href="https://drafts.fxtf.org/filter-effects-2/#BackdropFilterProperty">
 <link rel="match"  href="backdrop-filter-plus-mask-ref.html">
 
diff --git a/third_party/blink/web_tests/dom/attr/id-update-map-crash.html b/third_party/blink/web_tests/dom/attr/id-update-map-crash.html
index 60ade88..48c0d6c 100644
--- a/third_party/blink/web_tests/dom/attr/id-update-map-crash.html
+++ b/third_party/blink/web_tests/dom/attr/id-update-map-crash.html
@@ -1,6 +1,6 @@
 <!DOCTYPE html>
 <meta charset="utf-8">
-<link rel="author" title="Mason Freed" href="mailto:masonfreed@chromium.org">
+<link rel="author" href="mailto:masonf@chromium.org">
 <link rel="help" href="https://crbug.com/1178302">
 <meta name="assert" content="The renderer should not crash.">
 
diff --git a/third_party/blink/web_tests/external/wpt/css/css-position/fixed-z-index-blend-ref.html b/third_party/blink/web_tests/external/wpt/css/css-position/fixed-z-index-blend-ref.html
index 2675401..864297b 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-position/fixed-z-index-blend-ref.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-position/fixed-z-index-blend-ref.html
@@ -1,6 +1,6 @@
 <!DOCTYPE html>
 <title>fixed position, z-index, and mix-blend-mode</title>
-<link rel="author" title="Mason Freed" href="mailto:masonfreed@chromium.org">
+<link rel="author" href="mailto:masonf@chromium.org">
 
 <div style="width: 100px; height:4000px;"></div>
 <div style="background: green; width: 100px; height:100px;"></div>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-position/fixed-z-index-blend.html b/third_party/blink/web_tests/external/wpt/css/css-position/fixed-z-index-blend.html
index 55582286..2b0988d 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-position/fixed-z-index-blend.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-position/fixed-z-index-blend.html
@@ -6,7 +6,7 @@
 <link rel="help" href="https://www.w3.org/TR/compositing-1/#mix-blend-mode">
 <meta name="assert" content="Tests fixed, z-index, and mix-blend-mode.
 Passes if there is a green box when the page is scrolled to the bottom.">
-<link rel="author" title="Mason Freed" href="mailto:masonfreed@chromium.org">
+<link rel="author" href="mailto:masonf@chromium.org">
 <link rel="match" href="fixed-z-index-blend-ref.html">
 
 <div class="blend"></div>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-position/sticky/position-sticky-scroll-with-clip-and-abspos-ref.html b/third_party/blink/web_tests/external/wpt/css/css-position/sticky/position-sticky-scroll-with-clip-and-abspos-ref.html
index a36934e38..d9fe863 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-position/sticky/position-sticky-scroll-with-clip-and-abspos-ref.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-position/sticky/position-sticky-scroll-with-clip-and-abspos-ref.html
@@ -2,7 +2,7 @@
 <html class="reftest-wait">
 <meta charset="utf-8">
 <title>position:sticky should operate correctly</title>
-<link rel="author" title="Mason Freed" href="mailto:masonfreed@chromium.org">
+<link rel="author" href="mailto:masonf@chromium.org">
 
 
 
diff --git a/third_party/blink/web_tests/external/wpt/css/css-position/sticky/position-sticky-scroll-with-clip-and-abspos.html b/third_party/blink/web_tests/external/wpt/css/css-position/sticky/position-sticky-scroll-with-clip-and-abspos.html
index 46085b4..fb7edfe 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-position/sticky/position-sticky-scroll-with-clip-and-abspos.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-position/sticky/position-sticky-scroll-with-clip-and-abspos.html
@@ -2,7 +2,7 @@
 <html class="reftest-wait">
 <meta charset="utf-8">
 <title>position:sticky should operate correctly</title>
-<link rel="author" title="Mason Freed" href="mailto:masonfreed@chromium.org">
+<link rel="author" href="mailto:masonf@chromium.org">
 <link rel="help" href="https://www.w3.org/TR/css-position-3/#sticky-pos" />
 <meta name="assert" content="This test checks that the combination of position:sticky, overflow clip, and out-of-flow descendants are properly displayed when scrolled" />
 <link rel="match"  href="position-sticky-scroll-with-clip-and-abspos-ref.html">
diff --git a/third_party/blink/web_tests/external/wpt/css/css-sizing/button-min-width.html b/third_party/blink/web_tests/external/wpt/css/css-sizing/button-min-width.html
index d27d0e1..8f780ec 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-sizing/button-min-width.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-sizing/button-min-width.html
@@ -2,7 +2,7 @@
 <html>
 <meta charset="utf-8">
 <title>min-width: Should apply to buttons</title>
-<link rel="author" title="Mason Freed" href="mailto:masonfreed@chromium.org">
+<link rel="author" href="mailto:masonf@chromium.org">
 <link rel="help" href="https://www.w3.org/TR/CSS2/visudet.html#min-max-widths">
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
diff --git a/third_party/blink/web_tests/external/wpt/css/cssom-view/long_scroll_composited-ref.html b/third_party/blink/web_tests/external/wpt/css/cssom-view/long_scroll_composited-ref.html
index 6914cba3..3278d33 100644
--- a/third_party/blink/web_tests/external/wpt/css/cssom-view/long_scroll_composited-ref.html
+++ b/third_party/blink/web_tests/external/wpt/css/cssom-view/long_scroll_composited-ref.html
@@ -1,7 +1,7 @@
 <!DOCTYPE html>
 <meta charset="utf-8">
 <title>Long scrolling should work properly</title>
-<link rel="author" title="Mason Freed" href="mailto:masonfreed@chromium.org">
+<link rel="author" href="mailto:masonf@chromium.org">
 
 
 
diff --git a/third_party/blink/web_tests/external/wpt/css/cssom-view/long_scroll_composited.html b/third_party/blink/web_tests/external/wpt/css/cssom-view/long_scroll_composited.html
index aa91023..68a468b 100644
--- a/third_party/blink/web_tests/external/wpt/css/cssom-view/long_scroll_composited.html
+++ b/third_party/blink/web_tests/external/wpt/css/cssom-view/long_scroll_composited.html
@@ -1,7 +1,7 @@
 <!DOCTYPE html>
 <meta charset="utf-8">
 <title>Long scrolling should work properly</title>
-<link rel="author" title="Mason Freed" href="mailto:masonfreed@chromium.org">
+<link rel="author" href="mailto:masonf@chromium.org">
 <link rel="help" href="https://www.w3.org/TR/cssom-view/#scrolling">
 <link rel="match" href="long_scroll_composited-ref.html">
 
diff --git a/third_party/blink/web_tests/external/wpt/css/filter-effects/backdrop-filter-basic-background-color-ref.html b/third_party/blink/web_tests/external/wpt/css/filter-effects/backdrop-filter-basic-background-color-ref.html
index a3dbad0b..4997007f 100644
--- a/third_party/blink/web_tests/external/wpt/css/filter-effects/backdrop-filter-basic-background-color-ref.html
+++ b/third_party/blink/web_tests/external/wpt/css/filter-effects/backdrop-filter-basic-background-color-ref.html
@@ -1,7 +1,7 @@
 <!DOCTYPE html>
 <meta charset="utf-8">
 <title>backdrop-filter: Basic operation of filter with background color</title>
-<link rel="author" title="Mason Freed" href="mailto:masonfreed@chromium.org">
+<link rel="author" href="mailto:masonf@chromium.org">
 
 
 
diff --git a/third_party/blink/web_tests/external/wpt/css/filter-effects/backdrop-filter-basic-background-color.html b/third_party/blink/web_tests/external/wpt/css/filter-effects/backdrop-filter-basic-background-color.html
index c48cade..2623c11 100644
--- a/third_party/blink/web_tests/external/wpt/css/filter-effects/backdrop-filter-basic-background-color.html
+++ b/third_party/blink/web_tests/external/wpt/css/filter-effects/backdrop-filter-basic-background-color.html
@@ -1,7 +1,7 @@
 <!DOCTYPE html>
 <meta charset="utf-8">
 <title>backdrop-filter: Basic operation of filter with background color</title>
-<link rel="author" title="Mason Freed" href="mailto:masonfreed@chromium.org">
+<link rel="author" href="mailto:masonf@chromium.org">
 <link rel="help" href="https://drafts.fxtf.org/filter-effects-2/#BackdropFilterProperty">
 <link rel="match"  href="backdrop-filter-basic-background-color-ref.html">
 
diff --git a/third_party/blink/web_tests/external/wpt/css/filter-effects/backdrop-filter-basic-opacity-2-ref.html b/third_party/blink/web_tests/external/wpt/css/filter-effects/backdrop-filter-basic-opacity-2-ref.html
index 1027811..3a27cf3 100644
--- a/third_party/blink/web_tests/external/wpt/css/filter-effects/backdrop-filter-basic-opacity-2-ref.html
+++ b/third_party/blink/web_tests/external/wpt/css/filter-effects/backdrop-filter-basic-opacity-2-ref.html
@@ -1,7 +1,7 @@
 <!DOCTYPE html>
 <meta charset="utf-8">
 <title>backdrop-filter: Basic operation of filter with opacity</title>
-<link rel="author" title="Mason Freed" href="mailto:masonfreed@chromium.org">
+<link rel="author" href="mailto:masonf@chromium.org">
 
 
 
diff --git a/third_party/blink/web_tests/external/wpt/css/filter-effects/backdrop-filter-basic-opacity-2.html b/third_party/blink/web_tests/external/wpt/css/filter-effects/backdrop-filter-basic-opacity-2.html
index 8d7df67..c40e8bc0 100644
--- a/third_party/blink/web_tests/external/wpt/css/filter-effects/backdrop-filter-basic-opacity-2.html
+++ b/third_party/blink/web_tests/external/wpt/css/filter-effects/backdrop-filter-basic-opacity-2.html
@@ -1,7 +1,7 @@
 <!DOCTYPE html>
 <meta charset="utf-8">
 <title>backdrop-filter: Basic operation of filter with opacity</title>
-<link rel="author" title="Mason Freed" href="mailto:masonfreed@chromium.org">
+<link rel="author" href="mailto:masonf@chromium.org">
 <link rel="help" href="https://drafts.fxtf.org/filter-effects-2/#BackdropFilterProperty">
 <link rel="match"  href="backdrop-filter-basic-opacity-2-ref.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 e0c76c0..7e6d45c 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
@@ -1,7 +1,7 @@
 <!DOCTYPE html>
 <meta charset="utf-8">
 <title>backdrop-filter: Basic operation of filter with opacity</title>
-<link rel="author" title="Mason Freed" href="mailto:masonfreed@chromium.org">
+<link rel="author" href="mailto:masonf@chromium.org">
 
 
 
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 4cc5f33..52662f3 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
@@ -1,7 +1,7 @@
 <!DOCTYPE html>
 <meta charset="utf-8">
 <title>backdrop-filter: Basic operation of filter with opacity</title>
-<link rel="author" title="Mason Freed" href="mailto:masonfreed@chromium.org">
+<link rel="author" href="mailto:masonf@chromium.org">
 <link rel="help" href="https://drafts.fxtf.org/filter-effects-2/#BackdropFilterProperty">
 <link rel="match"  href="backdrop-filter-basic-opacity-ref.html">
 
diff --git a/third_party/blink/web_tests/external/wpt/css/filter-effects/backdrop-filter-basic-ref.html b/third_party/blink/web_tests/external/wpt/css/filter-effects/backdrop-filter-basic-ref.html
index 049e7c8..71e42e5 100644
--- a/third_party/blink/web_tests/external/wpt/css/filter-effects/backdrop-filter-basic-ref.html
+++ b/third_party/blink/web_tests/external/wpt/css/filter-effects/backdrop-filter-basic-ref.html
@@ -1,7 +1,7 @@
 <!DOCTYPE html>
 <meta charset="utf-8">
 <title>backdrop-filter: Basic operation of filter</title>
-<link rel="author" title="Mason Freed" href="mailto:masonfreed@chromium.org">
+<link rel="author" href="mailto:masonf@chromium.org">
 
 
 
diff --git a/third_party/blink/web_tests/external/wpt/css/filter-effects/backdrop-filter-basic.html b/third_party/blink/web_tests/external/wpt/css/filter-effects/backdrop-filter-basic.html
index 711064e..d5d170d 100644
--- a/third_party/blink/web_tests/external/wpt/css/filter-effects/backdrop-filter-basic.html
+++ b/third_party/blink/web_tests/external/wpt/css/filter-effects/backdrop-filter-basic.html
@@ -1,7 +1,7 @@
 <!DOCTYPE html>
 <meta charset="utf-8">
 <title>backdrop-filter: Basic operation of filter</title>
-<link rel="author" title="Mason Freed" href="mailto:masonfreed@chromium.org">
+<link rel="author" href="mailto:masonf@chromium.org">
 <link rel="help" href="https://drafts.fxtf.org/filter-effects-2/#BackdropFilterProperty">
 <link rel="match"  href="backdrop-filter-basic-ref.html">
 
diff --git a/third_party/blink/web_tests/external/wpt/css/filter-effects/backdrop-filter-clip-rect-ref.html b/third_party/blink/web_tests/external/wpt/css/filter-effects/backdrop-filter-clip-rect-ref.html
index 9082fa3..d1e785ec 100644
--- a/third_party/blink/web_tests/external/wpt/css/filter-effects/backdrop-filter-clip-rect-ref.html
+++ b/third_party/blink/web_tests/external/wpt/css/filter-effects/backdrop-filter-clip-rect-ref.html
@@ -1,7 +1,7 @@
 <!DOCTYPE html>
 <meta charset="utf-8">
 <title>backdrop-filter: Clip the filter at border box of element</title>
-<link rel="author" title="Mason Freed" href="mailto:masonfreed@chromium.org">
+<link rel="author" href="mailto:masonf@chromium.org">
 
 
 
diff --git a/third_party/blink/web_tests/external/wpt/css/filter-effects/backdrop-filter-clip-rect.html b/third_party/blink/web_tests/external/wpt/css/filter-effects/backdrop-filter-clip-rect.html
index 4fc4b73..5d5c7071 100644
--- a/third_party/blink/web_tests/external/wpt/css/filter-effects/backdrop-filter-clip-rect.html
+++ b/third_party/blink/web_tests/external/wpt/css/filter-effects/backdrop-filter-clip-rect.html
@@ -1,7 +1,7 @@
 <!DOCTYPE html>
 <meta charset="utf-8">
 <title>backdrop-filter: Clip the filter at border box of element</title>
-<link rel="author" title="Mason Freed" href="mailto:masonfreed@chromium.org">
+<link rel="author" href="mailto:masonf@chromium.org">
 <link rel="help" href="https://drafts.fxtf.org/filter-effects-2/#BackdropFilterProperty">
 <link rel="match"  href="backdrop-filter-clip-rect-ref.html">
 
diff --git a/third_party/blink/web_tests/external/wpt/css/filter-effects/backdrop-filter-clipped-ref.html b/third_party/blink/web_tests/external/wpt/css/filter-effects/backdrop-filter-clipped-ref.html
index 60b0b6c..5100d6a6 100644
--- a/third_party/blink/web_tests/external/wpt/css/filter-effects/backdrop-filter-clipped-ref.html
+++ b/third_party/blink/web_tests/external/wpt/css/filter-effects/backdrop-filter-clipped-ref.html
@@ -1,7 +1,7 @@
 <!DOCTYPE html>
 <meta charset="utf-8">
 <title>backdrop-filter: Basic operation of filter</title>
-<link rel="author" title="Mason Freed" href="mailto:masonfreed@chromium.org">
+<link rel="author" href="mailto:masonf@chromium.org">
 
 
 
diff --git a/third_party/blink/web_tests/external/wpt/css/filter-effects/backdrop-filter-clipped.html b/third_party/blink/web_tests/external/wpt/css/filter-effects/backdrop-filter-clipped.html
index bc25b259..b5535a8 100644
--- a/third_party/blink/web_tests/external/wpt/css/filter-effects/backdrop-filter-clipped.html
+++ b/third_party/blink/web_tests/external/wpt/css/filter-effects/backdrop-filter-clipped.html
@@ -1,7 +1,7 @@
 <!DOCTYPE html>
 <meta charset="utf-8">
 <title>backdrop-filter: A rounded-corner clip rect parent should not form a Backdrop Root</title>
-<link rel="author" title="Mason Freed" href="mailto:masonfreed@chromium.org">
+<link rel="author" href="mailto:masonf@chromium.org">
 <link rel="help" href="https://drafts.fxtf.org/filter-effects-2/#BackdropFilterProperty">
 <link rel="match"  href="backdrop-filter-clipped-ref.html">
 
diff --git a/third_party/blink/web_tests/external/wpt/css/filter-effects/backdrop-filter-containing-block-ref.html b/third_party/blink/web_tests/external/wpt/css/filter-effects/backdrop-filter-containing-block-ref.html
index f429dd9a..389802b 100644
--- a/third_party/blink/web_tests/external/wpt/css/filter-effects/backdrop-filter-containing-block-ref.html
+++ b/third_party/blink/web_tests/external/wpt/css/filter-effects/backdrop-filter-containing-block-ref.html
@@ -1,7 +1,7 @@
 <!DOCTYPE html>
 <meta charset="utf-8">
 <title>backdrop-filter: Forms a containing block for fixed/absolute</title>
-<link rel="author" title="Mason Freed" href="mailto:masonfreed@chromium.org">
+<link rel="author" href="mailto:masonf@chromium.org">
 
 
 
diff --git a/third_party/blink/web_tests/external/wpt/css/filter-effects/backdrop-filter-containing-block.html b/third_party/blink/web_tests/external/wpt/css/filter-effects/backdrop-filter-containing-block.html
index 7300d7d..364405c 100644
--- a/third_party/blink/web_tests/external/wpt/css/filter-effects/backdrop-filter-containing-block.html
+++ b/third_party/blink/web_tests/external/wpt/css/filter-effects/backdrop-filter-containing-block.html
@@ -1,7 +1,7 @@
 <!DOCTYPE html>
 <meta charset="utf-8">
 <title>backdrop-filter: Forms a containing block for fixed/absolute</title>
-<link rel="author" title="Mason Freed" href="mailto:masonfreed@chromium.org">
+<link rel="author" href="mailto:masonf@chromium.org">
 <link rel="help" href="https://drafts.fxtf.org/filter-effects-2/#BackdropFilterProperty">
 <link rel="match"  href="backdrop-filter-containing-block-ref.html">
 
diff --git a/third_party/blink/web_tests/external/wpt/css/filter-effects/backdrop-filter-edge-behavior-ref.html b/third_party/blink/web_tests/external/wpt/css/filter-effects/backdrop-filter-edge-behavior-ref.html
index 3b8a31c..b605e3251 100644
--- a/third_party/blink/web_tests/external/wpt/css/filter-effects/backdrop-filter-edge-behavior-ref.html
+++ b/third_party/blink/web_tests/external/wpt/css/filter-effects/backdrop-filter-edge-behavior-ref.html
@@ -1,7 +1,7 @@
 <!DOCTYPE html>
 <meta charset="utf-8">
 <title>backdrop-filter: Edge behavior</title>
-<link rel="author" title="Mason Freed" href="mailto:masonfreed@chromium.org">
+<link rel="author" href="mailto:masonf@chromium.org">
 
 
 
diff --git a/third_party/blink/web_tests/external/wpt/css/filter-effects/backdrop-filter-edge-behavior.html b/third_party/blink/web_tests/external/wpt/css/filter-effects/backdrop-filter-edge-behavior.html
index 9c279c8..29f5dc51 100644
--- a/third_party/blink/web_tests/external/wpt/css/filter-effects/backdrop-filter-edge-behavior.html
+++ b/third_party/blink/web_tests/external/wpt/css/filter-effects/backdrop-filter-edge-behavior.html
@@ -1,7 +1,7 @@
 <!DOCTYPE html>
 <meta charset="utf-8">
 <title>backdrop-filter: Edge behavior</title>
-<link rel="author" title="Mason Freed" href="mailto:masonfreed@chromium.org">
+<link rel="author" href="mailto:masonf@chromium.org">
 <link rel="help" href="https://drafts.fxtf.org/filter-effects-2/#BackdropFilterProperty">
 <link rel="help" href="https://crbug.com/1165868">
 <link rel="match"  href="backdrop-filter-edge-behavior-ref.html">
diff --git a/third_party/blink/web_tests/external/wpt/css/filter-effects/backdrop-filter-edge-clipping.html b/third_party/blink/web_tests/external/wpt/css/filter-effects/backdrop-filter-edge-clipping.html
index 5d09c414..f6f7d89 100644
--- a/third_party/blink/web_tests/external/wpt/css/filter-effects/backdrop-filter-edge-clipping.html
+++ b/third_party/blink/web_tests/external/wpt/css/filter-effects/backdrop-filter-edge-clipping.html
@@ -1,7 +1,7 @@
 <!DOCTYPE html>
 <meta charset="utf-8">
 <title>backdrop-filter: Filter input is at element bounds</title>
-<link rel="author" title="Mason Freed" href="mailto:masonfreed@chromium.org">
+<link rel="author" href="mailto:masonf@chromium.org">
 <link rel="help" href="https://drafts.fxtf.org/filter-effects-2/#BackdropFilterProperty">
 <link rel="match"  href="backdrop-filter-paint-order-ref.html">
 
diff --git a/third_party/blink/web_tests/external/wpt/css/filter-effects/backdrop-filter-edge-pixels-ref.html b/third_party/blink/web_tests/external/wpt/css/filter-effects/backdrop-filter-edge-pixels-ref.html
index bbd5628..36c509ff 100644
--- a/third_party/blink/web_tests/external/wpt/css/filter-effects/backdrop-filter-edge-pixels-ref.html
+++ b/third_party/blink/web_tests/external/wpt/css/filter-effects/backdrop-filter-edge-pixels-ref.html
@@ -1,7 +1,7 @@
 <!DOCTYPE html>
 <meta charset="utf-8">
 <title>backdrop-filter: Edge pixels shouldn't get filtered</title>
-<link rel="author" title="Mason Freed" href="mailto:masonfreed@chromium.org">
+<link rel="author" href="mailto:masonf@chromium.org">
 
 
 
diff --git a/third_party/blink/web_tests/external/wpt/css/filter-effects/backdrop-filter-edge-pixels.html b/third_party/blink/web_tests/external/wpt/css/filter-effects/backdrop-filter-edge-pixels.html
index c26e70c..5a70020 100644
--- a/third_party/blink/web_tests/external/wpt/css/filter-effects/backdrop-filter-edge-pixels.html
+++ b/third_party/blink/web_tests/external/wpt/css/filter-effects/backdrop-filter-edge-pixels.html
@@ -1,7 +1,7 @@
 <!DOCTYPE html>
 <meta charset="utf-8">
 <title>backdrop-filter: Edge pixels shouldn't get filtered</title>
-<link rel="author" title="Mason Freed" href="mailto:masonfreed@chromium.org">
+<link rel="author" href="mailto:masonf@chromium.org">
 <link rel="help" href="https://drafts.fxtf.org/filter-effects-2/#BackdropFilterProperty">
 <link rel="match"  href="backdrop-filter-edge-pixels-ref.html">
 
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
index 1fa5967..38a8c8b 100644
--- 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
@@ -1,7 +1,7 @@
 <!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="author" href="mailto:masonf@chromium.org">
 
 
 
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
index 2550eb1..346fad9 100644
--- 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
@@ -1,7 +1,7 @@
 <!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="author" href="mailto:masonf@chromium.org">
 <link rel="help" href="https://drafts.fxtf.org/filter-effects-2/#BackdropFilterProperty">
 <link rel="match"  href="backdrop-filter-fixed-clip-ref.html">
 
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
index f7835a1..9954ecb 100644
--- 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
@@ -1,7 +1,7 @@
 <!DOCTYPE html>
 <meta charset="utf-8">
 <title>backdrop-filter: fixed position should not cause a backdrop root.</title>
-<link rel="author" title="Mason Freed" href="mailto:masonfreed@chromium.org">
+<link rel="author" href="mailto:masonf@chromium.org">
 <link rel="help" href="https://drafts.fxtf.org/filter-effects-2/#BackdropFilterProperty">
 <link rel="match"  href="backdrop-filter-non-isolation-ref.html">
 
diff --git a/third_party/blink/web_tests/external/wpt/css/filter-effects/backdrop-filter-isolation-isolate.html b/third_party/blink/web_tests/external/wpt/css/filter-effects/backdrop-filter-isolation-isolate.html
index 008f6f8..97bb7fe2 100644
--- a/third_party/blink/web_tests/external/wpt/css/filter-effects/backdrop-filter-isolation-isolate.html
+++ b/third_party/blink/web_tests/external/wpt/css/filter-effects/backdrop-filter-isolation-isolate.html
@@ -1,7 +1,7 @@
 <!DOCTYPE html>
 <meta charset="utf-8">
 <title>backdrop-filter: isolation isolate should not cause a backdrop root.</title>
-<link rel="author" title="Mason Freed" href="mailto:masonfreed@chromium.org">
+<link rel="author" href="mailto:masonf@chromium.org">
 <link rel="help" href="https://drafts.fxtf.org/filter-effects-2/#BackdropFilterProperty">
 <link rel="match"  href="backdrop-filter-non-isolation-ref.html">
 
diff --git a/third_party/blink/web_tests/external/wpt/css/filter-effects/backdrop-filter-isolation-ref.html b/third_party/blink/web_tests/external/wpt/css/filter-effects/backdrop-filter-isolation-ref.html
index 470704a6..fc2bccc3 100644
--- a/third_party/blink/web_tests/external/wpt/css/filter-effects/backdrop-filter-isolation-ref.html
+++ b/third_party/blink/web_tests/external/wpt/css/filter-effects/backdrop-filter-isolation-ref.html
@@ -1,7 +1,7 @@
 <!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="author" href="mailto:masonf@chromium.org">
 
 
 
diff --git a/third_party/blink/web_tests/external/wpt/css/filter-effects/backdrop-filter-isolation.html b/third_party/blink/web_tests/external/wpt/css/filter-effects/backdrop-filter-isolation.html
index 500228d..06791f7 100644
--- a/third_party/blink/web_tests/external/wpt/css/filter-effects/backdrop-filter-isolation.html
+++ b/third_party/blink/web_tests/external/wpt/css/filter-effects/backdrop-filter-isolation.html
@@ -1,7 +1,7 @@
 <!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="author" href="mailto:masonf@chromium.org">
 <link rel="help" href="https://drafts.fxtf.org/filter-effects-2/#BackdropFilterProperty">
 <link rel="match"  href="backdrop-filter-isolation-ref.html">
 
diff --git a/third_party/blink/web_tests/external/wpt/css/filter-effects/backdrop-filter-non-isolation-ref.html b/third_party/blink/web_tests/external/wpt/css/filter-effects/backdrop-filter-non-isolation-ref.html
index 0453d7f..229d6b4f 100644
--- a/third_party/blink/web_tests/external/wpt/css/filter-effects/backdrop-filter-non-isolation-ref.html
+++ b/third_party/blink/web_tests/external/wpt/css/filter-effects/backdrop-filter-non-isolation-ref.html
@@ -1,7 +1,7 @@
 <!DOCTYPE html>
 <meta charset="utf-8">
 <title>backdrop-filter: Isolation</title>
-<link rel="author" title="Mason Freed" href="mailto:masonfreed@chromium.org">
+<link rel="author" href="mailto:masonf@chromium.org">
 
 
 
diff --git a/third_party/blink/web_tests/external/wpt/css/filter-effects/backdrop-filter-paint-order-ref.html b/third_party/blink/web_tests/external/wpt/css/filter-effects/backdrop-filter-paint-order-ref.html
index bd82e520..78b135b 100644
--- a/third_party/blink/web_tests/external/wpt/css/filter-effects/backdrop-filter-paint-order-ref.html
+++ b/third_party/blink/web_tests/external/wpt/css/filter-effects/backdrop-filter-paint-order-ref.html
@@ -1,7 +1,7 @@
 <!DOCTYPE html>
 <meta charset="utf-8">
 <title>backdrop-filter: Only filter objects painted before</title>
-<link rel="author" title="Mason Freed" href="mailto:masonfreed@chromium.org">
+<link rel="author" href="mailto:masonf@chromium.org">
 
 
 
diff --git a/third_party/blink/web_tests/external/wpt/css/filter-effects/backdrop-filter-paint-order.html b/third_party/blink/web_tests/external/wpt/css/filter-effects/backdrop-filter-paint-order.html
index 8853153..b5499d47 100644
--- a/third_party/blink/web_tests/external/wpt/css/filter-effects/backdrop-filter-paint-order.html
+++ b/third_party/blink/web_tests/external/wpt/css/filter-effects/backdrop-filter-paint-order.html
@@ -1,7 +1,7 @@
 <!DOCTYPE html>
 <meta charset="utf-8">
 <title>backdrop-filter: Only filter objects painted before</title>
-<link rel="author" title="Mason Freed" href="mailto:masonfreed@chromium.org">
+<link rel="author" href="mailto:masonf@chromium.org">
 <link rel="help" href="https://drafts.fxtf.org/filter-effects-2/#BackdropFilterProperty">
 <link rel="match"  href="backdrop-filter-paint-order-ref.html">
 
diff --git a/third_party/blink/web_tests/external/wpt/css/filter-effects/backdrop-filter-plus-filter-ref.html b/third_party/blink/web_tests/external/wpt/css/filter-effects/backdrop-filter-plus-filter-ref.html
index bf476ea..aa421fd98 100644
--- a/third_party/blink/web_tests/external/wpt/css/filter-effects/backdrop-filter-plus-filter-ref.html
+++ b/third_party/blink/web_tests/external/wpt/css/filter-effects/backdrop-filter-plus-filter-ref.html
@@ -1,7 +1,7 @@
 <!DOCTYPE html>
 <meta charset="utf-8">
 <title>backdrop-filter: Only filter objects painted before</title>
-<link rel="author" title="Mason Freed" href="mailto:masonfreed@chromium.org">
+<link rel="author" href="mailto:masonf@chromium.org">
 
 
 
diff --git a/third_party/blink/web_tests/external/wpt/css/filter-effects/backdrop-filter-plus-filter.html b/third_party/blink/web_tests/external/wpt/css/filter-effects/backdrop-filter-plus-filter.html
index 3a2d8fe..546786d8 100644
--- a/third_party/blink/web_tests/external/wpt/css/filter-effects/backdrop-filter-plus-filter.html
+++ b/third_party/blink/web_tests/external/wpt/css/filter-effects/backdrop-filter-plus-filter.html
@@ -1,7 +1,7 @@
 <!DOCTYPE html>
 <meta charset="utf-8">
 <title>backdrop-filter: Correctly apply filters to backdrop-filter content</title>
-<link rel="author" title="Mason Freed" href="mailto:masonfreed@chromium.org">
+<link rel="author" href="mailto:masonf@chromium.org">
 <link rel="help" href="https://drafts.fxtf.org/filter-effects-2/#BackdropFilterProperty">
 <link rel="match"  href="backdrop-filter-plus-filter-ref.html">
 
diff --git a/third_party/blink/web_tests/external/wpt/css/filter-effects/backdrop-filter-plus-opacity-ref.html b/third_party/blink/web_tests/external/wpt/css/filter-effects/backdrop-filter-plus-opacity-ref.html
index 347b7a6..aa7224fd 100644
--- a/third_party/blink/web_tests/external/wpt/css/filter-effects/backdrop-filter-plus-opacity-ref.html
+++ b/third_party/blink/web_tests/external/wpt/css/filter-effects/backdrop-filter-plus-opacity-ref.html
@@ -1,7 +1,7 @@
 <!DOCTYPE html>
 <meta charset="utf-8">
 <title>backdrop-filter: Correctly apply backdrop-filter with opacity</title>
-<link rel="author" title="Mason Freed" href="mailto:masonfreed@chromium.org">
+<link rel="author" href="mailto:masonf@chromium.org">
 
 
 
diff --git a/third_party/blink/web_tests/external/wpt/css/filter-effects/backdrop-filter-plus-opacity.html b/third_party/blink/web_tests/external/wpt/css/filter-effects/backdrop-filter-plus-opacity.html
index cb189f9..4909e59 100644
--- a/third_party/blink/web_tests/external/wpt/css/filter-effects/backdrop-filter-plus-opacity.html
+++ b/third_party/blink/web_tests/external/wpt/css/filter-effects/backdrop-filter-plus-opacity.html
@@ -1,7 +1,7 @@
 <!DOCTYPE html>
 <meta charset="utf-8">
 <title>backdrop-filter: Correctly apply backdrop-filter with opacity</title>
-<link rel="author" title="Mason Freed" href="mailto:masonfreed@chromium.org">
+<link rel="author" href="mailto:masonf@chromium.org">
 <link rel="help" href="https://drafts.fxtf.org/filter-effects-2/#BackdropFilterProperty">
 <link rel="match"  href="backdrop-filter-plus-opacity-ref.html">
 
diff --git a/third_party/blink/web_tests/external/wpt/css/filter-effects/backdrop-filter-reference-filter.html b/third_party/blink/web_tests/external/wpt/css/filter-effects/backdrop-filter-reference-filter.html
index 6c61a96..60a51d67 100644
--- a/third_party/blink/web_tests/external/wpt/css/filter-effects/backdrop-filter-reference-filter.html
+++ b/third_party/blink/web_tests/external/wpt/css/filter-effects/backdrop-filter-reference-filter.html
@@ -1,7 +1,7 @@
 <!DOCTYPE html>
 <meta charset="utf-8">
 <title>backdrop-filter: Correctly apply reference filter</title>
-<link rel="author" title="Mason Freed" href="mailto:masonfreed@chromium.org">
+<link rel="author" href="mailto:masonf@chromium.org">
 <link rel="help" href="https://drafts.fxtf.org/filter-effects-2/#BackdropFilterProperty">
 <link rel="match"  href="backdrop-filter-clip-rect-ref.html">
 
diff --git a/third_party/blink/web_tests/external/wpt/css/filter-effects/backdrop-filter-update-ref.html b/third_party/blink/web_tests/external/wpt/css/filter-effects/backdrop-filter-update-ref.html
index ec4d806..6e719b6 100644
--- a/third_party/blink/web_tests/external/wpt/css/filter-effects/backdrop-filter-update-ref.html
+++ b/third_party/blink/web_tests/external/wpt/css/filter-effects/backdrop-filter-update-ref.html
@@ -1,7 +1,7 @@
 <!DOCTYPE html>
 <meta charset="utf-8">
 <title>backdrop-filter: Update filter value</title>
-<link rel="author" title="Mason Freed" href="mailto:masonfreed@chromium.org">
+<link rel="author" href="mailto:masonf@chromium.org">
 
 
 
diff --git a/third_party/blink/web_tests/external/wpt/css/filter-effects/backdrop-filter-update.html b/third_party/blink/web_tests/external/wpt/css/filter-effects/backdrop-filter-update.html
index de629a1..6a81fc6 100644
--- a/third_party/blink/web_tests/external/wpt/css/filter-effects/backdrop-filter-update.html
+++ b/third_party/blink/web_tests/external/wpt/css/filter-effects/backdrop-filter-update.html
@@ -2,7 +2,7 @@
 <html class="reftest-wait">
 <meta charset="utf-8">
 <title>backdrop-filter: Update filter value</title>
-<link rel="author" title="Mason Freed" href="mailto:masonfreed@chromium.org">
+<link rel="author" href="mailto:masonf@chromium.org">
 <link rel="help" href="https://drafts.fxtf.org/filter-effects-2/#BackdropFilterProperty">
 <link rel="match"  href="backdrop-filter-update-ref.html">
 
diff --git a/third_party/blink/web_tests/external/wpt/css/filter-effects/backdrop-filter-zero-size-ref.html b/third_party/blink/web_tests/external/wpt/css/filter-effects/backdrop-filter-zero-size-ref.html
index 05383cd..e8f5ef7 100644
--- a/third_party/blink/web_tests/external/wpt/css/filter-effects/backdrop-filter-zero-size-ref.html
+++ b/third_party/blink/web_tests/external/wpt/css/filter-effects/backdrop-filter-zero-size-ref.html
@@ -1,7 +1,7 @@
 <!DOCTYPE html>
 <meta charset="utf-8">
 <title>backdrop-filter: Zero-size div with backdrop filter shouldn't filter anything</title>
-<link rel="author" title="Mason Freed" href="mailto:masonfreed@chromium.org">
+<link rel="author" href="mailto:masonf@chromium.org">
 
 
 
diff --git a/third_party/blink/web_tests/external/wpt/css/filter-effects/backdrop-filter-zero-size.html b/third_party/blink/web_tests/external/wpt/css/filter-effects/backdrop-filter-zero-size.html
index bcefe042..ecd7974 100644
--- a/third_party/blink/web_tests/external/wpt/css/filter-effects/backdrop-filter-zero-size.html
+++ b/third_party/blink/web_tests/external/wpt/css/filter-effects/backdrop-filter-zero-size.html
@@ -1,7 +1,7 @@
 <!DOCTYPE html>
 <meta charset="utf-8">
 <title>backdrop-filter: Zero-size div with backdrop filter shouldn't filter anything</title>
-<link rel="author" title="Mason Freed" href="mailto:masonfreed@chromium.org">
+<link rel="author" href="mailto:masonf@chromium.org">
 <link rel="help" href="https://drafts.fxtf.org/filter-effects-2/#BackdropFilterProperty">
 <link rel="match"  href="backdrop-filter-zero-size-ref.html">
 
diff --git a/third_party/blink/web_tests/external/wpt/css/filter-effects/backdrop-filters-brightness-ref.html b/third_party/blink/web_tests/external/wpt/css/filter-effects/backdrop-filters-brightness-ref.html
index cb6ea67..999894eb 100644
--- a/third_party/blink/web_tests/external/wpt/css/filter-effects/backdrop-filters-brightness-ref.html
+++ b/third_party/blink/web_tests/external/wpt/css/filter-effects/backdrop-filters-brightness-ref.html
@@ -1,6 +1,6 @@
 <!DOCTYPE html>
 <title>CSS Backdrop Filters Animation: Brightness</title>
-<link rel="author" title="Mason Freed" href="mailto:masonfreed@chromium.org">
+<link rel="author" href="mailto:masonf@chromium.org">
 
 
 <style>
diff --git a/third_party/blink/web_tests/external/wpt/css/filter-effects/backdrop-filters-brightness.html b/third_party/blink/web_tests/external/wpt/css/filter-effects/backdrop-filters-brightness.html
index 1cc63ed..dfb8ba2 100644
--- a/third_party/blink/web_tests/external/wpt/css/filter-effects/backdrop-filters-brightness.html
+++ b/third_party/blink/web_tests/external/wpt/css/filter-effects/backdrop-filters-brightness.html
@@ -1,6 +1,6 @@
 <!DOCTYPE html>
 <title>CSS Backdrop Filters Animation: Brightness</title>
-<link rel="author" title="Mason Freed" href="mailto:masonfreed@chromium.org">
+<link rel="author" href="mailto:masonf@chromium.org">
 <link rel="help" href="https://drafts.fxtf.org/filter-effects-2/#BackdropFilterProperty">
 <link rel="match" href="backdrop-filters-brightness-ref.html">
 <style>
diff --git a/third_party/blink/web_tests/external/wpt/css/filter-effects/backdrop-filters-contrast-ref.html b/third_party/blink/web_tests/external/wpt/css/filter-effects/backdrop-filters-contrast-ref.html
index 7978fec..01b38f7 100644
--- a/third_party/blink/web_tests/external/wpt/css/filter-effects/backdrop-filters-contrast-ref.html
+++ b/third_party/blink/web_tests/external/wpt/css/filter-effects/backdrop-filters-contrast-ref.html
@@ -1,6 +1,6 @@
 <!DOCTYPE html>
 <title>CSS Backdrop Filters Animation: Contrast</title>
-<link rel="author" title="Mason Freed" href="mailto:masonfreed@chromium.org">
+<link rel="author" href="mailto:masonf@chromium.org">
 
 
 <style>
diff --git a/third_party/blink/web_tests/external/wpt/css/filter-effects/backdrop-filters-contrast.html b/third_party/blink/web_tests/external/wpt/css/filter-effects/backdrop-filters-contrast.html
index 15c97a9..e3150d1 100644
--- a/third_party/blink/web_tests/external/wpt/css/filter-effects/backdrop-filters-contrast.html
+++ b/third_party/blink/web_tests/external/wpt/css/filter-effects/backdrop-filters-contrast.html
@@ -1,6 +1,6 @@
 <!DOCTYPE html>
 <title>CSS Backdrop Filters Animation: Contrast</title>
-<link rel="author" title="Mason Freed" href="mailto:masonfreed@chromium.org">
+<link rel="author" href="mailto:masonf@chromium.org">
 <link rel="help" href="https://drafts.fxtf.org/filter-effects-2/#BackdropFilterProperty">
 <link rel="match" href="backdrop-filters-contrast-ref.html">
 <style>
diff --git a/third_party/blink/web_tests/external/wpt/css/filter-effects/backdrop-filters-grayscale-001.html b/third_party/blink/web_tests/external/wpt/css/filter-effects/backdrop-filters-grayscale-001.html
index 75fe110b8..9bdcdb4 100644
--- a/third_party/blink/web_tests/external/wpt/css/filter-effects/backdrop-filters-grayscale-001.html
+++ b/third_party/blink/web_tests/external/wpt/css/filter-effects/backdrop-filters-grayscale-001.html
@@ -1,6 +1,6 @@
 <!DOCTYPE html>
 <title>CSS Backdrop Filters: Grayscale(50%)</title>
-<link rel="author" title="Mason Freed" href="mailto:masonfreed@chromium.org">
+<link rel="author" href="mailto:masonf@chromium.org">
 <link rel="help" href="https://drafts.fxtf.org/filter-effects-2/#BackdropFilterProperty">
 <link rel="help" href="https://drafts.fxtf.org/filter-effects-1/#supported-filter-functions">
 <link rel="help" href="https://bugs.chromium.org/p/chromium/issues/detail?id=831485">
diff --git a/third_party/blink/web_tests/external/wpt/css/filter-effects/backdrop-filters-hue-rotate-ref.html b/third_party/blink/web_tests/external/wpt/css/filter-effects/backdrop-filters-hue-rotate-ref.html
index c70f6bc..177872c 100644
--- a/third_party/blink/web_tests/external/wpt/css/filter-effects/backdrop-filters-hue-rotate-ref.html
+++ b/third_party/blink/web_tests/external/wpt/css/filter-effects/backdrop-filters-hue-rotate-ref.html
@@ -1,6 +1,6 @@
 <!DOCTYPE html>
 <title>CSS Backdrop Filters Animation: Hue Rotate</title>
-<link rel="author" title="Mason Freed" href="mailto:masonfreed@chromium.org">
+<link rel="author" href="mailto:masonf@chromium.org">
 
 
 <style>
diff --git a/third_party/blink/web_tests/external/wpt/css/filter-effects/backdrop-filters-hue-rotate.html b/third_party/blink/web_tests/external/wpt/css/filter-effects/backdrop-filters-hue-rotate.html
index 17d2a3c..203a6b7 100644
--- a/third_party/blink/web_tests/external/wpt/css/filter-effects/backdrop-filters-hue-rotate.html
+++ b/third_party/blink/web_tests/external/wpt/css/filter-effects/backdrop-filters-hue-rotate.html
@@ -1,6 +1,6 @@
 <!DOCTYPE html>
 <title>CSS Backdrop Filters Animation: Hue-rotate</title>
-<link rel="author" title="Mason Freed" href="mailto:masonfreed@chromium.org">
+<link rel="author" href="mailto:masonf@chromium.org">
 <link rel="help" href="https://drafts.fxtf.org/filter-effects-2/#BackdropFilterProperty">
 <link rel="match" href="backdrop-filters-hue-rotate-ref.html">
 <style>
diff --git a/third_party/blink/web_tests/external/wpt/css/filter-effects/backdrop-filters-invert-ref.html b/third_party/blink/web_tests/external/wpt/css/filter-effects/backdrop-filters-invert-ref.html
index ed3da42..61f2856 100644
--- a/third_party/blink/web_tests/external/wpt/css/filter-effects/backdrop-filters-invert-ref.html
+++ b/third_party/blink/web_tests/external/wpt/css/filter-effects/backdrop-filters-invert-ref.html
@@ -1,6 +1,6 @@
 <!DOCTYPE html>
 <title>CSS Backdrop Filters Animation: Invert</title>
-<link rel="author" title="Mason Freed" href="mailto:masonfreed@chromium.org">
+<link rel="author" href="mailto:masonf@chromium.org">
 
 
 <style>
diff --git a/third_party/blink/web_tests/external/wpt/css/filter-effects/backdrop-filters-invert.html b/third_party/blink/web_tests/external/wpt/css/filter-effects/backdrop-filters-invert.html
index 59ec599..7e0dea6 100644
--- a/third_party/blink/web_tests/external/wpt/css/filter-effects/backdrop-filters-invert.html
+++ b/third_party/blink/web_tests/external/wpt/css/filter-effects/backdrop-filters-invert.html
@@ -1,6 +1,6 @@
 <!DOCTYPE html>
 <title>CSS Backdrop Filters Animation: Invert</title>
-<link rel="author" title="Mason Freed" href="mailto:masonfreed@chromium.org">
+<link rel="author" href="mailto:masonf@chromium.org">
 <link rel="help" href="https://drafts.fxtf.org/filter-effects-2/#BackdropFilterProperty">
 <link rel="match" href="backdrop-filters-invert-ref.html">
 <style>
diff --git a/third_party/blink/web_tests/external/wpt/css/filter-effects/backdrop-filters-opacity-ref.html b/third_party/blink/web_tests/external/wpt/css/filter-effects/backdrop-filters-opacity-ref.html
index fef130ea..acdf050 100644
--- a/third_party/blink/web_tests/external/wpt/css/filter-effects/backdrop-filters-opacity-ref.html
+++ b/third_party/blink/web_tests/external/wpt/css/filter-effects/backdrop-filters-opacity-ref.html
@@ -1,6 +1,6 @@
 <!DOCTYPE html>
 <title>CSS Backdrop Filters Animation: Opacity</title>
-<link rel="author" title="Mason Freed" href="mailto:masonfreed@chromium.org">
+<link rel="author" href="mailto:masonf@chromium.org">
 
 
 <style>
diff --git a/third_party/blink/web_tests/external/wpt/css/filter-effects/backdrop-filters-opacity.html b/third_party/blink/web_tests/external/wpt/css/filter-effects/backdrop-filters-opacity.html
index 850c41d..b3703c3 100644
--- a/third_party/blink/web_tests/external/wpt/css/filter-effects/backdrop-filters-opacity.html
+++ b/third_party/blink/web_tests/external/wpt/css/filter-effects/backdrop-filters-opacity.html
@@ -1,6 +1,6 @@
 <!DOCTYPE html>
 <title>CSS Backdrop Filters Animation: Opacity</title>
-<link rel="author" title="Mason Freed" href="mailto:masonfreed@chromium.org">
+<link rel="author" href="mailto:masonf@chromium.org">
 <link rel="help" href="https://drafts.fxtf.org/filter-effects-2/#BackdropFilterProperty">
 <link rel="match" href="backdrop-filters-opacity-ref.html">
 <style>
diff --git a/third_party/blink/web_tests/external/wpt/css/filter-effects/backdrop-filters-saturate-ref.html b/third_party/blink/web_tests/external/wpt/css/filter-effects/backdrop-filters-saturate-ref.html
index d8b7234..f3407f3 100644
--- a/third_party/blink/web_tests/external/wpt/css/filter-effects/backdrop-filters-saturate-ref.html
+++ b/third_party/blink/web_tests/external/wpt/css/filter-effects/backdrop-filters-saturate-ref.html
@@ -1,6 +1,6 @@
 <!DOCTYPE html>
 <title>CSS Backdrop Filters Animation: Saturate</title>
-<link rel="author" title="Mason Freed" href="mailto:masonfreed@chromium.org">
+<link rel="author" href="mailto:masonf@chromium.org">
 
 
 <style>
diff --git a/third_party/blink/web_tests/external/wpt/css/filter-effects/backdrop-filters-saturate.html b/third_party/blink/web_tests/external/wpt/css/filter-effects/backdrop-filters-saturate.html
index f131641..2b7d33b2 100644
--- a/third_party/blink/web_tests/external/wpt/css/filter-effects/backdrop-filters-saturate.html
+++ b/third_party/blink/web_tests/external/wpt/css/filter-effects/backdrop-filters-saturate.html
@@ -1,6 +1,6 @@
 <!DOCTYPE html>
 <title>CSS Backdrop Filters Animation: Saturate</title>
-<link rel="author" title="Mason Freed" href="mailto:masonfreed@chromium.org">
+<link rel="author" href="mailto:masonf@chromium.org">
 <link rel="help" href="https://drafts.fxtf.org/filter-effects-2/#BackdropFilterProperty">
 <link rel="match" href="backdrop-filters-saturate-ref.html">
 <style>
diff --git a/third_party/blink/web_tests/external/wpt/css/filter-effects/backdrop-filters-sepia-ref.html b/third_party/blink/web_tests/external/wpt/css/filter-effects/backdrop-filters-sepia-ref.html
index 624fb87..eae10fe 100644
--- a/third_party/blink/web_tests/external/wpt/css/filter-effects/backdrop-filters-sepia-ref.html
+++ b/third_party/blink/web_tests/external/wpt/css/filter-effects/backdrop-filters-sepia-ref.html
@@ -1,6 +1,6 @@
 <!DOCTYPE html>
 <title>CSS Backdrop Filters Animation: Sepia</title>
-<link rel="author" title="Mason Freed" href="mailto:masonfreed@chromium.org">
+<link rel="author" href="mailto:masonf@chromium.org">
 
 
 <style>
diff --git a/third_party/blink/web_tests/external/wpt/css/filter-effects/backdrop-filters-sepia.html b/third_party/blink/web_tests/external/wpt/css/filter-effects/backdrop-filters-sepia.html
index bcfae3d2..3d10bf3 100644
--- a/third_party/blink/web_tests/external/wpt/css/filter-effects/backdrop-filters-sepia.html
+++ b/third_party/blink/web_tests/external/wpt/css/filter-effects/backdrop-filters-sepia.html
@@ -1,6 +1,6 @@
 <!DOCTYPE html>
 <title>CSS Backdrop Filters Animation: Sepia</title>
-<link rel="author" title="Mason Freed" href="mailto:masonfreed@chromium.org">
+<link rel="author" href="mailto:masonf@chromium.org">
 <link rel="help" href="https://drafts.fxtf.org/filter-effects-2/#BackdropFilterProperty">
 <link rel="match" href="backdrop-filters-sepia-ref.html">
 <style>
diff --git a/third_party/blink/web_tests/external/wpt/css/filter-effects/css-backdrop-filters-animation-blur-ref.html b/third_party/blink/web_tests/external/wpt/css/filter-effects/css-backdrop-filters-animation-blur-ref.html
index e433070b..643d084 100644
--- a/third_party/blink/web_tests/external/wpt/css/filter-effects/css-backdrop-filters-animation-blur-ref.html
+++ b/third_party/blink/web_tests/external/wpt/css/filter-effects/css-backdrop-filters-animation-blur-ref.html
@@ -1,6 +1,6 @@
 <!DOCTYPE html>
 <title>CSS Backdrop Filters Animation: Blur Reference</title>
-<link rel="author" title="Mason Freed" href="mailto:masonfreed@chromium.org">
+<link rel="author" href="mailto:masonf@chromium.org">
 <style>
     .square {
         position: absolute;
diff --git a/third_party/blink/web_tests/external/wpt/css/filter-effects/css-backdrop-filters-animation-blur.html b/third_party/blink/web_tests/external/wpt/css/filter-effects/css-backdrop-filters-animation-blur.html
index 3d883c2..0b3e429 100644
--- a/third_party/blink/web_tests/external/wpt/css/filter-effects/css-backdrop-filters-animation-blur.html
+++ b/third_party/blink/web_tests/external/wpt/css/filter-effects/css-backdrop-filters-animation-blur.html
@@ -1,6 +1,6 @@
 <!DOCTYPE html>
 <title>CSS Backdrop Filters Animation: Blur</title>
-<link rel="author" title="Mason Freed" href="mailto:masonfreed@chromium.org">
+<link rel="author" href="mailto:masonf@chromium.org">
 <link rel="help" href="https://drafts.fxtf.org/filter-effects-2/#BackdropFilterProperty">
 <link rel="match" href="css-backdrop-filters-animation-blur-ref.html">
 <style>
diff --git a/third_party/blink/web_tests/external/wpt/css/filter-effects/css-backdrop-filters-animation-brightness-ref.html b/third_party/blink/web_tests/external/wpt/css/filter-effects/css-backdrop-filters-animation-brightness-ref.html
index 8ba5b459..bf894ac 100644
--- a/third_party/blink/web_tests/external/wpt/css/filter-effects/css-backdrop-filters-animation-brightness-ref.html
+++ b/third_party/blink/web_tests/external/wpt/css/filter-effects/css-backdrop-filters-animation-brightness-ref.html
@@ -1,6 +1,6 @@
 <!DOCTYPE html>
 <title>CSS Backdrop Filters Animation: Brightness Reference</title>
-<link rel="author" title="Mason Freed" href="mailto:masonfreed@chromium.org">
+<link rel="author" href="mailto:masonf@chromium.org">
 <style>
     .square {
         position: absolute;
diff --git a/third_party/blink/web_tests/external/wpt/css/filter-effects/css-backdrop-filters-animation-brightness.html b/third_party/blink/web_tests/external/wpt/css/filter-effects/css-backdrop-filters-animation-brightness.html
index d377e5e..cdcd9b5 100644
--- a/third_party/blink/web_tests/external/wpt/css/filter-effects/css-backdrop-filters-animation-brightness.html
+++ b/third_party/blink/web_tests/external/wpt/css/filter-effects/css-backdrop-filters-animation-brightness.html
@@ -1,6 +1,6 @@
 <!DOCTYPE html>
 <title>CSS Backdrop Filters Animation: Brightness</title>
-<link rel="author" title="Mason Freed" href="mailto:masonfreed@chromium.org">
+<link rel="author" href="mailto:masonf@chromium.org">
 <link rel="help" href="https://drafts.fxtf.org/filter-effects-2/#BackdropFilterProperty">
 <link rel="match" href="css-backdrop-filters-animation-brightness-ref.html">
 <style>
diff --git a/third_party/blink/web_tests/external/wpt/css/filter-effects/css-backdrop-filters-animation-combined-ref.html b/third_party/blink/web_tests/external/wpt/css/filter-effects/css-backdrop-filters-animation-combined-ref.html
index b4c6bc9..575c56d 100644
--- a/third_party/blink/web_tests/external/wpt/css/filter-effects/css-backdrop-filters-animation-combined-ref.html
+++ b/third_party/blink/web_tests/external/wpt/css/filter-effects/css-backdrop-filters-animation-combined-ref.html
@@ -1,6 +1,6 @@
 <!DOCTYPE html>
 <title>CSS Backdrop Filters Animation: Combined filters 001 Reference</title>
-<link rel="author" title="Mason Freed" href="mailto:masonfreed@chromium.org">
+<link rel="author" href="mailto:masonf@chromium.org">
 <style>
     .square {
         position: absolute;
diff --git a/third_party/blink/web_tests/external/wpt/css/filter-effects/css-backdrop-filters-animation-combined.html b/third_party/blink/web_tests/external/wpt/css/filter-effects/css-backdrop-filters-animation-combined.html
index 920dc823..e8661cc 100644
--- a/third_party/blink/web_tests/external/wpt/css/filter-effects/css-backdrop-filters-animation-combined.html
+++ b/third_party/blink/web_tests/external/wpt/css/filter-effects/css-backdrop-filters-animation-combined.html
@@ -1,6 +1,6 @@
 <!DOCTYPE html>
 <title>CSS Backdrop Filters Animation: Combined filters 001</title>
-<link rel="author" title="Mason Freed" href="mailto:masonfreed@chromium.org">
+<link rel="author" href="mailto:masonf@chromium.org">
 <link rel="help" href="https://drafts.fxtf.org/filter-effects-2/#BackdropFilterProperty">
 <link rel="match" href="css-backdrop-filters-animation-combined-ref.html">
 <style>
diff --git a/third_party/blink/web_tests/external/wpt/css/filter-effects/css-backdrop-filters-animation-contrast-ref.html b/third_party/blink/web_tests/external/wpt/css/filter-effects/css-backdrop-filters-animation-contrast-ref.html
index e1bf0605..b118df6b 100644
--- a/third_party/blink/web_tests/external/wpt/css/filter-effects/css-backdrop-filters-animation-contrast-ref.html
+++ b/third_party/blink/web_tests/external/wpt/css/filter-effects/css-backdrop-filters-animation-contrast-ref.html
@@ -1,6 +1,6 @@
 <!DOCTYPE html>
 <title>CSS Backdrop Filters Animation: Contrast Reference</title>
-<link rel="author" title="Mason Freed" href="mailto:masonfreed@chromium.org">
+<link rel="author" href="mailto:masonf@chromium.org">
 <style>
     .square {
         position: absolute;
diff --git a/third_party/blink/web_tests/external/wpt/css/filter-effects/css-backdrop-filters-animation-contrast.html b/third_party/blink/web_tests/external/wpt/css/filter-effects/css-backdrop-filters-animation-contrast.html
index d9709f8..537f65cf 100644
--- a/third_party/blink/web_tests/external/wpt/css/filter-effects/css-backdrop-filters-animation-contrast.html
+++ b/third_party/blink/web_tests/external/wpt/css/filter-effects/css-backdrop-filters-animation-contrast.html
@@ -1,6 +1,6 @@
 <!DOCTYPE html>
 <title>CSS Backdrop Filters Animation: Contrast</title>
-<link rel="author" title="Mason Freed" href="mailto:masonfreed@chromium.org">
+<link rel="author" href="mailto:masonf@chromium.org">
 <link rel="help" href="https://drafts.fxtf.org/filter-effects-2/#BackdropFilterProperty">
 <link rel="match" href="css-backdrop-filters-animation-contrast-ref.html">
 <style>
diff --git a/third_party/blink/web_tests/external/wpt/css/filter-effects/css-backdrop-filters-animation-drop-shadow-ref.html b/third_party/blink/web_tests/external/wpt/css/filter-effects/css-backdrop-filters-animation-drop-shadow-ref.html
index 0b4a3f2d..4d1f437 100644
--- a/third_party/blink/web_tests/external/wpt/css/filter-effects/css-backdrop-filters-animation-drop-shadow-ref.html
+++ b/third_party/blink/web_tests/external/wpt/css/filter-effects/css-backdrop-filters-animation-drop-shadow-ref.html
@@ -1,6 +1,6 @@
 <!DOCTYPE html>
 <title>CSS Backdrop Filters Animation: Drop Shadow Reference</title>
-<link rel="author" title="Mason Freed" href="mailto:masonfreed@chromium.org">
+<link rel="author" href="mailto:masonf@chromium.org">
 <style>
     .square {
         position: absolute;
diff --git a/third_party/blink/web_tests/external/wpt/css/filter-effects/css-backdrop-filters-animation-drop-shadow.html b/third_party/blink/web_tests/external/wpt/css/filter-effects/css-backdrop-filters-animation-drop-shadow.html
index c7e19a9..c0f634a 100644
--- a/third_party/blink/web_tests/external/wpt/css/filter-effects/css-backdrop-filters-animation-drop-shadow.html
+++ b/third_party/blink/web_tests/external/wpt/css/filter-effects/css-backdrop-filters-animation-drop-shadow.html
@@ -1,6 +1,6 @@
 <!DOCTYPE html>
 <title>CSS Backdrop Filters Animation: Drop Shadow</title>
-<link rel="author" title="Mason Freed" href="mailto:masonfreed@chromium.org">
+<link rel="author" href="mailto:masonf@chromium.org">
 <link rel="help" href="https://drafts.fxtf.org/filter-effects-2/#BackdropFilterProperty">
 <link rel="match" href="css-backdrop-filters-animation-drop-shadow-ref.html">
 <style>
diff --git a/third_party/blink/web_tests/external/wpt/css/filter-effects/css-backdrop-filters-animation-grayscale-ref.html b/third_party/blink/web_tests/external/wpt/css/filter-effects/css-backdrop-filters-animation-grayscale-ref.html
index 75e457a..07265200 100644
--- a/third_party/blink/web_tests/external/wpt/css/filter-effects/css-backdrop-filters-animation-grayscale-ref.html
+++ b/third_party/blink/web_tests/external/wpt/css/filter-effects/css-backdrop-filters-animation-grayscale-ref.html
@@ -1,6 +1,6 @@
 <!DOCTYPE html>
 <title>CSS Backdrop Filters Animation: Blur Reference</title>
-<link rel="author" title="Mason Freed" href="mailto:masonfreed@chromium.org">
+<link rel="author" href="mailto:masonf@chromium.org">
 <style>
     .square {
         position: absolute;
diff --git a/third_party/blink/web_tests/external/wpt/css/filter-effects/css-backdrop-filters-animation-grayscale.html b/third_party/blink/web_tests/external/wpt/css/filter-effects/css-backdrop-filters-animation-grayscale.html
index c04118c..fe08d8f 100644
--- a/third_party/blink/web_tests/external/wpt/css/filter-effects/css-backdrop-filters-animation-grayscale.html
+++ b/third_party/blink/web_tests/external/wpt/css/filter-effects/css-backdrop-filters-animation-grayscale.html
@@ -1,6 +1,6 @@
 <!DOCTYPE html>
 <title>CSS Backdrop Filters Animation: Grayscale</title>
-<link rel="author" title="Mason Freed" href="mailto:masonfreed@chromium.org">
+<link rel="author" href="mailto:masonf@chromium.org">
 <link rel="help" href="https://drafts.fxtf.org/filter-effects-2/#BackdropFilterProperty">
 <link rel="match" href="css-backdrop-filters-animation-grayscale-ref.html">
 <style>
diff --git a/third_party/blink/web_tests/external/wpt/css/filter-effects/css-backdrop-filters-animation-hue-rotate-ref.html b/third_party/blink/web_tests/external/wpt/css/filter-effects/css-backdrop-filters-animation-hue-rotate-ref.html
index 0651e89e..f10855da 100644
--- a/third_party/blink/web_tests/external/wpt/css/filter-effects/css-backdrop-filters-animation-hue-rotate-ref.html
+++ b/third_party/blink/web_tests/external/wpt/css/filter-effects/css-backdrop-filters-animation-hue-rotate-ref.html
@@ -1,6 +1,6 @@
 <!DOCTYPE html>
 <title>CSS Backdrop Filters Animation: Hue-rotate Reference</title>
-<link rel="author" title="Mason Freed" href="mailto:masonfreed@chromium.org">
+<link rel="author" href="mailto:masonf@chromium.org">
 <style>
     .square {
         position: absolute;
diff --git a/third_party/blink/web_tests/external/wpt/css/filter-effects/css-backdrop-filters-animation-hue-rotate.html b/third_party/blink/web_tests/external/wpt/css/filter-effects/css-backdrop-filters-animation-hue-rotate.html
index e3cdd1f..3bc8fe49 100644
--- a/third_party/blink/web_tests/external/wpt/css/filter-effects/css-backdrop-filters-animation-hue-rotate.html
+++ b/third_party/blink/web_tests/external/wpt/css/filter-effects/css-backdrop-filters-animation-hue-rotate.html
@@ -1,6 +1,6 @@
 <!DOCTYPE html>
 <title>CSS Backdrop Filters Animation: Hue-rotate</title>
-<link rel="author" title="Mason Freed" href="mailto:masonfreed@chromium.org">
+<link rel="author" href="mailto:masonf@chromium.org">
 <link rel="help" href="https://drafts.fxtf.org/filter-effects-2/#BackdropFilterProperty">
 <link rel="match" href="css-backdrop-filters-animation-hue-rotate-ref.html">
 <style>
diff --git a/third_party/blink/web_tests/external/wpt/css/filter-effects/css-backdrop-filters-animation-invert-ref.html b/third_party/blink/web_tests/external/wpt/css/filter-effects/css-backdrop-filters-animation-invert-ref.html
index 50964200..9d4efb43 100644
--- a/third_party/blink/web_tests/external/wpt/css/filter-effects/css-backdrop-filters-animation-invert-ref.html
+++ b/third_party/blink/web_tests/external/wpt/css/filter-effects/css-backdrop-filters-animation-invert-ref.html
@@ -1,6 +1,6 @@
 <!DOCTYPE html>
 <title>CSS Backdrop Filters Animation: Invert Reference</title>
-<link rel="author" title="Mason Freed" href="mailto:masonfreed@chromium.org">
+<link rel="author" href="mailto:masonf@chromium.org">
 <style>
     .square {
         position: absolute;
diff --git a/third_party/blink/web_tests/external/wpt/css/filter-effects/css-backdrop-filters-animation-invert.html b/third_party/blink/web_tests/external/wpt/css/filter-effects/css-backdrop-filters-animation-invert.html
index 963ab7ed..3291fa54 100644
--- a/third_party/blink/web_tests/external/wpt/css/filter-effects/css-backdrop-filters-animation-invert.html
+++ b/third_party/blink/web_tests/external/wpt/css/filter-effects/css-backdrop-filters-animation-invert.html
@@ -1,6 +1,6 @@
 <!DOCTYPE html>
 <title>CSS Backdrop Filters Animation: Invert</title>
-<link rel="author" title="Mason Freed" href="mailto:masonfreed@chromium.org">
+<link rel="author" href="mailto:masonf@chromium.org">
 <link rel="help" href="https://drafts.fxtf.org/filter-effects-2/#BackdropFilterProperty">
 <link rel="match" href="css-backdrop-filters-animation-invert-ref.html">
 <style>
diff --git a/third_party/blink/web_tests/external/wpt/css/filter-effects/css-backdrop-filters-animation-opacity-ref.html b/third_party/blink/web_tests/external/wpt/css/filter-effects/css-backdrop-filters-animation-opacity-ref.html
index c9253d28..d95b01ab 100644
--- a/third_party/blink/web_tests/external/wpt/css/filter-effects/css-backdrop-filters-animation-opacity-ref.html
+++ b/third_party/blink/web_tests/external/wpt/css/filter-effects/css-backdrop-filters-animation-opacity-ref.html
@@ -1,6 +1,6 @@
 <!DOCTYPE html>
 <title>CSS Backdrop Filters Animation: Opacity Reference</title>
-<link rel="author" title="Mason Freed" href="mailto:masonfreed@chromium.org">
+<link rel="author" href="mailto:masonf@chromium.org">
 <style>
     .square {
         position: absolute;
diff --git a/third_party/blink/web_tests/external/wpt/css/filter-effects/css-backdrop-filters-animation-opacity.html b/third_party/blink/web_tests/external/wpt/css/filter-effects/css-backdrop-filters-animation-opacity.html
index 72078e7c..66bffefd 100644
--- a/third_party/blink/web_tests/external/wpt/css/filter-effects/css-backdrop-filters-animation-opacity.html
+++ b/third_party/blink/web_tests/external/wpt/css/filter-effects/css-backdrop-filters-animation-opacity.html
@@ -1,6 +1,6 @@
 <!DOCTYPE html>
 <title>CSS Backdrop Filters Animation: Opacity</title>
-<link rel="author" title="Mason Freed" href="mailto:masonfreed@chromium.org">
+<link rel="author" href="mailto:masonf@chromium.org">
 <link rel="help" href="https://drafts.fxtf.org/filter-effects-2/#BackdropFilterProperty">
 <link rel="match" href="css-backdrop-filters-animation-opacity-ref.html">
 <style>
diff --git a/third_party/blink/web_tests/external/wpt/css/filter-effects/css-backdrop-filters-animation-saturate-ref.html b/third_party/blink/web_tests/external/wpt/css/filter-effects/css-backdrop-filters-animation-saturate-ref.html
index 7f7b67f..572ae31 100644
--- a/third_party/blink/web_tests/external/wpt/css/filter-effects/css-backdrop-filters-animation-saturate-ref.html
+++ b/third_party/blink/web_tests/external/wpt/css/filter-effects/css-backdrop-filters-animation-saturate-ref.html
@@ -1,6 +1,6 @@
 <!DOCTYPE html>
 <title>CSS Backdrop Filters Animation: Saturate Reference</title>
-<link rel="author" title="Mason Freed" href="mailto:masonfreed@chromium.org">
+<link rel="author" href="mailto:masonf@chromium.org">
 <style>
     .square {
         position: absolute;
diff --git a/third_party/blink/web_tests/external/wpt/css/filter-effects/css-backdrop-filters-animation-saturate.html b/third_party/blink/web_tests/external/wpt/css/filter-effects/css-backdrop-filters-animation-saturate.html
index 13e2773..dda1671 100644
--- a/third_party/blink/web_tests/external/wpt/css/filter-effects/css-backdrop-filters-animation-saturate.html
+++ b/third_party/blink/web_tests/external/wpt/css/filter-effects/css-backdrop-filters-animation-saturate.html
@@ -1,6 +1,6 @@
 <!DOCTYPE html>
 <title>CSS Backdrop Filters Animation: Saturate</title>
-<link rel="author" title="Mason Freed" href="mailto:masonfreed@chromium.org">
+<link rel="author" href="mailto:masonf@chromium.org">
 <link rel="help" href="https://drafts.fxtf.org/filter-effects-2/#BackdropFilterProperty">
 <link rel="match" href="css-backdrop-filters-animation-saturate-ref.html">
 <style>
diff --git a/third_party/blink/web_tests/external/wpt/css/filter-effects/css-backdrop-filters-animation-sepia-ref.html b/third_party/blink/web_tests/external/wpt/css/filter-effects/css-backdrop-filters-animation-sepia-ref.html
index b368abb..4161d90c 100644
--- a/third_party/blink/web_tests/external/wpt/css/filter-effects/css-backdrop-filters-animation-sepia-ref.html
+++ b/third_party/blink/web_tests/external/wpt/css/filter-effects/css-backdrop-filters-animation-sepia-ref.html
@@ -1,6 +1,6 @@
 <!DOCTYPE html>
 <title>CSS Backdrop Filters Animation: Sepia Reference</title>
-<link rel="author" title="Mason Freed" href="mailto:masonfreed@chromium.org">
+<link rel="author" href="mailto:masonf@chromium.org">
 <style>
     .square {
         position: absolute;
diff --git a/third_party/blink/web_tests/external/wpt/css/filter-effects/css-backdrop-filters-animation-sepia.html b/third_party/blink/web_tests/external/wpt/css/filter-effects/css-backdrop-filters-animation-sepia.html
index 8cd9290..c28d3f9 100644
--- a/third_party/blink/web_tests/external/wpt/css/filter-effects/css-backdrop-filters-animation-sepia.html
+++ b/third_party/blink/web_tests/external/wpt/css/filter-effects/css-backdrop-filters-animation-sepia.html
@@ -1,6 +1,6 @@
 <!DOCTYPE html>
 <title>CSS Backdrop Filters Animation: Sepia</title>
-<link rel="author" title="Mason Freed" href="mailto:masonfreed@chromium.org">
+<link rel="author" href="mailto:masonf@chromium.org">
 <link rel="help" href="https://drafts.fxtf.org/filter-effects-2/#BackdropFilterProperty">
 <link rel="match" href="css-backdrop-filters-animation-sepia-ref.html">
 <style>
diff --git a/third_party/blink/web_tests/external/wpt/css/filter-effects/reference/backdrop-filters-grayscale-001-ref.html b/third_party/blink/web_tests/external/wpt/css/filter-effects/reference/backdrop-filters-grayscale-001-ref.html
index eb403f16..ba01ac6 100644
--- a/third_party/blink/web_tests/external/wpt/css/filter-effects/reference/backdrop-filters-grayscale-001-ref.html
+++ b/third_party/blink/web_tests/external/wpt/css/filter-effects/reference/backdrop-filters-grayscale-001-ref.html
@@ -1,6 +1,6 @@
 <!DOCTYPE html>
 <title>CSS Backdrop Filters Animation: Grayscale</title>
-<link rel="author" title="Mason Freed" href="mailto:masonfreed@chromium.org">
+<link rel="author" href="mailto:masonf@chromium.org">
 
 
 <style>
diff --git a/third_party/blink/web_tests/external/wpt/custom-elements/customized-built-in-constructor-exceptions.html b/third_party/blink/web_tests/external/wpt/custom-elements/customized-built-in-constructor-exceptions.html
index f9c440f..fbc1a6fd 100644
--- a/third_party/blink/web_tests/external/wpt/custom-elements/customized-built-in-constructor-exceptions.html
+++ b/third_party/blink/web_tests/external/wpt/custom-elements/customized-built-in-constructor-exceptions.html
@@ -1,6 +1,6 @@
 <!DOCTYPE html>
 <title>Customized built-in element constructor behavior</title>
-<meta name='author' title='Mason Freed' href='mailto:masonfreed@chromium.org'>
+<meta name='author' href='mailto:masonf@chromium.org'>
 <link rel='help' href='https://dom.spec.whatwg.org/#concept-create-element'>
 <script src='/resources/testharness.js'></script>
 <script src='/resources/testharnessreport.js'></script>
diff --git a/third_party/blink/web_tests/external/wpt/custom-elements/element-internals-shadowroot.html b/third_party/blink/web_tests/external/wpt/custom-elements/element-internals-shadowroot.html
index 0afd43dd..7ec0896 100644
--- a/third_party/blink/web_tests/external/wpt/custom-elements/element-internals-shadowroot.html
+++ b/third_party/blink/web_tests/external/wpt/custom-elements/element-internals-shadowroot.html
@@ -1,7 +1,7 @@
 <!DOCTYPE html>
 <meta charset="utf-8">
 <title>ElementInternals.shadowRoot</title>
-<link rel="author" title="Mason Freed" href="mailto:masonfreed@chromium.org">
+<link rel="author" href="mailto:masonf@chromium.org">
 <link rel="help" href="https://html.spec.whatwg.org/#dom-elementinternals-shadowroot">
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
diff --git a/third_party/blink/web_tests/external/wpt/dom/svg-insert-crash.html b/third_party/blink/web_tests/external/wpt/dom/svg-insert-crash.html
index 539fb1f..80eec1f 100644
--- a/third_party/blink/web_tests/external/wpt/dom/svg-insert-crash.html
+++ b/third_party/blink/web_tests/external/wpt/dom/svg-insert-crash.html
@@ -1,6 +1,6 @@
 <!DOCTYPE html>
 <meta charset="utf-8">
-<link rel="author" title="Mason Freed" href="mailto:masonfreed@chromium.org">
+<link rel="author" href="mailto:masonf@chromium.org">
 <link rel="help" href="https://crbug.com/1029262">
 <meta name="assert" content="The renderer should not crash.">
 <script src="/resources/testharness.js"></script>
diff --git a/third_party/blink/web_tests/external/wpt/html/rendering/non-replaced-elements/the-page/iframe-scrolling-attribute-ref.html b/third_party/blink/web_tests/external/wpt/html/rendering/non-replaced-elements/the-page/iframe-scrolling-attribute-ref.html
index 5697b06..424e039e 100644
--- a/third_party/blink/web_tests/external/wpt/html/rendering/non-replaced-elements/the-page/iframe-scrolling-attribute-ref.html
+++ b/third_party/blink/web_tests/external/wpt/html/rendering/non-replaced-elements/the-page/iframe-scrolling-attribute-ref.html
@@ -1,6 +1,6 @@
 <!DOCTYPE html>
 <title>iframe: changing the scrolling attribute</title>
-<link rel="author" title="Mason Freed" href="mailto:masonfreed@chromium.org">
+<link rel="author" href="mailto:masonf@chromium.org">
 
 
 
diff --git a/third_party/blink/web_tests/external/wpt/html/rendering/non-replaced-elements/the-page/iframe-scrolling-attribute-values-ref.html b/third_party/blink/web_tests/external/wpt/html/rendering/non-replaced-elements/the-page/iframe-scrolling-attribute-values-ref.html
index 5c416b2..2ead59a 100644
--- a/third_party/blink/web_tests/external/wpt/html/rendering/non-replaced-elements/the-page/iframe-scrolling-attribute-values-ref.html
+++ b/third_party/blink/web_tests/external/wpt/html/rendering/non-replaced-elements/the-page/iframe-scrolling-attribute-values-ref.html
@@ -1,6 +1,6 @@
 <!DOCTYPE html>
 <title>The scrolling attribute</title>
-<link rel="author" title="Mason Freed" href="mailto:masonfreed@chromium.org">
+<link rel="author" href="mailto:masonf@chromium.org">
 
 
 
diff --git a/third_party/blink/web_tests/external/wpt/html/rendering/non-replaced-elements/the-page/iframe-scrolling-attribute-values.html b/third_party/blink/web_tests/external/wpt/html/rendering/non-replaced-elements/the-page/iframe-scrolling-attribute-values.html
index 8213b8d..997745b 100644
--- a/third_party/blink/web_tests/external/wpt/html/rendering/non-replaced-elements/the-page/iframe-scrolling-attribute-values.html
+++ b/third_party/blink/web_tests/external/wpt/html/rendering/non-replaced-elements/the-page/iframe-scrolling-attribute-values.html
@@ -1,6 +1,6 @@
 <!DOCTYPE html>
 <title>The scrolling attribute</title>
-<link rel="author" title="Mason Freed" href="mailto:masonfreed@chromium.org">
+<link rel="author" href="mailto:masonf@chromium.org">
 <link rel="help" href="https://html.spec.whatwg.org/multipage/rendering.html#the-page">
 <link rel="match" href="iframe-scrolling-attribute-values-ref.html">
 <meta name="fuzzy" content="maxDifference=0-1;totalPixels=0-4">
diff --git a/third_party/blink/web_tests/external/wpt/html/rendering/non-replaced-elements/the-page/iframe-scrolling-attribute.html b/third_party/blink/web_tests/external/wpt/html/rendering/non-replaced-elements/the-page/iframe-scrolling-attribute.html
index 3af14871..d04e2dd5 100644
--- a/third_party/blink/web_tests/external/wpt/html/rendering/non-replaced-elements/the-page/iframe-scrolling-attribute.html
+++ b/third_party/blink/web_tests/external/wpt/html/rendering/non-replaced-elements/the-page/iframe-scrolling-attribute.html
@@ -1,6 +1,6 @@
 <!DOCTYPE html>
 <title>iframe: changing the scrolling attribute</title>
-<link rel="author" title="Mason Freed" href="mailto:masonfreed@chromium.org">
+<link rel="author" href="mailto:masonf@chromium.org">
 <link rel="help" href="https://html.spec.whatwg.org/multipage/rendering.html#the-page">
 <link rel="match" href="iframe-scrolling-attribute-ref.html">
 
diff --git a/third_party/blink/web_tests/external/wpt/html/rendering/the-details-element/details-blockification.html b/third_party/blink/web_tests/external/wpt/html/rendering/the-details-element/details-blockification.html
index cc94e92..9600748 100644
--- a/third_party/blink/web_tests/external/wpt/html/rendering/the-details-element/details-blockification.html
+++ b/third_party/blink/web_tests/external/wpt/html/rendering/the-details-element/details-blockification.html
@@ -1,7 +1,7 @@
 <!DOCTYPE html>
 <meta charset="utf-8">
 <title>CSS Test: details children blockification</title>
-<link rel="author" title="Mason Freed" href="mailto:masonfreed@chromium.org">
+<link rel="author" href="mailto:masonf@chromium.org">
 <link rel="help" href="https://html.spec.whatwg.org/multipage/rendering.html#the-details-and-summary-elements">
 <meta name="assert" content="Ensure blockification of <details> children">
 <script src=/resources/testharness.js></script>
diff --git a/third_party/blink/web_tests/external/wpt/html/semantics/document-metadata/the-style-element/style_load_event.html b/third_party/blink/web_tests/external/wpt/html/semantics/document-metadata/the-style-element/style_load_event.html
index 911a088..9e19855 100644
--- a/third_party/blink/web_tests/external/wpt/html/semantics/document-metadata/the-style-element/style_load_event.html
+++ b/third_party/blink/web_tests/external/wpt/html/semantics/document-metadata/the-style-element/style_load_event.html
@@ -1,6 +1,6 @@
 <!DOCTYPE html>
 <title>HTML Test: The style load event should fire when textContent is changed</title>
-<link rel="author" title="Mason Freed" href="mailto:masonfreed@chromium.org">
+<link rel="author" href="mailto:masonf@chromium.org">
 <link rel="help" href="https://html.spec.whatwg.org/multipage/semantics.html#update-a-style-block">
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
diff --git a/third_party/blink/web_tests/external/wpt/html/semantics/forms/form-submission-0/form-double-submit-2.html b/third_party/blink/web_tests/external/wpt/html/semantics/forms/form-submission-0/form-double-submit-2.html
index 4c715de2..f7939e0 100644
--- a/third_party/blink/web_tests/external/wpt/html/semantics/forms/form-submission-0/form-double-submit-2.html
+++ b/third_party/blink/web_tests/external/wpt/html/semantics/forms/form-submission-0/form-double-submit-2.html
@@ -1,6 +1,6 @@
 <!DOCTYPE html>
 <meta charset="utf-8">
-<link rel="author" title="Mason Freed" href="mailto:masonfreed@chromium.org">
+<link rel="author" href="mailto:masonf@chromium.org">
 <link rel="help" href="https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#form-submission-algorithm">
 
 <script src="/resources/testharness.js"></script>
diff --git a/third_party/blink/web_tests/external/wpt/html/semantics/forms/form-submission-0/form-double-submit-3.html b/third_party/blink/web_tests/external/wpt/html/semantics/forms/form-submission-0/form-double-submit-3.html
index cdb50d2..fbb6a42 100644
--- a/third_party/blink/web_tests/external/wpt/html/semantics/forms/form-submission-0/form-double-submit-3.html
+++ b/third_party/blink/web_tests/external/wpt/html/semantics/forms/form-submission-0/form-double-submit-3.html
@@ -1,6 +1,6 @@
 <!DOCTYPE html>
 <meta charset="utf-8">
-<link rel="author" title="Mason Freed" href="mailto:masonfreed@chromium.org">
+<link rel="author" href="mailto:masonf@chromium.org">
 <link rel="help" href="https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#form-submission-algorithm">
 
 <script src="/resources/testharness.js"></script>
diff --git a/third_party/blink/web_tests/external/wpt/html/semantics/forms/form-submission-0/form-double-submit-to-different-origin-frame.html b/third_party/blink/web_tests/external/wpt/html/semantics/forms/form-submission-0/form-double-submit-to-different-origin-frame.html
index 17ad1b1..aee1d41 100644
--- a/third_party/blink/web_tests/external/wpt/html/semantics/forms/form-submission-0/form-double-submit-to-different-origin-frame.html
+++ b/third_party/blink/web_tests/external/wpt/html/semantics/forms/form-submission-0/form-double-submit-to-different-origin-frame.html
@@ -1,6 +1,6 @@
 <!DOCTYPE html>
 <meta charset="utf-8">
-<link rel="author" title="Mason Freed" href="mailto:masonfreed@chromium.org">
+<link rel="author" href="mailto:masonf@chromium.org">
 <link rel="help" href="https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#form-submission-algorithm">
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
diff --git a/third_party/blink/web_tests/external/wpt/html/semantics/forms/form-submission-0/form-double-submit.html b/third_party/blink/web_tests/external/wpt/html/semantics/forms/form-submission-0/form-double-submit.html
index 005f393..b6ea0ce 100644
--- a/third_party/blink/web_tests/external/wpt/html/semantics/forms/form-submission-0/form-double-submit.html
+++ b/third_party/blink/web_tests/external/wpt/html/semantics/forms/form-submission-0/form-double-submit.html
@@ -1,6 +1,6 @@
 <!DOCTYPE html>
 <meta charset="utf-8">
-<link rel="author" title="Mason Freed" href="mailto:masonfreed@chromium.org">
+<link rel="author" href="mailto:masonf@chromium.org">
 <link rel="help" href="https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#form-submission-algorithm">
 
 <script src="/resources/testharness.js"></script>
diff --git a/third_party/blink/web_tests/external/wpt/html/semantics/interactive-elements/the-popup-element/popup-anchor-nesting.tentative.html b/third_party/blink/web_tests/external/wpt/html/semantics/interactive-elements/the-popup-element/popup-anchor-nesting.tentative.html
index 00b1140..8c375124 100644
--- a/third_party/blink/web_tests/external/wpt/html/semantics/interactive-elements/the-popup-element/popup-anchor-nesting.tentative.html
+++ b/third_party/blink/web_tests/external/wpt/html/semantics/interactive-elements/the-popup-element/popup-anchor-nesting.tentative.html
@@ -1,7 +1,7 @@
 <!DOCTYPE html>
 <meta charset="utf-8" />
 <title>Popup anchor nesting</title>
-<link rel="author" title="Mason Freed" href="mailto:masonfreed@chromium.org">
+<link rel="author" href="mailto:masonf@chromium.org">
 <link rel=help href="https://github.com/MicrosoftEdge/MSEdgeExplainers/blob/main/Popup/explainer.md">
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
diff --git a/third_party/blink/web_tests/external/wpt/html/semantics/interactive-elements/the-popup-element/popup-element-basic.tentative.html b/third_party/blink/web_tests/external/wpt/html/semantics/interactive-elements/the-popup-element/popup-element-basic.tentative.html
index 0fcc7f4..9909b2f 100644
--- a/third_party/blink/web_tests/external/wpt/html/semantics/interactive-elements/the-popup-element/popup-element-basic.tentative.html
+++ b/third_party/blink/web_tests/external/wpt/html/semantics/interactive-elements/the-popup-element/popup-element-basic.tentative.html
@@ -1,6 +1,6 @@
 <!DOCTYPE html>
 <meta charset="utf-8">
-<link rel="author" title="Mason Freed" href="mailto:masonfreed@chromium.org">
+<link rel="author" href="mailto:masonf@chromium.org">
 <link rel=help href="https://github.com/MicrosoftEdge/MSEdgeExplainers/blob/main/Popup/explainer.md">
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
diff --git a/third_party/blink/web_tests/external/wpt/html/semantics/interactive-elements/the-popup-element/popup-hidden-display-ref.tentative.html b/third_party/blink/web_tests/external/wpt/html/semantics/interactive-elements/the-popup-element/popup-hidden-display-ref.tentative.html
index 7f4ced3..e7cb73c 100644
--- a/third_party/blink/web_tests/external/wpt/html/semantics/interactive-elements/the-popup-element/popup-hidden-display-ref.tentative.html
+++ b/third_party/blink/web_tests/external/wpt/html/semantics/interactive-elements/the-popup-element/popup-hidden-display-ref.tentative.html
@@ -1,5 +1,5 @@
 <!DOCTYPE html>
 <meta charset="utf-8">
-<link rel=author title="Mason Freed" href="mailto:masonfreed@chromium.org">
+<link rel=author href="mailto:masonf@chromium.org">
 
 No popup should be displayed here.<p>
diff --git a/third_party/blink/web_tests/external/wpt/html/semantics/interactive-elements/the-popup-element/popup-hidden-display.tentative.html b/third_party/blink/web_tests/external/wpt/html/semantics/interactive-elements/the-popup-element/popup-hidden-display.tentative.html
index c80aecf1..8a070b9 100644
--- a/third_party/blink/web_tests/external/wpt/html/semantics/interactive-elements/the-popup-element/popup-hidden-display.tentative.html
+++ b/third_party/blink/web_tests/external/wpt/html/semantics/interactive-elements/the-popup-element/popup-hidden-display.tentative.html
@@ -1,6 +1,6 @@
 <!DOCTYPE html>
 <meta charset="utf-8">
-<link rel=author title="Mason Freed" href="mailto:masonfreed@chromium.org">
+<link rel=author href="mailto:masonf@chromium.org">
 <link rel=help href="https://github.com/MicrosoftEdge/MSEdgeExplainers/blob/main/Popup/explainer.md">
 <link rel=match href="popup-hidden-display-ref.tentative.html">
 
diff --git a/third_party/blink/web_tests/external/wpt/html/semantics/interactive-elements/the-popup-element/popup-inside-display-none.tentative.html b/third_party/blink/web_tests/external/wpt/html/semantics/interactive-elements/the-popup-element/popup-inside-display-none.tentative.html
index 824e7005..745403d 100644
--- a/third_party/blink/web_tests/external/wpt/html/semantics/interactive-elements/the-popup-element/popup-inside-display-none.tentative.html
+++ b/third_party/blink/web_tests/external/wpt/html/semantics/interactive-elements/the-popup-element/popup-inside-display-none.tentative.html
@@ -1,6 +1,6 @@
 <!DOCTYPE html>
 <meta charset="utf-8">
-<link rel=author title="Mason Freed" href="mailto:masonfreed@chromium.org">
+<link rel=author href="mailto:masonf@chromium.org">
 <link rel=help href="https://github.com/MicrosoftEdge/MSEdgeExplainers/blob/main/Popup/explainer.md">
 <link rel=match href="popup-hidden-display-ref.tentative.html">
 
diff --git a/third_party/blink/web_tests/external/wpt/html/semantics/interactive-elements/the-popup-element/popup-light-dismiss-on-scroll.tentative.html b/third_party/blink/web_tests/external/wpt/html/semantics/interactive-elements/the-popup-element/popup-light-dismiss-on-scroll.tentative.html
index 3f35ec24..28dd4f4 100644
--- a/third_party/blink/web_tests/external/wpt/html/semantics/interactive-elements/the-popup-element/popup-light-dismiss-on-scroll.tentative.html
+++ b/third_party/blink/web_tests/external/wpt/html/semantics/interactive-elements/the-popup-element/popup-light-dismiss-on-scroll.tentative.html
@@ -2,7 +2,7 @@
 <html lang="en">
 <meta charset="utf-8" />
 <title>Popup light dismiss on scroll</title>
-<link rel="author" title="Mason Freed" href="mailto:masonfreed@chromium.org">
+<link rel="author" href="mailto:masonf@chromium.org">
 <link rel=help href="https://github.com/MicrosoftEdge/MSEdgeExplainers/blob/main/Popup/explainer.md">
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
diff --git a/third_party/blink/web_tests/external/wpt/html/semantics/interactive-elements/the-popup-element/popup-light-dismiss.tentative.html b/third_party/blink/web_tests/external/wpt/html/semantics/interactive-elements/the-popup-element/popup-light-dismiss.tentative.html
index f2a55aa1..613b659 100644
--- a/third_party/blink/web_tests/external/wpt/html/semantics/interactive-elements/the-popup-element/popup-light-dismiss.tentative.html
+++ b/third_party/blink/web_tests/external/wpt/html/semantics/interactive-elements/the-popup-element/popup-light-dismiss.tentative.html
@@ -1,7 +1,7 @@
 <!DOCTYPE html>
 <meta charset="utf-8" />
 <title>Popup light dismiss behavior</title>
-<link rel="author" title="Mason Freed" href="mailto:masonfreed@chromium.org">
+<link rel="author" href="mailto:masonf@chromium.org">
 <link rel=help href="https://github.com/MicrosoftEdge/MSEdgeExplainers/blob/main/Popup/explainer.md">
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
diff --git a/third_party/blink/web_tests/external/wpt/html/semantics/interactive-elements/the-popup-element/popup-open-display-ref.tentative.html b/third_party/blink/web_tests/external/wpt/html/semantics/interactive-elements/the-popup-element/popup-open-display-ref.tentative.html
index 1b06344..660c4e0 100644
--- a/third_party/blink/web_tests/external/wpt/html/semantics/interactive-elements/the-popup-element/popup-open-display-ref.tentative.html
+++ b/third_party/blink/web_tests/external/wpt/html/semantics/interactive-elements/the-popup-element/popup-open-display-ref.tentative.html
@@ -1,6 +1,6 @@
 <!DOCTYPE html>
 <meta charset="utf-8">
-<link rel=author title="Mason Freed" href="mailto:masonfreed@chromium.org">
+<link rel=author href="mailto:masonf@chromium.org">
 
 <div>This is a popup</div>
 
diff --git a/third_party/blink/web_tests/external/wpt/html/semantics/interactive-elements/the-popup-element/popup-open-display.tentative.html b/third_party/blink/web_tests/external/wpt/html/semantics/interactive-elements/the-popup-element/popup-open-display.tentative.html
index 0f6bd93..7c5bf44b 100644
--- a/third_party/blink/web_tests/external/wpt/html/semantics/interactive-elements/the-popup-element/popup-open-display.tentative.html
+++ b/third_party/blink/web_tests/external/wpt/html/semantics/interactive-elements/the-popup-element/popup-open-display.tentative.html
@@ -1,6 +1,6 @@
 <!DOCTYPE html>
 <meta charset="utf-8">
-<link rel=author title="Mason Freed" href="mailto:masonfreed@chromium.org">
+<link rel=author href="mailto:masonf@chromium.org">
 <link rel=help href="https://github.com/MicrosoftEdge/MSEdgeExplainers/blob/main/Popup/explainer.md">
 <link rel=match href="popup-open-display-ref.tentative.html">
 
diff --git a/third_party/blink/web_tests/external/wpt/html/semantics/interactive-elements/the-popup-element/popup-open-overflow-display-ref.tentative.html b/third_party/blink/web_tests/external/wpt/html/semantics/interactive-elements/the-popup-element/popup-open-overflow-display-ref.tentative.html
index f324f57..dcf5998 100644
--- a/third_party/blink/web_tests/external/wpt/html/semantics/interactive-elements/the-popup-element/popup-open-overflow-display-ref.tentative.html
+++ b/third_party/blink/web_tests/external/wpt/html/semantics/interactive-elements/the-popup-element/popup-open-overflow-display-ref.tentative.html
@@ -1,6 +1,6 @@
 <!DOCTYPE html>
 <meta charset="utf-8">
-<link rel=author title="Mason Freed" href="mailto:masonfreed@chromium.org">
+<link rel=author href="mailto:masonf@chromium.org">
 
 <popup id=p1>This is popup 1<div id=anchor2></div></popup>
 <popup id=p2 anchor=anchor2>This is popup 2<div id=anchor3></div></popup>
diff --git a/third_party/blink/web_tests/external/wpt/html/semantics/interactive-elements/the-popup-element/popup-open-overflow-display.tentative.html b/third_party/blink/web_tests/external/wpt/html/semantics/interactive-elements/the-popup-element/popup-open-overflow-display.tentative.html
index eae8288..b2780b7 100644
--- a/third_party/blink/web_tests/external/wpt/html/semantics/interactive-elements/the-popup-element/popup-open-overflow-display.tentative.html
+++ b/third_party/blink/web_tests/external/wpt/html/semantics/interactive-elements/the-popup-element/popup-open-overflow-display.tentative.html
@@ -1,6 +1,6 @@
 <!DOCTYPE html>
 <meta charset="utf-8">
-<link rel=author title="Mason Freed" href="mailto:masonfreed@chromium.org">
+<link rel=author href="mailto:masonf@chromium.org">
 <link rel=help href="https://github.com/MicrosoftEdge/MSEdgeExplainers/blob/main/Popup/explainer.md">
 <link rel=match href="popup-open-overflow-display-ref.tentative.html">
 
diff --git a/third_party/blink/web_tests/external/wpt/html/semantics/interactive-elements/the-popup-element/popup-shadow-dom.tentative.html b/third_party/blink/web_tests/external/wpt/html/semantics/interactive-elements/the-popup-element/popup-shadow-dom.tentative.html
index eef0309a..386d0ca 100644
--- a/third_party/blink/web_tests/external/wpt/html/semantics/interactive-elements/the-popup-element/popup-shadow-dom.tentative.html
+++ b/third_party/blink/web_tests/external/wpt/html/semantics/interactive-elements/the-popup-element/popup-shadow-dom.tentative.html
@@ -1,6 +1,6 @@
 <!DOCTYPE html>
 <meta charset="utf-8">
-<link rel="author" title="Mason Freed" href="mailto:masonfreed@chromium.org">
+<link rel="author" href="mailto:masonf@chromium.org">
 <link rel=help href="https://github.com/MicrosoftEdge/MSEdgeExplainers/blob/main/Popup/explainer.md">
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
diff --git a/third_party/blink/web_tests/external/wpt/html/semantics/interactive-elements/the-popup-element/popup-stacking.tentative.html b/third_party/blink/web_tests/external/wpt/html/semantics/interactive-elements/the-popup-element/popup-stacking.tentative.html
index 167b350..4fccaa7 100644
--- a/third_party/blink/web_tests/external/wpt/html/semantics/interactive-elements/the-popup-element/popup-stacking.tentative.html
+++ b/third_party/blink/web_tests/external/wpt/html/semantics/interactive-elements/the-popup-element/popup-stacking.tentative.html
@@ -1,6 +1,6 @@
 <!DOCTYPE html>
 <meta charset="utf-8">
-<link rel="author" title="Mason Freed" href="mailto:masonfreed@chromium.org">
+<link rel="author" href="mailto:masonf@chromium.org">
 <link rel=help href="https://github.com/MicrosoftEdge/MSEdgeExplainers/blob/main/Popup/explainer.md">
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
diff --git a/third_party/blink/web_tests/external/wpt/html/syntax/parsing/html_content_in_foreign_context.html b/third_party/blink/web_tests/external/wpt/html/syntax/parsing/html_content_in_foreign_context.html
index a13c2fb1..7efafb4f 100644
--- a/third_party/blink/web_tests/external/wpt/html/syntax/parsing/html_content_in_foreign_context.html
+++ b/third_party/blink/web_tests/external/wpt/html/syntax/parsing/html_content_in_foreign_context.html
@@ -1,6 +1,6 @@
 <!DOCTYPE html>
 <title>Foreign contexts with HTML tag children</title>
-<link rel="author" title="Mason Freed" href="mailto:masonfreed@chromium.org">
+<link rel="author" href="mailto:masonf@chromium.org">
 <link rel="help" href="https://html.spec.whatwg.org/multipage/parsing.html#parsing-main-inbody">
 <link rel="help" href="https://html.spec.whatwg.org/multipage/parsing.html#parsing-main-inforeign">
 <script src="/resources/testharness.js"></script>
diff --git a/third_party/blink/web_tests/external/wpt/html/syntax/serializing-html-fragments/escaping.html b/third_party/blink/web_tests/external/wpt/html/syntax/serializing-html-fragments/escaping.html
index 1602964..7bbc556 100644
--- a/third_party/blink/web_tests/external/wpt/html/syntax/serializing-html-fragments/escaping.html
+++ b/third_party/blink/web_tests/external/wpt/html/syntax/serializing-html-fragments/escaping.html
@@ -1,7 +1,7 @@
 <!DOCTYPE html>
 <meta charset="utf-8">
 <title>Serialization of script-disabled documents should follow escaping rules</title>
-<link rel="author" title="Mason Freed" href="mailto:masonfreed@chromium.org">
+<link rel="author" href="mailto:masonf@chromium.org">
 <link rel="help" href="https://html.spec.whatwg.org/multipage/parsing.html#serialising-html-fragments">
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
diff --git a/third_party/blink/web_tests/external/wpt/html/webappapis/system-state-and-capabilities/the-navigator-object/navigator-pluginarray.html b/third_party/blink/web_tests/external/wpt/html/webappapis/system-state-and-capabilities/the-navigator-object/navigator-pluginarray.html
index cd5fc41..cc7664f 100644
--- a/third_party/blink/web_tests/external/wpt/html/webappapis/system-state-and-capabilities/the-navigator-object/navigator-pluginarray.html
+++ b/third_party/blink/web_tests/external/wpt/html/webappapis/system-state-and-capabilities/the-navigator-object/navigator-pluginarray.html
@@ -1,7 +1,7 @@
 <!DOCTYPE html>
 <meta charset=utf-8>
 <title>Test for empty plugins and mimeTypes arrays</title>
-<link rel='author' title='Mason Freed' href='mailto:masonfreed@chromium.org'>
+<link rel='author' href='mailto:masonf@chromium.org'>
 <link rel='help' href='https://github.com/whatwg/html/issues/6003'>
 <script src='/resources/testharness.js'></script>
 <script src='/resources/testharnessreport.js'></script>
diff --git a/third_party/blink/web_tests/external/wpt/lifecycle/set-composited-layer-position-ref.html b/third_party/blink/web_tests/external/wpt/lifecycle/set-composited-layer-position-ref.html
index 217edfa..600de56 100644
--- a/third_party/blink/web_tests/external/wpt/lifecycle/set-composited-layer-position-ref.html
+++ b/third_party/blink/web_tests/external/wpt/lifecycle/set-composited-layer-position-ref.html
@@ -2,7 +2,7 @@
 
 <meta charset="utf-8">
 <title>Setting composited layer position paints properly</title>
-<link rel="author" title="Mason Freed" href="masonfreed@chromium.org">
+<link rel="author" href="masonf@chromium.org">
 
 
 
diff --git a/third_party/blink/web_tests/external/wpt/lifecycle/set-composited-layer-position.html b/third_party/blink/web_tests/external/wpt/lifecycle/set-composited-layer-position.html
index 543677a..f1a3807 100644
--- a/third_party/blink/web_tests/external/wpt/lifecycle/set-composited-layer-position.html
+++ b/third_party/blink/web_tests/external/wpt/lifecycle/set-composited-layer-position.html
@@ -2,7 +2,7 @@
 <html class="reftest-wait">
 <meta charset="utf-8">
 <title>Setting composited layer position paints properly</title>
-<link rel="author" title="Mason Freed" href="masonfreed@chromium.org">
+<link rel="author" href="masonf@chromium.org">
 <link rel="help" href="https://drafts.csswg.org/cssom-view-1/">
 <link rel="match"  href="set-composited-layer-position-ref.html">
 
diff --git a/third_party/blink/web_tests/external/wpt/scroll-to-text-fragment/scroll-to-text-fragment-same-doc.html b/third_party/blink/web_tests/external/wpt/scroll-to-text-fragment/scroll-to-text-fragment-same-doc.html
index 3bfd453..ff33952 100644
--- a/third_party/blink/web_tests/external/wpt/scroll-to-text-fragment/scroll-to-text-fragment-same-doc.html
+++ b/third_party/blink/web_tests/external/wpt/scroll-to-text-fragment/scroll-to-text-fragment-same-doc.html
@@ -14,12 +14,13 @@
 
 function checkScroll(resolve) {
   let position = 'unknown';
-  if (window.scrollY == 0)
-    position = 'top';
-  else if (isInView(document.getElementById('text')))
-    position = 'text';
-
-  resolve(position);
+  requestAnimationFrame(() => {
+    if (window.scrollY == 0)
+      position = 'top';
+    else if (isInView(document.getElementById('text')))
+      position = 'text';
+    resolve(position);
+  });
 }
 
 function runTest() {
diff --git a/third_party/blink/web_tests/external/wpt/shadow-dom/declarative/declarative-after-attachshadow.tentative.html b/third_party/blink/web_tests/external/wpt/shadow-dom/declarative/declarative-after-attachshadow.tentative.html
index a313585..3dcc099 100644
--- a/third_party/blink/web_tests/external/wpt/shadow-dom/declarative/declarative-after-attachshadow.tentative.html
+++ b/third_party/blink/web_tests/external/wpt/shadow-dom/declarative/declarative-after-attachshadow.tentative.html
@@ -1,6 +1,6 @@
 <!DOCTYPE html>
 <title>Declarative Shadow DOM after attachShadow</title>
-<link rel='author' title='Mason Freed' href='mailto:masonfreed@chromium.org'>
+<link rel='author' href='mailto:masonf@chromium.org'>
 <link rel='help' href='https://github.com/whatwg/dom/issues/831'>
 <script src='/resources/testharness.js'></script>
 <script src='/resources/testharnessreport.js'></script>
diff --git a/third_party/blink/web_tests/external/wpt/shadow-dom/declarative/declarative-shadow-dom-attachment.tentative.html b/third_party/blink/web_tests/external/wpt/shadow-dom/declarative/declarative-shadow-dom-attachment.tentative.html
index b9033f5..081f9b2d 100644
--- a/third_party/blink/web_tests/external/wpt/shadow-dom/declarative/declarative-shadow-dom-attachment.tentative.html
+++ b/third_party/blink/web_tests/external/wpt/shadow-dom/declarative/declarative-shadow-dom-attachment.tentative.html
@@ -1,6 +1,6 @@
 <!DOCTYPE html>
 <title>Declarative Shadow DOM Element Attachment</title>
-<link rel='author' title='Mason Freed' href='mailto:masonfreed@chromium.org'>
+<link rel='author' href='mailto:masonf@chromium.org'>
 <link rel='help' href='https://github.com/whatwg/dom/issues/831'>
 <script src='/resources/testharness.js'></script>
 <script src='/resources/testharnessreport.js'></script>
diff --git a/third_party/blink/web_tests/external/wpt/shadow-dom/declarative/declarative-shadow-dom-basic.tentative.html b/third_party/blink/web_tests/external/wpt/shadow-dom/declarative/declarative-shadow-dom-basic.tentative.html
index a03743e4..0b99376 100644
--- a/third_party/blink/web_tests/external/wpt/shadow-dom/declarative/declarative-shadow-dom-basic.tentative.html
+++ b/third_party/blink/web_tests/external/wpt/shadow-dom/declarative/declarative-shadow-dom-basic.tentative.html
@@ -1,7 +1,7 @@
 <!DOCTYPE html>
 <meta charset="utf-8">
 <title>Declarative Shadow DOM</title>
-<link rel="author" title="Mason Freed" href="mailto:masonfreed@chromium.org">
+<link rel="author" href="mailto:masonf@chromium.org">
 <link rel="help" href="https://github.com/whatwg/dom/issues/831">
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
diff --git a/third_party/blink/web_tests/external/wpt/shadow-dom/declarative/declarative-shadow-dom-opt-in.tentative.html b/third_party/blink/web_tests/external/wpt/shadow-dom/declarative/declarative-shadow-dom-opt-in.tentative.html
index a49e759..2eec3c5 100644
--- a/third_party/blink/web_tests/external/wpt/shadow-dom/declarative/declarative-shadow-dom-opt-in.tentative.html
+++ b/third_party/blink/web_tests/external/wpt/shadow-dom/declarative/declarative-shadow-dom-opt-in.tentative.html
@@ -1,7 +1,7 @@
 <!DOCTYPE html>
 <meta charset="utf-8">
 <title>Declarative Shadow DOM</title>
-<link rel="author" title="Mason Freed" href="mailto:masonfreed@chromium.org">
+<link rel="author" href="mailto:masonf@chromium.org">
 <link rel="help" href="https://github.com/whatwg/dom/issues/831">
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
diff --git a/third_party/blink/web_tests/external/wpt/shadow-dom/declarative/getinnerhtml.tentative.html b/third_party/blink/web_tests/external/wpt/shadow-dom/declarative/getinnerhtml.tentative.html
index bcf5471..52a4847 100644
--- a/third_party/blink/web_tests/external/wpt/shadow-dom/declarative/getinnerhtml.tentative.html
+++ b/third_party/blink/web_tests/external/wpt/shadow-dom/declarative/getinnerhtml.tentative.html
@@ -1,6 +1,6 @@
 <!DOCTYPE html>
 <title>getInnerHTML </title>
-<link rel='author' title='Mason Freed' href='mailto:masonfreed@chromium.org'>
+<link rel='author' href='mailto:masonf@chromium.org'>
 <link rel='help' href='https://github.com/whatwg/dom/issues/831'>
 <script src='/resources/testharness.js'></script>
 <script src='/resources/testharnessreport.js'></script>
diff --git a/third_party/blink/web_tests/external/wpt/shadow-dom/declarative/innerhtml-before-closing-tag.tentative.html b/third_party/blink/web_tests/external/wpt/shadow-dom/declarative/innerhtml-before-closing-tag.tentative.html
index 653c419..9a60677 100644
--- a/third_party/blink/web_tests/external/wpt/shadow-dom/declarative/innerhtml-before-closing-tag.tentative.html
+++ b/third_party/blink/web_tests/external/wpt/shadow-dom/declarative/innerhtml-before-closing-tag.tentative.html
@@ -1,6 +1,6 @@
 <!DOCTYPE html>
 <title>Declarative Shadow DOM innerHTML</title>
-<link rel='author' title='Mason Freed' href='mailto:masonfreed@chromium.org'>
+<link rel='author' href='mailto:masonf@chromium.org'>
 <link rel='help' href='https://github.com/whatwg/dom/issues/831'>
 <script src='/resources/testharness.js'></script>
 <script src='/resources/testharnessreport.js'></script>
diff --git a/third_party/blink/web_tests/external/wpt/shadow-dom/declarative/innerhtml-on-ordinary-template.tentative.html b/third_party/blink/web_tests/external/wpt/shadow-dom/declarative/innerhtml-on-ordinary-template.tentative.html
index 988acc0..6d01ced 100644
--- a/third_party/blink/web_tests/external/wpt/shadow-dom/declarative/innerhtml-on-ordinary-template.tentative.html
+++ b/third_party/blink/web_tests/external/wpt/shadow-dom/declarative/innerhtml-on-ordinary-template.tentative.html
@@ -1,6 +1,6 @@
 <!DOCTYPE html>
 <title>Declarative Shadow DOM innerHTML</title>
-<link rel='author' title='Mason Freed' href='mailto:masonfreed@chromium.org'>
+<link rel='author' href='mailto:masonf@chromium.org'>
 <link rel='help' href='https://github.com/whatwg/dom/issues/831'>
 <script src='/resources/testharness.js'></script>
 <script src='/resources/testharnessreport.js'></script>
diff --git a/third_party/blink/web_tests/external/wpt/shadow-dom/declarative/move-template-before-closing-tag.tentative.html b/third_party/blink/web_tests/external/wpt/shadow-dom/declarative/move-template-before-closing-tag.tentative.html
index 88b75e7..46e6041 100644
--- a/third_party/blink/web_tests/external/wpt/shadow-dom/declarative/move-template-before-closing-tag.tentative.html
+++ b/third_party/blink/web_tests/external/wpt/shadow-dom/declarative/move-template-before-closing-tag.tentative.html
@@ -1,6 +1,6 @@
 <!DOCTYPE html>
 <title>Declarative Shadow DOM: moving the template doesn't change attachment point</title>
-<link rel='author' title='Mason Freed' href='mailto:masonfreed@chromium.org'>
+<link rel='author' href='mailto:masonf@chromium.org'>
 <link rel='help' href='https://github.com/whatwg/dom/issues/831'>
 <script src='/resources/testharness.js'></script>
 <script src='/resources/testharnessreport.js'></script>
diff --git a/third_party/blink/web_tests/external/wpt/shadow-dom/declarative/script-access.tentative.html b/third_party/blink/web_tests/external/wpt/shadow-dom/declarative/script-access.tentative.html
index b87bb7a..66d4b948 100644
--- a/third_party/blink/web_tests/external/wpt/shadow-dom/declarative/script-access.tentative.html
+++ b/third_party/blink/web_tests/external/wpt/shadow-dom/declarative/script-access.tentative.html
@@ -1,6 +1,6 @@
 <!DOCTYPE html>
 <title>Declarative Shadow DOM</title>
-<link rel='author' title='Mason Freed' href='mailto:masonfreed@chromium.org'>
+<link rel='author' href='mailto:masonf@chromium.org'>
 <link rel='help' href='https://github.com/whatwg/dom/issues/831'>
 <script src='/resources/testharness.js'></script>
 <script src='/resources/testharnessreport.js'></script>
diff --git a/third_party/blink/web_tests/external/wpt/shadow-dom/event-on-pseudo-element-crash.html b/third_party/blink/web_tests/external/wpt/shadow-dom/event-on-pseudo-element-crash.html
index 56674b6..c03f51a6 100644
--- a/third_party/blink/web_tests/external/wpt/shadow-dom/event-on-pseudo-element-crash.html
+++ b/third_party/blink/web_tests/external/wpt/shadow-dom/event-on-pseudo-element-crash.html
@@ -1,6 +1,6 @@
 <!DOCTYPE html>
 <meta charset="utf-8">
-<link rel="author" title="Mason Freed" href="mailto:masonfreed@chromium.org">
+<link rel="author" href="mailto:masonf@chromium.org">
 <link rel="help" href="https://crbug.com/1180286">
 <meta name="assert" content="The renderer should not crash.">
 
diff --git a/third_party/blink/web_tests/external/wpt/shadow-dom/nested-slot-remove-crash.html b/third_party/blink/web_tests/external/wpt/shadow-dom/nested-slot-remove-crash.html
index d53b3b4..2930e3a2 100644
--- a/third_party/blink/web_tests/external/wpt/shadow-dom/nested-slot-remove-crash.html
+++ b/third_party/blink/web_tests/external/wpt/shadow-dom/nested-slot-remove-crash.html
@@ -1,6 +1,6 @@
 <!DOCTYPE html>
 <meta charset="utf-8">
-<link rel="author" title="Mason Freed" href="mailto:masonfreed@chromium.org">
+<link rel="author" href="mailto:masonf@chromium.org">
 <link rel="help" href="https://crbug.com/1159328">
 <meta name="assert" content="The renderer should not crash.">
 
diff --git a/third_party/blink/web_tests/external/wpt/webdriver/tests/bidi/__init__.py b/third_party/blink/web_tests/external/wpt/webdriver/tests/bidi/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/webdriver/tests/bidi/__init__.py
diff --git a/third_party/blink/web_tests/external/wpt/webdriver/tests/bidi/new_session/__init__.py b/third_party/blink/web_tests/external/wpt/webdriver/tests/bidi/new_session/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/webdriver/tests/bidi/new_session/__init__.py
diff --git a/third_party/blink/web_tests/external/wpt/webdriver/tests/bidi/new_session/connect.py b/third_party/blink/web_tests/external/wpt/webdriver/tests/bidi/new_session/connect.py
new file mode 100644
index 0000000..219efdcd
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/webdriver/tests/bidi/new_session/connect.py
@@ -0,0 +1,36 @@
+import pytest
+import asyncio
+import websockets
+import webdriver
+
+# classic session to enable bidi capability manually
+# Intended to be the first test in this file
+@pytest.mark.asyncio
+@pytest.mark.capabilities({"webSocketUrl": True})
+async def test_websocket_url_connect(session):
+    assert not isinstance(session, webdriver.BidiSession)
+    websocket_url = session.capabilities["webSocketUrl"]
+    async with websockets.connect(websocket_url) as websocket:
+        await websocket.send("Hello world!")
+
+# test bidi_session send
+# using bidi_session is the recommended way to test bidi
+@pytest.mark.asyncio
+async def test_bidi_session_send(bidi_session):
+    await bidi_session.websocket_transport.send("test_bidi_session: send")
+
+# bidi session following a bidi session with a different capabilities
+# to test session recreation
+@pytest.mark.asyncio
+@pytest.mark.capabilities({"acceptInsecureCerts": True})
+async def test_bidi_session_with_different_capability(bidi_session):
+    await bidi_session.websocket_transport.send("test_bidi_session: different capability")
+
+# classic session following a bidi session to test session
+# recreation
+# Intended to be the last test in this file to make sure
+# classic session is not impacted by bidi tests
+@pytest.mark.asyncio
+def test_classic_after_bidi_session(session):
+    assert not isinstance(session, webdriver.BidiSession)
+
diff --git a/third_party/blink/web_tests/external/wpt/webdriver/tests/new_session/websocket_url.py b/third_party/blink/web_tests/external/wpt/webdriver/tests/new_session/websocket_url.py
new file mode 100644
index 0000000..ff6d6b32
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/webdriver/tests/new_session/websocket_url.py
@@ -0,0 +1,8 @@
+from tests.support.asserts import assert_success
+
+def test_websocket_url(new_session, add_browser_capabilities):
+    response, _ = new_session({"capabilities": {
+        "alwaysMatch": add_browser_capabilities({"webSocketUrl": True})}})
+    value = assert_success(response)
+    assert value["capabilities"]["webSocketUrl"].startswith("ws://")
+
diff --git a/third_party/blink/web_tests/external/wpt/webdriver/tests/support/fixtures.py b/third_party/blink/web_tests/external/wpt/webdriver/tests/support/fixtures.py
index 888c42b..0ecfcdb 100644
--- a/third_party/blink/web_tests/external/wpt/webdriver/tests/support/fixtures.py
+++ b/third_party/blink/web_tests/external/wpt/webdriver/tests/support/fixtures.py
@@ -2,6 +2,7 @@
 import json
 import os
 
+import asyncio
 import pytest
 import webdriver
 
@@ -39,6 +40,17 @@
             metafunc.parametrize("capabilities", marker.args, ids=None)
 
 
+# Ensure that the event loop is restarted once per session rather than the default  of once per test
+# if we don't do this, tests will try to reuse a closed event loop and fail with an error that the "future
+# belongs to a different loop"
+@pytest.fixture(scope="session")
+def event_loop():
+    """Change event_loop fixture to session level."""
+    loop = asyncio.get_event_loop_policy().new_event_loop()
+    yield loop
+    loop.close()
+
+
 @pytest.fixture
 def add_event_listeners(session):
     """Register listeners for tracked events on element."""
@@ -111,8 +123,23 @@
     }
 
 
+async def reset_current_session_if_necessary(caps, request_bidi):
+    global _current_session
+
+    # If there is a session with different capabilities active or the current session
+    # is of different type than the one we would like to create, end it now.
+    if _current_session is not None:
+        is_bidi = isinstance(_current_session, webdriver.BidiSession)
+        if is_bidi != request_bidi or not _current_session.match(caps):
+            if is_bidi:
+                await _current_session.end()
+            else:
+                _current_session.end()
+            _current_session = None
+
+
 @pytest.fixture(scope="function")
-def session(capabilities, configuration, request):
+async def session(capabilities, configuration, request):
     """Create and start a session for a test that does not itself test session creation.
 
     By default the session will stay open after each test, but we always try to start a
@@ -127,11 +154,7 @@
     deep_update(caps, capabilities)
     caps = {"alwaysMatch": caps}
 
-    # If there is a session with different capabilities active, end it now
-    if _current_session is not None and (
-            caps != _current_session.requested_capabilities):
-        _current_session.end()
-        _current_session = None
+    await reset_current_session_if_necessary(caps, False)
 
     if _current_session is None:
         _current_session = webdriver.Session(
@@ -140,6 +163,46 @@
             capabilities=caps)
     try:
         _current_session.start()
+
+    except webdriver.error.SessionNotCreatedException:
+        if not _current_session.session_id:
+            raise
+
+    # Enforce a fixed default window size and position
+    _current_session.window.size = defaults.WINDOW_SIZE
+    _current_session.window.position = defaults.WINDOW_POSITION
+
+    yield _current_session
+
+    cleanup_session(_current_session)
+
+
+@pytest.fixture(scope="function")
+async def bidi_session(capabilities, configuration, request):
+    """Create and start a bidi session for a test that does not itself test
+    bidi session creation.
+    By default the session will stay open after each test, but we always try to start a
+    new one and assume that if that fails there is already a valid session. This makes it
+    possible to recover from some errors that might leave the session in a bad state, but
+    does not demand that we start a new session per test."""
+    global _current_session
+
+    # Update configuration capabilities with custom ones from the
+    # capabilities fixture, which can be set by tests
+    caps = copy.deepcopy(configuration["capabilities"])
+    deep_update(caps, capabilities)
+    caps = {"alwaysMatch": caps}
+
+    await reset_current_session_if_necessary(caps, True)
+
+    if _current_session is None:
+        _current_session = webdriver.BidiSession(
+            configuration["host"],
+            configuration["port"],
+            capabilities=caps)
+    try:
+        await _current_session.start()
+
     except webdriver.error.SessionNotCreatedException:
         if not _current_session.session_id:
             raise
diff --git a/third_party/blink/web_tests/html/popup/popup-light-dismiss-keyboard.html b/third_party/blink/web_tests/html/popup/popup-light-dismiss-keyboard.html
index f271af2..4d6cd102 100644
--- a/third_party/blink/web_tests/html/popup/popup-light-dismiss-keyboard.html
+++ b/third_party/blink/web_tests/html/popup/popup-light-dismiss-keyboard.html
@@ -1,7 +1,7 @@
 <!DOCTYPE html>
 <meta charset="utf-8" />
 <title>Popup light dismiss behavior</title>
-<link rel="author" title="Mason Freed" href="mailto:masonfreed@chromium.org">
+<link rel="author" href="mailto:masonf@chromium.org">
 <link rel=help href="https://github.com/MicrosoftEdge/MSEdgeExplainers/blob/main/Popup/explainer.md">
 <script src='../../resources/testharness.js'></script>
 <script src='../../resources/testharnessreport.js'></script>
diff --git a/third_party/blink/web_tests/http/tests/inspector-protocol/page/consecutive-navigate-expected.txt b/third_party/blink/web_tests/http/tests/inspector-protocol/page/consecutive-navigate-expected.txt
new file mode 100644
index 0000000..11eed6ad
--- /dev/null
+++ b/third_party/blink/web_tests/http/tests/inspector-protocol/page/consecutive-navigate-expected.txt
@@ -0,0 +1,35 @@
+Tests that calling Page.navigate while a navigation is running cancels the previous navigation
+{
+    id : <number>
+    result : {
+        errorText : net::ERR_ABORTED
+        frameId : <string>
+    }
+    sessionId : <string>
+}
+{
+    id : <number>
+    result : {
+        frameId : <string>
+        loaderId : <string>
+    }
+    sessionId : <string>
+}
+{
+    id : <number>
+    result : {
+        errorText : net::ERR_ABORTED
+        frameId : <string>
+        loaderId : <string>
+    }
+    sessionId : <string>
+}
+{
+    id : <number>
+    result : {
+        frameId : <string>
+        loaderId : <string>
+    }
+    sessionId : <string>
+}
+
diff --git a/third_party/blink/web_tests/http/tests/inspector-protocol/page/consecutive-navigate.js b/third_party/blink/web_tests/http/tests/inspector-protocol/page/consecutive-navigate.js
new file mode 100644
index 0000000..1b7e64b8
--- /dev/null
+++ b/third_party/blink/web_tests/http/tests/inspector-protocol/page/consecutive-navigate.js
@@ -0,0 +1,30 @@
+(async function(testRunner) {
+  var {page, session, dp} = await testRunner.startBlank(
+      `Tests that calling Page.navigate while a navigation is running cancels the previous navigation`);
+
+  // Call Page.navigate() consecutively (start second navigation before first
+  // one has started).
+  await dp.Page.enable();
+  const navigatePromise1 = dp.Page.navigate({ url: testRunner.url('../resources/test-page.html') });
+  const navigatePromise2 = dp.Page.navigate({ url: testRunner.url('../resources/image.html') });
+  const loadPromise = dp.Page.onceLoadEventFired();
+
+  testRunner.log(await navigatePromise1);
+  testRunner.log(await navigatePromise2);
+
+  // Wait for navigation to finish in renderer.
+  await loadPromise;
+
+  // Call Page.navigate() consecutively (start second navigation after first
+  // one has started and has sent a network request).
+  await dp.Fetch.enable();
+  const navigatePromise3 = dp.Page.navigate({ url: testRunner.url('../resources/test-page.html')});
+  await dp.Fetch.onceRequestPaused();
+  const navigatePromise4 = dp.Page.navigate({ url: testRunner.url('../resources/final.html')});
+  await dp.Fetch.disable();
+
+  testRunner.log(await navigatePromise3);
+  testRunner.log(await navigatePromise4);
+
+  testRunner.completeTest();
+})
diff --git a/third_party/blink/web_tests/http/tests/security/frameNavigation/resources/iframe-that-performs-different-scheme-same-etld-plus-one-top-navigation-without-user-gesture.html b/third_party/blink/web_tests/http/tests/security/frameNavigation/resources/iframe-that-performs-different-scheme-same-etld-plus-one-top-navigation-without-user-gesture.html
new file mode 100644
index 0000000..b7a676f7
--- /dev/null
+++ b/third_party/blink/web_tests/http/tests/security/frameNavigation/resources/iframe-that-performs-different-scheme-same-etld-plus-one-top-navigation-without-user-gesture.html
@@ -0,0 +1,16 @@
+<html>
+<body>
+The navigation should fail. This text should be visible.
+<script>
+window.onload = function()
+{
+    try {
+        top.location = "https://127.0.0.1:8443/security/frameNavigation/resources/navigation-changed-iframe.html";
+        top.postMessage("FAIL", "*");
+    } catch(e) {
+        top.postMessage("PASS", "*");
+    }
+}
+</script>
+</body>
+</html>
diff --git a/third_party/blink/web_tests/http/tests/security/frameNavigation/xss-DENIED-different-scheme-same-etld-plus-1-top-navigation-without-user-gesture-expected.txt b/third_party/blink/web_tests/http/tests/security/frameNavigation/xss-DENIED-different-scheme-same-etld-plus-1-top-navigation-without-user-gesture-expected.txt
new file mode 100644
index 0000000..ee18088
--- /dev/null
+++ b/third_party/blink/web_tests/http/tests/security/frameNavigation/xss-DENIED-different-scheme-same-etld-plus-1-top-navigation-without-user-gesture-expected.txt
@@ -0,0 +1,6 @@
+
+
+--------
+Frame: '<!--framePath //<!--frame0-->-->'
+--------
+The navigation should fail. This text should be visible.
diff --git a/third_party/blink/web_tests/http/tests/security/frameNavigation/xss-DENIED-different-scheme-same-etld-plus-1-top-navigation-without-user-gesture.html b/third_party/blink/web_tests/http/tests/security/frameNavigation/xss-DENIED-different-scheme-same-etld-plus-1-top-navigation-without-user-gesture.html
new file mode 100644
index 0000000..70c20b55
--- /dev/null
+++ b/third_party/blink/web_tests/http/tests/security/frameNavigation/xss-DENIED-different-scheme-same-etld-plus-1-top-navigation-without-user-gesture.html
@@ -0,0 +1,22 @@
+<html>
+<head>
+    <script>
+    if (window.testRunner) {
+        testRunner.dumpAsText();
+        testRunner.dumpChildFrames();
+        testRunner.setDumpConsoleMessages(false);
+        testRunner.waitUntilDone();
+    }
+
+    window.addEventListener("message", e => {
+      if (e.data == "PASS")
+        testRunner.notifyDone();
+      else
+        testRunner.testFailed("'top.location' didn't throw.");
+    });
+    </script>
+</head>
+<body>
+    <iframe src="http://sub1.example.test:8000/security/frameNavigation/resources/iframe-that-performs-different-scheme-same-etld-plus-one-top-navigation-without-user-gesture.html"></iframe>
+</body>
+</html>
diff --git a/third_party/blink/web_tests/platform/fuchsia/fast/block/float/centered-float-avoidance-complexity-expected.png b/third_party/blink/web_tests/platform/fuchsia/fast/block/float/centered-float-avoidance-complexity-expected.png
index 1606638..a839d95 100644
--- a/third_party/blink/web_tests/platform/fuchsia/fast/block/float/centered-float-avoidance-complexity-expected.png
+++ b/third_party/blink/web_tests/platform/fuchsia/fast/block/float/centered-float-avoidance-complexity-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/fuchsia/fast/css3-text/css3-text-decoration/text-decoration-style-inherit-expected.png b/third_party/blink/web_tests/platform/fuchsia/fast/css3-text/css3-text-decoration/text-decoration-style-inherit-expected.png
index 99a0709f..3ec0aa9 100644
--- a/third_party/blink/web_tests/platform/fuchsia/fast/css3-text/css3-text-decoration/text-decoration-style-inherit-expected.png
+++ b/third_party/blink/web_tests/platform/fuchsia/fast/css3-text/css3-text-decoration/text-decoration-style-inherit-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/fuchsia/fast/events/reveal-link-when-focused-expected.png b/third_party/blink/web_tests/platform/fuchsia/fast/events/reveal-link-when-focused-expected.png
index 9feafde..a0a023b2 100644
--- a/third_party/blink/web_tests/platform/fuchsia/fast/events/reveal-link-when-focused-expected.png
+++ b/third_party/blink/web_tests/platform/fuchsia/fast/events/reveal-link-when-focused-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/fuchsia/fast/overflow/position-fixed-transform-clipping-expected.png b/third_party/blink/web_tests/platform/fuchsia/fast/overflow/position-fixed-transform-clipping-expected.png
index 8cd5fdc0..e447378d 100644
--- a/third_party/blink/web_tests/platform/fuchsia/fast/overflow/position-fixed-transform-clipping-expected.png
+++ b/third_party/blink/web_tests/platform/fuchsia/fast/overflow/position-fixed-transform-clipping-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/fuchsia/fast/table/border-collapsing/004-expected.png b/third_party/blink/web_tests/platform/fuchsia/fast/table/border-collapsing/004-expected.png
index b64aed3d..3e7f3ea 100644
--- a/third_party/blink/web_tests/platform/fuchsia/fast/table/border-collapsing/004-expected.png
+++ b/third_party/blink/web_tests/platform/fuchsia/fast/table/border-collapsing/004-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/fuchsia/tables/mozilla/bugs/bug2479-3-expected.png b/third_party/blink/web_tests/platform/fuchsia/tables/mozilla/bugs/bug2479-3-expected.png
index 556a9eae..c67cf26 100644
--- a/third_party/blink/web_tests/platform/fuchsia/tables/mozilla/bugs/bug2479-3-expected.png
+++ b/third_party/blink/web_tests/platform/fuchsia/tables/mozilla/bugs/bug2479-3-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/fuchsia/tables/mozilla/bugs/bug2479-4-expected.png b/third_party/blink/web_tests/platform/fuchsia/tables/mozilla/bugs/bug2479-4-expected.png
index 04679ab..805a09e 100644
--- a/third_party/blink/web_tests/platform/fuchsia/tables/mozilla/bugs/bug2479-4-expected.png
+++ b/third_party/blink/web_tests/platform/fuchsia/tables/mozilla/bugs/bug2479-4-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/fuchsia/tables/mozilla/core/bloomberg-expected.png b/third_party/blink/web_tests/platform/fuchsia/tables/mozilla/core/bloomberg-expected.png
index 1ddf755..d1cb9a8 100644
--- a/third_party/blink/web_tests/platform/fuchsia/tables/mozilla/core/bloomberg-expected.png
+++ b/third_party/blink/web_tests/platform/fuchsia/tables/mozilla/core/bloomberg-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/fuchsia/tables/mozilla_expected_failures/bugs/bug1055-2-expected.png b/third_party/blink/web_tests/platform/fuchsia/tables/mozilla_expected_failures/bugs/bug1055-2-expected.png
index a24832d..0bb51b5 100644
--- a/third_party/blink/web_tests/platform/fuchsia/tables/mozilla_expected_failures/bugs/bug1055-2-expected.png
+++ b/third_party/blink/web_tests/platform/fuchsia/tables/mozilla_expected_failures/bugs/bug1055-2-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/fuchsia/tables/mozilla_expected_failures/bugs/bug2479-5-expected.png b/third_party/blink/web_tests/platform/fuchsia/tables/mozilla_expected_failures/bugs/bug2479-5-expected.png
index 0db6218..a48f218 100644
--- a/third_party/blink/web_tests/platform/fuchsia/tables/mozilla_expected_failures/bugs/bug2479-5-expected.png
+++ b/third_party/blink/web_tests/platform/fuchsia/tables/mozilla_expected_failures/bugs/bug2479-5-expected.png
Binary files differ
diff --git a/third_party/wpt_tools/PRESUBMIT.py b/third_party/wpt_tools/PRESUBMIT.py
index 42e84c4..1b4418d 100644
--- a/third_party/wpt_tools/PRESUBMIT.py
+++ b/third_party/wpt_tools/PRESUBMIT.py
@@ -20,7 +20,8 @@
     name='web_tests/external/PRESUBMIT_test.py',
     cmd=[abspath_to_test],
     kwargs={},
-    message=output_api.PresubmitError
+    message=output_api.PresubmitError,
+    python3=True
   )
   if input_api.verbose:
     print('Running ' + abspath_to_test)
@@ -45,7 +46,7 @@
         blink_path, 'web_tests', 'external', 'wpt')
     try:
       input_api.subprocess.check_output(
-          ['python', wpt_exec_path, 'manifest', '--no-download',
+          ['python3', wpt_exec_path, 'manifest', '--no-download',
            '--path', f.name, '--tests-root', external_wpt])
     except input_api.subprocess.CalledProcessError as exc:
       return [output_api.PresubmitError('wpt manifest failed:', long_text=exc.output)]
diff --git a/third_party/wpt_tools/README.chromium b/third_party/wpt_tools/README.chromium
index 0d19fa9..0a9df00 100644
--- a/third_party/wpt_tools/README.chromium
+++ b/third_party/wpt_tools/README.chromium
@@ -1,7 +1,7 @@
 Name: web-platform-tests - Test Suites for Web Platform specifications
 Short Name: wpt
 URL: https://github.com/web-platform-tests/wpt/
-Version: 3eb2cdae07a8842112e804a59dbd0f9800c64e73
+Version: f6a1fd063fd77ea5865c82682458ae4fb7d04bb3
 License: LICENSES FOR W3C TEST SUITES (https://www.w3.org/Consortium/Legal/2008/03-bsd-license.html)
 License File: NOT_SHIPPED
 Security Critical: no
@@ -12,7 +12,3 @@
     for more details on maintenance.
 Local Modifications:
 - Removed all files except for those listed in wpt/WPTIncludeList.
-- Cherry-picked df9dc69c2340d79fbd56ee2adfa85ac8d4af0b93 temporarily to fix a
-  lint error blocking the importer.
-- Cherry-picked 9b5f845370d7c6fe83b51ceef3f91e42aa85bbc2 temporatily to resolve
-  https://crbug.com/1182579
diff --git a/third_party/wpt_tools/WPTIncludeList b/third_party/wpt_tools/WPTIncludeList
index 0d381c4..9b84204 100644
--- a/third_party/wpt_tools/WPTIncludeList
+++ b/third_party/wpt_tools/WPTIncludeList
@@ -175,6 +175,7 @@
 ./tools/third_party/webencodings/webencodings/tests.py
 ./tools/third_party/webencodings/webencodings/x_user_defined.py
 ./tools/webdriver/webdriver/__init__.py
+./tools/webdriver/webdriver/bidi.py
 ./tools/webdriver/webdriver/client.py
 ./tools/webdriver/webdriver/error.py
 ./tools/webdriver/webdriver/protocol.py
diff --git a/third_party/wpt_tools/checkout.sh b/third_party/wpt_tools/checkout.sh
index 24480dfe..ce43eb7 100755
--- a/third_party/wpt_tools/checkout.sh
+++ b/third_party/wpt_tools/checkout.sh
@@ -9,7 +9,7 @@
 
 TARGET_DIR=$DIR/wpt
 REMOTE_REPO="https://github.com/web-platform-tests/wpt.git"
-WPT_HEAD=3eb2cdae07a8842112e804a59dbd0f9800c64e73
+WPT_HEAD=f6a1fd063fd77ea5865c82682458ae4fb7d04bb3
 
 function clone {
   # Remove existing repo if already exists.
diff --git a/third_party/wpt_tools/wpt/docs/commands.json b/third_party/wpt_tools/wpt/docs/commands.json
index f3494b8..55408d8 100644
--- a/third_party/wpt_tools/wpt/docs/commands.json
+++ b/third_party/wpt_tools/wpt/docs/commands.json
@@ -3,7 +3,6 @@
     "path": "frontend.py",
     "script": "build",
     "help": "Build documentation",
-    "py3only": true,
     "virtualenv": true,
     "requirements": [
       "./requirements.txt"
diff --git a/third_party/wpt_tools/wpt/tools/certs/cacert.key b/third_party/wpt_tools/wpt/tools/certs/cacert.key
index 6b240e5..7b9bd97 100644
--- a/third_party/wpt_tools/wpt/tools/certs/cacert.key
+++ b/third_party/wpt_tools/wpt/tools/certs/cacert.key
@@ -1,30 +1,30 @@
 -----BEGIN ENCRYPTED PRIVATE KEY-----
-MIIFHDBOBgkqhkiG9w0BBQ0wQTApBgkqhkiG9w0BBQwwHAQI666o+tmBgXYCAggA
-MAwGCCqGSIb3DQIJBQAwFAYIKoZIhvcNAwcECILZgh0KpE9MBIIEyCgMCCFkSlDm
-wPDpQ0rFCyQyAcdP5HMnLM/W+2z5TYDqdhtLd+XqD2lGqr4lCd33CoDubRxZoU/t
-VQ0TuDQcfhCz8HG6nXMpai2NDrc5Ot29fVFdMFkk8na17RhMKUc6Gan/TDi+E4i8
-e2YakJ5JgaBUFBf/jyqJfLsYJezNFhn5+nCsz+KbZBoht2B+W/rq/FAUzwPGJ+Yc
-8J2gVcuRUctXwS47yI8aklXhIhwmL89Ioad3rpypeDX3tu5TeG2nv/d1uV3/FngC
-/18b4eJz6dpqdTVN5Uoa3NjHjY8a8mcBvWKBL6mWy2dTVl9W8wdgtVkvc0PsF3Pu
-oJy5oeWhP6dSqAvDHTDXa38rt3H9IRmTsIXd0VVOIhFjg8tDQTBR/CyJKp4JO8Oy
-y/RGZsdqcO03ujolWOlDY4Pi8e7JpHK+1kyhGOTimi/9JuWtiSp40gKDzylL1oaw
-hr1UaPHl/kA/gJgpDBKT+PXQt2Gm9qje9+B/U6zj31bVd0r1S0qMlXz81t/K+BcH
-u/5NaZsDXyE8rufyxAHPBKPxXMZONG7y6EbF868EHUrCAKlElfHtTnvP/k68V3eH
-GnVzsId8eirzutrRdugPXS7Pg1Uj1CL6Ga8ia9MfEIbSx/g2FQ9SMnurXVeWv1qM
-uMNZbRZUkkJD8T/VqB7ezrI553iTqZFGr6fQ0dyXBS0m1phPSkmPcqE42sYCIZcy
-KZGYeWS1LkFRoTxoAwRS8cf14NjmoG+Aby4iyB3QiMDOmsylZDF0vsYeUbUIkxyz
-GnSn2LaVpT3ZeuziLnNn3JaeIy946jEgZETvCDruurV5AoAb9pXG2Xuysevx/AuV
-Owzz82PqG308kq6xhcGNK+v9FRbagHaGZ5EG+iVUTjN348NjJIriDVdrH06fBqe+
-UFD9fAnN/Kj4dIJKUCun4UZiz2jHeNnAmkLwOZn/eTK1LbUrmT0J+3eupaF7zzxo
-CednARzHvAiEVAX8Wd5DauIjczesjdrO9ys7dTMeZeyV5t/s8W66TB6tVDIQwTIp
-3skdhbw22zI2lcrDhZbbYhwO9R6d4liENUZ/W5ISLDXn+w8kqqh1qXoE8vR0lxpy
-Oz27kg5DBqyUflx99HwLj8epMNpGzepMAeFXTd+gLFKk7gWtU/5mJItTEfBu8Iih
-FT3/eesClxxzBm2Mk1viv/iKspTH92+mKEz2ng6jONWxHXkfuxNWe4wX+KmO7s7P
-plzxnbr2TlNTewbuYqyKMwe9KOiXosdfxUkJq6D8R0bf+A69M/dggO7BVdxNfF71
-+xdfqjWjll/58lQ15KdlycfVlYj6iDvK1ChrK4UH2myGbmhThIItezPm8Oo4i3I6
-iPk8ns/BqfFH2raE7cw7G3Ug6cEJrc8OG4c1mfBuHPAeDrCWNu7tEqwKrGGJVA3b
-1GiBo72z5XVOiP7lku708ZEfrzh2UGuSvx0gbpW3aMDNnVq2O6euIquZugeewckq
-2Ox9C1vADIA8ugRdHbSVRQE33YiU1QS3MvQ9TYW/Zv7Buuhrdk2S1WLRpkWw+qR6
-62EFFAw6wjpK/vciGucrV8GyybdxPFqZPMtQkghaOPUO84Qsx2rZXAR+4/gZuHGm
-v2L3tYSDEmQ+6kEynWtX/w==
+MIIFHDBOBgkqhkiG9w0BBQ0wQTApBgkqhkiG9w0BBQwwHAQIy+AmMEgUCbACAggA
+MAwGCCqGSIb3DQIJBQAwFAYIKoZIhvcNAwcECJTlAsVqhvE6BIIEyG++PNkc48P8
+oprMaYzGL0QsYQb7xLR/JYhWB2GVUZGTTYORqiUIgKUQkayG1cxO2Jyhqb+qZfdI
+gAt9x7uS6A2omVXCm0ZW1vl4NGOyNdHvTmCnf9EkCzqWYYUW0nxxdV6DsVtsOb1/
+gDJxo3n8CYue/5zGuOprQJRUZAHrvx2huKMzg3wxSzYOaRfYVy5N9dKbgroyd/Vy
+05H+k9HY9+YwhRhXSTyqMRNj3ftbizdoPFyWihVABUvan6ExScuXNbfVu/qwxG86
+WRAFr6qu1EBhe5OLttHDYnhr14HjSTkTp289wKAn9fOMzvUh0NEMTx5PgF3u0AK6
+edhtDrJwjgrRc6ENJw3mM6VGa/FxR0eRyp38+/MteYyfz5eVnyPnJw22uiBd8i2j
+6dzDS+zZMA3vmp2NdB3rbgleQ5sdNEqJz4GC/yqTtwGDwH+OrbMpl2BAn1ckz9R9
+2b+DYsstBtLDx+R6JOy/sOFGcZla5T5TYOVJWD5i4vnW216rK6Pi+mbXa9WzGfGH
+UmghkDDh7iLYIBT3+YgEHqAeb+RKfeq7X3r6PBOAhavLp/4w2ZffywnyfxOxKQwE
+nb0bU8OPkPe3by3sbfS9H17729dTMMAJ3NGVjst2rEGYbDU/OZuBZzW3fwFnwKV2
+N3BTe3nPpZ/SXip7I6Hf+b6TNGeyVZUvBzw7spGGlXbbK76EdWY3dcs2OMH6JxUW
+Ib/bkNqclqSeXYFigzDozAuBJ2cM0TXxhnvLLuLLgL4+kH/d0tdCLJLNSqDy4Q+N
+GOFWZGtaowGuhpblGsAC/Z2wdPJ+fVQxGbHfGOttyoxCdfNfSclk7KuJFVQCGn1H
+aBvZfr1bf+o+mxuDmaDKFyb3fGQ55AgAdno5NxEInSofuopAefNxwfGzhTYmnAdF
+JY6WvXisz57EW9SjEvGHJMRREYw9a8tPKCOXa9CgPFsVdhPA+cFaBI3qDoY9E6rv
+cYdhXKatnORDHTQXPJz241cJK1yz6VDqAzExQlXli+dSqLl04X/3hAjIAMtDeeCc
+7F3U9XLkMIAiU3GKHIA0mq8wkTucOd7EVV+TS+i4e9MBIRueNyEd4u6m04G7Y4kf
+pkGowONXy1CETy4RoRNJi+nGC7TwCLzbEg6Sb9xs9q2lLh/cAiqdArPk9vLNe9Gh
+YSlcwN4WSPtQTN4hjiBhWe7O9Ux6f2llptyQhCzpb58h7IOQgTUcHOENoxUnVQwS
+RYsj+29aAYcLI+5NJzMwYXNR3Fdb3sDUn16f/NsfZUpxFzmu7zSrt7SJJfFasMN3
+EjwuSoEtVdwT7+IOO8A9gRuh5JRjuFYx252Mc0HY/R3qlTwrBNXyUUyFUO8t6TYN
+gxpJEKFEcsgUe+O345PrwzX+lbs9Vqvz52XO+aKsVDmbjTzWV3MnSPZcBnw1GTtq
+uKGJYDOCDhV7R8IElf87xQz6z6qNYDfAjlcxfeJK5V6U07Qwi4pxvoychK/4hQ7q
+X8mHWDMdyTGqED191NcI0EMHu6cjm5JSs7gXQibYrRaUGXcgk50o+LE6hIyd4VC1
+vmkKK48mcfi4GFFtmfiWX4vDK8Qzs2/PZNZURc3zgXxu4CtopFnL5SX+ZOAMSWUo
+DV8CBNpQbBi6sYo4aHNB3g==
 -----END ENCRYPTED PRIVATE KEY-----
diff --git a/third_party/wpt_tools/wpt/tools/certs/cacert.pem b/third_party/wpt_tools/wpt/tools/certs/cacert.pem
index fcc977b..f7de3a3 100644
--- a/third_party/wpt_tools/wpt/tools/certs/cacert.pem
+++ b/third_party/wpt_tools/wpt/tools/certs/cacert.pem
@@ -1,176 +1,176 @@
 -----BEGIN CERTIFICATE-----
-MIJAhzCCP2+gAwIBAgIDBlVFMA0GCSqGSIb3DQEBCwUAMB0xGzAZBgNVBAMMEndl
-Yi1wbGF0Zm9ybS10ZXN0czAeFw0yMTAxMTIxNjEzMjhaFw0yMjAxMTIxNjEzMjha
+MIJAhzCCP2+gAwIBAgIDDuxtMA0GCSqGSIb3DQEBCwUAMB0xGzAZBgNVBAMMEndl
+Yi1wbGF0Zm9ybS10ZXN0czAeFw0yMTAzMTIwMDI3MzNaFw0yMjAzMTIwMDI3MzNa
 MB0xGzAZBgNVBAMMEndlYi1wbGF0Zm9ybS10ZXN0czCCASIwDQYJKoZIhvcNAQEB
-BQADggEPADCCAQoCggEBAKlAc+52QkFGs3xjT0OiT3t7HajqFqelNp5toVZfL/SF
-cXqvhldvWzlKs3XW4+OKnGQP1nB7qmZZ8GjSY02Nho36Vq+YdzmHIHYPZcAlfmNO
-6iY/nca7C9MEIVJvxQsG/C5ZUTkKJ93iDehGay5YF8wiIb+k6cmaV5cDs+oBwmwu
-X3hxsDjOklUYCVY4Wvd4fU/zR/LdI3QZTAlNa4eLu7v/8z0vo8vG7T8VS09mc6eh
-BjB0x1L7XE6n+4v3gGE8RbxeaIpZbv8vVWK1LLLQ01gCOiNFjuuD3VcBqnZTbV9/
-v4MqHrPFfZm1MxesB/kybMTve4Y6PjT1U3zgJsrV0UcCAwEAAaOCPc4wgj3KMAwG
-A1UdEwQFMAMBAf8wHQYDVR0OBBYEFNxtjWLdVJjIYBfC01Gzv3NbXJC5MEcGA1Ud
-IwRAMD6AFNxtjWLdVJjIYBfC01Gzv3NbXJC5oSGkHzAdMRswGQYDVQQDDBJ3ZWIt
-cGxhdGZvcm0tdGVzdHOCAwZVRTALBgNVHQ8EBAMCAgQwgh+bBgNVHR4Egh+SMIIf
-jqCCH4owE4IRd2ViLXBsYXRmb3JtLnRlc3QwF4IVb3A4LndlYi1wbGF0Zm9ybS50
-ZXN0MBeCFW9wNy53ZWItcGxhdGZvcm0udGVzdDAXghVvcDkud2ViLXBsYXRmb3Jt
-LnRlc3QwF4IVb3A0LndlYi1wbGF0Zm9ybS50ZXN0MBeCFW5vdC13ZWItcGxhdGZv
-cm0udGVzdDAXghVvcDYud2ViLXBsYXRmb3JtLnRlc3QwF4IVb3AzLndlYi1wbGF0
-Zm9ybS50ZXN0MBeCFW9wMi53ZWItcGxhdGZvcm0udGVzdDAXghVvcDEud2ViLXBs
-YXRmb3JtLnRlc3QwF4IVd3d3LndlYi1wbGF0Zm9ybS50ZXN0MBeCFW9wNS53ZWIt
-cGxhdGZvcm0udGVzdDAYghZvcDg4LndlYi1wbGF0Zm9ybS50ZXN0MBiCFm9wOTgu
-d2ViLXBsYXRmb3JtLnRlc3QwGIIWb3A4NS53ZWItcGxhdGZvcm0udGVzdDAYghZv
-cDg5LndlYi1wbGF0Zm9ybS50ZXN0MBiCFm9wNjYud2ViLXBsYXRmb3JtLnRlc3Qw
-GIIWb3A3Mi53ZWItcGxhdGZvcm0udGVzdDAYghZvcDI0LndlYi1wbGF0Zm9ybS50
-ZXN0MBiCFm9wNDEud2ViLXBsYXRmb3JtLnRlc3QwGIIWb3A3OS53ZWItcGxhdGZv
-cm0udGVzdDAYghZvcDkxLndlYi1wbGF0Zm9ybS50ZXN0MBiCFm9wNTkud2ViLXBs
-YXRmb3JtLnRlc3QwGIIWb3AzOS53ZWItcGxhdGZvcm0udGVzdDAYghZvcDYwLndl
-Yi1wbGF0Zm9ybS50ZXN0MBiCFm9wNTgud2ViLXBsYXRmb3JtLnRlc3QwGIIWb3Ay
-OC53ZWItcGxhdGZvcm0udGVzdDAYghZ3d3cxLndlYi1wbGF0Zm9ybS50ZXN0MBiC
-Fm9wMTQud2ViLXBsYXRmb3JtLnRlc3QwGIIWb3A2OS53ZWItcGxhdGZvcm0udGVz
-dDAYghZvcDQwLndlYi1wbGF0Zm9ybS50ZXN0MBiCFm9wNzQud2ViLXBsYXRmb3Jt
-LnRlc3QwGIIWb3AzMS53ZWItcGxhdGZvcm0udGVzdDAYghZvcDE4LndlYi1wbGF0
-Zm9ybS50ZXN0MBiCFm9wNzMud2ViLXBsYXRmb3JtLnRlc3QwGIIWb3A3Ny53ZWIt
-cGxhdGZvcm0udGVzdDAYghZvcDEyLndlYi1wbGF0Zm9ybS50ZXN0MBiCFm9wNTQu
-d2ViLXBsYXRmb3JtLnRlc3QwGIIWb3A2My53ZWItcGxhdGZvcm0udGVzdDAYghZv
-cDcxLndlYi1wbGF0Zm9ybS50ZXN0MBiCFm9wOTUud2ViLXBsYXRmb3JtLnRlc3Qw
-GIIWb3AxNi53ZWItcGxhdGZvcm0udGVzdDAYghZvcDM2LndlYi1wbGF0Zm9ybS50
-ZXN0MBiCFm9wMjcud2ViLXBsYXRmb3JtLnRlc3QwGIIWb3AyOS53ZWItcGxhdGZv
-cm0udGVzdDAYghZvcDk0LndlYi1wbGF0Zm9ybS50ZXN0MBiCFm9wNDQud2ViLXBs
-YXRmb3JtLnRlc3QwGIIWb3AzMy53ZWItcGxhdGZvcm0udGVzdDAYghZvcDg0Lndl
-Yi1wbGF0Zm9ybS50ZXN0MBiCFm9wMzIud2ViLXBsYXRmb3JtLnRlc3QwGIIWb3A2
-MS53ZWItcGxhdGZvcm0udGVzdDAYghZvcDcwLndlYi1wbGF0Zm9ybS50ZXN0MBiC
-Fnd3dzIud2ViLXBsYXRmb3JtLnRlc3QwGIIWb3A0My53ZWItcGxhdGZvcm0udGVz
-dDAYghZvcDc4LndlYi1wbGF0Zm9ybS50ZXN0MBiCFm9wMjYud2ViLXBsYXRmb3Jt
-LnRlc3QwGIIWb3A3Ni53ZWItcGxhdGZvcm0udGVzdDAYghZvcDUyLndlYi1wbGF0
-Zm9ybS50ZXN0MBiCFm9wOTkud2ViLXBsYXRmb3JtLnRlc3QwGIIWb3A4Ni53ZWIt
-cGxhdGZvcm0udGVzdDAYghZvcDQ2LndlYi1wbGF0Zm9ybS50ZXN0MBiCFm9wMTcu
-d2ViLXBsYXRmb3JtLnRlc3QwGIIWb3A5MC53ZWItcGxhdGZvcm0udGVzdDAYghZv
-cDkzLndlYi1wbGF0Zm9ybS50ZXN0MBiCFm9wMTAud2ViLXBsYXRmb3JtLnRlc3Qw
-GIIWb3A1NS53ZWItcGxhdGZvcm0udGVzdDAYghZvcDQ3LndlYi1wbGF0Zm9ybS50
-ZXN0MBiCFm9wNTEud2ViLXBsYXRmb3JtLnRlc3QwGIIWb3A0NS53ZWItcGxhdGZv
-cm0udGVzdDAYghZvcDgwLndlYi1wbGF0Zm9ybS50ZXN0MBiCFm9wNjgud2ViLXBs
-YXRmb3JtLnRlc3QwGIIWb3A0OS53ZWItcGxhdGZvcm0udGVzdDAYghZvcDU3Lndl
-Yi1wbGF0Zm9ybS50ZXN0MBiCFm9wMzUud2ViLXBsYXRmb3JtLnRlc3QwGIIWb3A2
-Ny53ZWItcGxhdGZvcm0udGVzdDAYghZvcDkyLndlYi1wbGF0Zm9ybS50ZXN0MBiC
-Fm9wMTUud2ViLXBsYXRmb3JtLnRlc3QwGIIWb3AxMy53ZWItcGxhdGZvcm0udGVz
-dDAYghZvcDc1LndlYi1wbGF0Zm9ybS50ZXN0MBiCFm9wNjQud2ViLXBsYXRmb3Jt
-LnRlc3QwGIIWb3A5Ny53ZWItcGxhdGZvcm0udGVzdDAYghZvcDM3LndlYi1wbGF0
-Zm9ybS50ZXN0MBiCFm9wNTYud2ViLXBsYXRmb3JtLnRlc3QwGIIWb3A2Mi53ZWIt
-cGxhdGZvcm0udGVzdDAYghZvcDgyLndlYi1wbGF0Zm9ybS50ZXN0MBiCFm9wMjUu
-d2ViLXBsYXRmb3JtLnRlc3QwGIIWb3AxMS53ZWItcGxhdGZvcm0udGVzdDAYghZv
-cDUwLndlYi1wbGF0Zm9ybS50ZXN0MBiCFm9wMzgud2ViLXBsYXRmb3JtLnRlc3Qw
-GIIWb3A4My53ZWItcGxhdGZvcm0udGVzdDAYghZvcDgxLndlYi1wbGF0Zm9ybS50
-ZXN0MBiCFm9wMjAud2ViLXBsYXRmb3JtLnRlc3QwGIIWb3AyMS53ZWItcGxhdGZv
-cm0udGVzdDAYghZvcDIzLndlYi1wbGF0Zm9ybS50ZXN0MBiCFm9wNDIud2ViLXBs
-YXRmb3JtLnRlc3QwGIIWb3AyMi53ZWItcGxhdGZvcm0udGVzdDAYghZvcDY1Lndl
-Yi1wbGF0Zm9ybS50ZXN0MBiCFm9wOTYud2ViLXBsYXRmb3JtLnRlc3QwGIIWb3A4
-Ny53ZWItcGxhdGZvcm0udGVzdDAYghZvcDE5LndlYi1wbGF0Zm9ybS50ZXN0MBiC
-Fm9wNTMud2ViLXBsYXRmb3JtLnRlc3QwGIIWb3AzMC53ZWItcGxhdGZvcm0udGVz
-dDAYghZvcDQ4LndlYi1wbGF0Zm9ybS50ZXN0MBiCFm9wMzQud2ViLXBsYXRmb3Jt
-LnRlc3QwG4IZb3A2Lm5vdC13ZWItcGxhdGZvcm0udGVzdDAbghlvcDMubm90LXdl
-Yi1wbGF0Zm9ybS50ZXN0MBuCGW9wMi5ub3Qtd2ViLXBsYXRmb3JtLnRlc3QwG4IZ
-b3A1Lm5vdC13ZWItcGxhdGZvcm0udGVzdDAbghl3d3cubm90LXdlYi1wbGF0Zm9y
-bS50ZXN0MBuCGXd3dy53d3cud2ViLXBsYXRmb3JtLnRlc3QwG4IZb3A3Lm5vdC13
-ZWItcGxhdGZvcm0udGVzdDAbghlvcDQubm90LXdlYi1wbGF0Zm9ybS50ZXN0MBuC
-GW9wOC5ub3Qtd2ViLXBsYXRmb3JtLnRlc3QwG4IZb3A5Lm5vdC13ZWItcGxhdGZv
-cm0udGVzdDAbghlvcDEubm90LXdlYi1wbGF0Zm9ybS50ZXN0MByCGm9wMzYubm90
+BQADggEPADCCAQoCggEBAL28unmE4CuE50yyQ87wsXB1py9fN6vq9Re7kZrZVUbl
+p90KMqfb9mN9LTrJBZWygzz1lXEXTkYcsbmDhbxT5xpptpuK41EKl5GudhRd5k0V
+9urrVVANlWnFiaeSazFvqw0UPktrL2+C2VLXKy+b6cN7fUImS12+NGUkujmU11hY
+Yg3eP/habOoeKZG9gAnlIWLmFZ2GQ5qLru2IAH0OOL7C0w7JihKfYHI+Lq2a96KG
+FdPlOzDnUFidEqN3ssQIIX8C4jNhbQr2r7Magg8Lj4WeyrPlZGw2E/rGzmD1E1C3
+3KjOfBv0/AOprZGo1JsvsuYoq07Et/yx1WJwwm4fnO8CAwEAAaOCPc4wgj3KMAwG
+A1UdEwQFMAMBAf8wHQYDVR0OBBYEFL14OMI385wGqOs+r1qhiCQUhVu+MEcGA1Ud
+IwRAMD6AFL14OMI385wGqOs+r1qhiCQUhVu+oSGkHzAdMRswGQYDVQQDDBJ3ZWIt
+cGxhdGZvcm0tdGVzdHOCAw7sbTALBgNVHQ8EBAMCAgQwgh+bBgNVHR4Egh+SMIIf
+jqCCH4owE4IRd2ViLXBsYXRmb3JtLnRlc3QwF4IVbm90LXdlYi1wbGF0Zm9ybS50
+ZXN0MBeCFW9wNi53ZWItcGxhdGZvcm0udGVzdDAXghV3d3cud2ViLXBsYXRmb3Jt
+LnRlc3QwF4IVb3AxLndlYi1wbGF0Zm9ybS50ZXN0MBeCFW9wNy53ZWItcGxhdGZv
+cm0udGVzdDAXghVvcDgud2ViLXBsYXRmb3JtLnRlc3QwF4IVb3AyLndlYi1wbGF0
+Zm9ybS50ZXN0MBeCFW9wOS53ZWItcGxhdGZvcm0udGVzdDAXghVvcDUud2ViLXBs
+YXRmb3JtLnRlc3QwF4IVb3A0LndlYi1wbGF0Zm9ybS50ZXN0MBeCFW9wMy53ZWIt
+cGxhdGZvcm0udGVzdDAYghZvcDQwLndlYi1wbGF0Zm9ybS50ZXN0MBiCFm9wNjEu
+d2ViLXBsYXRmb3JtLnRlc3QwGIIWb3AzMy53ZWItcGxhdGZvcm0udGVzdDAYghZv
+cDc5LndlYi1wbGF0Zm9ybS50ZXN0MBiCFm9wMzgud2ViLXBsYXRmb3JtLnRlc3Qw
+GIIWb3A3Ny53ZWItcGxhdGZvcm0udGVzdDAYghZvcDQzLndlYi1wbGF0Zm9ybS50
+ZXN0MBiCFm9wNDQud2ViLXBsYXRmb3JtLnRlc3QwGIIWb3AxNC53ZWItcGxhdGZv
+cm0udGVzdDAYghZvcDQ5LndlYi1wbGF0Zm9ybS50ZXN0MBiCFm9wMzYud2ViLXBs
+YXRmb3JtLnRlc3QwGIIWb3AyMS53ZWItcGxhdGZvcm0udGVzdDAYghZvcDYwLndl
+Yi1wbGF0Zm9ybS50ZXN0MBiCFm9wNzEud2ViLXBsYXRmb3JtLnRlc3QwGIIWb3A1
+Ni53ZWItcGxhdGZvcm0udGVzdDAYghZvcDk0LndlYi1wbGF0Zm9ybS50ZXN0MBiC
+Fm9wMTgud2ViLXBsYXRmb3JtLnRlc3QwGIIWb3A1Ny53ZWItcGxhdGZvcm0udGVz
+dDAYghZ3d3cyLndlYi1wbGF0Zm9ybS50ZXN0MBiCFm9wOTEud2ViLXBsYXRmb3Jt
+LnRlc3QwGIIWb3A0MS53ZWItcGxhdGZvcm0udGVzdDAYghZvcDQ3LndlYi1wbGF0
+Zm9ybS50ZXN0MBiCFm9wNTIud2ViLXBsYXRmb3JtLnRlc3QwGIIWb3AyNS53ZWIt
+cGxhdGZvcm0udGVzdDAYghZvcDE2LndlYi1wbGF0Zm9ybS50ZXN0MBiCFm9wMjYu
+d2ViLXBsYXRmb3JtLnRlc3QwGIIWb3AxMC53ZWItcGxhdGZvcm0udGVzdDAYghZv
+cDY5LndlYi1wbGF0Zm9ybS50ZXN0MBiCFm9wMTIud2ViLXBsYXRmb3JtLnRlc3Qw
+GIIWb3A5Ni53ZWItcGxhdGZvcm0udGVzdDAYghZvcDc0LndlYi1wbGF0Zm9ybS50
+ZXN0MBiCFm9wODkud2ViLXBsYXRmb3JtLnRlc3QwGIIWb3A4NC53ZWItcGxhdGZv
+cm0udGVzdDAYghZvcDM3LndlYi1wbGF0Zm9ybS50ZXN0MBiCFm9wNDUud2ViLXBs
+YXRmb3JtLnRlc3QwGIIWb3A2Mi53ZWItcGxhdGZvcm0udGVzdDAYghZvcDg2Lndl
+Yi1wbGF0Zm9ybS50ZXN0MBiCFm9wNDIud2ViLXBsYXRmb3JtLnRlc3QwGIIWb3Az
+NC53ZWItcGxhdGZvcm0udGVzdDAYghZvcDQ4LndlYi1wbGF0Zm9ybS50ZXN0MBiC
+Fm9wNzUud2ViLXBsYXRmb3JtLnRlc3QwGIIWb3A1MC53ZWItcGxhdGZvcm0udGVz
+dDAYghZvcDIyLndlYi1wbGF0Zm9ybS50ZXN0MBiCFm9wMjMud2ViLXBsYXRmb3Jt
+LnRlc3QwGIIWb3A3My53ZWItcGxhdGZvcm0udGVzdDAYghZvcDIwLndlYi1wbGF0
+Zm9ybS50ZXN0MBiCFm9wOTIud2ViLXBsYXRmb3JtLnRlc3QwGIIWb3AzMi53ZWIt
+cGxhdGZvcm0udGVzdDAYghZvcDgyLndlYi1wbGF0Zm9ybS50ZXN0MBiCFm9wOTgu
+d2ViLXBsYXRmb3JtLnRlc3QwGIIWb3AzMS53ZWItcGxhdGZvcm0udGVzdDAYghZv
+cDMwLndlYi1wbGF0Zm9ybS50ZXN0MBiCFm9wODcud2ViLXBsYXRmb3JtLnRlc3Qw
+GIIWb3A3MC53ZWItcGxhdGZvcm0udGVzdDAYghZvcDY0LndlYi1wbGF0Zm9ybS50
+ZXN0MBiCFm9wMTEud2ViLXBsYXRmb3JtLnRlc3QwGIIWb3AxNS53ZWItcGxhdGZv
+cm0udGVzdDAYghZvcDg1LndlYi1wbGF0Zm9ybS50ZXN0MBiCFm9wNTgud2ViLXBs
+YXRmb3JtLnRlc3QwGIIWd3d3MS53ZWItcGxhdGZvcm0udGVzdDAYghZvcDgzLndl
+Yi1wbGF0Zm9ybS50ZXN0MBiCFm9wMTMud2ViLXBsYXRmb3JtLnRlc3QwGIIWb3A2
+OC53ZWItcGxhdGZvcm0udGVzdDAYghZvcDk5LndlYi1wbGF0Zm9ybS50ZXN0MBiC
+Fm9wMzkud2ViLXBsYXRmb3JtLnRlc3QwGIIWb3A1OS53ZWItcGxhdGZvcm0udGVz
+dDAYghZvcDk1LndlYi1wbGF0Zm9ybS50ZXN0MBiCFm9wNTMud2ViLXBsYXRmb3Jt
+LnRlc3QwGIIWb3A2My53ZWItcGxhdGZvcm0udGVzdDAYghZvcDU0LndlYi1wbGF0
+Zm9ybS50ZXN0MBiCFm9wODgud2ViLXBsYXRmb3JtLnRlc3QwGIIWb3A5My53ZWIt
+cGxhdGZvcm0udGVzdDAYghZvcDI4LndlYi1wbGF0Zm9ybS50ZXN0MBiCFm9wNTEu
+d2ViLXBsYXRmb3JtLnRlc3QwGIIWb3A2Ni53ZWItcGxhdGZvcm0udGVzdDAYghZv
+cDE5LndlYi1wbGF0Zm9ybS50ZXN0MBiCFm9wOTAud2ViLXBsYXRmb3JtLnRlc3Qw
+GIIWb3A4MC53ZWItcGxhdGZvcm0udGVzdDAYghZvcDY3LndlYi1wbGF0Zm9ybS50
+ZXN0MBiCFm9wNjUud2ViLXBsYXRmb3JtLnRlc3QwGIIWb3AyNy53ZWItcGxhdGZv
+cm0udGVzdDAYghZvcDc2LndlYi1wbGF0Zm9ybS50ZXN0MBiCFm9wNzIud2ViLXBs
+YXRmb3JtLnRlc3QwGIIWb3AyOS53ZWItcGxhdGZvcm0udGVzdDAYghZvcDk3Lndl
+Yi1wbGF0Zm9ybS50ZXN0MBiCFm9wMzUud2ViLXBsYXRmb3JtLnRlc3QwGIIWb3A1
+NS53ZWItcGxhdGZvcm0udGVzdDAYghZvcDQ2LndlYi1wbGF0Zm9ybS50ZXN0MBiC
+Fm9wNzgud2ViLXBsYXRmb3JtLnRlc3QwGIIWb3AxNy53ZWItcGxhdGZvcm0udGVz
+dDAYghZvcDI0LndlYi1wbGF0Zm9ybS50ZXN0MBiCFm9wODEud2ViLXBsYXRmb3Jt
+LnRlc3QwG4IZb3A3Lm5vdC13ZWItcGxhdGZvcm0udGVzdDAbghlvcDQubm90LXdl
+Yi1wbGF0Zm9ybS50ZXN0MBuCGW9wOC5ub3Qtd2ViLXBsYXRmb3JtLnRlc3QwG4IZ
+d3d3Lnd3dy53ZWItcGxhdGZvcm0udGVzdDAbghlvcDkubm90LXdlYi1wbGF0Zm9y
+bS50ZXN0MBuCGXd3dy5ub3Qtd2ViLXBsYXRmb3JtLnRlc3QwG4IZb3AxLm5vdC13
+ZWItcGxhdGZvcm0udGVzdDAbghlvcDMubm90LXdlYi1wbGF0Zm9ybS50ZXN0MBuC
+GW9wNS5ub3Qtd2ViLXBsYXRmb3JtLnRlc3QwG4IZb3AyLm5vdC13ZWItcGxhdGZv
+cm0udGVzdDAbghlvcDYubm90LXdlYi1wbGF0Zm9ybS50ZXN0MByCGm9wMjYubm90
+LXdlYi1wbGF0Zm9ybS50ZXN0MByCGm9wOTQubm90LXdlYi1wbGF0Zm9ybS50ZXN0
+MByCGm9wNzUubm90LXdlYi1wbGF0Zm9ybS50ZXN0MByCGm9wNTQubm90LXdlYi1w
+bGF0Zm9ybS50ZXN0MByCGm9wMjUubm90LXdlYi1wbGF0Zm9ybS50ZXN0MByCGm9w
+Mjgubm90LXdlYi1wbGF0Zm9ybS50ZXN0MByCGm9wOTIubm90LXdlYi1wbGF0Zm9y
+bS50ZXN0MByCGm9wMTcubm90LXdlYi1wbGF0Zm9ybS50ZXN0MByCGm9wOTUubm90
 LXdlYi1wbGF0Zm9ybS50ZXN0MByCGm9wNTMubm90LXdlYi1wbGF0Zm9ybS50ZXN0
-MByCGm9wNTAubm90LXdlYi1wbGF0Zm9ybS50ZXN0MByCGm9wMjQubm90LXdlYi1w
-bGF0Zm9ybS50ZXN0MByCGm9wMzEubm90LXdlYi1wbGF0Zm9ybS50ZXN0MByCGm9w
-OTUubm90LXdlYi1wbGF0Zm9ybS50ZXN0MByCGm9wODMubm90LXdlYi1wbGF0Zm9y
-bS50ZXN0MByCGnd3dzIubm90LXdlYi1wbGF0Zm9ybS50ZXN0MByCGm9wNzMubm90
-LXdlYi1wbGF0Zm9ybS50ZXN0MByCGm9wMTkubm90LXdlYi1wbGF0Zm9ybS50ZXN0
-MByCGm9wMjEubm90LXdlYi1wbGF0Zm9ybS50ZXN0MByCGm9wODEubm90LXdlYi1w
-bGF0Zm9ybS50ZXN0MByCGm9wNzAubm90LXdlYi1wbGF0Zm9ybS50ZXN0MByCGm9w
-Nzgubm90LXdlYi1wbGF0Zm9ybS50ZXN0MByCGm9wNDAubm90LXdlYi1wbGF0Zm9y
-bS50ZXN0MByCGm9wMjUubm90LXdlYi1wbGF0Zm9ybS50ZXN0MByCGm9wNjUubm90
-LXdlYi1wbGF0Zm9ybS50ZXN0MByCGnd3dy53d3cyLndlYi1wbGF0Zm9ybS50ZXN0
-MByCGm9wODAubm90LXdlYi1wbGF0Zm9ybS50ZXN0MByCGm9wNTIubm90LXdlYi1w
+MByCGm9wMzcubm90LXdlYi1wbGF0Zm9ybS50ZXN0MByCGm9wNDEubm90LXdlYi1w
 bGF0Zm9ybS50ZXN0MByCGm9wNjgubm90LXdlYi1wbGF0Zm9ybS50ZXN0MByCGm9w
-NDUubm90LXdlYi1wbGF0Zm9ybS50ZXN0MByCGm9wNzEubm90LXdlYi1wbGF0Zm9y
-bS50ZXN0MByCGm9wNzIubm90LXdlYi1wbGF0Zm9ybS50ZXN0MByCGm9wOTAubm90
-LXdlYi1wbGF0Zm9ybS50ZXN0MByCGm9wODkubm90LXdlYi1wbGF0Zm9ybS50ZXN0
-MByCGm9wNDkubm90LXdlYi1wbGF0Zm9ybS50ZXN0MByCGm9wNzcubm90LXdlYi1w
-bGF0Zm9ybS50ZXN0MByCGm9wNzkubm90LXdlYi1wbGF0Zm9ybS50ZXN0MByCGm9w
-ODIubm90LXdlYi1wbGF0Zm9ybS50ZXN0MByCGnd3dy53d3cxLndlYi1wbGF0Zm9y
-bS50ZXN0MByCGm9wMTIubm90LXdlYi1wbGF0Zm9ybS50ZXN0MByCGm9wMzkubm90
-LXdlYi1wbGF0Zm9ybS50ZXN0MByCGm9wNDQubm90LXdlYi1wbGF0Zm9ybS50ZXN0
-MByCGnd3dzEubm90LXdlYi1wbGF0Zm9ybS50ZXN0MByCGm9wNTgubm90LXdlYi1w
-bGF0Zm9ybS50ZXN0MByCGm9wMTQubm90LXdlYi1wbGF0Zm9ybS50ZXN0MByCGm9w
-MzAubm90LXdlYi1wbGF0Zm9ybS50ZXN0MByCGm9wNjIubm90LXdlYi1wbGF0Zm9y
-bS50ZXN0MByCGm9wNjEubm90LXdlYi1wbGF0Zm9ybS50ZXN0MByCGm9wOTIubm90
-LXdlYi1wbGF0Zm9ybS50ZXN0MByCGm9wMjkubm90LXdlYi1wbGF0Zm9ybS50ZXN0
-MByCGm9wOTgubm90LXdlYi1wbGF0Zm9ybS50ZXN0MByCGm9wNjQubm90LXdlYi1w
-bGF0Zm9ybS50ZXN0MByCGm9wMjYubm90LXdlYi1wbGF0Zm9ybS50ZXN0MByCGm9w
-MjIubm90LXdlYi1wbGF0Zm9ybS50ZXN0MByCGm9wOTQubm90LXdlYi1wbGF0Zm9y
-bS50ZXN0MByCGm9wMzgubm90LXdlYi1wbGF0Zm9ybS50ZXN0MByCGm9wMzMubm90
-LXdlYi1wbGF0Zm9ybS50ZXN0MByCGm9wMjMubm90LXdlYi1wbGF0Zm9ybS50ZXN0
-MByCGm9wNTcubm90LXdlYi1wbGF0Zm9ybS50ZXN0MByCGm9wNTQubm90LXdlYi1w
-bGF0Zm9ybS50ZXN0MByCGm9wODUubm90LXdlYi1wbGF0Zm9ybS50ZXN0MByCGm9w
-NDYubm90LXdlYi1wbGF0Zm9ybS50ZXN0MByCGm9wOTcubm90LXdlYi1wbGF0Zm9y
-bS50ZXN0MByCGm9wMzIubm90LXdlYi1wbGF0Zm9ybS50ZXN0MByCGm9wNjAubm90
-LXdlYi1wbGF0Zm9ybS50ZXN0MByCGm9wOTYubm90LXdlYi1wbGF0Zm9ybS50ZXN0
-MByCGm9wNTEubm90LXdlYi1wbGF0Zm9ybS50ZXN0MByCGm9wNDEubm90LXdlYi1w
-bGF0Zm9ybS50ZXN0MByCGm9wMzUubm90LXdlYi1wbGF0Zm9ybS50ZXN0MByCGm9w
-OTkubm90LXdlYi1wbGF0Zm9ybS50ZXN0MByCGm9wNDIubm90LXdlYi1wbGF0Zm9y
-bS50ZXN0MByCGm9wNjcubm90LXdlYi1wbGF0Zm9ybS50ZXN0MByCGm9wMzcubm90
-LXdlYi1wbGF0Zm9ybS50ZXN0MByCGm9wNDgubm90LXdlYi1wbGF0Zm9ybS50ZXN0
-MByCGm9wNTUubm90LXdlYi1wbGF0Zm9ybS50ZXN0MByCGm9wNTYubm90LXdlYi1w
-bGF0Zm9ybS50ZXN0MByCGm9wODQubm90LXdlYi1wbGF0Zm9ybS50ZXN0MByCGm9w
-MzQubm90LXdlYi1wbGF0Zm9ybS50ZXN0MByCGm9wNjkubm90LXdlYi1wbGF0Zm9y
-bS50ZXN0MByCGm9wMTEubm90LXdlYi1wbGF0Zm9ybS50ZXN0MByCGm9wOTMubm90
-LXdlYi1wbGF0Zm9ybS50ZXN0MByCGnd3dzEud3d3LndlYi1wbGF0Zm9ybS50ZXN0
-MByCGm9wODYubm90LXdlYi1wbGF0Zm9ybS50ZXN0MByCGm9wMTMubm90LXdlYi1w
-bGF0Zm9ybS50ZXN0MByCGm9wMjAubm90LXdlYi1wbGF0Zm9ybS50ZXN0MByCGm9w
-NzYubm90LXdlYi1wbGF0Zm9ybS50ZXN0MByCGm9wMjcubm90LXdlYi1wbGF0Zm9y
-bS50ZXN0MByCGm9wMTcubm90LXdlYi1wbGF0Zm9ybS50ZXN0MByCGm9wNzUubm90
-LXdlYi1wbGF0Zm9ybS50ZXN0MByCGm9wMTUubm90LXdlYi1wbGF0Zm9ybS50ZXN0
-MByCGm9wNDcubm90LXdlYi1wbGF0Zm9ybS50ZXN0MByCGm9wMTgubm90LXdlYi1w
+ODcubm90LXdlYi1wbGF0Zm9ybS50ZXN0MByCGnd3dzEud3d3LndlYi1wbGF0Zm9y
+bS50ZXN0MByCGm9wNzAubm90LXdlYi1wbGF0Zm9ybS50ZXN0MByCGm9wODMubm90
+LXdlYi1wbGF0Zm9ybS50ZXN0MByCGm9wOTEubm90LXdlYi1wbGF0Zm9ybS50ZXN0
+MByCGm9wODEubm90LXdlYi1wbGF0Zm9ybS50ZXN0MByCGm9wOTkubm90LXdlYi1w
+bGF0Zm9ybS50ZXN0MByCGm9wNDgubm90LXdlYi1wbGF0Zm9ybS50ZXN0MByCGm9w
+NjQubm90LXdlYi1wbGF0Zm9ybS50ZXN0MByCGm9wNzYubm90LXdlYi1wbGF0Zm9y
+bS50ZXN0MByCGnd3dy53d3cyLndlYi1wbGF0Zm9ybS50ZXN0MByCGm9wMjQubm90
+LXdlYi1wbGF0Zm9ybS50ZXN0MByCGm9wNzgubm90LXdlYi1wbGF0Zm9ybS50ZXN0
+MByCGnd3dy53d3cxLndlYi1wbGF0Zm9ybS50ZXN0MByCGm9wNzEubm90LXdlYi1w
+bGF0Zm9ybS50ZXN0MByCGm9wMTUubm90LXdlYi1wbGF0Zm9ybS50ZXN0MByCGm9w
+NzIubm90LXdlYi1wbGF0Zm9ybS50ZXN0MByCGm9wNjUubm90LXdlYi1wbGF0Zm9y
+bS50ZXN0MByCGm9wOTgubm90LXdlYi1wbGF0Zm9ybS50ZXN0MByCGm9wNDkubm90
+LXdlYi1wbGF0Zm9ybS50ZXN0MByCGm9wNTAubm90LXdlYi1wbGF0Zm9ybS50ZXN0
+MByCGnd3dzEubm90LXdlYi1wbGF0Zm9ybS50ZXN0MByCGm9wNDcubm90LXdlYi1w
+bGF0Zm9ybS50ZXN0MByCGm9wODIubm90LXdlYi1wbGF0Zm9ybS50ZXN0MByCGm9w
+MTIubm90LXdlYi1wbGF0Zm9ybS50ZXN0MByCGm9wMjcubm90LXdlYi1wbGF0Zm9y
+bS50ZXN0MByCGm9wODkubm90LXdlYi1wbGF0Zm9ybS50ZXN0MByCGm9wODYubm90
+LXdlYi1wbGF0Zm9ybS50ZXN0MByCGm9wNTIubm90LXdlYi1wbGF0Zm9ybS50ZXN0
+MByCGm9wMjAubm90LXdlYi1wbGF0Zm9ybS50ZXN0MByCGm9wMjMubm90LXdlYi1w
 bGF0Zm9ybS50ZXN0MByCGm9wNjMubm90LXdlYi1wbGF0Zm9ybS50ZXN0MByCGm9w
-Mjgubm90LXdlYi1wbGF0Zm9ybS50ZXN0MByCGm9wNDMubm90LXdlYi1wbGF0Zm9y
-bS50ZXN0MByCGm9wNjYubm90LXdlYi1wbGF0Zm9ybS50ZXN0MByCGnd3dzIud3d3
-LndlYi1wbGF0Zm9ybS50ZXN0MByCGm9wOTEubm90LXdlYi1wbGF0Zm9ybS50ZXN0
-MByCGm9wNzQubm90LXdlYi1wbGF0Zm9ybS50ZXN0MByCGm9wNTkubm90LXdlYi1w
-bGF0Zm9ybS50ZXN0MByCGm9wODgubm90LXdlYi1wbGF0Zm9ybS50ZXN0MByCGm9w
-ODcubm90LXdlYi1wbGF0Zm9ybS50ZXN0MByCGm9wMTAubm90LXdlYi1wbGF0Zm9y
-bS50ZXN0MByCGm9wMTYubm90LXdlYi1wbGF0Zm9ybS50ZXN0MB2CG3d3dzEud3d3
-Mi53ZWItcGxhdGZvcm0udGVzdDAdght3d3cyLnd3dzIud2ViLXBsYXRmb3JtLnRl
-c3QwHYIbd3d3Mi53d3cxLndlYi1wbGF0Zm9ybS50ZXN0MB2CG3d3dzEud3d3MS53
+NDIubm90LXdlYi1wbGF0Zm9ybS50ZXN0MByCGm9wNzQubm90LXdlYi1wbGF0Zm9y
+bS50ZXN0MByCGm9wNjIubm90LXdlYi1wbGF0Zm9ybS50ZXN0MByCGm9wMTMubm90
+LXdlYi1wbGF0Zm9ybS50ZXN0MByCGm9wNTgubm90LXdlYi1wbGF0Zm9ybS50ZXN0
+MByCGm9wNjYubm90LXdlYi1wbGF0Zm9ybS50ZXN0MByCGnd3dzIubm90LXdlYi1w
+bGF0Zm9ybS50ZXN0MByCGm9wMzEubm90LXdlYi1wbGF0Zm9ybS50ZXN0MByCGm9w
+Mzkubm90LXdlYi1wbGF0Zm9ybS50ZXN0MByCGm9wNzMubm90LXdlYi1wbGF0Zm9y
+bS50ZXN0MByCGm9wNzcubm90LXdlYi1wbGF0Zm9ybS50ZXN0MByCGm9wNTUubm90
+LXdlYi1wbGF0Zm9ybS50ZXN0MByCGm9wNjkubm90LXdlYi1wbGF0Zm9ybS50ZXN0
+MByCGm9wNTEubm90LXdlYi1wbGF0Zm9ybS50ZXN0MByCGm9wOTYubm90LXdlYi1w
+bGF0Zm9ybS50ZXN0MByCGm9wMTYubm90LXdlYi1wbGF0Zm9ybS50ZXN0MByCGm9w
+MTAubm90LXdlYi1wbGF0Zm9ybS50ZXN0MByCGnd3dzIud3d3LndlYi1wbGF0Zm9y
+bS50ZXN0MByCGm9wNjEubm90LXdlYi1wbGF0Zm9ybS50ZXN0MByCGm9wMjEubm90
+LXdlYi1wbGF0Zm9ybS50ZXN0MByCGm9wODAubm90LXdlYi1wbGF0Zm9ybS50ZXN0
+MByCGm9wOTAubm90LXdlYi1wbGF0Zm9ybS50ZXN0MByCGm9wNjcubm90LXdlYi1w
+bGF0Zm9ybS50ZXN0MByCGm9wNDMubm90LXdlYi1wbGF0Zm9ybS50ZXN0MByCGm9w
+NTkubm90LXdlYi1wbGF0Zm9ybS50ZXN0MByCGm9wNDQubm90LXdlYi1wbGF0Zm9y
+bS50ZXN0MByCGm9wMTQubm90LXdlYi1wbGF0Zm9ybS50ZXN0MByCGm9wMTkubm90
+LXdlYi1wbGF0Zm9ybS50ZXN0MByCGm9wMzMubm90LXdlYi1wbGF0Zm9ybS50ZXN0
+MByCGm9wMjIubm90LXdlYi1wbGF0Zm9ybS50ZXN0MByCGm9wODgubm90LXdlYi1w
+bGF0Zm9ybS50ZXN0MByCGm9wNjAubm90LXdlYi1wbGF0Zm9ybS50ZXN0MByCGm9w
+NDAubm90LXdlYi1wbGF0Zm9ybS50ZXN0MByCGm9wNDYubm90LXdlYi1wbGF0Zm9y
+bS50ZXN0MByCGm9wMzIubm90LXdlYi1wbGF0Zm9ybS50ZXN0MByCGm9wNTYubm90
+LXdlYi1wbGF0Zm9ybS50ZXN0MByCGm9wMjkubm90LXdlYi1wbGF0Zm9ybS50ZXN0
+MByCGm9wMzQubm90LXdlYi1wbGF0Zm9ybS50ZXN0MByCGm9wOTcubm90LXdlYi1w
+bGF0Zm9ybS50ZXN0MByCGm9wODQubm90LXdlYi1wbGF0Zm9ybS50ZXN0MByCGm9w
+MzUubm90LXdlYi1wbGF0Zm9ybS50ZXN0MByCGm9wOTMubm90LXdlYi1wbGF0Zm9y
+bS50ZXN0MByCGm9wNzkubm90LXdlYi1wbGF0Zm9ybS50ZXN0MByCGm9wMTgubm90
+LXdlYi1wbGF0Zm9ybS50ZXN0MByCGm9wNTcubm90LXdlYi1wbGF0Zm9ybS50ZXN0
+MByCGm9wMzYubm90LXdlYi1wbGF0Zm9ybS50ZXN0MByCGm9wMTEubm90LXdlYi1w
+bGF0Zm9ybS50ZXN0MByCGm9wMzgubm90LXdlYi1wbGF0Zm9ybS50ZXN0MByCGm9w
+ODUubm90LXdlYi1wbGF0Zm9ybS50ZXN0MByCGm9wMzAubm90LXdlYi1wbGF0Zm9y
+bS50ZXN0MByCGm9wNDUubm90LXdlYi1wbGF0Zm9ybS50ZXN0MB2CG3d3dzIud3d3
+Mi53ZWItcGxhdGZvcm0udGVzdDAdght3d3cxLnd3dzEud2ViLXBsYXRmb3JtLnRl
+c3QwHYIbd3d3MS53d3cyLndlYi1wbGF0Zm9ybS50ZXN0MB2CG3d3dzIud3d3MS53
 ZWItcGxhdGZvcm0udGVzdDAfgh13d3cud3d3Lm5vdC13ZWItcGxhdGZvcm0udGVz
-dDAggh54bi0tbHZlLTZsYWQud2ViLXBsYXRmb3JtLnRlc3QwIIIed3d3MS53d3cu
+dDAggh53d3cxLnd3dy5ub3Qtd2ViLXBsYXRmb3JtLnRlc3QwIIIed3d3Lnd3dzEu
 bm90LXdlYi1wbGF0Zm9ybS50ZXN0MCCCHnd3dy53d3cyLm5vdC13ZWItcGxhdGZv
-cm0udGVzdDAggh53d3cyLnd3dy5ub3Qtd2ViLXBsYXRmb3JtLnRlc3QwIIIed3d3
-Lnd3dzEubm90LXdlYi1wbGF0Zm9ybS50ZXN0MCGCH3d3dzIud3d3Mi5ub3Qtd2Vi
-LXBsYXRmb3JtLnRlc3QwIYIfd3d3Mi53d3cxLm5vdC13ZWItcGxhdGZvcm0udGVz
-dDAhgh93d3cxLnd3dzEubm90LXdlYi1wbGF0Zm9ybS50ZXN0MCGCH3d3dzEud3d3
+cm0udGVzdDAggh53d3cyLnd3dy5ub3Qtd2ViLXBsYXRmb3JtLnRlc3QwIIIeeG4t
+LWx2ZS02bGFkLndlYi1wbGF0Zm9ybS50ZXN0MCGCH3d3dzIud3d3MS5ub3Qtd2Vi
+LXBsYXRmb3JtLnRlc3QwIYIfd3d3MS53d3cxLm5vdC13ZWItcGxhdGZvcm0udGVz
+dDAhgh93d3cyLnd3dzIubm90LXdlYi1wbGF0Zm9ybS50ZXN0MCGCH3d3dzEud3d3
 Mi5ub3Qtd2ViLXBsYXRmb3JtLnRlc3QwJIIieG4tLWx2ZS02bGFkLnd3dy53ZWIt
-cGxhdGZvcm0udGVzdDAkgiJ4bi0tbHZlLTZsYWQubm90LXdlYi1wbGF0Zm9ybS50
-ZXN0MCSCInd3dy54bi0tbHZlLTZsYWQud2ViLXBsYXRmb3JtLnRlc3QwJYIjd3d3
-Mi54bi0tbHZlLTZsYWQud2ViLXBsYXRmb3JtLnRlc3QwJYIjeG4tLWx2ZS02bGFk
-Lnd3dzIud2ViLXBsYXRmb3JtLnRlc3QwJYIjeG4tLWx2ZS02bGFkLnd3dzEud2Vi
+cGxhdGZvcm0udGVzdDAkgiJ3d3cueG4tLWx2ZS02bGFkLndlYi1wbGF0Zm9ybS50
+ZXN0MCSCInhuLS1sdmUtNmxhZC5ub3Qtd2ViLXBsYXRmb3JtLnRlc3QwJYIjeG4t
+LWx2ZS02bGFkLnd3dzEud2ViLXBsYXRmb3JtLnRlc3QwJYIjeG4tLWx2ZS02bGFk
+Lnd3dzIud2ViLXBsYXRmb3JtLnRlc3QwJYIjd3d3Mi54bi0tbHZlLTZsYWQud2Vi
 LXBsYXRmb3JtLnRlc3QwJYIjd3d3MS54bi0tbHZlLTZsYWQud2ViLXBsYXRmb3Jt
 LnRlc3QwKIImeG4tLWx2ZS02bGFkLnd3dy5ub3Qtd2ViLXBsYXRmb3JtLnRlc3Qw
-KIImd3d3LnhuLS1sdmUtNmxhZC5ub3Qtd2ViLXBsYXRmb3JtLnRlc3QwKYIneG4t
-LWx2ZS02bGFkLnd3dzEubm90LXdlYi1wbGF0Zm9ybS50ZXN0MCmCJ3d3dzIueG4t
-LWx2ZS02bGFkLm5vdC13ZWItcGxhdGZvcm0udGVzdDApgid3d3cxLnhuLS1sdmUt
-NmxhZC5ub3Qtd2ViLXBsYXRmb3JtLnRlc3QwKYIneG4tLWx2ZS02bGFkLnd3dzIu
+KIImd3d3LnhuLS1sdmUtNmxhZC5ub3Qtd2ViLXBsYXRmb3JtLnRlc3QwKYInd3d3
+MS54bi0tbHZlLTZsYWQubm90LXdlYi1wbGF0Zm9ybS50ZXN0MCmCJ3huLS1sdmUt
+NmxhZC53d3cxLm5vdC13ZWItcGxhdGZvcm0udGVzdDApgid4bi0tbHZlLTZsYWQu
+d3d3Mi5ub3Qtd2ViLXBsYXRmb3JtLnRlc3QwKYInd3d3Mi54bi0tbHZlLTZsYWQu
 bm90LXdlYi1wbGF0Zm9ybS50ZXN0MCuCKXhuLS1uOGo2ZHM1M2x3d2tycWh2Mjhh
 LndlYi1wbGF0Zm9ybS50ZXN0MC2CK3huLS1sdmUtNmxhZC54bi0tbHZlLTZsYWQu
-d2ViLXBsYXRmb3JtLnRlc3QwL4Itd3d3LnhuLS1uOGo2ZHM1M2x3d2tycWh2Mjhh
+d2ViLXBsYXRmb3JtLnRlc3QwL4IteG4tLW44ajZkczUzbHd3a3JxaHYyOGEud3d3
 LndlYi1wbGF0Zm9ybS50ZXN0MC+CLXhuLS1uOGo2ZHM1M2x3d2tycWh2MjhhLm5v
-dC13ZWItcGxhdGZvcm0udGVzdDAvgi14bi0tbjhqNmRzNTNsd3drcnFodjI4YS53
-d3cud2ViLXBsYXRmb3JtLnRlc3QwMIIud3d3MS54bi0tbjhqNmRzNTNsd3drcnFo
-djI4YS53ZWItcGxhdGZvcm0udGVzdDAwgi54bi0tbjhqNmRzNTNsd3drcnFodjI4
-YS53d3cyLndlYi1wbGF0Zm9ybS50ZXN0MDCCLnhuLS1uOGo2ZHM1M2x3d2tycWh2
-MjhhLnd3dzEud2ViLXBsYXRmb3JtLnRlc3QwMIIud3d3Mi54bi0tbjhqNmRzNTNs
-d3drcnFodjI4YS53ZWItcGxhdGZvcm0udGVzdDAxgi94bi0tbHZlLTZsYWQueG4t
+dC13ZWItcGxhdGZvcm0udGVzdDAvgi13d3cueG4tLW44ajZkczUzbHd3a3JxaHYy
+OGEud2ViLXBsYXRmb3JtLnRlc3QwMIIud3d3Mi54bi0tbjhqNmRzNTNsd3drcnFo
+djI4YS53ZWItcGxhdGZvcm0udGVzdDAwgi53d3cxLnhuLS1uOGo2ZHM1M2x3d2ty
+cWh2MjhhLndlYi1wbGF0Zm9ybS50ZXN0MDCCLnhuLS1uOGo2ZHM1M2x3d2tycWh2
+MjhhLnd3dzEud2ViLXBsYXRmb3JtLnRlc3QwMIIueG4tLW44ajZkczUzbHd3a3Jx
+aHYyOGEud3d3Mi53ZWItcGxhdGZvcm0udGVzdDAxgi94bi0tbHZlLTZsYWQueG4t
 LWx2ZS02bGFkLm5vdC13ZWItcGxhdGZvcm0udGVzdDAzgjF3d3cueG4tLW44ajZk
 czUzbHd3a3JxaHYyOGEubm90LXdlYi1wbGF0Zm9ybS50ZXN0MDOCMXhuLS1uOGo2
 ZHM1M2x3d2tycWh2MjhhLnd3dy5ub3Qtd2ViLXBsYXRmb3JtLnRlc3QwNIIyeG4t
-LW44ajZkczUzbHd3a3JxaHYyOGEud3d3Mi5ub3Qtd2ViLXBsYXRmb3JtLnRlc3Qw
-NIIyd3d3MS54bi0tbjhqNmRzNTNsd3drcnFodjI4YS5ub3Qtd2ViLXBsYXRmb3Jt
-LnRlc3QwNIIyd3d3Mi54bi0tbjhqNmRzNTNsd3drcnFodjI4YS5ub3Qtd2ViLXBs
-YXRmb3JtLnRlc3QwNIIyeG4tLW44ajZkczUzbHd3a3JxaHYyOGEud3d3MS5ub3Qt
+LW44ajZkczUzbHd3a3JxaHYyOGEud3d3MS5ub3Qtd2ViLXBsYXRmb3JtLnRlc3Qw
+NIIyd3d3Mi54bi0tbjhqNmRzNTNsd3drcnFodjI4YS5ub3Qtd2ViLXBsYXRmb3Jt
+LnRlc3QwNIIyd3d3MS54bi0tbjhqNmRzNTNsd3drcnFodjI4YS5ub3Qtd2ViLXBs
+YXRmb3JtLnRlc3QwNIIyeG4tLW44ajZkczUzbHd3a3JxaHYyOGEud3d3Mi5ub3Qt
 d2ViLXBsYXRmb3JtLnRlc3QwOII2eG4tLW44ajZkczUzbHd3a3JxaHYyOGEueG4t
 LWx2ZS02bGFkLndlYi1wbGF0Zm9ybS50ZXN0MDiCNnhuLS1sdmUtNmxhZC54bi0t
 bjhqNmRzNTNsd3drcnFodjI4YS53ZWItcGxhdGZvcm0udGVzdDA8gjp4bi0tbjhq
@@ -180,156 +180,156 @@
 LW44ajZkczUzbHd3a3JxaHYyOGEud2ViLXBsYXRmb3JtLnRlc3QwR4JFeG4tLW44
 ajZkczUzbHd3a3JxaHYyOGEueG4tLW44ajZkczUzbHd3a3JxaHYyOGEubm90LXdl
 Yi1wbGF0Zm9ybS50ZXN0MBMGA1UdJQQMMAoGCCsGAQUFBwMBMIIdjwYDVR0RBIId
-hjCCHYKCEXdlYi1wbGF0Zm9ybS50ZXN0ghVvcDgud2ViLXBsYXRmb3JtLnRlc3SC
-FW9wNy53ZWItcGxhdGZvcm0udGVzdIIVb3A5LndlYi1wbGF0Zm9ybS50ZXN0ghVv
-cDQud2ViLXBsYXRmb3JtLnRlc3SCFW5vdC13ZWItcGxhdGZvcm0udGVzdIIVb3A2
-LndlYi1wbGF0Zm9ybS50ZXN0ghVvcDMud2ViLXBsYXRmb3JtLnRlc3SCFW9wMi53
-ZWItcGxhdGZvcm0udGVzdIIVb3AxLndlYi1wbGF0Zm9ybS50ZXN0ghV3d3cud2Vi
-LXBsYXRmb3JtLnRlc3SCFW9wNS53ZWItcGxhdGZvcm0udGVzdIIWb3A4OC53ZWIt
-cGxhdGZvcm0udGVzdIIWb3A5OC53ZWItcGxhdGZvcm0udGVzdIIWb3A4NS53ZWIt
-cGxhdGZvcm0udGVzdIIWb3A4OS53ZWItcGxhdGZvcm0udGVzdIIWb3A2Ni53ZWIt
-cGxhdGZvcm0udGVzdIIWb3A3Mi53ZWItcGxhdGZvcm0udGVzdIIWb3AyNC53ZWIt
-cGxhdGZvcm0udGVzdIIWb3A0MS53ZWItcGxhdGZvcm0udGVzdIIWb3A3OS53ZWIt
-cGxhdGZvcm0udGVzdIIWb3A5MS53ZWItcGxhdGZvcm0udGVzdIIWb3A1OS53ZWIt
-cGxhdGZvcm0udGVzdIIWb3AzOS53ZWItcGxhdGZvcm0udGVzdIIWb3A2MC53ZWIt
-cGxhdGZvcm0udGVzdIIWb3A1OC53ZWItcGxhdGZvcm0udGVzdIIWb3AyOC53ZWIt
-cGxhdGZvcm0udGVzdIIWd3d3MS53ZWItcGxhdGZvcm0udGVzdIIWb3AxNC53ZWIt
-cGxhdGZvcm0udGVzdIIWb3A2OS53ZWItcGxhdGZvcm0udGVzdIIWb3A0MC53ZWIt
-cGxhdGZvcm0udGVzdIIWb3A3NC53ZWItcGxhdGZvcm0udGVzdIIWb3AzMS53ZWIt
-cGxhdGZvcm0udGVzdIIWb3AxOC53ZWItcGxhdGZvcm0udGVzdIIWb3A3My53ZWIt
-cGxhdGZvcm0udGVzdIIWb3A3Ny53ZWItcGxhdGZvcm0udGVzdIIWb3AxMi53ZWIt
-cGxhdGZvcm0udGVzdIIWb3A1NC53ZWItcGxhdGZvcm0udGVzdIIWb3A2My53ZWIt
-cGxhdGZvcm0udGVzdIIWb3A3MS53ZWItcGxhdGZvcm0udGVzdIIWb3A5NS53ZWIt
-cGxhdGZvcm0udGVzdIIWb3AxNi53ZWItcGxhdGZvcm0udGVzdIIWb3AzNi53ZWIt
-cGxhdGZvcm0udGVzdIIWb3AyNy53ZWItcGxhdGZvcm0udGVzdIIWb3AyOS53ZWIt
-cGxhdGZvcm0udGVzdIIWb3A5NC53ZWItcGxhdGZvcm0udGVzdIIWb3A0NC53ZWIt
-cGxhdGZvcm0udGVzdIIWb3AzMy53ZWItcGxhdGZvcm0udGVzdIIWb3A4NC53ZWIt
-cGxhdGZvcm0udGVzdIIWb3AzMi53ZWItcGxhdGZvcm0udGVzdIIWb3A2MS53ZWIt
-cGxhdGZvcm0udGVzdIIWb3A3MC53ZWItcGxhdGZvcm0udGVzdIIWd3d3Mi53ZWIt
-cGxhdGZvcm0udGVzdIIWb3A0My53ZWItcGxhdGZvcm0udGVzdIIWb3A3OC53ZWIt
-cGxhdGZvcm0udGVzdIIWb3AyNi53ZWItcGxhdGZvcm0udGVzdIIWb3A3Ni53ZWIt
-cGxhdGZvcm0udGVzdIIWb3A1Mi53ZWItcGxhdGZvcm0udGVzdIIWb3A5OS53ZWIt
-cGxhdGZvcm0udGVzdIIWb3A4Ni53ZWItcGxhdGZvcm0udGVzdIIWb3A0Ni53ZWIt
-cGxhdGZvcm0udGVzdIIWb3AxNy53ZWItcGxhdGZvcm0udGVzdIIWb3A5MC53ZWIt
-cGxhdGZvcm0udGVzdIIWb3A5My53ZWItcGxhdGZvcm0udGVzdIIWb3AxMC53ZWIt
-cGxhdGZvcm0udGVzdIIWb3A1NS53ZWItcGxhdGZvcm0udGVzdIIWb3A0Ny53ZWIt
-cGxhdGZvcm0udGVzdIIWb3A1MS53ZWItcGxhdGZvcm0udGVzdIIWb3A0NS53ZWIt
-cGxhdGZvcm0udGVzdIIWb3A4MC53ZWItcGxhdGZvcm0udGVzdIIWb3A2OC53ZWIt
-cGxhdGZvcm0udGVzdIIWb3A0OS53ZWItcGxhdGZvcm0udGVzdIIWb3A1Ny53ZWIt
-cGxhdGZvcm0udGVzdIIWb3AzNS53ZWItcGxhdGZvcm0udGVzdIIWb3A2Ny53ZWIt
-cGxhdGZvcm0udGVzdIIWb3A5Mi53ZWItcGxhdGZvcm0udGVzdIIWb3AxNS53ZWIt
-cGxhdGZvcm0udGVzdIIWb3AxMy53ZWItcGxhdGZvcm0udGVzdIIWb3A3NS53ZWIt
-cGxhdGZvcm0udGVzdIIWb3A2NC53ZWItcGxhdGZvcm0udGVzdIIWb3A5Ny53ZWIt
-cGxhdGZvcm0udGVzdIIWb3AzNy53ZWItcGxhdGZvcm0udGVzdIIWb3A1Ni53ZWIt
-cGxhdGZvcm0udGVzdIIWb3A2Mi53ZWItcGxhdGZvcm0udGVzdIIWb3A4Mi53ZWIt
-cGxhdGZvcm0udGVzdIIWb3AyNS53ZWItcGxhdGZvcm0udGVzdIIWb3AxMS53ZWIt
-cGxhdGZvcm0udGVzdIIWb3A1MC53ZWItcGxhdGZvcm0udGVzdIIWb3AzOC53ZWIt
-cGxhdGZvcm0udGVzdIIWb3A4My53ZWItcGxhdGZvcm0udGVzdIIWb3A4MS53ZWIt
-cGxhdGZvcm0udGVzdIIWb3AyMC53ZWItcGxhdGZvcm0udGVzdIIWb3AyMS53ZWIt
-cGxhdGZvcm0udGVzdIIWb3AyMy53ZWItcGxhdGZvcm0udGVzdIIWb3A0Mi53ZWIt
-cGxhdGZvcm0udGVzdIIWb3AyMi53ZWItcGxhdGZvcm0udGVzdIIWb3A2NS53ZWIt
-cGxhdGZvcm0udGVzdIIWb3A5Ni53ZWItcGxhdGZvcm0udGVzdIIWb3A4Ny53ZWIt
-cGxhdGZvcm0udGVzdIIWb3AxOS53ZWItcGxhdGZvcm0udGVzdIIWb3A1My53ZWIt
-cGxhdGZvcm0udGVzdIIWb3AzMC53ZWItcGxhdGZvcm0udGVzdIIWb3A0OC53ZWIt
-cGxhdGZvcm0udGVzdIIWb3AzNC53ZWItcGxhdGZvcm0udGVzdIIZb3A2Lm5vdC13
-ZWItcGxhdGZvcm0udGVzdIIZb3AzLm5vdC13ZWItcGxhdGZvcm0udGVzdIIZb3Ay
-Lm5vdC13ZWItcGxhdGZvcm0udGVzdIIZb3A1Lm5vdC13ZWItcGxhdGZvcm0udGVz
-dIIZd3d3Lm5vdC13ZWItcGxhdGZvcm0udGVzdIIZd3d3Lnd3dy53ZWItcGxhdGZv
-cm0udGVzdIIZb3A3Lm5vdC13ZWItcGxhdGZvcm0udGVzdIIZb3A0Lm5vdC13ZWIt
-cGxhdGZvcm0udGVzdIIZb3A4Lm5vdC13ZWItcGxhdGZvcm0udGVzdIIZb3A5Lm5v
-dC13ZWItcGxhdGZvcm0udGVzdIIZb3AxLm5vdC13ZWItcGxhdGZvcm0udGVzdIIa
-b3AzNi5ub3Qtd2ViLXBsYXRmb3JtLnRlc3SCGm9wNTMubm90LXdlYi1wbGF0Zm9y
-bS50ZXN0ghpvcDUwLm5vdC13ZWItcGxhdGZvcm0udGVzdIIab3AyNC5ub3Qtd2Vi
-LXBsYXRmb3JtLnRlc3SCGm9wMzEubm90LXdlYi1wbGF0Zm9ybS50ZXN0ghpvcDk1
-Lm5vdC13ZWItcGxhdGZvcm0udGVzdIIab3A4My5ub3Qtd2ViLXBsYXRmb3JtLnRl
-c3SCGnd3dzIubm90LXdlYi1wbGF0Zm9ybS50ZXN0ghpvcDczLm5vdC13ZWItcGxh
-dGZvcm0udGVzdIIab3AxOS5ub3Qtd2ViLXBsYXRmb3JtLnRlc3SCGm9wMjEubm90
-LXdlYi1wbGF0Zm9ybS50ZXN0ghpvcDgxLm5vdC13ZWItcGxhdGZvcm0udGVzdIIa
-b3A3MC5ub3Qtd2ViLXBsYXRmb3JtLnRlc3SCGm9wNzgubm90LXdlYi1wbGF0Zm9y
-bS50ZXN0ghpvcDQwLm5vdC13ZWItcGxhdGZvcm0udGVzdIIab3AyNS5ub3Qtd2Vi
-LXBsYXRmb3JtLnRlc3SCGm9wNjUubm90LXdlYi1wbGF0Zm9ybS50ZXN0ghp3d3cu
-d3d3Mi53ZWItcGxhdGZvcm0udGVzdIIab3A4MC5ub3Qtd2ViLXBsYXRmb3JtLnRl
-c3SCGm9wNTIubm90LXdlYi1wbGF0Zm9ybS50ZXN0ghpvcDY4Lm5vdC13ZWItcGxh
-dGZvcm0udGVzdIIab3A0NS5ub3Qtd2ViLXBsYXRmb3JtLnRlc3SCGm9wNzEubm90
-LXdlYi1wbGF0Zm9ybS50ZXN0ghpvcDcyLm5vdC13ZWItcGxhdGZvcm0udGVzdIIa
-b3A5MC5ub3Qtd2ViLXBsYXRmb3JtLnRlc3SCGm9wODkubm90LXdlYi1wbGF0Zm9y
-bS50ZXN0ghpvcDQ5Lm5vdC13ZWItcGxhdGZvcm0udGVzdIIab3A3Ny5ub3Qtd2Vi
-LXBsYXRmb3JtLnRlc3SCGm9wNzkubm90LXdlYi1wbGF0Zm9ybS50ZXN0ghpvcDgy
-Lm5vdC13ZWItcGxhdGZvcm0udGVzdIIad3d3Lnd3dzEud2ViLXBsYXRmb3JtLnRl
-c3SCGm9wMTIubm90LXdlYi1wbGF0Zm9ybS50ZXN0ghpvcDM5Lm5vdC13ZWItcGxh
-dGZvcm0udGVzdIIab3A0NC5ub3Qtd2ViLXBsYXRmb3JtLnRlc3SCGnd3dzEubm90
-LXdlYi1wbGF0Zm9ybS50ZXN0ghpvcDU4Lm5vdC13ZWItcGxhdGZvcm0udGVzdIIa
-b3AxNC5ub3Qtd2ViLXBsYXRmb3JtLnRlc3SCGm9wMzAubm90LXdlYi1wbGF0Zm9y
-bS50ZXN0ghpvcDYyLm5vdC13ZWItcGxhdGZvcm0udGVzdIIab3A2MS5ub3Qtd2Vi
-LXBsYXRmb3JtLnRlc3SCGm9wOTIubm90LXdlYi1wbGF0Zm9ybS50ZXN0ghpvcDI5
-Lm5vdC13ZWItcGxhdGZvcm0udGVzdIIab3A5OC5ub3Qtd2ViLXBsYXRmb3JtLnRl
-c3SCGm9wNjQubm90LXdlYi1wbGF0Zm9ybS50ZXN0ghpvcDI2Lm5vdC13ZWItcGxh
-dGZvcm0udGVzdIIab3AyMi5ub3Qtd2ViLXBsYXRmb3JtLnRlc3SCGm9wOTQubm90
-LXdlYi1wbGF0Zm9ybS50ZXN0ghpvcDM4Lm5vdC13ZWItcGxhdGZvcm0udGVzdIIa
-b3AzMy5ub3Qtd2ViLXBsYXRmb3JtLnRlc3SCGm9wMjMubm90LXdlYi1wbGF0Zm9y
-bS50ZXN0ghpvcDU3Lm5vdC13ZWItcGxhdGZvcm0udGVzdIIab3A1NC5ub3Qtd2Vi
-LXBsYXRmb3JtLnRlc3SCGm9wODUubm90LXdlYi1wbGF0Zm9ybS50ZXN0ghpvcDQ2
-Lm5vdC13ZWItcGxhdGZvcm0udGVzdIIab3A5Ny5ub3Qtd2ViLXBsYXRmb3JtLnRl
-c3SCGm9wMzIubm90LXdlYi1wbGF0Zm9ybS50ZXN0ghpvcDYwLm5vdC13ZWItcGxh
-dGZvcm0udGVzdIIab3A5Ni5ub3Qtd2ViLXBsYXRmb3JtLnRlc3SCGm9wNTEubm90
+hjCCHYKCEXdlYi1wbGF0Zm9ybS50ZXN0ghVub3Qtd2ViLXBsYXRmb3JtLnRlc3SC
+FW9wNi53ZWItcGxhdGZvcm0udGVzdIIVd3d3LndlYi1wbGF0Zm9ybS50ZXN0ghVv
+cDEud2ViLXBsYXRmb3JtLnRlc3SCFW9wNy53ZWItcGxhdGZvcm0udGVzdIIVb3A4
+LndlYi1wbGF0Zm9ybS50ZXN0ghVvcDIud2ViLXBsYXRmb3JtLnRlc3SCFW9wOS53
+ZWItcGxhdGZvcm0udGVzdIIVb3A1LndlYi1wbGF0Zm9ybS50ZXN0ghVvcDQud2Vi
+LXBsYXRmb3JtLnRlc3SCFW9wMy53ZWItcGxhdGZvcm0udGVzdIIWb3A0MC53ZWIt
+cGxhdGZvcm0udGVzdIIWb3A2MS53ZWItcGxhdGZvcm0udGVzdIIWb3AzMy53ZWIt
+cGxhdGZvcm0udGVzdIIWb3A3OS53ZWItcGxhdGZvcm0udGVzdIIWb3AzOC53ZWIt
+cGxhdGZvcm0udGVzdIIWb3A3Ny53ZWItcGxhdGZvcm0udGVzdIIWb3A0My53ZWIt
+cGxhdGZvcm0udGVzdIIWb3A0NC53ZWItcGxhdGZvcm0udGVzdIIWb3AxNC53ZWIt
+cGxhdGZvcm0udGVzdIIWb3A0OS53ZWItcGxhdGZvcm0udGVzdIIWb3AzNi53ZWIt
+cGxhdGZvcm0udGVzdIIWb3AyMS53ZWItcGxhdGZvcm0udGVzdIIWb3A2MC53ZWIt
+cGxhdGZvcm0udGVzdIIWb3A3MS53ZWItcGxhdGZvcm0udGVzdIIWb3A1Ni53ZWIt
+cGxhdGZvcm0udGVzdIIWb3A5NC53ZWItcGxhdGZvcm0udGVzdIIWb3AxOC53ZWIt
+cGxhdGZvcm0udGVzdIIWb3A1Ny53ZWItcGxhdGZvcm0udGVzdIIWd3d3Mi53ZWIt
+cGxhdGZvcm0udGVzdIIWb3A5MS53ZWItcGxhdGZvcm0udGVzdIIWb3A0MS53ZWIt
+cGxhdGZvcm0udGVzdIIWb3A0Ny53ZWItcGxhdGZvcm0udGVzdIIWb3A1Mi53ZWIt
+cGxhdGZvcm0udGVzdIIWb3AyNS53ZWItcGxhdGZvcm0udGVzdIIWb3AxNi53ZWIt
+cGxhdGZvcm0udGVzdIIWb3AyNi53ZWItcGxhdGZvcm0udGVzdIIWb3AxMC53ZWIt
+cGxhdGZvcm0udGVzdIIWb3A2OS53ZWItcGxhdGZvcm0udGVzdIIWb3AxMi53ZWIt
+cGxhdGZvcm0udGVzdIIWb3A5Ni53ZWItcGxhdGZvcm0udGVzdIIWb3A3NC53ZWIt
+cGxhdGZvcm0udGVzdIIWb3A4OS53ZWItcGxhdGZvcm0udGVzdIIWb3A4NC53ZWIt
+cGxhdGZvcm0udGVzdIIWb3AzNy53ZWItcGxhdGZvcm0udGVzdIIWb3A0NS53ZWIt
+cGxhdGZvcm0udGVzdIIWb3A2Mi53ZWItcGxhdGZvcm0udGVzdIIWb3A4Ni53ZWIt
+cGxhdGZvcm0udGVzdIIWb3A0Mi53ZWItcGxhdGZvcm0udGVzdIIWb3AzNC53ZWIt
+cGxhdGZvcm0udGVzdIIWb3A0OC53ZWItcGxhdGZvcm0udGVzdIIWb3A3NS53ZWIt
+cGxhdGZvcm0udGVzdIIWb3A1MC53ZWItcGxhdGZvcm0udGVzdIIWb3AyMi53ZWIt
+cGxhdGZvcm0udGVzdIIWb3AyMy53ZWItcGxhdGZvcm0udGVzdIIWb3A3My53ZWIt
+cGxhdGZvcm0udGVzdIIWb3AyMC53ZWItcGxhdGZvcm0udGVzdIIWb3A5Mi53ZWIt
+cGxhdGZvcm0udGVzdIIWb3AzMi53ZWItcGxhdGZvcm0udGVzdIIWb3A4Mi53ZWIt
+cGxhdGZvcm0udGVzdIIWb3A5OC53ZWItcGxhdGZvcm0udGVzdIIWb3AzMS53ZWIt
+cGxhdGZvcm0udGVzdIIWb3AzMC53ZWItcGxhdGZvcm0udGVzdIIWb3A4Ny53ZWIt
+cGxhdGZvcm0udGVzdIIWb3A3MC53ZWItcGxhdGZvcm0udGVzdIIWb3A2NC53ZWIt
+cGxhdGZvcm0udGVzdIIWb3AxMS53ZWItcGxhdGZvcm0udGVzdIIWb3AxNS53ZWIt
+cGxhdGZvcm0udGVzdIIWb3A4NS53ZWItcGxhdGZvcm0udGVzdIIWb3A1OC53ZWIt
+cGxhdGZvcm0udGVzdIIWd3d3MS53ZWItcGxhdGZvcm0udGVzdIIWb3A4My53ZWIt
+cGxhdGZvcm0udGVzdIIWb3AxMy53ZWItcGxhdGZvcm0udGVzdIIWb3A2OC53ZWIt
+cGxhdGZvcm0udGVzdIIWb3A5OS53ZWItcGxhdGZvcm0udGVzdIIWb3AzOS53ZWIt
+cGxhdGZvcm0udGVzdIIWb3A1OS53ZWItcGxhdGZvcm0udGVzdIIWb3A5NS53ZWIt
+cGxhdGZvcm0udGVzdIIWb3A1My53ZWItcGxhdGZvcm0udGVzdIIWb3A2My53ZWIt
+cGxhdGZvcm0udGVzdIIWb3A1NC53ZWItcGxhdGZvcm0udGVzdIIWb3A4OC53ZWIt
+cGxhdGZvcm0udGVzdIIWb3A5My53ZWItcGxhdGZvcm0udGVzdIIWb3AyOC53ZWIt
+cGxhdGZvcm0udGVzdIIWb3A1MS53ZWItcGxhdGZvcm0udGVzdIIWb3A2Ni53ZWIt
+cGxhdGZvcm0udGVzdIIWb3AxOS53ZWItcGxhdGZvcm0udGVzdIIWb3A5MC53ZWIt
+cGxhdGZvcm0udGVzdIIWb3A4MC53ZWItcGxhdGZvcm0udGVzdIIWb3A2Ny53ZWIt
+cGxhdGZvcm0udGVzdIIWb3A2NS53ZWItcGxhdGZvcm0udGVzdIIWb3AyNy53ZWIt
+cGxhdGZvcm0udGVzdIIWb3A3Ni53ZWItcGxhdGZvcm0udGVzdIIWb3A3Mi53ZWIt
+cGxhdGZvcm0udGVzdIIWb3AyOS53ZWItcGxhdGZvcm0udGVzdIIWb3A5Ny53ZWIt
+cGxhdGZvcm0udGVzdIIWb3AzNS53ZWItcGxhdGZvcm0udGVzdIIWb3A1NS53ZWIt
+cGxhdGZvcm0udGVzdIIWb3A0Ni53ZWItcGxhdGZvcm0udGVzdIIWb3A3OC53ZWIt
+cGxhdGZvcm0udGVzdIIWb3AxNy53ZWItcGxhdGZvcm0udGVzdIIWb3AyNC53ZWIt
+cGxhdGZvcm0udGVzdIIWb3A4MS53ZWItcGxhdGZvcm0udGVzdIIZb3A3Lm5vdC13
+ZWItcGxhdGZvcm0udGVzdIIZb3A0Lm5vdC13ZWItcGxhdGZvcm0udGVzdIIZb3A4
+Lm5vdC13ZWItcGxhdGZvcm0udGVzdIIZd3d3Lnd3dy53ZWItcGxhdGZvcm0udGVz
+dIIZb3A5Lm5vdC13ZWItcGxhdGZvcm0udGVzdIIZd3d3Lm5vdC13ZWItcGxhdGZv
+cm0udGVzdIIZb3AxLm5vdC13ZWItcGxhdGZvcm0udGVzdIIZb3AzLm5vdC13ZWIt
+cGxhdGZvcm0udGVzdIIZb3A1Lm5vdC13ZWItcGxhdGZvcm0udGVzdIIZb3AyLm5v
+dC13ZWItcGxhdGZvcm0udGVzdIIZb3A2Lm5vdC13ZWItcGxhdGZvcm0udGVzdIIa
+b3AyNi5ub3Qtd2ViLXBsYXRmb3JtLnRlc3SCGm9wOTQubm90LXdlYi1wbGF0Zm9y
+bS50ZXN0ghpvcDc1Lm5vdC13ZWItcGxhdGZvcm0udGVzdIIab3A1NC5ub3Qtd2Vi
+LXBsYXRmb3JtLnRlc3SCGm9wMjUubm90LXdlYi1wbGF0Zm9ybS50ZXN0ghpvcDI4
+Lm5vdC13ZWItcGxhdGZvcm0udGVzdIIab3A5Mi5ub3Qtd2ViLXBsYXRmb3JtLnRl
+c3SCGm9wMTcubm90LXdlYi1wbGF0Zm9ybS50ZXN0ghpvcDk1Lm5vdC13ZWItcGxh
+dGZvcm0udGVzdIIab3A1My5ub3Qtd2ViLXBsYXRmb3JtLnRlc3SCGm9wMzcubm90
 LXdlYi1wbGF0Zm9ybS50ZXN0ghpvcDQxLm5vdC13ZWItcGxhdGZvcm0udGVzdIIa
-b3AzNS5ub3Qtd2ViLXBsYXRmb3JtLnRlc3SCGm9wOTkubm90LXdlYi1wbGF0Zm9y
-bS50ZXN0ghpvcDQyLm5vdC13ZWItcGxhdGZvcm0udGVzdIIab3A2Ny5ub3Qtd2Vi
-LXBsYXRmb3JtLnRlc3SCGm9wMzcubm90LXdlYi1wbGF0Zm9ybS50ZXN0ghpvcDQ4
-Lm5vdC13ZWItcGxhdGZvcm0udGVzdIIab3A1NS5ub3Qtd2ViLXBsYXRmb3JtLnRl
-c3SCGm9wNTYubm90LXdlYi1wbGF0Zm9ybS50ZXN0ghpvcDg0Lm5vdC13ZWItcGxh
-dGZvcm0udGVzdIIab3AzNC5ub3Qtd2ViLXBsYXRmb3JtLnRlc3SCGm9wNjkubm90
-LXdlYi1wbGF0Zm9ybS50ZXN0ghpvcDExLm5vdC13ZWItcGxhdGZvcm0udGVzdIIa
-b3A5My5ub3Qtd2ViLXBsYXRmb3JtLnRlc3SCGnd3dzEud3d3LndlYi1wbGF0Zm9y
-bS50ZXN0ghpvcDg2Lm5vdC13ZWItcGxhdGZvcm0udGVzdIIab3AxMy5ub3Qtd2Vi
-LXBsYXRmb3JtLnRlc3SCGm9wMjAubm90LXdlYi1wbGF0Zm9ybS50ZXN0ghpvcDc2
-Lm5vdC13ZWItcGxhdGZvcm0udGVzdIIab3AyNy5ub3Qtd2ViLXBsYXRmb3JtLnRl
-c3SCGm9wMTcubm90LXdlYi1wbGF0Zm9ybS50ZXN0ghpvcDc1Lm5vdC13ZWItcGxh
-dGZvcm0udGVzdIIab3AxNS5ub3Qtd2ViLXBsYXRmb3JtLnRlc3SCGm9wNDcubm90
-LXdlYi1wbGF0Zm9ybS50ZXN0ghpvcDE4Lm5vdC13ZWItcGxhdGZvcm0udGVzdIIa
-b3A2My5ub3Qtd2ViLXBsYXRmb3JtLnRlc3SCGm9wMjgubm90LXdlYi1wbGF0Zm9y
-bS50ZXN0ghpvcDQzLm5vdC13ZWItcGxhdGZvcm0udGVzdIIab3A2Ni5ub3Qtd2Vi
-LXBsYXRmb3JtLnRlc3SCGnd3dzIud3d3LndlYi1wbGF0Zm9ybS50ZXN0ghpvcDkx
-Lm5vdC13ZWItcGxhdGZvcm0udGVzdIIab3A3NC5ub3Qtd2ViLXBsYXRmb3JtLnRl
-c3SCGm9wNTkubm90LXdlYi1wbGF0Zm9ybS50ZXN0ghpvcDg4Lm5vdC13ZWItcGxh
-dGZvcm0udGVzdIIab3A4Ny5ub3Qtd2ViLXBsYXRmb3JtLnRlc3SCGm9wMTAubm90
-LXdlYi1wbGF0Zm9ybS50ZXN0ghpvcDE2Lm5vdC13ZWItcGxhdGZvcm0udGVzdIIb
-d3d3MS53d3cyLndlYi1wbGF0Zm9ybS50ZXN0ght3d3cyLnd3dzIud2ViLXBsYXRm
-b3JtLnRlc3SCG3d3dzIud3d3MS53ZWItcGxhdGZvcm0udGVzdIIbd3d3MS53d3cx
+b3A2OC5ub3Qtd2ViLXBsYXRmb3JtLnRlc3SCGm9wODcubm90LXdlYi1wbGF0Zm9y
+bS50ZXN0ghp3d3cxLnd3dy53ZWItcGxhdGZvcm0udGVzdIIab3A3MC5ub3Qtd2Vi
+LXBsYXRmb3JtLnRlc3SCGm9wODMubm90LXdlYi1wbGF0Zm9ybS50ZXN0ghpvcDkx
+Lm5vdC13ZWItcGxhdGZvcm0udGVzdIIab3A4MS5ub3Qtd2ViLXBsYXRmb3JtLnRl
+c3SCGm9wOTkubm90LXdlYi1wbGF0Zm9ybS50ZXN0ghpvcDQ4Lm5vdC13ZWItcGxh
+dGZvcm0udGVzdIIab3A2NC5ub3Qtd2ViLXBsYXRmb3JtLnRlc3SCGm9wNzYubm90
+LXdlYi1wbGF0Zm9ybS50ZXN0ghp3d3cud3d3Mi53ZWItcGxhdGZvcm0udGVzdIIa
+b3AyNC5ub3Qtd2ViLXBsYXRmb3JtLnRlc3SCGm9wNzgubm90LXdlYi1wbGF0Zm9y
+bS50ZXN0ghp3d3cud3d3MS53ZWItcGxhdGZvcm0udGVzdIIab3A3MS5ub3Qtd2Vi
+LXBsYXRmb3JtLnRlc3SCGm9wMTUubm90LXdlYi1wbGF0Zm9ybS50ZXN0ghpvcDcy
+Lm5vdC13ZWItcGxhdGZvcm0udGVzdIIab3A2NS5ub3Qtd2ViLXBsYXRmb3JtLnRl
+c3SCGm9wOTgubm90LXdlYi1wbGF0Zm9ybS50ZXN0ghpvcDQ5Lm5vdC13ZWItcGxh
+dGZvcm0udGVzdIIab3A1MC5ub3Qtd2ViLXBsYXRmb3JtLnRlc3SCGnd3dzEubm90
+LXdlYi1wbGF0Zm9ybS50ZXN0ghpvcDQ3Lm5vdC13ZWItcGxhdGZvcm0udGVzdIIa
+b3A4Mi5ub3Qtd2ViLXBsYXRmb3JtLnRlc3SCGm9wMTIubm90LXdlYi1wbGF0Zm9y
+bS50ZXN0ghpvcDI3Lm5vdC13ZWItcGxhdGZvcm0udGVzdIIab3A4OS5ub3Qtd2Vi
+LXBsYXRmb3JtLnRlc3SCGm9wODYubm90LXdlYi1wbGF0Zm9ybS50ZXN0ghpvcDUy
+Lm5vdC13ZWItcGxhdGZvcm0udGVzdIIab3AyMC5ub3Qtd2ViLXBsYXRmb3JtLnRl
+c3SCGm9wMjMubm90LXdlYi1wbGF0Zm9ybS50ZXN0ghpvcDYzLm5vdC13ZWItcGxh
+dGZvcm0udGVzdIIab3A0Mi5ub3Qtd2ViLXBsYXRmb3JtLnRlc3SCGm9wNzQubm90
+LXdlYi1wbGF0Zm9ybS50ZXN0ghpvcDYyLm5vdC13ZWItcGxhdGZvcm0udGVzdIIa
+b3AxMy5ub3Qtd2ViLXBsYXRmb3JtLnRlc3SCGm9wNTgubm90LXdlYi1wbGF0Zm9y
+bS50ZXN0ghpvcDY2Lm5vdC13ZWItcGxhdGZvcm0udGVzdIIad3d3Mi5ub3Qtd2Vi
+LXBsYXRmb3JtLnRlc3SCGm9wMzEubm90LXdlYi1wbGF0Zm9ybS50ZXN0ghpvcDM5
+Lm5vdC13ZWItcGxhdGZvcm0udGVzdIIab3A3My5ub3Qtd2ViLXBsYXRmb3JtLnRl
+c3SCGm9wNzcubm90LXdlYi1wbGF0Zm9ybS50ZXN0ghpvcDU1Lm5vdC13ZWItcGxh
+dGZvcm0udGVzdIIab3A2OS5ub3Qtd2ViLXBsYXRmb3JtLnRlc3SCGm9wNTEubm90
+LXdlYi1wbGF0Zm9ybS50ZXN0ghpvcDk2Lm5vdC13ZWItcGxhdGZvcm0udGVzdIIa
+b3AxNi5ub3Qtd2ViLXBsYXRmb3JtLnRlc3SCGm9wMTAubm90LXdlYi1wbGF0Zm9y
+bS50ZXN0ghp3d3cyLnd3dy53ZWItcGxhdGZvcm0udGVzdIIab3A2MS5ub3Qtd2Vi
+LXBsYXRmb3JtLnRlc3SCGm9wMjEubm90LXdlYi1wbGF0Zm9ybS50ZXN0ghpvcDgw
+Lm5vdC13ZWItcGxhdGZvcm0udGVzdIIab3A5MC5ub3Qtd2ViLXBsYXRmb3JtLnRl
+c3SCGm9wNjcubm90LXdlYi1wbGF0Zm9ybS50ZXN0ghpvcDQzLm5vdC13ZWItcGxh
+dGZvcm0udGVzdIIab3A1OS5ub3Qtd2ViLXBsYXRmb3JtLnRlc3SCGm9wNDQubm90
+LXdlYi1wbGF0Zm9ybS50ZXN0ghpvcDE0Lm5vdC13ZWItcGxhdGZvcm0udGVzdIIa
+b3AxOS5ub3Qtd2ViLXBsYXRmb3JtLnRlc3SCGm9wMzMubm90LXdlYi1wbGF0Zm9y
+bS50ZXN0ghpvcDIyLm5vdC13ZWItcGxhdGZvcm0udGVzdIIab3A4OC5ub3Qtd2Vi
+LXBsYXRmb3JtLnRlc3SCGm9wNjAubm90LXdlYi1wbGF0Zm9ybS50ZXN0ghpvcDQw
+Lm5vdC13ZWItcGxhdGZvcm0udGVzdIIab3A0Ni5ub3Qtd2ViLXBsYXRmb3JtLnRl
+c3SCGm9wMzIubm90LXdlYi1wbGF0Zm9ybS50ZXN0ghpvcDU2Lm5vdC13ZWItcGxh
+dGZvcm0udGVzdIIab3AyOS5ub3Qtd2ViLXBsYXRmb3JtLnRlc3SCGm9wMzQubm90
+LXdlYi1wbGF0Zm9ybS50ZXN0ghpvcDk3Lm5vdC13ZWItcGxhdGZvcm0udGVzdIIa
+b3A4NC5ub3Qtd2ViLXBsYXRmb3JtLnRlc3SCGm9wMzUubm90LXdlYi1wbGF0Zm9y
+bS50ZXN0ghpvcDkzLm5vdC13ZWItcGxhdGZvcm0udGVzdIIab3A3OS5ub3Qtd2Vi
+LXBsYXRmb3JtLnRlc3SCGm9wMTgubm90LXdlYi1wbGF0Zm9ybS50ZXN0ghpvcDU3
+Lm5vdC13ZWItcGxhdGZvcm0udGVzdIIab3AzNi5ub3Qtd2ViLXBsYXRmb3JtLnRl
+c3SCGm9wMTEubm90LXdlYi1wbGF0Zm9ybS50ZXN0ghpvcDM4Lm5vdC13ZWItcGxh
+dGZvcm0udGVzdIIab3A4NS5ub3Qtd2ViLXBsYXRmb3JtLnRlc3SCGm9wMzAubm90
+LXdlYi1wbGF0Zm9ybS50ZXN0ghpvcDQ1Lm5vdC13ZWItcGxhdGZvcm0udGVzdIIb
+d3d3Mi53d3cyLndlYi1wbGF0Zm9ybS50ZXN0ght3d3cxLnd3dzEud2ViLXBsYXRm
+b3JtLnRlc3SCG3d3dzEud3d3Mi53ZWItcGxhdGZvcm0udGVzdIIbd3d3Mi53d3cx
 LndlYi1wbGF0Zm9ybS50ZXN0gh13d3cud3d3Lm5vdC13ZWItcGxhdGZvcm0udGVz
-dIIeeG4tLWx2ZS02bGFkLndlYi1wbGF0Zm9ybS50ZXN0gh53d3cxLnd3dy5ub3Qt
+dIIed3d3MS53d3cubm90LXdlYi1wbGF0Zm9ybS50ZXN0gh53d3cud3d3MS5ub3Qt
 d2ViLXBsYXRmb3JtLnRlc3SCHnd3dy53d3cyLm5vdC13ZWItcGxhdGZvcm0udGVz
-dIIed3d3Mi53d3cubm90LXdlYi1wbGF0Zm9ybS50ZXN0gh53d3cud3d3MS5ub3Qt
-d2ViLXBsYXRmb3JtLnRlc3SCH3d3dzIud3d3Mi5ub3Qtd2ViLXBsYXRmb3JtLnRl
-c3SCH3d3dzIud3d3MS5ub3Qtd2ViLXBsYXRmb3JtLnRlc3SCH3d3dzEud3d3MS5u
+dIIed3d3Mi53d3cubm90LXdlYi1wbGF0Zm9ybS50ZXN0gh54bi0tbHZlLTZsYWQu
+d2ViLXBsYXRmb3JtLnRlc3SCH3d3dzIud3d3MS5ub3Qtd2ViLXBsYXRmb3JtLnRl
+c3SCH3d3dzEud3d3MS5ub3Qtd2ViLXBsYXRmb3JtLnRlc3SCH3d3dzIud3d3Mi5u
 b3Qtd2ViLXBsYXRmb3JtLnRlc3SCH3d3dzEud3d3Mi5ub3Qtd2ViLXBsYXRmb3Jt
-LnRlc3SCInhuLS1sdmUtNmxhZC53d3cud2ViLXBsYXRmb3JtLnRlc3SCInhuLS1s
-dmUtNmxhZC5ub3Qtd2ViLXBsYXRmb3JtLnRlc3SCInd3dy54bi0tbHZlLTZsYWQu
-d2ViLXBsYXRmb3JtLnRlc3SCI3d3dzIueG4tLWx2ZS02bGFkLndlYi1wbGF0Zm9y
-bS50ZXN0giN4bi0tbHZlLTZsYWQud3d3Mi53ZWItcGxhdGZvcm0udGVzdIIjeG4t
-LWx2ZS02bGFkLnd3dzEud2ViLXBsYXRmb3JtLnRlc3SCI3d3dzEueG4tLWx2ZS02
+LnRlc3SCInhuLS1sdmUtNmxhZC53d3cud2ViLXBsYXRmb3JtLnRlc3SCInd3dy54
+bi0tbHZlLTZsYWQud2ViLXBsYXRmb3JtLnRlc3SCInhuLS1sdmUtNmxhZC5ub3Qt
+d2ViLXBsYXRmb3JtLnRlc3SCI3huLS1sdmUtNmxhZC53d3cxLndlYi1wbGF0Zm9y
+bS50ZXN0giN4bi0tbHZlLTZsYWQud3d3Mi53ZWItcGxhdGZvcm0udGVzdIIjd3d3
+Mi54bi0tbHZlLTZsYWQud2ViLXBsYXRmb3JtLnRlc3SCI3d3dzEueG4tLWx2ZS02
 bGFkLndlYi1wbGF0Zm9ybS50ZXN0giZ4bi0tbHZlLTZsYWQud3d3Lm5vdC13ZWIt
 cGxhdGZvcm0udGVzdIImd3d3LnhuLS1sdmUtNmxhZC5ub3Qtd2ViLXBsYXRmb3Jt
-LnRlc3SCJ3huLS1sdmUtNmxhZC53d3cxLm5vdC13ZWItcGxhdGZvcm0udGVzdIIn
-d3d3Mi54bi0tbHZlLTZsYWQubm90LXdlYi1wbGF0Zm9ybS50ZXN0gid3d3cxLnhu
-LS1sdmUtNmxhZC5ub3Qtd2ViLXBsYXRmb3JtLnRlc3SCJ3huLS1sdmUtNmxhZC53
-d3cyLm5vdC13ZWItcGxhdGZvcm0udGVzdIIpeG4tLW44ajZkczUzbHd3a3JxaHYy
+LnRlc3SCJ3d3dzEueG4tLWx2ZS02bGFkLm5vdC13ZWItcGxhdGZvcm0udGVzdIIn
+eG4tLWx2ZS02bGFkLnd3dzEubm90LXdlYi1wbGF0Zm9ybS50ZXN0gid4bi0tbHZl
+LTZsYWQud3d3Mi5ub3Qtd2ViLXBsYXRmb3JtLnRlc3SCJ3d3dzIueG4tLWx2ZS02
+bGFkLm5vdC13ZWItcGxhdGZvcm0udGVzdIIpeG4tLW44ajZkczUzbHd3a3JxaHYy
 OGEud2ViLXBsYXRmb3JtLnRlc3SCK3huLS1sdmUtNmxhZC54bi0tbHZlLTZsYWQu
-d2ViLXBsYXRmb3JtLnRlc3SCLXd3dy54bi0tbjhqNmRzNTNsd3drcnFodjI4YS53
+d2ViLXBsYXRmb3JtLnRlc3SCLXhuLS1uOGo2ZHM1M2x3d2tycWh2MjhhLnd3dy53
 ZWItcGxhdGZvcm0udGVzdIIteG4tLW44ajZkczUzbHd3a3JxaHYyOGEubm90LXdl
-Yi1wbGF0Zm9ybS50ZXN0gi14bi0tbjhqNmRzNTNsd3drcnFodjI4YS53d3cud2Vi
-LXBsYXRmb3JtLnRlc3SCLnd3dzEueG4tLW44ajZkczUzbHd3a3JxaHYyOGEud2Vi
-LXBsYXRmb3JtLnRlc3SCLnhuLS1uOGo2ZHM1M2x3d2tycWh2MjhhLnd3dzIud2Vi
-LXBsYXRmb3JtLnRlc3SCLnhuLS1uOGo2ZHM1M2x3d2tycWh2MjhhLnd3dzEud2Vi
+Yi1wbGF0Zm9ybS50ZXN0gi13d3cueG4tLW44ajZkczUzbHd3a3JxaHYyOGEud2Vi
 LXBsYXRmb3JtLnRlc3SCLnd3dzIueG4tLW44ajZkczUzbHd3a3JxaHYyOGEud2Vi
+LXBsYXRmb3JtLnRlc3SCLnd3dzEueG4tLW44ajZkczUzbHd3a3JxaHYyOGEud2Vi
+LXBsYXRmb3JtLnRlc3SCLnhuLS1uOGo2ZHM1M2x3d2tycWh2MjhhLnd3dzEud2Vi
+LXBsYXRmb3JtLnRlc3SCLnhuLS1uOGo2ZHM1M2x3d2tycWh2MjhhLnd3dzIud2Vi
 LXBsYXRmb3JtLnRlc3SCL3huLS1sdmUtNmxhZC54bi0tbHZlLTZsYWQubm90LXdl
 Yi1wbGF0Zm9ybS50ZXN0gjF3d3cueG4tLW44ajZkczUzbHd3a3JxaHYyOGEubm90
 LXdlYi1wbGF0Zm9ybS50ZXN0gjF4bi0tbjhqNmRzNTNsd3drcnFodjI4YS53d3cu
 bm90LXdlYi1wbGF0Zm9ybS50ZXN0gjJ4bi0tbjhqNmRzNTNsd3drcnFodjI4YS53
-d3cyLm5vdC13ZWItcGxhdGZvcm0udGVzdIIyd3d3MS54bi0tbjhqNmRzNTNsd3dr
-cnFodjI4YS5ub3Qtd2ViLXBsYXRmb3JtLnRlc3SCMnd3dzIueG4tLW44ajZkczUz
+d3cxLm5vdC13ZWItcGxhdGZvcm0udGVzdIIyd3d3Mi54bi0tbjhqNmRzNTNsd3dr
+cnFodjI4YS5ub3Qtd2ViLXBsYXRmb3JtLnRlc3SCMnd3dzEueG4tLW44ajZkczUz
 bHd3a3JxaHYyOGEubm90LXdlYi1wbGF0Zm9ybS50ZXN0gjJ4bi0tbjhqNmRzNTNs
-d3drcnFodjI4YS53d3cxLm5vdC13ZWItcGxhdGZvcm0udGVzdII2eG4tLW44ajZk
+d3drcnFodjI4YS53d3cyLm5vdC13ZWItcGxhdGZvcm0udGVzdII2eG4tLW44ajZk
 czUzbHd3a3JxaHYyOGEueG4tLWx2ZS02bGFkLndlYi1wbGF0Zm9ybS50ZXN0gjZ4
 bi0tbHZlLTZsYWQueG4tLW44ajZkczUzbHd3a3JxaHYyOGEud2ViLXBsYXRmb3Jt
 LnRlc3SCOnhuLS1uOGo2ZHM1M2x3d2tycWh2MjhhLnhuLS1sdmUtNmxhZC5ub3Qt
@@ -337,11 +337,11 @@
 cnFodjI4YS5ub3Qtd2ViLXBsYXRmb3JtLnRlc3SCQXhuLS1uOGo2ZHM1M2x3d2ty
 cWh2MjhhLnhuLS1uOGo2ZHM1M2x3d2tycWh2MjhhLndlYi1wbGF0Zm9ybS50ZXN0
 gkV4bi0tbjhqNmRzNTNsd3drcnFodjI4YS54bi0tbjhqNmRzNTNsd3drcnFodjI4
-YS5ub3Qtd2ViLXBsYXRmb3JtLnRlc3QwDQYJKoZIhvcNAQELBQADggEBAHOGp2Ji
-xKvvqNucL2gpFBIpsT8abmKBLBm4LsSBGEFPy12fDztkWBVTEN/WiyHRL93PPnn2
-YFn3/jSuAgq0LkSx8VB/Xn2CZgY9+WzL4++GN6I6kYAuuvG4/P6iwwDCwX7y2coD
-D75E4WVVTjEsKG2vRiVWzccmg/BTmvXQJU8DSPhzPQtU/D8qHUIe/McHmEW9sxpG
-ktJSXqAe0VnvwPXhJ/scOiyJaXvC8mRjM50nUGny0n9Nywltm3oxOAVAZIahZa7g
-KMnRywojNqlkccXeHCjH1wXOhzuyQX+MvvBqq968ttIV/hbUXh+D/Su9M0qQclbA
-09vdXeld+rSxP8s=
+YS5ub3Qtd2ViLXBsYXRmb3JtLnRlc3QwDQYJKoZIhvcNAQELBQADggEBADVc8bl1
+d4P2NmDg9XR7Lf97e9v4JIQMfvBpKk9kiyRrMGKHpWlGetSipBL4MAR0yvrHUs7+
+61Bbzu0L5Tzd/gyD7ZG8nYHZCThjrWFMdxdltwO2FNmb3xs8lAS8pCOXEcXwY8bv
+y/ODP24UZvkBsizQ/cm4uOD70cdPZq+gKf103nUtPNIkq+NhbpzoNfzV/XYIG4Mb
+TxAngSBX9/swqkYkgojpSJX+BPr0lZ9iNh/JNCUnwX/HRDYdt0br+ytEf2U0RQ97
+ZUlyC264H2+g8Gnv4BRbfvktaS4PpVSE29tAHBL0cowmx7jXuie3n2O35SDxQkgz
+P57oA3Ezv83wshY=
 -----END CERTIFICATE-----
diff --git a/third_party/wpt_tools/wpt/tools/certs/web-platform.test.key b/third_party/wpt_tools/wpt/tools/certs/web-platform.test.key
index b69ed2a..7b5d9ff1 100644
--- a/third_party/wpt_tools/wpt/tools/certs/web-platform.test.key
+++ b/third_party/wpt_tools/wpt/tools/certs/web-platform.test.key
@@ -1,28 +1,28 @@
 -----BEGIN PRIVATE KEY-----
-MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDcwhdziEv2WAkv
-K/wpecn5LRAprbWRjaotacXvktoxS0aK2ff34wiV7GrBucxxU81i3LCucnd82dGT
-4vHfzTKKab5/+vut1FNoG5BKmGYZCpOo6MZOWAJJ4zXYQBAEeKurSjglzOXWmjSp
-AEzM9TqUG66lpoSK58zv7gneDbjDnJhbKYZQurNOm3PLMd0ep3/yxqwqVia1nOn+
-xa4ZoFrWwguWDKx09yhBKEVDHXtAGfZp9EWATquEsqFSoeLqyQDvfe5Hefi4dPmY
-32OiJG4ArSKegxyRekQZ8E4x3mnlt3prC6nZFexLG60UOix/AN3RifpHDey8Zq/L
-pGsy+NT7AgMBAAECggEBAJrkOA4hAKNs66zEYN49DKCfpKqJrk7dJh1NDMtmr19M
-4McF3r1394sx4Unh8ndFhGMsU29i80GPl0P7RRhxYlfJkBc94574zjjKtjgQq/o/
-+JDYGmPXzmtHV31Ona51eIXrwm+LT2x+sBowErLwEVTgA29I4dCQibOCwjuiRxQ/
-mn2A4Q60OllIRCwSE3/uA9+mUpeMp5jgJd+oN3uT58Tme/SRNXxOKgCie8WRUnue
-X/cghKYSNmAyDSkbJ0KY4oavb5YRKXNpAt2hNW9JsN0qd3+j6JZ21odhydDbtqSq
-mxjPTUFQNFeLVvDTaRYw+zK+I/noZIrlQsS6JPNfXkECgYEA+m3L9jLe2lz0SJmG
-NnFuY8rwwYVJm4TLhjYDmdmYbGcYCPo9CHHKhR6ZXrmNzxcXvwaZk8ZTspAZ2Bzw
-1AM52mhGZejR7yV5FECh+UKrx/Aud+jm0WhPtwYjixz8IhmGG81orYueux50HUcO
-Q/K5Nqo1esBBC+X5Ya2rb3jShGECgYEA4atRn6P5DCj4ug3SSlUNIS3dJxbe3QhB
-lTkCtXUQC+p+VIUI0mVk6NgN4drgViKOlhzMKYDMtiVY2XLXQNVPEJ4ngMSC1uzr
-tGefMuL/WRpsysxwjN0b+0fDVeXtM16CtXOpqnoYi3XX/R6aIqZ3zVi/ttwEOy6G
-TdgnZNcJVtsCgYAW70tIpuwF75FnvLev8L99YC6gaoaNOaIyDmxSAL2W3/IxkEla
-pqE3g8/j/vZfyuuf0QjrobQ0nEHhqvTbVdhMilQ4LRRc5H+sPScYXuTAkNyQmsHY
-18bFKkjDCsqEjPXdQfiePDUzSdy0ebdyvZ38xaXUMhtC7bLjITacJOKSwQKBgGlJ
-1kZmab8rqoicBD5cGkkdre4b9JUp0fd+Zu4klP0KRjDG9Qu89OzSSP/UcBCgBOiy
-vOqsRlbBbAfgVd/Q5he5wnKIvQbr+Tjtk9BZKov3EUU5R1Xhn7mIjPGZ2ia6dL+W
-HFYGq0b+D2zwhzeddY3gV2pIkszN8ymErTSWQ6w7AoGBAIECBMwE3YL0TGWPdU9A
-RV3a5G9slunqhVGQCwvBfWwj6tIplhtOLAp4y400DHbw4Jwi5Z+hDQu9PzMGhhwu
-qZLMJZJ4BAUaVHoEcuo1sab25UH6a0pdGf7BgCmjKjPAHtvzMyfwfKpju7ZObpqx
-Yet1DMpvmPlX1kZsEh072zBs
+MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQC0NvriO1g8i0L7
+kEIwIDMFeyOvfiHxY8SnFioFfxtIiaVde1lC4DZm8+nC5z4uJa/IFghDpsPh0Mx3
+X8fZ/MK/W8i28hVf8tecB1NGevxZGBeVxDDZt7rMAsPCkRxw03BH2udMIY+8v68K
+ErT0G7ryzO8R3UN57RZsA3vtDfkvjaLgAbedjb4A/ETqBk/e+s36587iBPvqxaAm
+wFWyqzacRI/agK6EoOkEps8AeJlhHiVzLevF/MQxAxmS9ZQ4aGBrMmhSZEfGSfsk
+4bTwFucFrlFmwaNayWBlhrC677pqFZpWJYw5JBY+J+eYE8RiOCQfacD/XkQIktUy
+Djrq7GQrAgMBAAECggEBAIwW5SXx3eCKnRIzDNCeZysOkyn7keU0mg0M1LObIBle
+LrchiLzM0F1tpDzHR3XWA/Q09E2qmEC08Ayn3xJcQwNAFSHpw6Xnf6WhqCF0vy9+
+4+6fKR5FjabDS2u3I/Ws1250AzMSL8sY8reQ3SuZVtUha4Jmjup7298xQid/okJM
+uOemXHg/jBhwoYW8pWG331ezGTOxQExIVE3FTtlwSk5wkDbf7+klT4adwpEDRwpW
+JbI/xdq+r7SxGRKA5DNWETePQNQSKEzRXeZ+eh/ipWoNG6mXrppLs0EeaF55XB1b
+e1UvNFOCozsiTY2s8DBEg9hlqL+k7nLeaUGUtui5/EECgYEA2ST2jpNJfXcI9oPl
+yjg88cbBpaMGp3w1ygQzfnVQ2h05drM6frLqg/1MlpHW5XWxadeNn3PzlnBb6JVo
+v522Y/IRcSNLC8enfUTy/Oltem7RTggL4hMMi2yZordJ5AMvWqYCw22SNSMt3m9Z
+K4UnbkFpIDYJtetfPeNSkKm1e6ECgYEA1HZUfWwdJUc1mgOBSIzZ9v/SPRdIjz9E
+p3JqNIaJaXO/bE91Y1C0L26hX7gNmEtBjsHvQskOUSrDNkyCMGANRepyH3FfrF6V
+kuKUK3bSUkUNowL0ztzsk7QE/dRIg8gpLHQzHj9JTdz5qRSoDjJm2ph2rigRmJ9V
+2d59O04RrEsCgYEAxTA13n+OSytfrk1UzXCIl57Al6QWFN5NEmkCQiJTC99iIZLc
+2dWr9bR+anWByto4BD/E0jo/yCu8qteTSf70dIqMoEtGiSoDxVRpvJZV3srns47H
+C8P0rmAunH8J0M+7nvwGomXMUgjiTI6dUVIX3p3z01Z/Nv7JfLAEeG5E6kECgYBm
+iXk7Uss6K4TOALULW5byIwLHIw6Mu78ZhRmGogt9TjRrRGnl9ZQQdDcDqCM/hcps
+6GHdfIUhXR77fK80Q5cEUCKl1CSVXsyXKCzUUTMuK09qhcm6cFro6e+ixSn+F8Lv
+RmFJTsfFAUmodWSp/V8wTnawlHvxiax4Sm1sCsBywwKBgCL3fpUb8bMddwm7dgIT
+DWBVdjTbEvvZhJ4Ec0sSTxv3AwUoD70evuphotIucruLPhG0NWrPIQgZfjTKaZ24
+MBjMSWSfu+FH3J+UYaaFQKWp7fu65ioaNIrb/AhlCP68yl30m0vLGrDiZk66XAUx
+JPGxuQ/gxnR2RogWiLK9tQ+k
 -----END PRIVATE KEY-----
diff --git a/third_party/wpt_tools/wpt/tools/certs/web-platform.test.pem b/third_party/wpt_tools/wpt/tools/certs/web-platform.test.pem
index e79323d..9f0929a 100644
--- a/third_party/wpt_tools/wpt/tools/certs/web-platform.test.pem
+++ b/third_party/wpt_tools/wpt/tools/certs/web-platform.test.pem
@@ -1,228 +1,228 @@
 Certificate:
     Data:
         Version: 3 (0x2)
-        Serial Number: 415046 (0x65546)
+        Serial Number: 978030 (0xeec6e)
         Signature Algorithm: sha256WithRSAEncryption
         Issuer: CN=web-platform-tests
         Validity
-            Not Before: Jan 12 16:13:28 2021 GMT
-            Not After : Jan 12 16:13:28 2022 GMT
+            Not Before: Mar 12 00:27:34 2021 GMT
+            Not After : Mar 12 00:27:34 2022 GMT
         Subject: CN=web-platform.test
         Subject Public Key Info:
             Public Key Algorithm: rsaEncryption
                 RSA Public-Key: (2048 bit)
                 Modulus:
-                    00:dc:c2:17:73:88:4b:f6:58:09:2f:2b:fc:29:79:
-                    c9:f9:2d:10:29:ad:b5:91:8d:aa:2d:69:c5:ef:92:
-                    da:31:4b:46:8a:d9:f7:f7:e3:08:95:ec:6a:c1:b9:
-                    cc:71:53:cd:62:dc:b0:ae:72:77:7c:d9:d1:93:e2:
-                    f1:df:cd:32:8a:69:be:7f:fa:fb:ad:d4:53:68:1b:
-                    90:4a:98:66:19:0a:93:a8:e8:c6:4e:58:02:49:e3:
-                    35:d8:40:10:04:78:ab:ab:4a:38:25:cc:e5:d6:9a:
-                    34:a9:00:4c:cc:f5:3a:94:1b:ae:a5:a6:84:8a:e7:
-                    cc:ef:ee:09:de:0d:b8:c3:9c:98:5b:29:86:50:ba:
-                    b3:4e:9b:73:cb:31:dd:1e:a7:7f:f2:c6:ac:2a:56:
-                    26:b5:9c:e9:fe:c5:ae:19:a0:5a:d6:c2:0b:96:0c:
-                    ac:74:f7:28:41:28:45:43:1d:7b:40:19:f6:69:f4:
-                    45:80:4e:ab:84:b2:a1:52:a1:e2:ea:c9:00:ef:7d:
-                    ee:47:79:f8:b8:74:f9:98:df:63:a2:24:6e:00:ad:
-                    22:9e:83:1c:91:7a:44:19:f0:4e:31:de:69:e5:b7:
-                    7a:6b:0b:a9:d9:15:ec:4b:1b:ad:14:3a:2c:7f:00:
-                    dd:d1:89:fa:47:0d:ec:bc:66:af:cb:a4:6b:32:f8:
-                    d4:fb
+                    00:b4:36:fa:e2:3b:58:3c:8b:42:fb:90:42:30:20:
+                    33:05:7b:23:af:7e:21:f1:63:c4:a7:16:2a:05:7f:
+                    1b:48:89:a5:5d:7b:59:42:e0:36:66:f3:e9:c2:e7:
+                    3e:2e:25:af:c8:16:08:43:a6:c3:e1:d0:cc:77:5f:
+                    c7:d9:fc:c2:bf:5b:c8:b6:f2:15:5f:f2:d7:9c:07:
+                    53:46:7a:fc:59:18:17:95:c4:30:d9:b7:ba:cc:02:
+                    c3:c2:91:1c:70:d3:70:47:da:e7:4c:21:8f:bc:bf:
+                    af:0a:12:b4:f4:1b:ba:f2:cc:ef:11:dd:43:79:ed:
+                    16:6c:03:7b:ed:0d:f9:2f:8d:a2:e0:01:b7:9d:8d:
+                    be:00:fc:44:ea:06:4f:de:fa:cd:fa:e7:ce:e2:04:
+                    fb:ea:c5:a0:26:c0:55:b2:ab:36:9c:44:8f:da:80:
+                    ae:84:a0:e9:04:a6:cf:00:78:99:61:1e:25:73:2d:
+                    eb:c5:fc:c4:31:03:19:92:f5:94:38:68:60:6b:32:
+                    68:52:64:47:c6:49:fb:24:e1:b4:f0:16:e7:05:ae:
+                    51:66:c1:a3:5a:c9:60:65:86:b0:ba:ef:ba:6a:15:
+                    9a:56:25:8c:39:24:16:3e:27:e7:98:13:c4:62:38:
+                    24:1f:69:c0:ff:5e:44:08:92:d5:32:0e:3a:ea:ec:
+                    64:2b
                 Exponent: 65537 (0x10001)
         X509v3 extensions:
             X509v3 Basic Constraints: 
                 CA:FALSE
             X509v3 Subject Key Identifier: 
-                CD:F1:C8:62:D1:EC:A5:3D:E4:1A:91:70:F1:02:E6:6E:BC:2F:E2:05
+                C9:06:D2:C5:1F:09:F4:48:6A:0F:40:E8:29:4B:33:22:A0:8A:FB:33
             X509v3 Authority Key Identifier: 
-                keyid:DC:6D:8D:62:DD:54:98:C8:60:17:C2:D3:51:B3:BF:73:5B:5C:90:B9
+                keyid:BD:78:38:C2:37:F3:9C:06:A8:EB:3E:AF:5A:A1:88:24:14:85:5B:BE
 
             X509v3 Key Usage: 
                 Digital Signature, Non Repudiation, Key Encipherment
             X509v3 Extended Key Usage: 
                 TLS Web Server Authentication
             X509v3 Subject Alternative Name: 
-                DNS:web-platform.test, DNS:op8.web-platform.test, DNS:op7.web-platform.test, DNS:op9.web-platform.test, DNS:op4.web-platform.test, DNS:not-web-platform.test, DNS:op6.web-platform.test, DNS:op3.web-platform.test, DNS:op2.web-platform.test, DNS:op1.web-platform.test, DNS:www.web-platform.test, DNS:op5.web-platform.test, DNS:op88.web-platform.test, DNS:op98.web-platform.test, DNS:op85.web-platform.test, DNS:op89.web-platform.test, DNS:op66.web-platform.test, DNS:op72.web-platform.test, DNS:op24.web-platform.test, DNS:op41.web-platform.test, DNS:op79.web-platform.test, DNS:op91.web-platform.test, DNS:op59.web-platform.test, DNS:op39.web-platform.test, DNS:op60.web-platform.test, DNS:op58.web-platform.test, DNS:op28.web-platform.test, DNS:www1.web-platform.test, DNS:op14.web-platform.test, DNS:op69.web-platform.test, DNS:op40.web-platform.test, DNS:op74.web-platform.test, DNS:op31.web-platform.test, DNS:op18.web-platform.test, DNS:op73.web-platform.test, DNS:op77.web-platform.test, DNS:op12.web-platform.test, DNS:op54.web-platform.test, DNS:op63.web-platform.test, DNS:op71.web-platform.test, DNS:op95.web-platform.test, DNS:op16.web-platform.test, DNS:op36.web-platform.test, DNS:op27.web-platform.test, DNS:op29.web-platform.test, DNS:op94.web-platform.test, DNS:op44.web-platform.test, DNS:op33.web-platform.test, DNS:op84.web-platform.test, DNS:op32.web-platform.test, DNS:op61.web-platform.test, DNS:op70.web-platform.test, DNS:www2.web-platform.test, DNS:op43.web-platform.test, DNS:op78.web-platform.test, DNS:op26.web-platform.test, DNS:op76.web-platform.test, DNS:op52.web-platform.test, DNS:op99.web-platform.test, DNS:op86.web-platform.test, DNS:op46.web-platform.test, DNS:op17.web-platform.test, DNS:op90.web-platform.test, DNS:op93.web-platform.test, DNS:op10.web-platform.test, DNS:op55.web-platform.test, DNS:op47.web-platform.test, DNS:op51.web-platform.test, DNS:op45.web-platform.test, DNS:op80.web-platform.test, DNS:op68.web-platform.test, DNS:op49.web-platform.test, DNS:op57.web-platform.test, DNS:op35.web-platform.test, DNS:op67.web-platform.test, DNS:op92.web-platform.test, DNS:op15.web-platform.test, DNS:op13.web-platform.test, DNS:op75.web-platform.test, DNS:op64.web-platform.test, DNS:op97.web-platform.test, DNS:op37.web-platform.test, DNS:op56.web-platform.test, DNS:op62.web-platform.test, DNS:op82.web-platform.test, DNS:op25.web-platform.test, DNS:op11.web-platform.test, DNS:op50.web-platform.test, DNS:op38.web-platform.test, DNS:op83.web-platform.test, DNS:op81.web-platform.test, DNS:op20.web-platform.test, DNS:op21.web-platform.test, DNS:op23.web-platform.test, DNS:op42.web-platform.test, DNS:op22.web-platform.test, DNS:op65.web-platform.test, DNS:op96.web-platform.test, DNS:op87.web-platform.test, DNS:op19.web-platform.test, DNS:op53.web-platform.test, DNS:op30.web-platform.test, DNS:op48.web-platform.test, DNS:op34.web-platform.test, DNS:op6.not-web-platform.test, DNS:op3.not-web-platform.test, DNS:op2.not-web-platform.test, DNS:op5.not-web-platform.test, DNS:www.not-web-platform.test, DNS:www.www.web-platform.test, DNS:op7.not-web-platform.test, DNS:op4.not-web-platform.test, DNS:op8.not-web-platform.test, DNS:op9.not-web-platform.test, DNS:op1.not-web-platform.test, DNS:op36.not-web-platform.test, DNS:op53.not-web-platform.test, DNS:op50.not-web-platform.test, DNS:op24.not-web-platform.test, DNS:op31.not-web-platform.test, DNS:op95.not-web-platform.test, DNS:op83.not-web-platform.test, DNS:www2.not-web-platform.test, DNS:op73.not-web-platform.test, DNS:op19.not-web-platform.test, DNS:op21.not-web-platform.test, DNS:op81.not-web-platform.test, DNS:op70.not-web-platform.test, DNS:op78.not-web-platform.test, DNS:op40.not-web-platform.test, DNS:op25.not-web-platform.test, DNS:op65.not-web-platform.test, DNS:www.www2.web-platform.test, DNS:op80.not-web-platform.test, DNS:op52.not-web-platform.test, DNS:op68.not-web-platform.test, DNS:op45.not-web-platform.test, DNS:op71.not-web-platform.test, DNS:op72.not-web-platform.test, DNS:op90.not-web-platform.test, DNS:op89.not-web-platform.test, DNS:op49.not-web-platform.test, DNS:op77.not-web-platform.test, DNS:op79.not-web-platform.test, DNS:op82.not-web-platform.test, DNS:www.www1.web-platform.test, DNS:op12.not-web-platform.test, DNS:op39.not-web-platform.test, DNS:op44.not-web-platform.test, DNS:www1.not-web-platform.test, DNS:op58.not-web-platform.test, DNS:op14.not-web-platform.test, DNS:op30.not-web-platform.test, DNS:op62.not-web-platform.test, DNS:op61.not-web-platform.test, DNS:op92.not-web-platform.test, DNS:op29.not-web-platform.test, DNS:op98.not-web-platform.test, DNS:op64.not-web-platform.test, DNS:op26.not-web-platform.test, DNS:op22.not-web-platform.test, DNS:op94.not-web-platform.test, DNS:op38.not-web-platform.test, DNS:op33.not-web-platform.test, DNS:op23.not-web-platform.test, DNS:op57.not-web-platform.test, DNS:op54.not-web-platform.test, DNS:op85.not-web-platform.test, DNS:op46.not-web-platform.test, DNS:op97.not-web-platform.test, DNS:op32.not-web-platform.test, DNS:op60.not-web-platform.test, DNS:op96.not-web-platform.test, DNS:op51.not-web-platform.test, DNS:op41.not-web-platform.test, DNS:op35.not-web-platform.test, DNS:op99.not-web-platform.test, DNS:op42.not-web-platform.test, DNS:op67.not-web-platform.test, DNS:op37.not-web-platform.test, DNS:op48.not-web-platform.test, DNS:op55.not-web-platform.test, DNS:op56.not-web-platform.test, DNS:op84.not-web-platform.test, DNS:op34.not-web-platform.test, DNS:op69.not-web-platform.test, DNS:op11.not-web-platform.test, DNS:op93.not-web-platform.test, DNS:www1.www.web-platform.test, DNS:op86.not-web-platform.test, DNS:op13.not-web-platform.test, DNS:op20.not-web-platform.test, DNS:op76.not-web-platform.test, DNS:op27.not-web-platform.test, DNS:op17.not-web-platform.test, DNS:op75.not-web-platform.test, DNS:op15.not-web-platform.test, DNS:op47.not-web-platform.test, DNS:op18.not-web-platform.test, DNS:op63.not-web-platform.test, DNS:op28.not-web-platform.test, DNS:op43.not-web-platform.test, DNS:op66.not-web-platform.test, DNS:www2.www.web-platform.test, DNS:op91.not-web-platform.test, DNS:op74.not-web-platform.test, DNS:op59.not-web-platform.test, DNS:op88.not-web-platform.test, DNS:op87.not-web-platform.test, DNS:op10.not-web-platform.test, DNS:op16.not-web-platform.test, DNS:www1.www2.web-platform.test, DNS:www2.www2.web-platform.test, DNS:www2.www1.web-platform.test, DNS:www1.www1.web-platform.test, DNS:www.www.not-web-platform.test, DNS:xn--lve-6lad.web-platform.test, DNS:www1.www.not-web-platform.test, DNS:www.www2.not-web-platform.test, DNS:www2.www.not-web-platform.test, DNS:www.www1.not-web-platform.test, DNS:www2.www2.not-web-platform.test, DNS:www2.www1.not-web-platform.test, DNS:www1.www1.not-web-platform.test, DNS:www1.www2.not-web-platform.test, DNS:xn--lve-6lad.www.web-platform.test, DNS:xn--lve-6lad.not-web-platform.test, DNS:www.xn--lve-6lad.web-platform.test, DNS:www2.xn--lve-6lad.web-platform.test, DNS:xn--lve-6lad.www2.web-platform.test, DNS:xn--lve-6lad.www1.web-platform.test, DNS:www1.xn--lve-6lad.web-platform.test, DNS:xn--lve-6lad.www.not-web-platform.test, DNS:www.xn--lve-6lad.not-web-platform.test, DNS:xn--lve-6lad.www1.not-web-platform.test, DNS:www2.xn--lve-6lad.not-web-platform.test, DNS:www1.xn--lve-6lad.not-web-platform.test, DNS:xn--lve-6lad.www2.not-web-platform.test, DNS:xn--n8j6ds53lwwkrqhv28a.web-platform.test, DNS:xn--lve-6lad.xn--lve-6lad.web-platform.test, DNS:www.xn--n8j6ds53lwwkrqhv28a.web-platform.test, DNS:xn--n8j6ds53lwwkrqhv28a.not-web-platform.test, DNS:xn--n8j6ds53lwwkrqhv28a.www.web-platform.test, DNS:www1.xn--n8j6ds53lwwkrqhv28a.web-platform.test, DNS:xn--n8j6ds53lwwkrqhv28a.www2.web-platform.test, DNS:xn--n8j6ds53lwwkrqhv28a.www1.web-platform.test, DNS:www2.xn--n8j6ds53lwwkrqhv28a.web-platform.test, DNS:xn--lve-6lad.xn--lve-6lad.not-web-platform.test, DNS:www.xn--n8j6ds53lwwkrqhv28a.not-web-platform.test, DNS:xn--n8j6ds53lwwkrqhv28a.www.not-web-platform.test, DNS:xn--n8j6ds53lwwkrqhv28a.www2.not-web-platform.test, DNS:www1.xn--n8j6ds53lwwkrqhv28a.not-web-platform.test, DNS:www2.xn--n8j6ds53lwwkrqhv28a.not-web-platform.test, DNS:xn--n8j6ds53lwwkrqhv28a.www1.not-web-platform.test, DNS:xn--n8j6ds53lwwkrqhv28a.xn--lve-6lad.web-platform.test, DNS:xn--lve-6lad.xn--n8j6ds53lwwkrqhv28a.web-platform.test, DNS:xn--n8j6ds53lwwkrqhv28a.xn--lve-6lad.not-web-platform.test, DNS:xn--lve-6lad.xn--n8j6ds53lwwkrqhv28a.not-web-platform.test, DNS:xn--n8j6ds53lwwkrqhv28a.xn--n8j6ds53lwwkrqhv28a.web-platform.test, DNS:xn--n8j6ds53lwwkrqhv28a.xn--n8j6ds53lwwkrqhv28a.not-web-platform.test
+                DNS:web-platform.test, DNS:not-web-platform.test, DNS:op6.web-platform.test, DNS:www.web-platform.test, DNS:op1.web-platform.test, DNS:op7.web-platform.test, DNS:op8.web-platform.test, DNS:op2.web-platform.test, DNS:op9.web-platform.test, DNS:op5.web-platform.test, DNS:op4.web-platform.test, DNS:op3.web-platform.test, DNS:op40.web-platform.test, DNS:op61.web-platform.test, DNS:op33.web-platform.test, DNS:op79.web-platform.test, DNS:op38.web-platform.test, DNS:op77.web-platform.test, DNS:op43.web-platform.test, DNS:op44.web-platform.test, DNS:op14.web-platform.test, DNS:op49.web-platform.test, DNS:op36.web-platform.test, DNS:op21.web-platform.test, DNS:op60.web-platform.test, DNS:op71.web-platform.test, DNS:op56.web-platform.test, DNS:op94.web-platform.test, DNS:op18.web-platform.test, DNS:op57.web-platform.test, DNS:www2.web-platform.test, DNS:op91.web-platform.test, DNS:op41.web-platform.test, DNS:op47.web-platform.test, DNS:op52.web-platform.test, DNS:op25.web-platform.test, DNS:op16.web-platform.test, DNS:op26.web-platform.test, DNS:op10.web-platform.test, DNS:op69.web-platform.test, DNS:op12.web-platform.test, DNS:op96.web-platform.test, DNS:op74.web-platform.test, DNS:op89.web-platform.test, DNS:op84.web-platform.test, DNS:op37.web-platform.test, DNS:op45.web-platform.test, DNS:op62.web-platform.test, DNS:op86.web-platform.test, DNS:op42.web-platform.test, DNS:op34.web-platform.test, DNS:op48.web-platform.test, DNS:op75.web-platform.test, DNS:op50.web-platform.test, DNS:op22.web-platform.test, DNS:op23.web-platform.test, DNS:op73.web-platform.test, DNS:op20.web-platform.test, DNS:op92.web-platform.test, DNS:op32.web-platform.test, DNS:op82.web-platform.test, DNS:op98.web-platform.test, DNS:op31.web-platform.test, DNS:op30.web-platform.test, DNS:op87.web-platform.test, DNS:op70.web-platform.test, DNS:op64.web-platform.test, DNS:op11.web-platform.test, DNS:op15.web-platform.test, DNS:op85.web-platform.test, DNS:op58.web-platform.test, DNS:www1.web-platform.test, DNS:op83.web-platform.test, DNS:op13.web-platform.test, DNS:op68.web-platform.test, DNS:op99.web-platform.test, DNS:op39.web-platform.test, DNS:op59.web-platform.test, DNS:op95.web-platform.test, DNS:op53.web-platform.test, DNS:op63.web-platform.test, DNS:op54.web-platform.test, DNS:op88.web-platform.test, DNS:op93.web-platform.test, DNS:op28.web-platform.test, DNS:op51.web-platform.test, DNS:op66.web-platform.test, DNS:op19.web-platform.test, DNS:op90.web-platform.test, DNS:op80.web-platform.test, DNS:op67.web-platform.test, DNS:op65.web-platform.test, DNS:op27.web-platform.test, DNS:op76.web-platform.test, DNS:op72.web-platform.test, DNS:op29.web-platform.test, DNS:op97.web-platform.test, DNS:op35.web-platform.test, DNS:op55.web-platform.test, DNS:op46.web-platform.test, DNS:op78.web-platform.test, DNS:op17.web-platform.test, DNS:op24.web-platform.test, DNS:op81.web-platform.test, DNS:op7.not-web-platform.test, DNS:op4.not-web-platform.test, DNS:op8.not-web-platform.test, DNS:www.www.web-platform.test, DNS:op9.not-web-platform.test, DNS:www.not-web-platform.test, DNS:op1.not-web-platform.test, DNS:op3.not-web-platform.test, DNS:op5.not-web-platform.test, DNS:op2.not-web-platform.test, DNS:op6.not-web-platform.test, DNS:op26.not-web-platform.test, DNS:op94.not-web-platform.test, DNS:op75.not-web-platform.test, DNS:op54.not-web-platform.test, DNS:op25.not-web-platform.test, DNS:op28.not-web-platform.test, DNS:op92.not-web-platform.test, DNS:op17.not-web-platform.test, DNS:op95.not-web-platform.test, DNS:op53.not-web-platform.test, DNS:op37.not-web-platform.test, DNS:op41.not-web-platform.test, DNS:op68.not-web-platform.test, DNS:op87.not-web-platform.test, DNS:www1.www.web-platform.test, DNS:op70.not-web-platform.test, DNS:op83.not-web-platform.test, DNS:op91.not-web-platform.test, DNS:op81.not-web-platform.test, DNS:op99.not-web-platform.test, DNS:op48.not-web-platform.test, DNS:op64.not-web-platform.test, DNS:op76.not-web-platform.test, DNS:www.www2.web-platform.test, DNS:op24.not-web-platform.test, DNS:op78.not-web-platform.test, DNS:www.www1.web-platform.test, DNS:op71.not-web-platform.test, DNS:op15.not-web-platform.test, DNS:op72.not-web-platform.test, DNS:op65.not-web-platform.test, DNS:op98.not-web-platform.test, DNS:op49.not-web-platform.test, DNS:op50.not-web-platform.test, DNS:www1.not-web-platform.test, DNS:op47.not-web-platform.test, DNS:op82.not-web-platform.test, DNS:op12.not-web-platform.test, DNS:op27.not-web-platform.test, DNS:op89.not-web-platform.test, DNS:op86.not-web-platform.test, DNS:op52.not-web-platform.test, DNS:op20.not-web-platform.test, DNS:op23.not-web-platform.test, DNS:op63.not-web-platform.test, DNS:op42.not-web-platform.test, DNS:op74.not-web-platform.test, DNS:op62.not-web-platform.test, DNS:op13.not-web-platform.test, DNS:op58.not-web-platform.test, DNS:op66.not-web-platform.test, DNS:www2.not-web-platform.test, DNS:op31.not-web-platform.test, DNS:op39.not-web-platform.test, DNS:op73.not-web-platform.test, DNS:op77.not-web-platform.test, DNS:op55.not-web-platform.test, DNS:op69.not-web-platform.test, DNS:op51.not-web-platform.test, DNS:op96.not-web-platform.test, DNS:op16.not-web-platform.test, DNS:op10.not-web-platform.test, DNS:www2.www.web-platform.test, DNS:op61.not-web-platform.test, DNS:op21.not-web-platform.test, DNS:op80.not-web-platform.test, DNS:op90.not-web-platform.test, DNS:op67.not-web-platform.test, DNS:op43.not-web-platform.test, DNS:op59.not-web-platform.test, DNS:op44.not-web-platform.test, DNS:op14.not-web-platform.test, DNS:op19.not-web-platform.test, DNS:op33.not-web-platform.test, DNS:op22.not-web-platform.test, DNS:op88.not-web-platform.test, DNS:op60.not-web-platform.test, DNS:op40.not-web-platform.test, DNS:op46.not-web-platform.test, DNS:op32.not-web-platform.test, DNS:op56.not-web-platform.test, DNS:op29.not-web-platform.test, DNS:op34.not-web-platform.test, DNS:op97.not-web-platform.test, DNS:op84.not-web-platform.test, DNS:op35.not-web-platform.test, DNS:op93.not-web-platform.test, DNS:op79.not-web-platform.test, DNS:op18.not-web-platform.test, DNS:op57.not-web-platform.test, DNS:op36.not-web-platform.test, DNS:op11.not-web-platform.test, DNS:op38.not-web-platform.test, DNS:op85.not-web-platform.test, DNS:op30.not-web-platform.test, DNS:op45.not-web-platform.test, DNS:www2.www2.web-platform.test, DNS:www1.www1.web-platform.test, DNS:www1.www2.web-platform.test, DNS:www2.www1.web-platform.test, DNS:www.www.not-web-platform.test, DNS:www1.www.not-web-platform.test, DNS:www.www1.not-web-platform.test, DNS:www.www2.not-web-platform.test, DNS:www2.www.not-web-platform.test, DNS:xn--lve-6lad.web-platform.test, DNS:www2.www1.not-web-platform.test, DNS:www1.www1.not-web-platform.test, DNS:www2.www2.not-web-platform.test, DNS:www1.www2.not-web-platform.test, DNS:xn--lve-6lad.www.web-platform.test, DNS:www.xn--lve-6lad.web-platform.test, DNS:xn--lve-6lad.not-web-platform.test, DNS:xn--lve-6lad.www1.web-platform.test, DNS:xn--lve-6lad.www2.web-platform.test, DNS:www2.xn--lve-6lad.web-platform.test, DNS:www1.xn--lve-6lad.web-platform.test, DNS:xn--lve-6lad.www.not-web-platform.test, DNS:www.xn--lve-6lad.not-web-platform.test, DNS:www1.xn--lve-6lad.not-web-platform.test, DNS:xn--lve-6lad.www1.not-web-platform.test, DNS:xn--lve-6lad.www2.not-web-platform.test, DNS:www2.xn--lve-6lad.not-web-platform.test, DNS:xn--n8j6ds53lwwkrqhv28a.web-platform.test, DNS:xn--lve-6lad.xn--lve-6lad.web-platform.test, DNS:xn--n8j6ds53lwwkrqhv28a.www.web-platform.test, DNS:xn--n8j6ds53lwwkrqhv28a.not-web-platform.test, DNS:www.xn--n8j6ds53lwwkrqhv28a.web-platform.test, DNS:www2.xn--n8j6ds53lwwkrqhv28a.web-platform.test, DNS:www1.xn--n8j6ds53lwwkrqhv28a.web-platform.test, DNS:xn--n8j6ds53lwwkrqhv28a.www1.web-platform.test, DNS:xn--n8j6ds53lwwkrqhv28a.www2.web-platform.test, DNS:xn--lve-6lad.xn--lve-6lad.not-web-platform.test, DNS:www.xn--n8j6ds53lwwkrqhv28a.not-web-platform.test, DNS:xn--n8j6ds53lwwkrqhv28a.www.not-web-platform.test, DNS:xn--n8j6ds53lwwkrqhv28a.www1.not-web-platform.test, DNS:www2.xn--n8j6ds53lwwkrqhv28a.not-web-platform.test, DNS:www1.xn--n8j6ds53lwwkrqhv28a.not-web-platform.test, DNS:xn--n8j6ds53lwwkrqhv28a.www2.not-web-platform.test, DNS:xn--n8j6ds53lwwkrqhv28a.xn--lve-6lad.web-platform.test, DNS:xn--lve-6lad.xn--n8j6ds53lwwkrqhv28a.web-platform.test, DNS:xn--n8j6ds53lwwkrqhv28a.xn--lve-6lad.not-web-platform.test, DNS:xn--lve-6lad.xn--n8j6ds53lwwkrqhv28a.not-web-platform.test, DNS:xn--n8j6ds53lwwkrqhv28a.xn--n8j6ds53lwwkrqhv28a.web-platform.test, DNS:xn--n8j6ds53lwwkrqhv28a.xn--n8j6ds53lwwkrqhv28a.not-web-platform.test
     Signature Algorithm: sha256WithRSAEncryption
-         38:a7:ef:eb:55:30:e8:6f:bf:51:a9:eb:52:9a:66:ec:3d:19:
-         5d:f4:1d:84:17:c0:f2:26:3e:2b:66:5d:7a:a9:44:86:c3:ba:
-         d7:f0:9e:e7:b2:b3:05:0c:25:ef:e1:b7:94:8c:44:1c:d2:57:
-         4c:30:51:96:29:96:2e:b3:e3:1c:f8:9c:e6:7d:96:14:3f:3f:
-         3e:a6:96:4b:01:dc:f2:ed:bf:fc:e3:64:93:49:81:be:c9:dd:
-         dd:1b:ce:da:72:45:d1:2d:ca:99:cd:d1:7d:f9:17:e1:ee:12:
-         f4:ed:76:41:ca:6f:a5:5c:72:00:4a:8d:76:c5:2b:b7:18:7b:
-         ce:e8:ab:76:9a:75:e9:1d:53:9b:75:93:6d:0b:2e:49:3f:bf:
-         84:1f:71:be:49:23:ab:2c:f8:a6:a2:28:93:82:04:6c:8e:85:
-         22:0e:b6:6d:c5:1e:82:d1:0d:c6:08:9a:86:a1:29:5c:79:53:
-         d3:ae:d6:c4:33:4b:d2:04:5a:d9:a0:c3:6d:26:ec:e8:35:06:
-         e7:88:12:03:7c:c5:83:26:b2:b5:32:a3:41:51:b4:94:b0:84:
-         0d:4a:05:52:02:29:41:5b:03:04:f0:c3:e5:24:e6:5a:ef:70:
-         45:45:58:25:2b:5e:be:7d:ca:73:02:2b:1d:4f:3c:4b:00:f4:
-         69:5f:1a:6e
+         20:ff:20:71:e5:2d:9f:16:4a:f0:b8:ef:cb:23:63:56:70:bc:
+         8c:c6:6b:0e:fa:f5:80:55:5a:f5:75:58:cd:c6:7e:66:6d:11:
+         33:ef:fc:a6:6b:5b:98:04:d5:6b:5b:ee:8d:3c:0b:13:d4:f9:
+         6b:6e:9b:b5:ad:a6:6e:05:d4:d3:f5:ff:b1:a7:17:46:8e:98:
+         7b:89:87:14:a4:c1:fa:26:02:6d:44:c5:c8:c6:5a:d2:9c:e8:
+         32:83:ab:77:07:80:c8:80:ab:1f:c6:56:7a:ee:7f:aa:1d:6b:
+         37:e1:d5:c1:04:54:e5:30:41:e9:a5:9e:f0:7d:8f:f5:24:60:
+         f0:24:79:cb:d2:e8:2d:95:33:9d:ca:98:02:11:37:47:97:43:
+         13:c2:8a:73:a3:e1:14:cb:86:de:70:c2:b1:d5:5b:d9:6d:f6:
+         ad:6f:cc:a5:2b:9b:21:8b:d7:78:03:5c:2b:e4:f8:42:c5:7d:
+         0d:07:b0:e1:41:ed:ac:26:95:93:db:12:9b:a3:6e:ff:90:62:
+         0d:10:ae:d5:f4:87:3d:3a:50:55:c7:c4:e0:48:53:91:e9:22:
+         eb:97:a6:31:42:2b:3f:24:06:12:5c:92:ff:69:5e:af:00:fd:
+         e6:ec:2d:66:30:7f:59:db:d1:6f:bc:2d:f4:8b:d3:74:25:e1:
+         a5:46:79:2d
 -----BEGIN CERTIFICATE-----
-MIIgvDCCH6SgAwIBAgIDBlVGMA0GCSqGSIb3DQEBCwUAMB0xGzAZBgNVBAMMEndl
-Yi1wbGF0Zm9ybS10ZXN0czAeFw0yMTAxMTIxNjEzMjhaFw0yMjAxMTIxNjEzMjha
+MIIgvDCCH6SgAwIBAgIDDuxuMA0GCSqGSIb3DQEBCwUAMB0xGzAZBgNVBAMMEndl
+Yi1wbGF0Zm9ybS10ZXN0czAeFw0yMTAzMTIwMDI3MzRaFw0yMjAzMTIwMDI3MzRa
 MBwxGjAYBgNVBAMMEXdlYi1wbGF0Zm9ybS50ZXN0MIIBIjANBgkqhkiG9w0BAQEF
-AAOCAQ8AMIIBCgKCAQEA3MIXc4hL9lgJLyv8KXnJ+S0QKa21kY2qLWnF75LaMUtG
-itn39+MIlexqwbnMcVPNYtywrnJ3fNnRk+Lx380yimm+f/r7rdRTaBuQSphmGQqT
-qOjGTlgCSeM12EAQBHirq0o4Jczl1po0qQBMzPU6lBuupaaEiufM7+4J3g24w5yY
-WymGULqzTptzyzHdHqd/8sasKlYmtZzp/sWuGaBa1sILlgysdPcoQShFQx17QBn2
-afRFgE6rhLKhUqHi6skA733uR3n4uHT5mN9joiRuAK0inoMckXpEGfBOMd5p5bd6
-awup2RXsSxutFDosfwDd0Yn6Rw3svGavy6RrMvjU+wIDAQABo4IeBDCCHgAwCQYD
-VR0TBAIwADAdBgNVHQ4EFgQUzfHIYtHspT3kGpFw8QLmbrwv4gUwHwYDVR0jBBgw
-FoAU3G2NYt1UmMhgF8LTUbO/c1tckLkwCwYDVR0PBAQDAgXgMBMGA1UdJQQMMAoG
-CCsGAQUFBwMBMIIdjwYDVR0RBIIdhjCCHYKCEXdlYi1wbGF0Zm9ybS50ZXN0ghVv
-cDgud2ViLXBsYXRmb3JtLnRlc3SCFW9wNy53ZWItcGxhdGZvcm0udGVzdIIVb3A5
-LndlYi1wbGF0Zm9ybS50ZXN0ghVvcDQud2ViLXBsYXRmb3JtLnRlc3SCFW5vdC13
-ZWItcGxhdGZvcm0udGVzdIIVb3A2LndlYi1wbGF0Zm9ybS50ZXN0ghVvcDMud2Vi
-LXBsYXRmb3JtLnRlc3SCFW9wMi53ZWItcGxhdGZvcm0udGVzdIIVb3AxLndlYi1w
-bGF0Zm9ybS50ZXN0ghV3d3cud2ViLXBsYXRmb3JtLnRlc3SCFW9wNS53ZWItcGxh
-dGZvcm0udGVzdIIWb3A4OC53ZWItcGxhdGZvcm0udGVzdIIWb3A5OC53ZWItcGxh
-dGZvcm0udGVzdIIWb3A4NS53ZWItcGxhdGZvcm0udGVzdIIWb3A4OS53ZWItcGxh
-dGZvcm0udGVzdIIWb3A2Ni53ZWItcGxhdGZvcm0udGVzdIIWb3A3Mi53ZWItcGxh
-dGZvcm0udGVzdIIWb3AyNC53ZWItcGxhdGZvcm0udGVzdIIWb3A0MS53ZWItcGxh
-dGZvcm0udGVzdIIWb3A3OS53ZWItcGxhdGZvcm0udGVzdIIWb3A5MS53ZWItcGxh
-dGZvcm0udGVzdIIWb3A1OS53ZWItcGxhdGZvcm0udGVzdIIWb3AzOS53ZWItcGxh
-dGZvcm0udGVzdIIWb3A2MC53ZWItcGxhdGZvcm0udGVzdIIWb3A1OC53ZWItcGxh
-dGZvcm0udGVzdIIWb3AyOC53ZWItcGxhdGZvcm0udGVzdIIWd3d3MS53ZWItcGxh
-dGZvcm0udGVzdIIWb3AxNC53ZWItcGxhdGZvcm0udGVzdIIWb3A2OS53ZWItcGxh
-dGZvcm0udGVzdIIWb3A0MC53ZWItcGxhdGZvcm0udGVzdIIWb3A3NC53ZWItcGxh
-dGZvcm0udGVzdIIWb3AzMS53ZWItcGxhdGZvcm0udGVzdIIWb3AxOC53ZWItcGxh
-dGZvcm0udGVzdIIWb3A3My53ZWItcGxhdGZvcm0udGVzdIIWb3A3Ny53ZWItcGxh
-dGZvcm0udGVzdIIWb3AxMi53ZWItcGxhdGZvcm0udGVzdIIWb3A1NC53ZWItcGxh
-dGZvcm0udGVzdIIWb3A2My53ZWItcGxhdGZvcm0udGVzdIIWb3A3MS53ZWItcGxh
-dGZvcm0udGVzdIIWb3A5NS53ZWItcGxhdGZvcm0udGVzdIIWb3AxNi53ZWItcGxh
-dGZvcm0udGVzdIIWb3AzNi53ZWItcGxhdGZvcm0udGVzdIIWb3AyNy53ZWItcGxh
-dGZvcm0udGVzdIIWb3AyOS53ZWItcGxhdGZvcm0udGVzdIIWb3A5NC53ZWItcGxh
-dGZvcm0udGVzdIIWb3A0NC53ZWItcGxhdGZvcm0udGVzdIIWb3AzMy53ZWItcGxh
-dGZvcm0udGVzdIIWb3A4NC53ZWItcGxhdGZvcm0udGVzdIIWb3AzMi53ZWItcGxh
-dGZvcm0udGVzdIIWb3A2MS53ZWItcGxhdGZvcm0udGVzdIIWb3A3MC53ZWItcGxh
-dGZvcm0udGVzdIIWd3d3Mi53ZWItcGxhdGZvcm0udGVzdIIWb3A0My53ZWItcGxh
-dGZvcm0udGVzdIIWb3A3OC53ZWItcGxhdGZvcm0udGVzdIIWb3AyNi53ZWItcGxh
-dGZvcm0udGVzdIIWb3A3Ni53ZWItcGxhdGZvcm0udGVzdIIWb3A1Mi53ZWItcGxh
-dGZvcm0udGVzdIIWb3A5OS53ZWItcGxhdGZvcm0udGVzdIIWb3A4Ni53ZWItcGxh
-dGZvcm0udGVzdIIWb3A0Ni53ZWItcGxhdGZvcm0udGVzdIIWb3AxNy53ZWItcGxh
-dGZvcm0udGVzdIIWb3A5MC53ZWItcGxhdGZvcm0udGVzdIIWb3A5My53ZWItcGxh
-dGZvcm0udGVzdIIWb3AxMC53ZWItcGxhdGZvcm0udGVzdIIWb3A1NS53ZWItcGxh
-dGZvcm0udGVzdIIWb3A0Ny53ZWItcGxhdGZvcm0udGVzdIIWb3A1MS53ZWItcGxh
-dGZvcm0udGVzdIIWb3A0NS53ZWItcGxhdGZvcm0udGVzdIIWb3A4MC53ZWItcGxh
-dGZvcm0udGVzdIIWb3A2OC53ZWItcGxhdGZvcm0udGVzdIIWb3A0OS53ZWItcGxh
-dGZvcm0udGVzdIIWb3A1Ny53ZWItcGxhdGZvcm0udGVzdIIWb3AzNS53ZWItcGxh
-dGZvcm0udGVzdIIWb3A2Ny53ZWItcGxhdGZvcm0udGVzdIIWb3A5Mi53ZWItcGxh
-dGZvcm0udGVzdIIWb3AxNS53ZWItcGxhdGZvcm0udGVzdIIWb3AxMy53ZWItcGxh
-dGZvcm0udGVzdIIWb3A3NS53ZWItcGxhdGZvcm0udGVzdIIWb3A2NC53ZWItcGxh
-dGZvcm0udGVzdIIWb3A5Ny53ZWItcGxhdGZvcm0udGVzdIIWb3AzNy53ZWItcGxh
-dGZvcm0udGVzdIIWb3A1Ni53ZWItcGxhdGZvcm0udGVzdIIWb3A2Mi53ZWItcGxh
-dGZvcm0udGVzdIIWb3A4Mi53ZWItcGxhdGZvcm0udGVzdIIWb3AyNS53ZWItcGxh
-dGZvcm0udGVzdIIWb3AxMS53ZWItcGxhdGZvcm0udGVzdIIWb3A1MC53ZWItcGxh
-dGZvcm0udGVzdIIWb3AzOC53ZWItcGxhdGZvcm0udGVzdIIWb3A4My53ZWItcGxh
-dGZvcm0udGVzdIIWb3A4MS53ZWItcGxhdGZvcm0udGVzdIIWb3AyMC53ZWItcGxh
-dGZvcm0udGVzdIIWb3AyMS53ZWItcGxhdGZvcm0udGVzdIIWb3AyMy53ZWItcGxh
-dGZvcm0udGVzdIIWb3A0Mi53ZWItcGxhdGZvcm0udGVzdIIWb3AyMi53ZWItcGxh
-dGZvcm0udGVzdIIWb3A2NS53ZWItcGxhdGZvcm0udGVzdIIWb3A5Ni53ZWItcGxh
-dGZvcm0udGVzdIIWb3A4Ny53ZWItcGxhdGZvcm0udGVzdIIWb3AxOS53ZWItcGxh
-dGZvcm0udGVzdIIWb3A1My53ZWItcGxhdGZvcm0udGVzdIIWb3AzMC53ZWItcGxh
-dGZvcm0udGVzdIIWb3A0OC53ZWItcGxhdGZvcm0udGVzdIIWb3AzNC53ZWItcGxh
-dGZvcm0udGVzdIIZb3A2Lm5vdC13ZWItcGxhdGZvcm0udGVzdIIZb3AzLm5vdC13
-ZWItcGxhdGZvcm0udGVzdIIZb3AyLm5vdC13ZWItcGxhdGZvcm0udGVzdIIZb3A1
-Lm5vdC13ZWItcGxhdGZvcm0udGVzdIIZd3d3Lm5vdC13ZWItcGxhdGZvcm0udGVz
-dIIZd3d3Lnd3dy53ZWItcGxhdGZvcm0udGVzdIIZb3A3Lm5vdC13ZWItcGxhdGZv
-cm0udGVzdIIZb3A0Lm5vdC13ZWItcGxhdGZvcm0udGVzdIIZb3A4Lm5vdC13ZWIt
-cGxhdGZvcm0udGVzdIIZb3A5Lm5vdC13ZWItcGxhdGZvcm0udGVzdIIZb3AxLm5v
-dC13ZWItcGxhdGZvcm0udGVzdIIab3AzNi5ub3Qtd2ViLXBsYXRmb3JtLnRlc3SC
-Gm9wNTMubm90LXdlYi1wbGF0Zm9ybS50ZXN0ghpvcDUwLm5vdC13ZWItcGxhdGZv
-cm0udGVzdIIab3AyNC5ub3Qtd2ViLXBsYXRmb3JtLnRlc3SCGm9wMzEubm90LXdl
-Yi1wbGF0Zm9ybS50ZXN0ghpvcDk1Lm5vdC13ZWItcGxhdGZvcm0udGVzdIIab3A4
-My5ub3Qtd2ViLXBsYXRmb3JtLnRlc3SCGnd3dzIubm90LXdlYi1wbGF0Zm9ybS50
-ZXN0ghpvcDczLm5vdC13ZWItcGxhdGZvcm0udGVzdIIab3AxOS5ub3Qtd2ViLXBs
-YXRmb3JtLnRlc3SCGm9wMjEubm90LXdlYi1wbGF0Zm9ybS50ZXN0ghpvcDgxLm5v
-dC13ZWItcGxhdGZvcm0udGVzdIIab3A3MC5ub3Qtd2ViLXBsYXRmb3JtLnRlc3SC
-Gm9wNzgubm90LXdlYi1wbGF0Zm9ybS50ZXN0ghpvcDQwLm5vdC13ZWItcGxhdGZv
-cm0udGVzdIIab3AyNS5ub3Qtd2ViLXBsYXRmb3JtLnRlc3SCGm9wNjUubm90LXdl
-Yi1wbGF0Zm9ybS50ZXN0ghp3d3cud3d3Mi53ZWItcGxhdGZvcm0udGVzdIIab3A4
-MC5ub3Qtd2ViLXBsYXRmb3JtLnRlc3SCGm9wNTIubm90LXdlYi1wbGF0Zm9ybS50
-ZXN0ghpvcDY4Lm5vdC13ZWItcGxhdGZvcm0udGVzdIIab3A0NS5ub3Qtd2ViLXBs
-YXRmb3JtLnRlc3SCGm9wNzEubm90LXdlYi1wbGF0Zm9ybS50ZXN0ghpvcDcyLm5v
-dC13ZWItcGxhdGZvcm0udGVzdIIab3A5MC5ub3Qtd2ViLXBsYXRmb3JtLnRlc3SC
-Gm9wODkubm90LXdlYi1wbGF0Zm9ybS50ZXN0ghpvcDQ5Lm5vdC13ZWItcGxhdGZv
-cm0udGVzdIIab3A3Ny5ub3Qtd2ViLXBsYXRmb3JtLnRlc3SCGm9wNzkubm90LXdl
-Yi1wbGF0Zm9ybS50ZXN0ghpvcDgyLm5vdC13ZWItcGxhdGZvcm0udGVzdIIad3d3
-Lnd3dzEud2ViLXBsYXRmb3JtLnRlc3SCGm9wMTIubm90LXdlYi1wbGF0Zm9ybS50
-ZXN0ghpvcDM5Lm5vdC13ZWItcGxhdGZvcm0udGVzdIIab3A0NC5ub3Qtd2ViLXBs
-YXRmb3JtLnRlc3SCGnd3dzEubm90LXdlYi1wbGF0Zm9ybS50ZXN0ghpvcDU4Lm5v
-dC13ZWItcGxhdGZvcm0udGVzdIIab3AxNC5ub3Qtd2ViLXBsYXRmb3JtLnRlc3SC
-Gm9wMzAubm90LXdlYi1wbGF0Zm9ybS50ZXN0ghpvcDYyLm5vdC13ZWItcGxhdGZv
-cm0udGVzdIIab3A2MS5ub3Qtd2ViLXBsYXRmb3JtLnRlc3SCGm9wOTIubm90LXdl
-Yi1wbGF0Zm9ybS50ZXN0ghpvcDI5Lm5vdC13ZWItcGxhdGZvcm0udGVzdIIab3A5
-OC5ub3Qtd2ViLXBsYXRmb3JtLnRlc3SCGm9wNjQubm90LXdlYi1wbGF0Zm9ybS50
-ZXN0ghpvcDI2Lm5vdC13ZWItcGxhdGZvcm0udGVzdIIab3AyMi5ub3Qtd2ViLXBs
-YXRmb3JtLnRlc3SCGm9wOTQubm90LXdlYi1wbGF0Zm9ybS50ZXN0ghpvcDM4Lm5v
-dC13ZWItcGxhdGZvcm0udGVzdIIab3AzMy5ub3Qtd2ViLXBsYXRmb3JtLnRlc3SC
-Gm9wMjMubm90LXdlYi1wbGF0Zm9ybS50ZXN0ghpvcDU3Lm5vdC13ZWItcGxhdGZv
-cm0udGVzdIIab3A1NC5ub3Qtd2ViLXBsYXRmb3JtLnRlc3SCGm9wODUubm90LXdl
-Yi1wbGF0Zm9ybS50ZXN0ghpvcDQ2Lm5vdC13ZWItcGxhdGZvcm0udGVzdIIab3A5
-Ny5ub3Qtd2ViLXBsYXRmb3JtLnRlc3SCGm9wMzIubm90LXdlYi1wbGF0Zm9ybS50
-ZXN0ghpvcDYwLm5vdC13ZWItcGxhdGZvcm0udGVzdIIab3A5Ni5ub3Qtd2ViLXBs
-YXRmb3JtLnRlc3SCGm9wNTEubm90LXdlYi1wbGF0Zm9ybS50ZXN0ghpvcDQxLm5v
-dC13ZWItcGxhdGZvcm0udGVzdIIab3AzNS5ub3Qtd2ViLXBsYXRmb3JtLnRlc3SC
-Gm9wOTkubm90LXdlYi1wbGF0Zm9ybS50ZXN0ghpvcDQyLm5vdC13ZWItcGxhdGZv
-cm0udGVzdIIab3A2Ny5ub3Qtd2ViLXBsYXRmb3JtLnRlc3SCGm9wMzcubm90LXdl
-Yi1wbGF0Zm9ybS50ZXN0ghpvcDQ4Lm5vdC13ZWItcGxhdGZvcm0udGVzdIIab3A1
-NS5ub3Qtd2ViLXBsYXRmb3JtLnRlc3SCGm9wNTYubm90LXdlYi1wbGF0Zm9ybS50
-ZXN0ghpvcDg0Lm5vdC13ZWItcGxhdGZvcm0udGVzdIIab3AzNC5ub3Qtd2ViLXBs
-YXRmb3JtLnRlc3SCGm9wNjkubm90LXdlYi1wbGF0Zm9ybS50ZXN0ghpvcDExLm5v
-dC13ZWItcGxhdGZvcm0udGVzdIIab3A5My5ub3Qtd2ViLXBsYXRmb3JtLnRlc3SC
-Gnd3dzEud3d3LndlYi1wbGF0Zm9ybS50ZXN0ghpvcDg2Lm5vdC13ZWItcGxhdGZv
-cm0udGVzdIIab3AxMy5ub3Qtd2ViLXBsYXRmb3JtLnRlc3SCGm9wMjAubm90LXdl
-Yi1wbGF0Zm9ybS50ZXN0ghpvcDc2Lm5vdC13ZWItcGxhdGZvcm0udGVzdIIab3Ay
-Ny5ub3Qtd2ViLXBsYXRmb3JtLnRlc3SCGm9wMTcubm90LXdlYi1wbGF0Zm9ybS50
-ZXN0ghpvcDc1Lm5vdC13ZWItcGxhdGZvcm0udGVzdIIab3AxNS5ub3Qtd2ViLXBs
-YXRmb3JtLnRlc3SCGm9wNDcubm90LXdlYi1wbGF0Zm9ybS50ZXN0ghpvcDE4Lm5v
-dC13ZWItcGxhdGZvcm0udGVzdIIab3A2My5ub3Qtd2ViLXBsYXRmb3JtLnRlc3SC
-Gm9wMjgubm90LXdlYi1wbGF0Zm9ybS50ZXN0ghpvcDQzLm5vdC13ZWItcGxhdGZv
-cm0udGVzdIIab3A2Ni5ub3Qtd2ViLXBsYXRmb3JtLnRlc3SCGnd3dzIud3d3Lndl
-Yi1wbGF0Zm9ybS50ZXN0ghpvcDkxLm5vdC13ZWItcGxhdGZvcm0udGVzdIIab3A3
-NC5ub3Qtd2ViLXBsYXRmb3JtLnRlc3SCGm9wNTkubm90LXdlYi1wbGF0Zm9ybS50
-ZXN0ghpvcDg4Lm5vdC13ZWItcGxhdGZvcm0udGVzdIIab3A4Ny5ub3Qtd2ViLXBs
-YXRmb3JtLnRlc3SCGm9wMTAubm90LXdlYi1wbGF0Zm9ybS50ZXN0ghpvcDE2Lm5v
-dC13ZWItcGxhdGZvcm0udGVzdIIbd3d3MS53d3cyLndlYi1wbGF0Zm9ybS50ZXN0
-ght3d3cyLnd3dzIud2ViLXBsYXRmb3JtLnRlc3SCG3d3dzIud3d3MS53ZWItcGxh
-dGZvcm0udGVzdIIbd3d3MS53d3cxLndlYi1wbGF0Zm9ybS50ZXN0gh13d3cud3d3
-Lm5vdC13ZWItcGxhdGZvcm0udGVzdIIeeG4tLWx2ZS02bGFkLndlYi1wbGF0Zm9y
-bS50ZXN0gh53d3cxLnd3dy5ub3Qtd2ViLXBsYXRmb3JtLnRlc3SCHnd3dy53d3cy
+AAOCAQ8AMIIBCgKCAQEAtDb64jtYPItC+5BCMCAzBXsjr34h8WPEpxYqBX8bSIml
+XXtZQuA2ZvPpwuc+LiWvyBYIQ6bD4dDMd1/H2fzCv1vItvIVX/LXnAdTRnr8WRgX
+lcQw2be6zALDwpEccNNwR9rnTCGPvL+vChK09Bu68szvEd1Dee0WbAN77Q35L42i
+4AG3nY2+APxE6gZP3vrN+ufO4gT76sWgJsBVsqs2nESP2oCuhKDpBKbPAHiZYR4l
+cy3rxfzEMQMZkvWUOGhgazJoUmRHxkn7JOG08BbnBa5RZsGjWslgZYawuu+6ahWa
+ViWMOSQWPifnmBPEYjgkH2nA/15ECJLVMg466uxkKwIDAQABo4IeBDCCHgAwCQYD
+VR0TBAIwADAdBgNVHQ4EFgQUyQbSxR8J9EhqD0DoKUszIqCK+zMwHwYDVR0jBBgw
+FoAUvXg4wjfznAao6z6vWqGIJBSFW74wCwYDVR0PBAQDAgXgMBMGA1UdJQQMMAoG
+CCsGAQUFBwMBMIIdjwYDVR0RBIIdhjCCHYKCEXdlYi1wbGF0Zm9ybS50ZXN0ghVu
+b3Qtd2ViLXBsYXRmb3JtLnRlc3SCFW9wNi53ZWItcGxhdGZvcm0udGVzdIIVd3d3
+LndlYi1wbGF0Zm9ybS50ZXN0ghVvcDEud2ViLXBsYXRmb3JtLnRlc3SCFW9wNy53
+ZWItcGxhdGZvcm0udGVzdIIVb3A4LndlYi1wbGF0Zm9ybS50ZXN0ghVvcDIud2Vi
+LXBsYXRmb3JtLnRlc3SCFW9wOS53ZWItcGxhdGZvcm0udGVzdIIVb3A1LndlYi1w
+bGF0Zm9ybS50ZXN0ghVvcDQud2ViLXBsYXRmb3JtLnRlc3SCFW9wMy53ZWItcGxh
+dGZvcm0udGVzdIIWb3A0MC53ZWItcGxhdGZvcm0udGVzdIIWb3A2MS53ZWItcGxh
+dGZvcm0udGVzdIIWb3AzMy53ZWItcGxhdGZvcm0udGVzdIIWb3A3OS53ZWItcGxh
+dGZvcm0udGVzdIIWb3AzOC53ZWItcGxhdGZvcm0udGVzdIIWb3A3Ny53ZWItcGxh
+dGZvcm0udGVzdIIWb3A0My53ZWItcGxhdGZvcm0udGVzdIIWb3A0NC53ZWItcGxh
+dGZvcm0udGVzdIIWb3AxNC53ZWItcGxhdGZvcm0udGVzdIIWb3A0OS53ZWItcGxh
+dGZvcm0udGVzdIIWb3AzNi53ZWItcGxhdGZvcm0udGVzdIIWb3AyMS53ZWItcGxh
+dGZvcm0udGVzdIIWb3A2MC53ZWItcGxhdGZvcm0udGVzdIIWb3A3MS53ZWItcGxh
+dGZvcm0udGVzdIIWb3A1Ni53ZWItcGxhdGZvcm0udGVzdIIWb3A5NC53ZWItcGxh
+dGZvcm0udGVzdIIWb3AxOC53ZWItcGxhdGZvcm0udGVzdIIWb3A1Ny53ZWItcGxh
+dGZvcm0udGVzdIIWd3d3Mi53ZWItcGxhdGZvcm0udGVzdIIWb3A5MS53ZWItcGxh
+dGZvcm0udGVzdIIWb3A0MS53ZWItcGxhdGZvcm0udGVzdIIWb3A0Ny53ZWItcGxh
+dGZvcm0udGVzdIIWb3A1Mi53ZWItcGxhdGZvcm0udGVzdIIWb3AyNS53ZWItcGxh
+dGZvcm0udGVzdIIWb3AxNi53ZWItcGxhdGZvcm0udGVzdIIWb3AyNi53ZWItcGxh
+dGZvcm0udGVzdIIWb3AxMC53ZWItcGxhdGZvcm0udGVzdIIWb3A2OS53ZWItcGxh
+dGZvcm0udGVzdIIWb3AxMi53ZWItcGxhdGZvcm0udGVzdIIWb3A5Ni53ZWItcGxh
+dGZvcm0udGVzdIIWb3A3NC53ZWItcGxhdGZvcm0udGVzdIIWb3A4OS53ZWItcGxh
+dGZvcm0udGVzdIIWb3A4NC53ZWItcGxhdGZvcm0udGVzdIIWb3AzNy53ZWItcGxh
+dGZvcm0udGVzdIIWb3A0NS53ZWItcGxhdGZvcm0udGVzdIIWb3A2Mi53ZWItcGxh
+dGZvcm0udGVzdIIWb3A4Ni53ZWItcGxhdGZvcm0udGVzdIIWb3A0Mi53ZWItcGxh
+dGZvcm0udGVzdIIWb3AzNC53ZWItcGxhdGZvcm0udGVzdIIWb3A0OC53ZWItcGxh
+dGZvcm0udGVzdIIWb3A3NS53ZWItcGxhdGZvcm0udGVzdIIWb3A1MC53ZWItcGxh
+dGZvcm0udGVzdIIWb3AyMi53ZWItcGxhdGZvcm0udGVzdIIWb3AyMy53ZWItcGxh
+dGZvcm0udGVzdIIWb3A3My53ZWItcGxhdGZvcm0udGVzdIIWb3AyMC53ZWItcGxh
+dGZvcm0udGVzdIIWb3A5Mi53ZWItcGxhdGZvcm0udGVzdIIWb3AzMi53ZWItcGxh
+dGZvcm0udGVzdIIWb3A4Mi53ZWItcGxhdGZvcm0udGVzdIIWb3A5OC53ZWItcGxh
+dGZvcm0udGVzdIIWb3AzMS53ZWItcGxhdGZvcm0udGVzdIIWb3AzMC53ZWItcGxh
+dGZvcm0udGVzdIIWb3A4Ny53ZWItcGxhdGZvcm0udGVzdIIWb3A3MC53ZWItcGxh
+dGZvcm0udGVzdIIWb3A2NC53ZWItcGxhdGZvcm0udGVzdIIWb3AxMS53ZWItcGxh
+dGZvcm0udGVzdIIWb3AxNS53ZWItcGxhdGZvcm0udGVzdIIWb3A4NS53ZWItcGxh
+dGZvcm0udGVzdIIWb3A1OC53ZWItcGxhdGZvcm0udGVzdIIWd3d3MS53ZWItcGxh
+dGZvcm0udGVzdIIWb3A4My53ZWItcGxhdGZvcm0udGVzdIIWb3AxMy53ZWItcGxh
+dGZvcm0udGVzdIIWb3A2OC53ZWItcGxhdGZvcm0udGVzdIIWb3A5OS53ZWItcGxh
+dGZvcm0udGVzdIIWb3AzOS53ZWItcGxhdGZvcm0udGVzdIIWb3A1OS53ZWItcGxh
+dGZvcm0udGVzdIIWb3A5NS53ZWItcGxhdGZvcm0udGVzdIIWb3A1My53ZWItcGxh
+dGZvcm0udGVzdIIWb3A2My53ZWItcGxhdGZvcm0udGVzdIIWb3A1NC53ZWItcGxh
+dGZvcm0udGVzdIIWb3A4OC53ZWItcGxhdGZvcm0udGVzdIIWb3A5My53ZWItcGxh
+dGZvcm0udGVzdIIWb3AyOC53ZWItcGxhdGZvcm0udGVzdIIWb3A1MS53ZWItcGxh
+dGZvcm0udGVzdIIWb3A2Ni53ZWItcGxhdGZvcm0udGVzdIIWb3AxOS53ZWItcGxh
+dGZvcm0udGVzdIIWb3A5MC53ZWItcGxhdGZvcm0udGVzdIIWb3A4MC53ZWItcGxh
+dGZvcm0udGVzdIIWb3A2Ny53ZWItcGxhdGZvcm0udGVzdIIWb3A2NS53ZWItcGxh
+dGZvcm0udGVzdIIWb3AyNy53ZWItcGxhdGZvcm0udGVzdIIWb3A3Ni53ZWItcGxh
+dGZvcm0udGVzdIIWb3A3Mi53ZWItcGxhdGZvcm0udGVzdIIWb3AyOS53ZWItcGxh
+dGZvcm0udGVzdIIWb3A5Ny53ZWItcGxhdGZvcm0udGVzdIIWb3AzNS53ZWItcGxh
+dGZvcm0udGVzdIIWb3A1NS53ZWItcGxhdGZvcm0udGVzdIIWb3A0Ni53ZWItcGxh
+dGZvcm0udGVzdIIWb3A3OC53ZWItcGxhdGZvcm0udGVzdIIWb3AxNy53ZWItcGxh
+dGZvcm0udGVzdIIWb3AyNC53ZWItcGxhdGZvcm0udGVzdIIWb3A4MS53ZWItcGxh
+dGZvcm0udGVzdIIZb3A3Lm5vdC13ZWItcGxhdGZvcm0udGVzdIIZb3A0Lm5vdC13
+ZWItcGxhdGZvcm0udGVzdIIZb3A4Lm5vdC13ZWItcGxhdGZvcm0udGVzdIIZd3d3
+Lnd3dy53ZWItcGxhdGZvcm0udGVzdIIZb3A5Lm5vdC13ZWItcGxhdGZvcm0udGVz
+dIIZd3d3Lm5vdC13ZWItcGxhdGZvcm0udGVzdIIZb3AxLm5vdC13ZWItcGxhdGZv
+cm0udGVzdIIZb3AzLm5vdC13ZWItcGxhdGZvcm0udGVzdIIZb3A1Lm5vdC13ZWIt
+cGxhdGZvcm0udGVzdIIZb3AyLm5vdC13ZWItcGxhdGZvcm0udGVzdIIZb3A2Lm5v
+dC13ZWItcGxhdGZvcm0udGVzdIIab3AyNi5ub3Qtd2ViLXBsYXRmb3JtLnRlc3SC
+Gm9wOTQubm90LXdlYi1wbGF0Zm9ybS50ZXN0ghpvcDc1Lm5vdC13ZWItcGxhdGZv
+cm0udGVzdIIab3A1NC5ub3Qtd2ViLXBsYXRmb3JtLnRlc3SCGm9wMjUubm90LXdl
+Yi1wbGF0Zm9ybS50ZXN0ghpvcDI4Lm5vdC13ZWItcGxhdGZvcm0udGVzdIIab3A5
+Mi5ub3Qtd2ViLXBsYXRmb3JtLnRlc3SCGm9wMTcubm90LXdlYi1wbGF0Zm9ybS50
+ZXN0ghpvcDk1Lm5vdC13ZWItcGxhdGZvcm0udGVzdIIab3A1My5ub3Qtd2ViLXBs
+YXRmb3JtLnRlc3SCGm9wMzcubm90LXdlYi1wbGF0Zm9ybS50ZXN0ghpvcDQxLm5v
+dC13ZWItcGxhdGZvcm0udGVzdIIab3A2OC5ub3Qtd2ViLXBsYXRmb3JtLnRlc3SC
+Gm9wODcubm90LXdlYi1wbGF0Zm9ybS50ZXN0ghp3d3cxLnd3dy53ZWItcGxhdGZv
+cm0udGVzdIIab3A3MC5ub3Qtd2ViLXBsYXRmb3JtLnRlc3SCGm9wODMubm90LXdl
+Yi1wbGF0Zm9ybS50ZXN0ghpvcDkxLm5vdC13ZWItcGxhdGZvcm0udGVzdIIab3A4
+MS5ub3Qtd2ViLXBsYXRmb3JtLnRlc3SCGm9wOTkubm90LXdlYi1wbGF0Zm9ybS50
+ZXN0ghpvcDQ4Lm5vdC13ZWItcGxhdGZvcm0udGVzdIIab3A2NC5ub3Qtd2ViLXBs
+YXRmb3JtLnRlc3SCGm9wNzYubm90LXdlYi1wbGF0Zm9ybS50ZXN0ghp3d3cud3d3
+Mi53ZWItcGxhdGZvcm0udGVzdIIab3AyNC5ub3Qtd2ViLXBsYXRmb3JtLnRlc3SC
+Gm9wNzgubm90LXdlYi1wbGF0Zm9ybS50ZXN0ghp3d3cud3d3MS53ZWItcGxhdGZv
+cm0udGVzdIIab3A3MS5ub3Qtd2ViLXBsYXRmb3JtLnRlc3SCGm9wMTUubm90LXdl
+Yi1wbGF0Zm9ybS50ZXN0ghpvcDcyLm5vdC13ZWItcGxhdGZvcm0udGVzdIIab3A2
+NS5ub3Qtd2ViLXBsYXRmb3JtLnRlc3SCGm9wOTgubm90LXdlYi1wbGF0Zm9ybS50
+ZXN0ghpvcDQ5Lm5vdC13ZWItcGxhdGZvcm0udGVzdIIab3A1MC5ub3Qtd2ViLXBs
+YXRmb3JtLnRlc3SCGnd3dzEubm90LXdlYi1wbGF0Zm9ybS50ZXN0ghpvcDQ3Lm5v
+dC13ZWItcGxhdGZvcm0udGVzdIIab3A4Mi5ub3Qtd2ViLXBsYXRmb3JtLnRlc3SC
+Gm9wMTIubm90LXdlYi1wbGF0Zm9ybS50ZXN0ghpvcDI3Lm5vdC13ZWItcGxhdGZv
+cm0udGVzdIIab3A4OS5ub3Qtd2ViLXBsYXRmb3JtLnRlc3SCGm9wODYubm90LXdl
+Yi1wbGF0Zm9ybS50ZXN0ghpvcDUyLm5vdC13ZWItcGxhdGZvcm0udGVzdIIab3Ay
+MC5ub3Qtd2ViLXBsYXRmb3JtLnRlc3SCGm9wMjMubm90LXdlYi1wbGF0Zm9ybS50
+ZXN0ghpvcDYzLm5vdC13ZWItcGxhdGZvcm0udGVzdIIab3A0Mi5ub3Qtd2ViLXBs
+YXRmb3JtLnRlc3SCGm9wNzQubm90LXdlYi1wbGF0Zm9ybS50ZXN0ghpvcDYyLm5v
+dC13ZWItcGxhdGZvcm0udGVzdIIab3AxMy5ub3Qtd2ViLXBsYXRmb3JtLnRlc3SC
+Gm9wNTgubm90LXdlYi1wbGF0Zm9ybS50ZXN0ghpvcDY2Lm5vdC13ZWItcGxhdGZv
+cm0udGVzdIIad3d3Mi5ub3Qtd2ViLXBsYXRmb3JtLnRlc3SCGm9wMzEubm90LXdl
+Yi1wbGF0Zm9ybS50ZXN0ghpvcDM5Lm5vdC13ZWItcGxhdGZvcm0udGVzdIIab3A3
+My5ub3Qtd2ViLXBsYXRmb3JtLnRlc3SCGm9wNzcubm90LXdlYi1wbGF0Zm9ybS50
+ZXN0ghpvcDU1Lm5vdC13ZWItcGxhdGZvcm0udGVzdIIab3A2OS5ub3Qtd2ViLXBs
+YXRmb3JtLnRlc3SCGm9wNTEubm90LXdlYi1wbGF0Zm9ybS50ZXN0ghpvcDk2Lm5v
+dC13ZWItcGxhdGZvcm0udGVzdIIab3AxNi5ub3Qtd2ViLXBsYXRmb3JtLnRlc3SC
+Gm9wMTAubm90LXdlYi1wbGF0Zm9ybS50ZXN0ghp3d3cyLnd3dy53ZWItcGxhdGZv
+cm0udGVzdIIab3A2MS5ub3Qtd2ViLXBsYXRmb3JtLnRlc3SCGm9wMjEubm90LXdl
+Yi1wbGF0Zm9ybS50ZXN0ghpvcDgwLm5vdC13ZWItcGxhdGZvcm0udGVzdIIab3A5
+MC5ub3Qtd2ViLXBsYXRmb3JtLnRlc3SCGm9wNjcubm90LXdlYi1wbGF0Zm9ybS50
+ZXN0ghpvcDQzLm5vdC13ZWItcGxhdGZvcm0udGVzdIIab3A1OS5ub3Qtd2ViLXBs
+YXRmb3JtLnRlc3SCGm9wNDQubm90LXdlYi1wbGF0Zm9ybS50ZXN0ghpvcDE0Lm5v
+dC13ZWItcGxhdGZvcm0udGVzdIIab3AxOS5ub3Qtd2ViLXBsYXRmb3JtLnRlc3SC
+Gm9wMzMubm90LXdlYi1wbGF0Zm9ybS50ZXN0ghpvcDIyLm5vdC13ZWItcGxhdGZv
+cm0udGVzdIIab3A4OC5ub3Qtd2ViLXBsYXRmb3JtLnRlc3SCGm9wNjAubm90LXdl
+Yi1wbGF0Zm9ybS50ZXN0ghpvcDQwLm5vdC13ZWItcGxhdGZvcm0udGVzdIIab3A0
+Ni5ub3Qtd2ViLXBsYXRmb3JtLnRlc3SCGm9wMzIubm90LXdlYi1wbGF0Zm9ybS50
+ZXN0ghpvcDU2Lm5vdC13ZWItcGxhdGZvcm0udGVzdIIab3AyOS5ub3Qtd2ViLXBs
+YXRmb3JtLnRlc3SCGm9wMzQubm90LXdlYi1wbGF0Zm9ybS50ZXN0ghpvcDk3Lm5v
+dC13ZWItcGxhdGZvcm0udGVzdIIab3A4NC5ub3Qtd2ViLXBsYXRmb3JtLnRlc3SC
+Gm9wMzUubm90LXdlYi1wbGF0Zm9ybS50ZXN0ghpvcDkzLm5vdC13ZWItcGxhdGZv
+cm0udGVzdIIab3A3OS5ub3Qtd2ViLXBsYXRmb3JtLnRlc3SCGm9wMTgubm90LXdl
+Yi1wbGF0Zm9ybS50ZXN0ghpvcDU3Lm5vdC13ZWItcGxhdGZvcm0udGVzdIIab3Az
+Ni5ub3Qtd2ViLXBsYXRmb3JtLnRlc3SCGm9wMTEubm90LXdlYi1wbGF0Zm9ybS50
+ZXN0ghpvcDM4Lm5vdC13ZWItcGxhdGZvcm0udGVzdIIab3A4NS5ub3Qtd2ViLXBs
+YXRmb3JtLnRlc3SCGm9wMzAubm90LXdlYi1wbGF0Zm9ybS50ZXN0ghpvcDQ1Lm5v
+dC13ZWItcGxhdGZvcm0udGVzdIIbd3d3Mi53d3cyLndlYi1wbGF0Zm9ybS50ZXN0
+ght3d3cxLnd3dzEud2ViLXBsYXRmb3JtLnRlc3SCG3d3dzEud3d3Mi53ZWItcGxh
+dGZvcm0udGVzdIIbd3d3Mi53d3cxLndlYi1wbGF0Zm9ybS50ZXN0gh13d3cud3d3
+Lm5vdC13ZWItcGxhdGZvcm0udGVzdIIed3d3MS53d3cubm90LXdlYi1wbGF0Zm9y
+bS50ZXN0gh53d3cud3d3MS5ub3Qtd2ViLXBsYXRmb3JtLnRlc3SCHnd3dy53d3cy
 Lm5vdC13ZWItcGxhdGZvcm0udGVzdIIed3d3Mi53d3cubm90LXdlYi1wbGF0Zm9y
-bS50ZXN0gh53d3cud3d3MS5ub3Qtd2ViLXBsYXRmb3JtLnRlc3SCH3d3dzIud3d3
-Mi5ub3Qtd2ViLXBsYXRmb3JtLnRlc3SCH3d3dzIud3d3MS5ub3Qtd2ViLXBsYXRm
-b3JtLnRlc3SCH3d3dzEud3d3MS5ub3Qtd2ViLXBsYXRmb3JtLnRlc3SCH3d3dzEu
+bS50ZXN0gh54bi0tbHZlLTZsYWQud2ViLXBsYXRmb3JtLnRlc3SCH3d3dzIud3d3
+MS5ub3Qtd2ViLXBsYXRmb3JtLnRlc3SCH3d3dzEud3d3MS5ub3Qtd2ViLXBsYXRm
+b3JtLnRlc3SCH3d3dzIud3d3Mi5ub3Qtd2ViLXBsYXRmb3JtLnRlc3SCH3d3dzEu
 d3d3Mi5ub3Qtd2ViLXBsYXRmb3JtLnRlc3SCInhuLS1sdmUtNmxhZC53d3cud2Vi
-LXBsYXRmb3JtLnRlc3SCInhuLS1sdmUtNmxhZC5ub3Qtd2ViLXBsYXRmb3JtLnRl
-c3SCInd3dy54bi0tbHZlLTZsYWQud2ViLXBsYXRmb3JtLnRlc3SCI3d3dzIueG4t
-LWx2ZS02bGFkLndlYi1wbGF0Zm9ybS50ZXN0giN4bi0tbHZlLTZsYWQud3d3Mi53
-ZWItcGxhdGZvcm0udGVzdIIjeG4tLWx2ZS02bGFkLnd3dzEud2ViLXBsYXRmb3Jt
+LXBsYXRmb3JtLnRlc3SCInd3dy54bi0tbHZlLTZsYWQud2ViLXBsYXRmb3JtLnRl
+c3SCInhuLS1sdmUtNmxhZC5ub3Qtd2ViLXBsYXRmb3JtLnRlc3SCI3huLS1sdmUt
+NmxhZC53d3cxLndlYi1wbGF0Zm9ybS50ZXN0giN4bi0tbHZlLTZsYWQud3d3Mi53
+ZWItcGxhdGZvcm0udGVzdIIjd3d3Mi54bi0tbHZlLTZsYWQud2ViLXBsYXRmb3Jt
 LnRlc3SCI3d3dzEueG4tLWx2ZS02bGFkLndlYi1wbGF0Zm9ybS50ZXN0giZ4bi0t
 bHZlLTZsYWQud3d3Lm5vdC13ZWItcGxhdGZvcm0udGVzdIImd3d3LnhuLS1sdmUt
-NmxhZC5ub3Qtd2ViLXBsYXRmb3JtLnRlc3SCJ3huLS1sdmUtNmxhZC53d3cxLm5v
-dC13ZWItcGxhdGZvcm0udGVzdIInd3d3Mi54bi0tbHZlLTZsYWQubm90LXdlYi1w
-bGF0Zm9ybS50ZXN0gid3d3cxLnhuLS1sdmUtNmxhZC5ub3Qtd2ViLXBsYXRmb3Jt
-LnRlc3SCJ3huLS1sdmUtNmxhZC53d3cyLm5vdC13ZWItcGxhdGZvcm0udGVzdIIp
+NmxhZC5ub3Qtd2ViLXBsYXRmb3JtLnRlc3SCJ3d3dzEueG4tLWx2ZS02bGFkLm5v
+dC13ZWItcGxhdGZvcm0udGVzdIIneG4tLWx2ZS02bGFkLnd3dzEubm90LXdlYi1w
+bGF0Zm9ybS50ZXN0gid4bi0tbHZlLTZsYWQud3d3Mi5ub3Qtd2ViLXBsYXRmb3Jt
+LnRlc3SCJ3d3dzIueG4tLWx2ZS02bGFkLm5vdC13ZWItcGxhdGZvcm0udGVzdIIp
 eG4tLW44ajZkczUzbHd3a3JxaHYyOGEud2ViLXBsYXRmb3JtLnRlc3SCK3huLS1s
-dmUtNmxhZC54bi0tbHZlLTZsYWQud2ViLXBsYXRmb3JtLnRlc3SCLXd3dy54bi0t
-bjhqNmRzNTNsd3drcnFodjI4YS53ZWItcGxhdGZvcm0udGVzdIIteG4tLW44ajZk
-czUzbHd3a3JxaHYyOGEubm90LXdlYi1wbGF0Zm9ybS50ZXN0gi14bi0tbjhqNmRz
-NTNsd3drcnFodjI4YS53d3cud2ViLXBsYXRmb3JtLnRlc3SCLnd3dzEueG4tLW44
+dmUtNmxhZC54bi0tbHZlLTZsYWQud2ViLXBsYXRmb3JtLnRlc3SCLXhuLS1uOGo2
+ZHM1M2x3d2tycWh2MjhhLnd3dy53ZWItcGxhdGZvcm0udGVzdIIteG4tLW44ajZk
+czUzbHd3a3JxaHYyOGEubm90LXdlYi1wbGF0Zm9ybS50ZXN0gi13d3cueG4tLW44
+ajZkczUzbHd3a3JxaHYyOGEud2ViLXBsYXRmb3JtLnRlc3SCLnd3dzIueG4tLW44
+ajZkczUzbHd3a3JxaHYyOGEud2ViLXBsYXRmb3JtLnRlc3SCLnd3dzEueG4tLW44
 ajZkczUzbHd3a3JxaHYyOGEud2ViLXBsYXRmb3JtLnRlc3SCLnhuLS1uOGo2ZHM1
-M2x3d2tycWh2MjhhLnd3dzIud2ViLXBsYXRmb3JtLnRlc3SCLnhuLS1uOGo2ZHM1
-M2x3d2tycWh2MjhhLnd3dzEud2ViLXBsYXRmb3JtLnRlc3SCLnd3dzIueG4tLW44
-ajZkczUzbHd3a3JxaHYyOGEud2ViLXBsYXRmb3JtLnRlc3SCL3huLS1sdmUtNmxh
+M2x3d2tycWh2MjhhLnd3dzEud2ViLXBsYXRmb3JtLnRlc3SCLnhuLS1uOGo2ZHM1
+M2x3d2tycWh2MjhhLnd3dzIud2ViLXBsYXRmb3JtLnRlc3SCL3huLS1sdmUtNmxh
 ZC54bi0tbHZlLTZsYWQubm90LXdlYi1wbGF0Zm9ybS50ZXN0gjF3d3cueG4tLW44
 ajZkczUzbHd3a3JxaHYyOGEubm90LXdlYi1wbGF0Zm9ybS50ZXN0gjF4bi0tbjhq
 NmRzNTNsd3drcnFodjI4YS53d3cubm90LXdlYi1wbGF0Zm9ybS50ZXN0gjJ4bi0t
-bjhqNmRzNTNsd3drcnFodjI4YS53d3cyLm5vdC13ZWItcGxhdGZvcm0udGVzdIIy
-d3d3MS54bi0tbjhqNmRzNTNsd3drcnFodjI4YS5ub3Qtd2ViLXBsYXRmb3JtLnRl
-c3SCMnd3dzIueG4tLW44ajZkczUzbHd3a3JxaHYyOGEubm90LXdlYi1wbGF0Zm9y
-bS50ZXN0gjJ4bi0tbjhqNmRzNTNsd3drcnFodjI4YS53d3cxLm5vdC13ZWItcGxh
+bjhqNmRzNTNsd3drcnFodjI4YS53d3cxLm5vdC13ZWItcGxhdGZvcm0udGVzdIIy
+d3d3Mi54bi0tbjhqNmRzNTNsd3drcnFodjI4YS5ub3Qtd2ViLXBsYXRmb3JtLnRl
+c3SCMnd3dzEueG4tLW44ajZkczUzbHd3a3JxaHYyOGEubm90LXdlYi1wbGF0Zm9y
+bS50ZXN0gjJ4bi0tbjhqNmRzNTNsd3drcnFodjI4YS53d3cyLm5vdC13ZWItcGxh
 dGZvcm0udGVzdII2eG4tLW44ajZkczUzbHd3a3JxaHYyOGEueG4tLWx2ZS02bGFk
 LndlYi1wbGF0Zm9ybS50ZXN0gjZ4bi0tbHZlLTZsYWQueG4tLW44ajZkczUzbHd3
 a3JxaHYyOGEud2ViLXBsYXRmb3JtLnRlc3SCOnhuLS1uOGo2ZHM1M2x3d2tycWh2
@@ -231,10 +231,10 @@
 c3SCQXhuLS1uOGo2ZHM1M2x3d2tycWh2MjhhLnhuLS1uOGo2ZHM1M2x3d2tycWh2
 MjhhLndlYi1wbGF0Zm9ybS50ZXN0gkV4bi0tbjhqNmRzNTNsd3drcnFodjI4YS54
 bi0tbjhqNmRzNTNsd3drcnFodjI4YS5ub3Qtd2ViLXBsYXRmb3JtLnRlc3QwDQYJ
-KoZIhvcNAQELBQADggEBADin7+tVMOhvv1Gp61KaZuw9GV30HYQXwPImPitmXXqp
-RIbDutfwnueyswUMJe/ht5SMRBzSV0wwUZYpli6z4xz4nOZ9lhQ/Pz6mlksB3PLt
-v/zjZJNJgb7J3d0bztpyRdEtypnN0X35F+HuEvTtdkHKb6VccgBKjXbFK7cYe87o
-q3aadekdU5t1k20LLkk/v4Qfcb5JI6ss+KaiKJOCBGyOhSIOtm3FHoLRDcYImoah
-KVx5U9Ou1sQzS9IEWtmgw20m7Og1BueIEgN8xYMmsrUyo0FRtJSwhA1KBVICKUFb
-AwTww+Uk5lrvcEVFWCUrXr59ynMCKx1PPEsA9GlfGm4=
+KoZIhvcNAQELBQADggEBACD/IHHlLZ8WSvC478sjY1ZwvIzGaw769YBVWvV1WM3G
+fmZtETPv/KZrW5gE1Wtb7o08CxPU+Wtum7Wtpm4F1NP1/7GnF0aOmHuJhxSkwfom
+Am1ExcjGWtKc6DKDq3cHgMiAqx/GVnruf6odazfh1cEEVOUwQemlnvB9j/UkYPAk
+ecvS6C2VM53KmAIRN0eXQxPCinOj4RTLht5wwrHVW9lt9q1vzKUrmyGL13gDXCvk
++ELFfQ0HsOFB7awmlZPbEpujbv+QYg0QrtX0hz06UFXHxOBIU5HpIuuXpjFCKz8k
+BhJckv9pXq8A/ebsLWYwf1nb0W+8LfSL03Ql4aVGeS0=
 -----END CERTIFICATE-----
diff --git a/third_party/wpt_tools/wpt/tools/gitignore/gitignore.py b/third_party/wpt_tools/wpt/tools/gitignore/gitignore.py
index dbe83c2..500fe783 100644
--- a/third_party/wpt_tools/wpt/tools/gitignore/gitignore.py
+++ b/third_party/wpt_tools/wpt/tools/gitignore/gitignore.py
@@ -1,7 +1,7 @@
 import re
 import os
 import itertools
-from six import ensure_binary, itervalues, iteritems
+from six import ensure_binary
 from collections import defaultdict
 
 MYPY = False
@@ -194,13 +194,13 @@
                 rule = cast(Tuple[bool, Pattern[bytes]], rule)
             if not dir_only:
                 rules_iter = itertools.chain(
-                    itertools.chain(*(iteritems(item) for item in itervalues(self.literals_dir))),
-                    itertools.chain(*(iteritems(item) for item in itervalues(self.literals_file))),
+                    itertools.chain(*(item.items() for item in self.literals_dir.values())),
+                    itertools.chain(*(item.items() for item in self.literals_file.values())),
                     self.patterns_dir,
                     self.patterns_file)  # type: Iterable[Tuple[Any, List[Tuple[bool, Pattern[bytes]]]]]
             else:
                 rules_iter = itertools.chain(
-                    itertools.chain(*(iteritems(item) for item in itervalues(self.literals_dir))),
+                    itertools.chain(*(item.items() for item in self.literals_dir.values())),
                     self.patterns_dir)
 
             for rules in rules_iter:
diff --git a/third_party/wpt_tools/wpt/tools/lint/lint.py b/third_party/wpt_tools/wpt/tools/lint/lint.py
index f936055..d8fb9fc 100644
--- a/third_party/wpt_tools/wpt/tools/lint/lint.py
+++ b/third_party/wpt_tools/wpt/tools/lint/lint.py
@@ -14,6 +14,7 @@
 import tempfile
 
 from collections import defaultdict
+from urllib.parse import urlsplit, urljoin
 
 from . import fnmatch
 from . import rules
@@ -24,9 +25,7 @@
 from ..manifest.vcs import walk
 
 from ..manifest.sourcefile import SourceFile, js_meta_re, python_meta_re, space_chars, get_any_variants
-from six import binary_type, ensure_binary, ensure_text, iteritems, itervalues, with_metaclass
-from six.moves import range
-from six.moves.urllib.parse import urlsplit, urljoin
+from six import ensure_binary, ensure_text
 
 MYPY = False
 if MYPY:
@@ -325,7 +324,7 @@
 
     errors = []
 
-    for name, colliding in iteritems(test_files):
+    for name, colliding in test_files.items():
         if len(colliding) > 1:
             if not _all_files_equal([os.path.join(repo_root, x) for x in colliding]):
                 # Only compute by_spec if there are prima-facie collisions because of cost
@@ -342,7 +341,7 @@
                             continue
                         by_spec[spec].add(path)
 
-                for spec, spec_paths in iteritems(by_spec):
+                for spec, spec_paths in by_spec.items():
                     if not _all_files_equal([os.path.join(repo_root, x) for x in spec_paths]):
                         for x in spec_paths:
                             context1 = (name, spec, ", ".join(sorted(spec_paths)))
@@ -351,7 +350,7 @@
 
     for rule_class, d in [(rules.CSSCollidingRefName, ref_files),
                           (rules.CSSCollidingSupportName, support_files)]:
-        for name, colliding in iteritems(d):
+        for name, colliding in d.items():
             if len(colliding) > 1:
                 if not _all_files_equal([os.path.join(repo_root, x) for x in colliding]):
                     context2 = (name, ", ".join(sorted(colliding)))
@@ -451,7 +450,7 @@
         # which explains how to fix it correctly and shouldn't be skipped.
         if error_type in data and error_type != "IGNORED PATH":
             wl_files = data[error_type]
-            for file_match, allowed_lines in iteritems(wl_files):
+            for file_match, allowed_lines in wl_files.items():
                 if None in allowed_lines or line in allowed_lines:
                     if fnmatch.fnmatchcase(normpath, file_match):
                         skipped[i] = True
@@ -668,7 +667,7 @@
 
     return errors
 
-class ASTCheck(with_metaclass(abc.ABCMeta)):
+class ASTCheck(metaclass=abc.ABCMeta):
     @abc.abstractproperty
     def rule(self):
         # type: () -> Type[rules.Rule]
@@ -741,7 +740,7 @@
     done = False
     errors = []
     for idx, line in enumerate(f):
-        assert isinstance(line, binary_type), line
+        assert isinstance(line, bytes), line
 
         m = meta_re.match(line)
         if m:
@@ -975,7 +974,7 @@
 
 def main(**kwargs_str):
     # type: (**Any) -> int
-    kwargs = {ensure_text(key): value for key, value in iteritems(kwargs_str)}
+    kwargs = {ensure_text(key): value for key, value in kwargs_str.items()}
 
     assert logger is not None
     if kwargs.get("json") and kwargs.get("markdown"):
@@ -1103,7 +1102,7 @@
     if error_count and github_checks_outputter:
         github_checks_outputter.output("```")
 
-    return sum(itervalues(error_count))
+    return sum(error_count.values())
 
 
 path_lints = [check_file_type, check_path_length, check_worker_collision, check_ahem_copy,
diff --git a/third_party/wpt_tools/wpt/tools/lint/rules.py b/third_party/wpt_tools/wpt/tools/lint/rules.py
index b389e3d..e9bb30b5 100644
--- a/third_party/wpt_tools/wpt/tools/lint/rules.py
+++ b/third_party/wpt_tools/wpt/tools/lint/rules.py
@@ -5,8 +5,6 @@
 import os
 import re
 
-import six
-
 MYPY = False
 if MYPY:
     # MYPY is set to True when run under Mypy.
@@ -19,7 +17,7 @@
     return inspect.cleandoc(str(text)).replace("\n", " ")
 
 
-class Rule(six.with_metaclass(abc.ABCMeta)):
+class Rule(metaclass=abc.ABCMeta):
     @abc.abstractproperty
     def name(self):
         # type: () -> Text
@@ -367,7 +365,7 @@
     to_fix = "rename directory to be called 'tentative'"
 
 
-class Regexp(six.with_metaclass(abc.ABCMeta)):
+class Regexp(metaclass=abc.ABCMeta):
     @abc.abstractproperty
     def pattern(self):
         # type: () -> bytes
diff --git a/third_party/wpt_tools/wpt/tools/localpaths.py b/third_party/wpt_tools/wpt/tools/localpaths.py
index a79acb82..1cb6f5b3 100644
--- a/third_party/wpt_tools/wpt/tools/localpaths.py
+++ b/third_party/wpt_tools/wpt/tools/localpaths.py
@@ -18,6 +18,7 @@
 sys.path.insert(0, os.path.join(here, "third_party", "py"))
 sys.path.insert(0, os.path.join(here, "third_party", "pytest"))
 sys.path.insert(0, os.path.join(here, "third_party", "pytest", "src"))
+sys.path.insert(0, os.path.join(here, "third_party", "pytest-asyncio"))
 sys.path.insert(0, os.path.join(here, "third_party", "six"))
 sys.path.insert(0, os.path.join(here, "third_party", "webencodings"))
 sys.path.insert(0, os.path.join(here, "third_party", "h2"))
@@ -25,6 +26,8 @@
 sys.path.insert(0, os.path.join(here, "third_party", "hyperframe"))
 sys.path.insert(0, os.path.join(here, "third_party", "certifi"))
 sys.path.insert(0, os.path.join(here, "third_party", "hyper"))
+sys.path.insert(0, os.path.join(here, "third_party", "websockets", "src"))
+sys.path.insert(0, os.path.join(here, "third_party", "iniconfig", "src"))
 if sys.version_info < (3, 8):
     sys.path.insert(0, os.path.join(here, "third_party", "importlib_metadata"))
 sys.path.insert(0, os.path.join(here, "webdriver"))
diff --git a/third_party/wpt_tools/wpt/tools/manifest/XMLParser.py b/third_party/wpt_tools/wpt/tools/manifest/XMLParser.py
index 80aa3b5..45a8a54 100644
--- a/third_party/wpt_tools/wpt/tools/manifest/XMLParser.py
+++ b/third_party/wpt_tools/wpt/tools/manifest/XMLParser.py
@@ -6,8 +6,6 @@
 from xml.parsers import expat
 import xml.etree.ElementTree as etree  # noqa: N813
 
-from six import text_type
-
 MYPY = False
 if MYPY:
     # MYPY is set to True when run under Mypy.
@@ -83,7 +81,7 @@
 
     def _start(self, tag, attrib_in):
         # type: (Text, List[str]) -> etree.Element
-        assert isinstance(tag, text_type)
+        assert isinstance(tag, str)
         self._fed_data = None
         tag = _fixname(tag)
         attrib = OrderedDict()  # type: Dict[Union[bytes, Text], Union[bytes, Text]]
diff --git a/third_party/wpt_tools/wpt/tools/manifest/download.py b/third_party/wpt_tools/wpt/tools/manifest/download.py
index 9d76318..88d478f 100644
--- a/third_party/wpt_tools/wpt/tools/manifest/download.py
+++ b/third_party/wpt_tools/wpt/tools/manifest/download.py
@@ -7,8 +7,7 @@
 import io
 import os
 from datetime import datetime, timedelta
-
-from six.moves.urllib.request import urlopen
+from urllib.request import urlopen
 
 try:
     import zstandard
diff --git a/third_party/wpt_tools/wpt/tools/manifest/item.py b/third_party/wpt_tools/wpt/tools/manifest/item.py
index 857c8485..4b973d5 100644
--- a/third_party/wpt_tools/wpt/tools/manifest/item.py
+++ b/third_party/wpt_tools/wpt/tools/manifest/item.py
@@ -1,7 +1,6 @@
 import os.path
 from inspect import isabstract
-from six import iteritems, with_metaclass
-from six.moves.urllib.parse import urljoin, urlparse, parse_qs
+from urllib.parse import urljoin, urlparse, parse_qs
 from abc import ABCMeta, abstractproperty
 
 from .utils import to_os_path
@@ -42,7 +41,7 @@
         return rv  # type: ignore
 
 
-class ManifestItem(with_metaclass(ManifestItemMeta)):
+class ManifestItem(metaclass=ManifestItemMeta):
     __slots__ = ("_tests_root", "path")
 
     def __init__(self, tests_root, path):
@@ -289,7 +288,7 @@
         if self.dpi is not None:
             extras["dpi"] = self.dpi
         if self.fuzzy:
-            extras["fuzzy"] = list(iteritems(self.fuzzy))
+            extras["fuzzy"] = list(self.fuzzy.items())
         return rv
 
     @classmethod
diff --git a/third_party/wpt_tools/wpt/tools/manifest/jsonlib.py b/third_party/wpt_tools/wpt/tools/manifest/jsonlib.py
index e7f07c3..49eaf02 100644
--- a/third_party/wpt_tools/wpt/tools/manifest/jsonlib.py
+++ b/third_party/wpt_tools/wpt/tools/manifest/jsonlib.py
@@ -1,8 +1,6 @@
 import re
 import json
 
-from six import PY3
-
 
 MYPY = False
 if MYPY:
@@ -49,11 +47,9 @@
     'ensure_ascii': False,
     'escape_forward_slashes': False,
     'indent': 1,
+    'reject_bytes': True,
 }  # type: Dict[str, Any]
 
-if PY3:
-    _ujson_dump_local_kwargs['reject_bytes'] = True
-
 
 _json_dump_local_kwargs = {
     'ensure_ascii': False,
@@ -99,10 +95,9 @@
 _ujson_dump_dist_kwargs = {
     'sort_keys': True,
     'indent': 1,
+    'reject_bytes': True,
 }  # type: Dict[str, Any]
 
-if PY3:
-    _ujson_dump_dist_kwargs['reject_bytes'] = True
 
 _json_dump_dist_kwargs = {
     'sort_keys': True,
diff --git a/third_party/wpt_tools/wpt/tools/manifest/manifest.py b/third_party/wpt_tools/wpt/tools/manifest/manifest.py
index 123f045..9f1e902d 100644
--- a/third_party/wpt_tools/wpt/tools/manifest/manifest.py
+++ b/third_party/wpt_tools/wpt/tools/manifest/manifest.py
@@ -1,17 +1,10 @@
 import io
-import itertools
 import os
 import sys
 from atomicwrites import atomic_write
 from copy import deepcopy
 from multiprocessing import Pool, cpu_count
-from six import (
-    PY3,
-    ensure_text,
-    iteritems,
-    itervalues,
-    string_types,
-)
+from six import ensure_text
 
 from . import jsonlib
 from . import vcs
@@ -93,7 +86,7 @@
         """Dictionary subclass containing a TypeData instance for each test type,
         keyed by type name"""
         self.initialized = False  # type: bool
-        for key, value in iteritems(item_classes):
+        for key, value in item_classes.items():
             self[key] = TypeData(manifest, value)
         self.initialized = True
         self.json_obj = None  # type: None
@@ -109,7 +102,7 @@
         """Get a list of all paths containing test items
         without actually constructing all the items"""
         rv = set()  # type: Set[Text]
-        for item_data in itervalues(self):
+        for item_data in self.values():
             for item in item_data:
                 rv.add(os.path.sep.join(item))
         return rv
@@ -117,7 +110,7 @@
     def type_by_path(self):
         # type: () -> Dict[Tuple[Text, ...], Text]
         rv = {}
-        for item_type, item_data in iteritems(self):
+        for item_type, item_data in self.items():
             for item in item_data:
                 rv[item] = item_type
         return rv
@@ -159,7 +152,7 @@
         tpath_len = len(tpath)
 
         for type_tests in self._data.values():
-            for path, tests in iteritems(type_tests):
+            for path, tests in type_tests.items():
                 if path[:tpath_len] == tpath:
                     for test in tests:
                         yield test
@@ -253,10 +246,8 @@
                                           to_update,
                                           chunksize=chunksize
                                           )  # type: Iterator[Tuple[Tuple[Text, ...], Text, Set[ManifestItem], Text]]
-        elif PY3:
-            results = map(compute_manifest_items, to_update)
         else:
-            results = itertools.imap(compute_manifest_items, to_update)
+            results = map(compute_manifest_items, to_update)
 
         for result in results:
             rel_path_parts, new_type, manifest_items, file_hash = result
@@ -271,7 +262,7 @@
         if remaining_manifest_paths:
             changed = True
             for rel_path_parts in remaining_manifest_paths:
-                for test_data in itervalues(data):
+                for test_data in data.values():
                     if rel_path_parts in test_data:
                         del test_data[rel_path_parts]
 
@@ -291,7 +282,7 @@
         """
         out_items = {
             test_type: type_paths.to_json()
-            for test_type, type_paths in iteritems(self._data) if type_paths
+            for test_type, type_paths in self._data.items() if type_paths
         }
 
         if caller_owns_obj:
@@ -325,7 +316,7 @@
         if not hasattr(obj, "items"):
             raise ManifestError
 
-        for test_type, type_paths in iteritems(obj["items"]):
+        for test_type, type_paths in obj["items"].items():
             if test_type not in item_classes:
                 raise ManifestError
 
@@ -358,12 +349,12 @@
           allow_cached=True  # type: bool
           ):
     # type: (...) -> Optional[Manifest]
-    manifest_path = (manifest if isinstance(manifest, string_types)
+    manifest_path = (manifest if isinstance(manifest, str)
                      else manifest.name)
     if allow_cached and manifest_path in __load_cache:
         return __load_cache[manifest_path]
 
-    if isinstance(manifest, string_types):
+    if isinstance(manifest, str):
         if os.path.exists(manifest):
             logger.debug("Opening manifest at %s" % manifest)
         else:
diff --git a/third_party/wpt_tools/wpt/tools/manifest/sourcefile.py b/third_party/wpt_tools/wpt/tools/manifest/sourcefile.py
index ce81c625..34e679e 100644
--- a/third_party/wpt_tools/wpt/tools/manifest/sourcefile.py
+++ b/third_party/wpt_tools/wpt/tools/manifest/sourcefile.py
@@ -3,8 +3,7 @@
 import os
 from collections import deque
 from io import BytesIO
-from six import binary_type, iteritems, text_type
-from six.moves.urllib.parse import urljoin
+from urllib.parse import urljoin
 from fnmatch import fnmatch
 
 MYPY = False
@@ -74,7 +73,7 @@
                value.
     """
     for line in f:
-        assert isinstance(line, binary_type), line
+        assert isinstance(line, bytes), line
         m = regexp.match(line)
         if not m:
             break
@@ -85,9 +84,13 @@
 _any_variants = {
     "window": {"suffix": ".any.html"},
     "serviceworker": {"force_https": True},
+    "serviceworker-module": {"force_https": True},
     "sharedworker": {},
+    "sharedworker-module": {},
     "dedicatedworker": {"suffix": ".any.worker.html"},
+    "dedicatedworker-module": {"suffix": ".any.worker-module.html"},
     "worker": {"longhand": {"dedicatedworker", "sharedworker", "serviceworker"}},
+    "worker-module": {},
     "jsshell": {"suffix": ".any.js"},
 }  # type: Dict[Text, Dict[Text, Any]]
 
@@ -97,7 +100,7 @@
     """
     Returns a set of variants (strings) defined by the given keyword.
     """
-    assert isinstance(item, text_type), item
+    assert isinstance(item, str), item
 
     variant = _any_variants.get(item, None)
     if variant is None:
@@ -119,7 +122,7 @@
     """
     Returns a set of variants (strings) defined by a comma-separated value.
     """
-    assert isinstance(value, text_type), value
+    assert isinstance(value, str), value
 
     if value == "":
         return get_default_any_variants()
@@ -138,7 +141,7 @@
     variant is intended to run in a JS shell, for the variants defined by the
     given comma-separated value.
     """
-    assert isinstance(value, text_type), value
+    assert isinstance(value, str), value
 
     rv = set()
 
@@ -243,7 +246,7 @@
 
         if "__cached_properties__" in rv:
             cached_properties = rv["__cached_properties__"]
-            rv = {key:value for key, value in iteritems(rv) if key not in cached_properties}
+            rv = {key:value for key, value in rv.items() if key not in cached_properties}
             del rv["__cached_properties__"]
         return rv
 
@@ -304,7 +307,7 @@
                 content = f.read()
 
             data = b"".join((b"blob ", b"%d" % len(content), b"\0", content))
-            self._hash = text_type(hashlib.sha1(data).hexdigest())
+            self._hash = str(hashlib.sha1(data).hexdigest())
 
         return self._hash
 
diff --git a/third_party/wpt_tools/wpt/tools/manifest/testpaths.py b/third_party/wpt_tools/wpt/tools/manifest/testpaths.py
index 2197792c..6902f0c 100644
--- a/third_party/wpt_tools/wpt/tools/manifest/testpaths.py
+++ b/third_party/wpt_tools/wpt/tools/manifest/testpaths.py
@@ -3,8 +3,6 @@
 import os
 from collections import defaultdict
 
-from six import iteritems
-
 from .manifest import load_and_update, Manifest
 from .log import get_logger
 
@@ -102,7 +100,7 @@
     if as_json:
         print(json.dumps(path_id_map))
     else:
-        for path, test_ids in sorted(iteritems(path_id_map)):
+        for path, test_ids in sorted(path_id_map.items()):
             print(path)
             for test_id in sorted(test_ids):
                 print("  " + test_id)
diff --git a/third_party/wpt_tools/wpt/tools/manifest/typedata.py b/third_party/wpt_tools/wpt/tools/manifest/typedata.py
index 01bb827..174008e 100644
--- a/third_party/wpt_tools/wpt/tools/manifest/typedata.py
+++ b/third_party/wpt_tools/wpt/tools/manifest/typedata.py
@@ -1,5 +1,4 @@
-from six import itervalues, iteritems
-from six.moves.collections_abc import MutableMapping
+from collections.abc import MutableMapping
 
 
 MYPY = False
@@ -181,7 +180,7 @@
             if isinstance(v, set):
                 count += 1
             else:
-                stack.extend(itervalues(v))
+                stack.extend(v.values())
 
         stack = [self._json_data]
         while stack:
@@ -189,7 +188,7 @@
             if isinstance(v, list):
                 count += 1
             else:
-                stack.extend(itervalues(v))
+                stack.extend(v.values())
 
         return count
 
@@ -270,7 +269,7 @@
         stack = [(self._data, json_rv, tuple())]  # type: List[Tuple[Dict[Text, Any], Dict[Text, Any], Tuple[Text, ...]]]
         while stack:
             data_node, json_node, par_full_key = stack.pop()
-            for k, v in iteritems(data_node):
+            for k, v in data_node.items():
                 full_key = par_full_key + (k,)
                 if isinstance(v, set):
                     assert k not in json_node
diff --git a/third_party/wpt_tools/wpt/tools/manifest/vcs.py b/third_party/wpt_tools/wpt/tools/manifest/vcs.py
index 4f3bfda..65ba308 100644
--- a/third_party/wpt_tools/wpt/tools/manifest/vcs.py
+++ b/third_party/wpt_tools/wpt/tools/manifest/vcs.py
@@ -2,9 +2,7 @@
 import os
 import stat
 from collections import deque
-
-from six import with_metaclass, PY2
-from six.moves.collections_abc import MutableMapping
+from collections.abc import MutableMapping
 
 from . import jsonlib
 from .utils import git
@@ -19,10 +17,7 @@
     # MYPY is set to True when run under Mypy.
     from typing import Dict, Optional, List, Set, Text, Iterable, Any, Tuple, Iterator
     from .manifest import Manifest  # cyclic import under MYPY guard
-    if PY2:
-        stat_result = Any
-    else:
-        stat_result = os.stat_result
+    stat_result = os.stat_result
 
     GitIgnoreCacheType = MutableMapping[bytes, bool]
 else:
@@ -132,7 +127,7 @@
                 cache.dump()
 
 
-class CacheFile(with_metaclass(abc.ABCMeta)):
+class CacheFile(metaclass=abc.ABCMeta):
     def __init__(self, cache_root, tests_root, rebuild=False):
         # type: (Text, Text, bool) -> None
         self.tests_root = tests_root
diff --git a/third_party/wpt_tools/wpt/tools/quic/commands.json b/third_party/wpt_tools/wpt/tools/quic/commands.json
index d4e3ce8..4496f48 100644
--- a/third_party/wpt_tools/wpt/tools/quic/commands.json
+++ b/third_party/wpt_tools/wpt/tools/quic/commands.json
@@ -3,7 +3,6 @@
     "path": "serve.py",
     "script": "run",
     "parser": "get_parser",
-    "py3only": true,
     "help": "Start the QUIC server for WebTransport",
     "virtualenv": true,
     "requirements": [
diff --git a/third_party/wpt_tools/wpt/tools/serve/serve.py b/third_party/wpt_tools/wpt/tools/serve/serve.py
index f09fe24..8e1aaa8 100644
--- a/third_party/wpt_tools/wpt/tools/serve/serve.py
+++ b/third_party/wpt_tools/wpt/tools/serve/serve.py
@@ -4,6 +4,7 @@
 
 import abc
 import argparse
+import importlib
 import json
 import logging
 import multiprocessing
@@ -16,13 +17,12 @@
 import threading
 import time
 import traceback
-from six.moves import urllib
+import urllib
 import uuid
 from collections import defaultdict, OrderedDict
 from itertools import chain, product
 
 from localpaths import repo_root
-from six.moves import reload_module
 
 from manifest.sourcefile import read_script_metadata, js_meta_re, parse_variants
 from wptserve import server as wptserve, handlers
@@ -227,6 +227,22 @@
 """
 
 
+class WorkerModulesHandler(HtmlWrapperHandler):
+    global_type = "dedicatedworker-module"
+    path_replace = [(".any.worker-module.html", ".any.js", ".any.worker-module.js"),
+                    (".worker.html", ".worker.js")]
+    wrapper = """<!doctype html>
+<meta charset=utf-8>
+%(meta)s
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id=log></div>
+<script>
+fetch_tests_from_worker(new Worker("%(path)s%(query)s", { type: "module" }));
+</script>
+"""
+
+
 class WindowHandler(HtmlWrapperHandler):
     path_replace = [(".window.html", ".window.js")]
     wrapper = """<!doctype html>
@@ -275,6 +291,21 @@
 """
 
 
+class SharedWorkerModulesHandler(HtmlWrapperHandler):
+    global_type = "sharedworker-module"
+    path_replace = [(".any.sharedworker-module.html", ".any.js", ".any.worker-module.js")]
+    wrapper = """<!doctype html>
+<meta charset=utf-8>
+%(meta)s
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id=log></div>
+<script>
+fetch_tests_from_worker(new SharedWorker("%(path)s%(query)s", { type: "module" }));
+</script>
+"""
+
+
 class ServiceWorkersHandler(HtmlWrapperHandler):
     global_type = "serviceworker"
     path_replace = [(".any.serviceworker.html", ".any.js", ".any.worker.js")]
@@ -296,8 +327,54 @@
 """
 
 
-class AnyWorkerHandler(WrapperHandler):
+class ServiceWorkerModulesHandler(HtmlWrapperHandler):
+    global_type = "serviceworker-module"
+    path_replace = [(".any.serviceworker-module.html",
+                     ".any.js", ".any.worker-module.js")]
+    wrapper = """<!doctype html>
+<meta charset=utf-8>
+%(meta)s
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id=log></div>
+<script>
+(async function() {
+  const scope = 'does/not/exist';
+  let reg = await navigator.serviceWorker.getRegistration(scope);
+  if (reg) await reg.unregister();
+  reg = await navigator.serviceWorker.register(
+    "%(path)s%(query)s",
+    { scope, type: 'module' },
+  );
+  fetch_tests_from_worker(reg.installing);
+})();
+</script>
+"""
+
+
+class BaseWorkerHandler(WrapperHandler):
     headers = [('Content-Type', 'text/javascript')]
+
+    def _meta_replacement(self, key, value):
+        return None
+
+    @abc.abstractmethod
+    def _create_script_import(self, attribute):
+        # Take attribute (a string URL to a JS script) and return JS source to import the script
+        # into the worker.
+        pass
+
+    def _script_replacement(self, key, value):
+        if key == "script":
+            attribute = value.replace("\\", "\\\\").replace('"', '\\"')
+            return self._create_script_import(attribute)
+        if key == "title":
+            value = value.replace("\\", "\\\\").replace('"', '\\"')
+            return 'self.META_TITLE = "%s";' % value
+        return None
+
+
+class ClassicWorkerHandler(BaseWorkerHandler):
     path_replace = [(".any.worker.js", ".any.js")]
     wrapper = """%(meta)s
 self.GLOBAL = {
@@ -310,17 +387,25 @@
 done();
 """
 
-    def _meta_replacement(self, key, value):
-        return None
+    def _create_script_import(self, attribute):
+        return 'importScripts("%s")' % attribute
 
-    def _script_replacement(self, key, value):
-        if key == "script":
-            attribute = value.replace("\\", "\\\\").replace('"', '\\"')
-            return 'importScripts("%s")' % attribute
-        if key == "title":
-            value = value.replace("\\", "\\\\").replace('"', '\\"')
-            return 'self.META_TITLE = "%s";' % value
-        return None
+
+class ModuleWorkerHandler(BaseWorkerHandler):
+    path_replace = [(".any.worker-module.js", ".any.js")]
+    wrapper = """%(meta)s
+self.GLOBAL = {
+  isWindow: function() { return false; },
+  isWorker: function() { return true; },
+};
+import "/resources/testharness.js";
+%(script)s
+import "%(path)s";
+done();
+"""
+
+    def _create_script_import(self, attribute):
+        return 'import "%s";' % attribute
 
 
 rewrites = [("GET", "/resources/WebIDLParser.js", "/resources/webidl2/lib/webidl2.js")]
@@ -368,11 +453,15 @@
 
         routes = [
             ("GET", "*.worker.html", WorkersHandler),
+            ("GET", "*.worker-module.html", WorkerModulesHandler),
             ("GET", "*.window.html", WindowHandler),
             ("GET", "*.any.html", AnyHtmlHandler),
             ("GET", "*.any.sharedworker.html", SharedWorkersHandler),
+            ("GET", "*.any.sharedworker-module.html", SharedWorkerModulesHandler),
             ("GET", "*.any.serviceworker.html", ServiceWorkersHandler),
-            ("GET", "*.any.worker.js", AnyWorkerHandler),
+            ("GET", "*.any.serviceworker-module.html", ServiceWorkerModulesHandler),
+            ("GET", "*.any.worker.js", ClassicWorkerHandler),
+            ("GET", "*.any.worker-module.js", ModuleWorkerHandler),
             ("GET", "*.asis", handlers.AsIsHandler),
             ("GET", "/.well-known/origin-policy", handlers.PythonScriptHandler),
             ("*", "*.py", handlers.PythonScriptHandler),
@@ -697,7 +786,7 @@
 def start_ws_server(host, port, paths, routes, bind_address, config, **kwargs):
     # Ensure that when we start this in a new process we have the global lock
     # in the logging module unlocked
-    reload_module(logging)
+    importlib.reload(logging)
     release_mozlog_lock()
     try:
         return WebSocketDaemon(host,
@@ -713,7 +802,7 @@
 def start_wss_server(host, port, paths, routes, bind_address, config, **kwargs):
     # Ensure that when we start this in a new process we have the global lock
     # in the logging module unlocked
-    reload_module(logging)
+    importlib.reload(logging)
     release_mozlog_lock()
     try:
         return WebSocketDaemon(host,
@@ -771,7 +860,7 @@
 def start_quic_transport_server(host, port, paths, routes, bind_address, config, **kwargs):
     # Ensure that when we start this in a new process we have the global lock
     # in the logging module unlocked
-    reload_module(logging)
+    importlib.reload(logging)
     release_mozlog_lock()
     try:
         return QuicTransportDaemon(host,
diff --git a/third_party/wpt_tools/wpt/tools/third_party/pywebsocket3/mod_pywebsocket/standalone.py b/third_party/wpt_tools/wpt/tools/third_party/pywebsocket3/mod_pywebsocket/standalone.py
index b075d98..0a3bcdb 100755
--- a/third_party/wpt_tools/wpt/tools/third_party/pywebsocket3/mod_pywebsocket/standalone.py
+++ b/third_party/wpt_tools/wpt/tools/third_party/pywebsocket3/mod_pywebsocket/standalone.py
@@ -403,8 +403,7 @@
 def _main(args=None):
     """You can call this function from your own program, but please note that
     this function has some side-effects that might affect your program. For
-    example, util.wrap_popen3_for_win use in this method replaces implementation
-    of os.popen3.
+    example, it changes the current directory.
     """
 
     options, args = _parse_args_and_config(args=args)
@@ -427,7 +426,6 @@
             # full path of third_party/cygwin/bin.
             if 'CYGWIN_PATH' in os.environ:
                 cygwin_path = os.environ['CYGWIN_PATH']
-            util.wrap_popen3_for_win(cygwin_path)
 
             def __check_script(scriptpath):
                 return util.get_script_interp(scriptpath, cygwin_path)
diff --git a/third_party/wpt_tools/wpt/tools/third_party/pywebsocket3/mod_pywebsocket/util.py b/third_party/wpt_tools/wpt/tools/third_party/pywebsocket3/mod_pywebsocket/util.py
index e164e6b8..04006ec 100644
--- a/third_party/wpt_tools/wpt/tools/third_party/pywebsocket3/mod_pywebsocket/util.py
+++ b/third_party/wpt_tools/wpt/tools/third_party/pywebsocket3/mod_pywebsocket/util.py
@@ -97,25 +97,6 @@
     return None
 
 
-def wrap_popen3_for_win(cygwin_path):
-    """Wrap popen3 to support #!-script on Windows.
-
-    Args:
-      cygwin_path:  path for cygwin binary if command path is needed to be
-                    translated.  None if no translation required.
-    """
-    __orig_popen3 = os.popen3
-
-    def __wrap_popen3(cmd, mode='t', bufsize=-1):
-        cmdline = cmd.split(' ')
-        interp = get_script_interp(cmdline[0], cygwin_path)
-        if interp:
-            cmd = interp + ' ' + cmd
-        return __orig_popen3(cmd, mode, bufsize)
-
-    os.popen3 = __wrap_popen3
-
-
 def hexify(s):
     return ' '.join(['%02x' % x for x in six.iterbytes(s)])
 
diff --git a/third_party/wpt_tools/wpt/tools/third_party/pywebsocket3/setup.py b/third_party/wpt_tools/wpt/tools/third_party/pywebsocket3/setup.py
index 57e9428d..b65904c9 100755
--- a/third_party/wpt_tools/wpt/tools/third_party/pywebsocket3/setup.py
+++ b/third_party/wpt_tools/wpt/tools/third_party/pywebsocket3/setup.py
@@ -43,7 +43,7 @@
 
 # This is used since python_requires field is not recognized with
 # pip version 9.0.0 and earlier
-if sys.version < '2.7':
+if sys.hexversion < 0x020700f0:
     print('%s requires Python 2.7 or later.' % _PACKAGE_NAME, file=sys.stderr)
     sys.exit(1)
 
@@ -66,9 +66,8 @@
     packages=[_PACKAGE_NAME, _PACKAGE_NAME + '.handshake'],
     python_requires='>=2.7',
     install_requires=['six'],
-    #TODO(suzukikeita): Update this to new Github URL
-    url='http://code.google.com/p/pywebsocket/',
-    version='3.0.0',
+    url='https://github.com/GoogleChromeLabs/pywebsocket3',
+    version='3.0.1',
 )
 
 # vi:sts=4 sw=4 et
diff --git a/third_party/wpt_tools/wpt/tools/webdriver/webdriver/__init__.py b/third_party/wpt_tools/wpt/tools/webdriver/webdriver/__init__.py
index 0e1a9de..a817514 100644
--- a/third_party/wpt_tools/wpt/tools/webdriver/webdriver/__init__.py
+++ b/third_party/wpt_tools/wpt/tools/webdriver/webdriver/__init__.py
@@ -35,3 +35,5 @@
     UnknownMethodException,
     UnsupportedOperationException,
     WebDriverException)
+from .bidi import (
+    BidiSession)
diff --git a/third_party/wpt_tools/wpt/tools/webdriver/webdriver/bidi.py b/third_party/wpt_tools/wpt/tools/webdriver/webdriver/bidi.py
new file mode 100644
index 0000000..0bcd489
--- /dev/null
+++ b/third_party/wpt_tools/wpt/tools/webdriver/webdriver/bidi.py
@@ -0,0 +1,56 @@
+import copy
+import websockets
+
+from . import client
+
+class BidiSession(client.Session):
+    def __init__(self,
+                 host,
+                 port,
+                 url_prefix="/",
+                 capabilities=None,
+                 extension=None):
+        """
+        Add a capability of "webSocketUrl": True to enable
+        Bidirectional connection in session creation.
+        """
+        self.websocket_transport = None
+        capabilities = self._enable_websocket(capabilities)
+        super().__init__(host, port, url_prefix, capabilities, extension)
+
+    def _enable_websocket(self, caps):
+        if caps:
+            caps.setdefault("alwaysMatch", {}).update({"webSocketUrl": True})
+        else:
+            caps = {"alwaysMatch": {"webSocketUrl": True}}
+        return caps
+
+    def match(self, capabilities):
+        """Expensive match to see if capabilities is the same as previously
+        requested capabilities if websocket would be enabled.
+
+        :return Boolean.
+        """
+        caps = copy.deepcopy(capabilities)
+        caps = self._enable_websocket(caps)
+        return super().match(caps)
+
+    async def start(self):
+        """Start a new WebDriver Bidirectional session
+        with websocket connected.
+
+        :return: Dictionary with `capabilities` and `sessionId`.
+        """
+        value = super().start()
+
+        if not self.websocket_transport or not self.websocket_transport.open:
+            self.websocket_transport = await websockets.connect(self.capabilities["webSocketUrl"])
+        return value
+
+    async def end(self):
+        """Close websocket connection first before closing session.
+        """
+        if self.websocket_transport:
+            await self.websocket_transport.close()
+            self.websocket_transport = None
+        super().end()
diff --git a/third_party/wpt_tools/wpt/tools/webdriver/webdriver/client.py b/third_party/wpt_tools/wpt/tools/webdriver/webdriver/client.py
index 19fe336a..22533409 100644
--- a/third_party/wpt_tools/wpt/tools/webdriver/webdriver/client.py
+++ b/third_party/wpt_tools/wpt/tools/webdriver/webdriver/client.py
@@ -2,8 +2,7 @@
 from . import protocol
 from . import transport
 
-from six import string_types
-from six.moves.urllib import parse as urlparse
+from urllib import parse as urlparse
 
 
 def command(func):
@@ -435,7 +434,7 @@
         cookie = {"name": name,
                   "value": None}
 
-        if isinstance(name, string_types):
+        if isinstance(name, str):
             cookie["value"] = value
         elif hasattr(value, "value"):
             cookie["value"] = value.value
@@ -506,6 +505,9 @@
     def __del__(self):
         self.end()
 
+    def match(self, capabilities):
+        return self.requested_capabilities == capabilities
+
     def start(self):
         """Start a new WebDriver session.
 
@@ -753,7 +755,6 @@
     def screenshot(self):
         return self.send_session_command("GET", "screenshot")
 
-
 class Element(object):
     """
     Representation of a web element.
diff --git a/third_party/wpt_tools/wpt/tools/webdriver/webdriver/error.py b/third_party/wpt_tools/wpt/tools/webdriver/webdriver/error.py
index 6426129..807c592 100644
--- a/third_party/wpt_tools/wpt/tools/webdriver/webdriver/error.py
+++ b/third_party/wpt_tools/wpt/tools/webdriver/webdriver/error.py
@@ -1,8 +1,6 @@
 import collections
 import json
 
-from six import itervalues
-
 
 class WebDriverException(Exception):
     http_status = None
@@ -220,6 +218,6 @@
 
 
 _errors = collections.defaultdict()
-for item in list(itervalues(locals())):
+for item in list(locals().values()):
     if type(item) == type and issubclass(item, WebDriverException):
         _errors[item.status_code] = item
diff --git a/third_party/wpt_tools/wpt/tools/webdriver/webdriver/protocol.py b/third_party/wpt_tools/wpt/tools/webdriver/webdriver/protocol.py
index d3faa850..e71e01d6 100644
--- a/third_party/wpt_tools/wpt/tools/webdriver/webdriver/protocol.py
+++ b/third_party/wpt_tools/wpt/tools/webdriver/webdriver/protocol.py
@@ -2,8 +2,6 @@
 
 import webdriver
 
-from six import iteritems
-
 
 """WebDriver wire protocol codecs."""
 
@@ -45,5 +43,5 @@
         elif isinstance(payload, dict) and webdriver.ShadowRoot.identifier in payload:
             return webdriver.ShadowRoot.from_json(payload, self.session)
         elif isinstance(payload, dict):
-            return {k: self.object_hook(v) for k, v in iteritems(payload)}
+            return {k: self.object_hook(v) for k, v in payload.items()}
         return payload
diff --git a/third_party/wpt_tools/wpt/tools/webdriver/webdriver/transport.py b/third_party/wpt_tools/wpt/tools/webdriver/webdriver/transport.py
index 33142d3..15ba6b8fe 100644
--- a/third_party/wpt_tools/wpt/tools/webdriver/webdriver/transport.py
+++ b/third_party/wpt_tools/wpt/tools/webdriver/webdriver/transport.py
@@ -1,10 +1,9 @@
 import json
 import select
 
-from six import text_type, PY3
-from six.moves.collections_abc import Mapping
-from six.moves.http_client import HTTPConnection
-from six.moves.urllib import parse as urlparse
+from collections.abc import Mapping
+from http.client import HTTPConnection
+from urllib import parse as urlparse
 
 from . import error
 
@@ -157,8 +156,6 @@
         """Gets the current HTTP connection, or lazily creates one."""
         if not self._conn:
             conn_kwargs = {}
-            if not PY3:
-                conn_kwargs["strict"] = True
             # We are not setting an HTTP timeout other than the default when the
             # connection its created. The send method has a timeout value if needed.
             self._conn = HTTPConnection(self.host, self.port, **conn_kwargs)
@@ -240,7 +237,7 @@
         return Response.from_http(response, decoder=decoder, **codec_kwargs)
 
     def _request(self, method, uri, payload, headers=None, timeout=None):
-        if isinstance(payload, text_type):
+        if isinstance(payload, str):
             payload = payload.encode("utf-8")
 
         if headers is None:
diff --git a/third_party/wpt_tools/wpt/tools/wpt/android.py b/third_party/wpt_tools/wpt/tools/wpt/android.py
index 744e134d..8ded6e1 100644
--- a/third_party/wpt_tools/wpt/tools/wpt/android.py
+++ b/third_party/wpt_tools/wpt/tools/wpt/android.py
@@ -89,8 +89,8 @@
 
     #TODO: Not sure what's really needed here
     packages = ["platform-tools",
-                "build-tools;29.0.3",
-                "platforms;android-29",
+                "build-tools;30.0.2",
+                "platforms;android-30",
                 "emulator"]
 
     # TODO: make this work non-internactively
diff --git a/third_party/wpt_tools/wpt/tools/wpt/browser.py b/third_party/wpt_tools/wpt/tools/wpt/browser.py
index 800080e..2995d52 100644
--- a/third_party/wpt_tools/wpt/tools/wpt/browser.py
+++ b/third_party/wpt_tools/wpt/tools/wpt/browser.py
@@ -9,7 +9,7 @@
 from datetime import datetime, timedelta
 from distutils.spawn import find_executable
 
-from six.moves.urllib.parse import urlsplit
+from urllib.parse import urlsplit
 import requests
 
 from .utils import call, get, rmtree, untar, unzip, get_download_to_descriptor, sha256sum
diff --git a/third_party/wpt_tools/wpt/tools/wpt/run.py b/third_party/wpt_tools/wpt/tools/wpt/run.py
index 68f8e32..1dcb1d4 100644
--- a/third_party/wpt_tools/wpt/tools/wpt/run.py
+++ b/third_party/wpt_tools/wpt/tools/wpt/run.py
@@ -3,7 +3,6 @@
 import platform
 import sys
 from distutils.spawn import find_executable
-from six.moves import input
 
 wpt_root = os.path.abspath(os.path.join(os.path.dirname(__file__), os.pardir, os.pardir))
 sys.path.insert(0, os.path.abspath(os.path.join(wpt_root, "tools")))
@@ -757,9 +756,8 @@
 
 def setup_wptrunner(venv, **kwargs):
     from wptrunner import wptcommandline
-    from six import iteritems
 
-    kwargs = utils.Kwargs(iteritems(kwargs))
+    kwargs = utils.Kwargs(kwargs.items())
 
     kwargs["product"] = kwargs["product"].replace("-", "_")
 
diff --git a/third_party/wpt_tools/wpt/tools/wpt/testfiles.py b/third_party/wpt_tools/wpt/tools/wpt/testfiles.py
index fee19cd1..5c398588 100644
--- a/third_party/wpt_tools/wpt/tools/wpt/testfiles.py
+++ b/third_party/wpt_tools/wpt/tools/wpt/testfiles.py
@@ -6,7 +6,7 @@
 import sys
 
 from collections import OrderedDict
-from six import ensure_text, ensure_str, iteritems
+from six import ensure_text, ensure_str
 
 try:
     from ..manifest import manifest
@@ -98,7 +98,7 @@
         branch_point = None
 
         # if there are any commits, take the first parent that is not in commits
-        for commit, parents in iteritems(commit_parents):
+        for commit, parents in commit_parents.items():
             for parent in parents:
                 if parent not in commit_parents:
                     branch_point = parent
@@ -251,7 +251,7 @@
     nontests_changed = set(files_changed)
     wpt_manifest = load_manifest(manifest_path, manifest_update)
 
-    test_types = ["crashtest", "testharness", "reftest", "wdspec"]
+    test_types = ["crashtest", "print-reftest", "reftest", "testharness", "wdspec"]
     support_files = {os.path.join(wpt_root, path)
                      for _, path, _ in wpt_manifest.itertypes("support")}
     wdspec_test_files = {os.path.join(wpt_root, path)
diff --git a/third_party/wpt_tools/wpt/tools/wpt/utils.py b/third_party/wpt_tools/wpt/tools/wpt/utils.py
index 61dcda5..30e9574 100644
--- a/third_party/wpt_tools/wpt/tools/wpt/utils.py
+++ b/third_party/wpt_tools/wpt/tools/wpt/utils.py
@@ -9,7 +9,7 @@
 import zipfile
 from io import BytesIO
 from socket import error as SocketError  # NOQA: N812
-from six.moves.urllib.request import urlopen
+from urllib.request import urlopen
 
 MYPY = False
 if MYPY:
diff --git a/third_party/wpt_tools/wpt/tools/wpt/virtualenv.py b/third_party/wpt_tools/wpt/tools/wpt/virtualenv.py
index 51b97cea..1cfb150 100644
--- a/third_party/wpt_tools/wpt/tools/wpt/virtualenv.py
+++ b/third_party/wpt_tools/wpt/tools/wpt/virtualenv.py
@@ -55,13 +55,9 @@
 
     @property
     def pip_path(self):
-        if sys.version_info.major >= 3:
-            pip_executable = "pip3"
-        else:
-            pip_executable = "pip2"
-        path = find_executable(pip_executable, self.bin_path)
+        path = find_executable("pip3", self.bin_path)
         if path is None:
-            raise ValueError("%s not found" % pip_executable)
+            raise ValueError("pip3 not found")
         return path
 
     @property
diff --git a/third_party/wpt_tools/wpt/tools/wpt/wpt.py b/third_party/wpt_tools/wpt/tools/wpt/wpt.py
index 9bc6ce6c..1faf7d7 100644
--- a/third_party/wpt_tools/wpt/tools/wpt/wpt.py
+++ b/third_party/wpt_tools/wpt/tools/wpt/wpt.py
@@ -6,7 +6,6 @@
 
 from tools import localpaths  # noqa: F401
 
-from six import iteritems
 from . import virtualenv
 
 
@@ -23,7 +22,7 @@
         base_dir = os.path.dirname(abs_path)
         with open(abs_path, "r") as f:
             data = json.load(f)
-            for command, props in iteritems(data):
+            for command, props in data.items():
                 assert "path" in props
                 assert "script" in props
                 rv[command] = {
@@ -31,7 +30,6 @@
                     "script": props["script"],
                     "parser": props.get("parser"),
                     "parse_known": props.get("parse_known", False),
-                    "py3only": props.get("py3only", False),
                     "help": props.get("help"),
                     "virtualenv": props.get("virtualenv", True),
                     "install": props.get("install", []),
@@ -50,12 +48,8 @@
                         dest="skip_venv_setup",
                         help="Whether to use the virtualenv as-is. Must set --venv as well")
     parser.add_argument("--debug", action="store_true", help="Run the debugger in case of an exception")
-    parser.add_argument("--py3", action="store_true",
-                        help="Run with Python 3 (requires a `python3` binary on the PATH)")
-    parser.add_argument("--py2", action="store_true",
-                        help="Run with Python 2 (requires a `python2` binary on the PATH)")
     subparsers = parser.add_subparsers(dest="command")
-    for command, props in iteritems(commands):
+    for command, props in commands.items():
         subparsers.add_parser(command, help=props["help"], add_help=False)
 
     args, extra = parser.parse_known_args(argv)
@@ -100,8 +94,7 @@
     for command in commands:
         props = commands[command]
 
-        if (props["virtualenv"] and
-            (not props["py3only"] or sys.version_info.major == 3)):
+        if props["virtualenv"]:
             setup_virtualenv(None, False, props)
 
         subparser = import_command('wpt', command, props)[1]
diff --git a/third_party/wpt_tools/wpt/tools/wptrunner/requirements.txt b/third_party/wpt_tools/wpt/tools/wptrunner/requirements.txt
index 1dd4174..440a88d 100644
--- a/third_party/wpt_tools/wpt/tools/wptrunner/requirements.txt
+++ b/third_party/wpt_tools/wpt/tools/wptrunner/requirements.txt
@@ -4,7 +4,7 @@
 mozdebug==0.2
 # Pillow 7 requires Python 3
 pillow==6.2.2; python_version <= '2.7'  # pyup: <7.0
-pillow==8.1.0; python_version >= '3.0'
-urllib3[secure]==1.26.2
+pillow==8.1.2; python_version >= '3.0'
+urllib3[secure]==1.26.4
 requests==2.25.1
 six==1.15.0
diff --git a/third_party/wpt_tools/wpt/tools/wptrunner/requirements_safari.txt b/third_party/wpt_tools/wpt/tools/wptrunner/requirements_safari.txt
index 874980b3..55b474ba9 100644
--- a/third_party/wpt_tools/wpt/tools/wptrunner/requirements_safari.txt
+++ b/third_party/wpt_tools/wpt/tools/wptrunner/requirements_safari.txt
@@ -1 +1,2 @@
 mozprocess==1.2.1
+psutil==5.8.0
diff --git a/third_party/wpt_tools/wpt/tools/wptrunner/tox.ini b/third_party/wpt_tools/wpt/tools/wptrunner/tox.ini
index b39b5eb..2b9bca17 100644
--- a/third_party/wpt_tools/wpt/tools/wptrunner/tox.ini
+++ b/third_party/wpt_tools/wpt/tools/wptrunner/tox.ini
@@ -2,7 +2,7 @@
 xfail_strict=true
 
 [tox]
-envlist = py27-{base,chrome,edge,firefox,ie,opera,safari,sauce,servo,webkit,webkitgtk_minibrowser,epiphany},{py35,py36,py37,py38}-base
+envlist = py38-{base,chrome,edge,firefox,ie,opera,safari,sauce,servo,webkit,webkitgtk_minibrowser,epiphany},{py36,py37}-base
 skip_missing_interpreters = False
 
 [testenv]
diff --git a/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/browsers/android_weblayer.py b/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/browsers/android_weblayer.py
index f68312f..ec05c76 100644
--- a/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/browsers/android_weblayer.py
+++ b/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/browsers/android_weblayer.py
@@ -1,9 +1,7 @@
-import subprocess
-
-from .base import Browser, ExecutorBrowser, require_arg
+from .base import require_arg
 from .base import get_timeout_multiplier   # noqa: F401
 from .chrome import executor_kwargs as chrome_executor_kwargs
-from ..webdriver_server import ChromeDriverServer
+from .chrome_android import ChromeAndroidBrowserBase
 from ..executors.executorwebdriver import (WebDriverTestharnessExecutor,  # noqa: F401
                                            WebDriverRefTestExecutor)  # noqa: F401
 from ..executors.executorchrome import ChromeDriverWdspecExecutor  # noqa: F401
@@ -32,7 +30,9 @@
     return {"binary": kwargs["binary"],
             "device_serial": kwargs["device_serial"],
             "webdriver_binary": kwargs["webdriver_binary"],
-            "webdriver_args": kwargs.get("webdriver_args")}
+            "webdriver_args": kwargs.get("webdriver_args"),
+            "stackparser_script": kwargs.get("stackparser_script"),
+            "output_directory": kwargs.get("output_directory")}
 
 
 def executor_kwargs(logger, test_type, server_config, cache_manager, run_info_data,
@@ -74,61 +74,22 @@
     return {"server_host": "127.0.0.1"}
 
 
-#TODO: refactor common elements of WeblayerShell and ChromeAndroidBrowser
-class WeblayerShell(Browser):
+class WeblayerShell(ChromeAndroidBrowserBase):
     """Chrome is backed by chromedriver, which is supplied through
     ``wptrunner.webdriver.ChromeDriverServer``.
     """
 
-    def __init__(self, logger, binary, webdriver_binary="chromedriver",
+    def __init__(self, logger, binary,
+                 webdriver_binary="chromedriver",
+                 remote_queue=None,
                  device_serial=None,
-                 webdriver_args=None):
+                 webdriver_args=None,
+                 stackparser_script=None,
+                 output_directory=None):
         """Creates a new representation of Chrome.  The `binary` argument gives
         the browser binary to use for testing."""
-        Browser.__init__(self, logger)
+        super(WeblayerShell, self).__init__(logger,
+                webdriver_binary, remote_queue, device_serial,
+                webdriver_args, stackparser_script, output_directory)
         self.binary = binary
-        self.device_serial = device_serial
-        self.server = ChromeDriverServer(self.logger,
-                                         binary=webdriver_binary,
-                                         args=webdriver_args)
-        self.setup_adb_reverse()
-
-    def _adb_run(self, args):
-        cmd = ['adb']
-        if self.device_serial:
-            cmd.extend(['-s', self.device_serial])
-        cmd.extend(args)
-        self.logger.info(' '.join(cmd))
-        subprocess.check_call(cmd)
-
-    def setup_adb_reverse(self):
-        self._adb_run(['wait-for-device'])
-        self._adb_run(['forward', '--remove-all'])
-        self._adb_run(['reverse', '--remove-all'])
-        # "adb reverse" basically forwards network connection from device to
-        # host.
-        for port in _wptserve_ports:
-            self._adb_run(['reverse', 'tcp:%d' % port, 'tcp:%d' % port])
-
-    def start(self, **kwargs):
-        self.server.start(block=False)
-
-    def stop(self, force=False):
-        self.server.stop(force=force)
-
-    def pid(self):
-        return self.server.pid
-
-    def is_alive(self):
-        # TODO(ato): This only indicates the driver is alive,
-        # and doesn't say anything about whether a browser session
-        # is active.
-        return self.server.is_alive()
-
-    def cleanup(self):
-        self.stop()
-        self._adb_run(['forward', '--remove-all'])
-        self._adb_run(['reverse', '--remove-all'])
-
-    def executor_browser(self):
-        return ExecutorBrowser, {"webdriver_url": self.server.url}
+        self.wptserver_ports = _wptserve_ports
diff --git a/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/browsers/android_webview.py b/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/browsers/android_webview.py
index 3cde248b..54dd128 100644
--- a/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/browsers/android_webview.py
+++ b/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/browsers/android_webview.py
@@ -1,9 +1,7 @@
-import subprocess
-
-from .base import Browser, ExecutorBrowser, require_arg
+from .base import require_arg
 from .base import get_timeout_multiplier   # noqa: F401
 from .chrome import executor_kwargs as chrome_executor_kwargs
-from ..webdriver_server import ChromeDriverServer
+from .chrome_android import ChromeAndroidBrowserBase
 from ..executors.executorwebdriver import (WebDriverTestharnessExecutor,  # noqa: F401
                                            WebDriverRefTestExecutor)  # noqa: F401
 from ..executors.executorchrome import ChromeDriverWdspecExecutor  # noqa: F401
@@ -32,7 +30,9 @@
     return {"binary": kwargs["binary"],
             "device_serial": kwargs["device_serial"],
             "webdriver_binary": kwargs["webdriver_binary"],
-            "webdriver_args": kwargs.get("webdriver_args")}
+            "webdriver_args": kwargs.get("webdriver_args"),
+            "stackparser_script": kwargs.get("stackparser_script"),
+            "output_directory": kwargs.get("output_directory")}
 
 
 def executor_kwargs(logger, test_type, server_config, cache_manager, run_info_data,
@@ -51,10 +51,11 @@
     # Note that for WebView, we launch a test shell and have the test shell use WebView.
     # https://chromium.googlesource.com/chromium/src/+/HEAD/android_webview/docs/webview-shell.md
     capabilities["goog:chromeOptions"]["androidPackage"] = \
-        "org.chromium.webview_shell"
-    capabilities["goog:chromeOptions"]["androidActivity"] = ".WebPlatformTestsActivity"
-    if kwargs.get('device_serial'):
-        capabilities["goog:chromeOptions"]["androidDeviceSerial"] = kwargs['device_serial']
+        kwargs.get("package_name", "org.chromium.webview_shell")
+    capabilities["goog:chromeOptions"]["androidActivity"] = \
+        "org.chromium.webview_shell.WebPlatformTestsActivity"
+    if kwargs.get("device_serial"):
+        capabilities["goog:chromeOptions"]["androidDeviceSerial"] = kwargs["device_serial"]
 
     # Workaround: driver.quit() cannot quit SystemWebViewShell.
     executor_kwargs["pause_after_test"] = False
@@ -73,61 +74,21 @@
     return {"server_host": "127.0.0.1"}
 
 
-#TODO: refactor common elements of SystemWebViewShell and ChromeAndroidBrowser
-class SystemWebViewShell(Browser):
+class SystemWebViewShell(ChromeAndroidBrowserBase):
     """Chrome is backed by chromedriver, which is supplied through
     ``wptrunner.webdriver.ChromeDriverServer``.
     """
 
     def __init__(self, logger, binary, webdriver_binary="chromedriver",
+                 remote_queue=None,
                  device_serial=None,
-                 webdriver_args=None):
+                 webdriver_args=None,
+                 stackparser_script=None,
+                 output_directory=None):
         """Creates a new representation of Chrome.  The `binary` argument gives
         the browser binary to use for testing."""
-        Browser.__init__(self, logger)
+        super(SystemWebViewShell, self).__init__(logger,
+                webdriver_binary, remote_queue, device_serial,
+                webdriver_args, stackparser_script, output_directory)
         self.binary = binary
-        self.device_serial = device_serial
-        self.server = ChromeDriverServer(self.logger,
-                                         binary=webdriver_binary,
-                                         args=webdriver_args)
-        self.setup_adb_reverse()
-
-    def _adb_run(self, args):
-        cmd = ['adb']
-        if self.device_serial:
-            cmd.extend(['-s', self.device_serial])
-        cmd.extend(args)
-        self.logger.info(' '.join(cmd))
-        subprocess.check_call(cmd)
-
-    def setup_adb_reverse(self):
-        self._adb_run(['wait-for-device'])
-        self._adb_run(['forward', '--remove-all'])
-        self._adb_run(['reverse', '--remove-all'])
-        # "adb reverse" basically forwards network connection from device to
-        # host.
-        for port in _wptserve_ports:
-            self._adb_run(['reverse', 'tcp:%d' % port, 'tcp:%d' % port])
-
-    def start(self, **kwargs):
-        self.server.start(block=False)
-
-    def stop(self, force=False):
-        self.server.stop(force=force)
-
-    def pid(self):
-        return self.server.pid
-
-    def is_alive(self):
-        # TODO(ato): This only indicates the driver is alive,
-        # and doesn't say anything about whether a browser session
-        # is active.
-        return self.server.is_alive()
-
-    def cleanup(self):
-        self.stop()
-        self._adb_run(['forward', '--remove-all'])
-        self._adb_run(['reverse', '--remove-all'])
-
-    def executor_browser(self):
-        return ExecutorBrowser, {"webdriver_url": self.server.url}
+        self.wptserver_ports = _wptserve_ports
diff --git a/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/browsers/base.py b/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/browsers/base.py
index f2205b16..f7388ad7 100644
--- a/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/browsers/base.py
+++ b/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/browsers/base.py
@@ -3,7 +3,6 @@
 import socket
 from abc import ABCMeta, abstractmethod
 from copy import deepcopy
-from six import iteritems
 
 from ..wptcommandline import require_arg  # noqa: F401
 
@@ -163,6 +162,10 @@
         with which it should be instantiated"""
         return ExecutorBrowser, {}
 
+    def maybe_parse_tombstone(self):
+        """Possibly parse tombstones on Android device for Android target"""
+        pass
+
     def check_crash(self, process, test):
         """Check if a crash occured and output any useful information to the
         log. Returns a boolean indicating whether a crash occured."""
@@ -202,5 +205,5 @@
     up the browser from the runner process.
     """
     def __init__(self, **kwargs):
-        for k, v in iteritems(kwargs):
+        for k, v in kwargs.items():
             setattr(self, k, v)
diff --git a/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/browsers/chrome.py b/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/browsers/chrome.py
index d3e27a9..04ac372 100644
--- a/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/browsers/chrome.py
+++ b/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/browsers/chrome.py
@@ -113,7 +113,7 @@
     """
 
     def __init__(self, logger, binary, webdriver_binary="chromedriver",
-                 webdriver_args=None):
+                 webdriver_args=None, **kwargs):
         """Creates a new representation of Chrome.  The `binary` argument gives
         the browser binary to use for testing."""
         Browser.__init__(self, logger)
diff --git a/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/browsers/chrome_android.py b/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/browsers/chrome_android.py
index d7e3e210..7580bed 100644
--- a/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/browsers/chrome_android.py
+++ b/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/browsers/chrome_android.py
@@ -1,3 +1,4 @@
+import mozprocess
 import subprocess
 
 from .base import Browser, ExecutorBrowser, require_arg
@@ -33,7 +34,9 @@
     return {"package_name": kwargs["package_name"],
             "device_serial": kwargs["device_serial"],
             "webdriver_binary": kwargs["webdriver_binary"],
-            "webdriver_args": kwargs.get("webdriver_args")}
+            "webdriver_args": kwargs.get("webdriver_args"),
+            "stackparser_script": kwargs.get("stackparser_script"),
+            "output_directory": kwargs.get("output_directory")}
 
 
 def executor_kwargs(logger, test_type, server_config, cache_manager, run_info_data,
@@ -68,21 +71,80 @@
     # allow the use of host-resolver-rules in lieu of modifying /etc/hosts file
     return {"server_host": "127.0.0.1"}
 
+class LogcatRunner(object):
+    def __init__(self, logger, browser, remote_queue):
+        self.logger = logger
+        self.browser = browser
+        self.remote_queue = remote_queue
 
-class ChromeAndroidBrowser(Browser):
-    """Chrome is backed by chromedriver, which is supplied through
-    ``wptrunner.webdriver.ChromeDriverServer``.
-    """
+    def start(self):
+        try:
+            self._run()
+        except KeyboardInterrupt:
+            self.stop()
 
-    def __init__(self, logger, package_name, webdriver_binary="chromedriver",
-                 device_serial=None, webdriver_args=None):
-        Browser.__init__(self, logger)
-        self.package_name = package_name
+    def _run(self):
+        try:
+            # TODO: adb logcat -c fail randomly with message
+            # "failed to clear the 'main' log"
+            self.browser.clear_log()
+        except subprocess.CalledProcessError:
+            self.logger.error("Failed to clear logcat buffer")
+
+        self._cmd = self.browser.logcat_cmd()
+        self._proc = mozprocess.ProcessHandler(
+            self._cmd,
+            processOutputLine=self.on_output,
+            storeOutput=False)
+        self._proc.run()
+
+    def _send_message(self, command, *args):
+        try:
+            self.remote_queue.put((command, args))
+        except AssertionError:
+            self.logger.warning("Error when send to remote queue")
+
+    def stop(self, force=False):
+        if self.is_alive():
+            kill_result = self._proc.kill()
+            if force and kill_result != 0:
+                self._proc.kill(9)
+
+    def is_alive(self):
+        return hasattr(self._proc, "proc") and self._proc.poll() is None
+
+    def on_output(self, line):
+        data = {
+            "process": "LOGCAT",
+            "command": "logcat",
+            "data": line
+        }
+        self._send_message("log", "process_output", data)
+
+class ChromeAndroidBrowserBase(Browser):
+    def __init__(self, logger,
+                 webdriver_binary="chromedriver",
+                 remote_queue = None,
+                 device_serial=None,
+                 webdriver_args=None,
+                 stackparser_script=None,
+                 output_directory=None):
+        super(ChromeAndroidBrowserBase, self).__init__(logger)
         self.device_serial = device_serial
+        self.stackparser_script = stackparser_script
+        self.output_directory = output_directory
+        self.remote_queue = remote_queue
         self.server = ChromeDriverServer(self.logger,
                                          binary=webdriver_binary,
                                          args=webdriver_args)
+        if self.remote_queue is not None:
+            self.logcat_runner = LogcatRunner(self.logger,
+                                          self, self.remote_queue)
+
+    def setup(self):
         self.setup_adb_reverse()
+        if self.remote_queue is not None:
+            self.logcat_runner.start()
 
     def _adb_run(self, args):
         cmd = ['adb']
@@ -92,14 +154,6 @@
         self.logger.info(' '.join(cmd))
         subprocess.check_call(cmd)
 
-    def setup_adb_reverse(self):
-        self._adb_run(['wait-for-device'])
-        self._adb_run(['forward', '--remove-all'])
-        self._adb_run(['reverse', '--remove-all'])
-        # "adb reverse" forwards network connection from device to host.
-        for port in _wptserve_ports:
-            self._adb_run(['reverse', 'tcp:%d' % port, 'tcp:%d' % port])
-
     def start(self, **kwargs):
         self.server.start(block=False)
 
@@ -119,6 +173,54 @@
         self.stop()
         self._adb_run(['forward', '--remove-all'])
         self._adb_run(['reverse', '--remove-all'])
+        if self.remote_queue is not None:
+            self.logcat_runner.stop(force=True)
 
     def executor_browser(self):
         return ExecutorBrowser, {"webdriver_url": self.server.url}
+
+    def clear_log(self):
+        self._adb_run(['logcat', '-c'])
+
+    def logcat_cmd(self):
+        cmd = ['adb']
+        if self.device_serial:
+            cmd.extend(['-s', self.device_serial])
+        cmd.extend(['logcat', '*:D'])
+        return cmd
+
+    def maybe_parse_tombstone(self, logger):
+        if self.stackparser_script:
+            cmd = [self.stackparser_script, "-a", "-w"]
+            if self.device_serial:
+                cmd.extend(["--device", self.device_serial])
+            cmd.extend(["--output-directory", self.output_directory])
+            raw_output = subprocess.check_output(cmd)
+            for line in raw_output.splitlines():
+                logger.process_output("TRACE", line, "logcat")
+
+    def setup_adb_reverse(self):
+        self._adb_run(['wait-for-device'])
+        self._adb_run(['forward', '--remove-all'])
+        self._adb_run(['reverse', '--remove-all'])
+        # "adb reverse" forwards network connection from device to host.
+        for port in self.wptserver_ports:
+            self._adb_run(['reverse', 'tcp:%d' % port, 'tcp:%d' % port])
+
+class ChromeAndroidBrowser(ChromeAndroidBrowserBase):
+    """Chrome is backed by chromedriver, which is supplied through
+    ``wptrunner.webdriver.ChromeDriverServer``.
+    """
+
+    def __init__(self, logger, package_name,
+                 webdriver_binary="chromedriver",
+                 remote_queue = None,
+                 device_serial=None,
+                 webdriver_args=None,
+                 stackparser_script=None,
+                 output_directory=None):
+        super(ChromeAndroidBrowser, self).__init__(logger,
+                webdriver_binary, remote_queue, device_serial,
+                webdriver_args, stackparser_script, output_directory)
+        self.package_name = package_name
+        self.wptserver_ports = _wptserve_ports
diff --git a/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/browsers/chrome_ios.py b/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/browsers/chrome_ios.py
index ecdbc3a..f349b194 100644
--- a/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/browsers/chrome_ios.py
+++ b/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/browsers/chrome_ios.py
@@ -51,7 +51,7 @@
 
     init_timeout = 120
 
-    def __init__(self, logger, webdriver_binary, webdriver_args=None):
+    def __init__(self, logger, webdriver_binary, webdriver_args=None, **kwargs):
         """Creates a new representation of Chrome."""
         Browser.__init__(self, logger)
         self.server = CWTChromeDriverServer(self.logger,
diff --git a/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/browsers/chrome_spki_certs.py b/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/browsers/chrome_spki_certs.py
index 5ee5179..87bbc956 100644
--- a/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/browsers/chrome_spki_certs.py
+++ b/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/browsers/chrome_spki_certs.py
@@ -2,7 +2,7 @@
 # DO NOT EDIT MANUALLY.
 
 # tools/certs/web-platform.test.pem
-WPT_FINGERPRINT = 'OXb4O8pcDI8Nwx3KzqNuTbJ1Znf52VjEVWiYYCjHcIM='
+WPT_FINGERPRINT = 'LjjEE/m/0BKndI/KeccXvPp5wHuXfV09jw9QS7OGIvI='
 
 # signed-exchange/resources/127.0.0.1.sxg.pem
 SXG_WPT_FINGERPRINT = '0Rt4mT6SJXojEMHTnKnlJ/hBKMBcI4kteBlhR1eTTdk='
diff --git a/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/browsers/edge.py b/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/browsers/edge.py
index b908684..db4795c 100644
--- a/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/browsers/edge.py
+++ b/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/browsers/edge.py
@@ -68,7 +68,8 @@
     used_ports = set()
     init_timeout = 60
 
-    def __init__(self, logger, webdriver_binary, timeout_multiplier=None, webdriver_args=None):
+    def __init__(self, logger, webdriver_binary,
+            timeout_multiplier=None, webdriver_args=None, **kwargs):
         Browser.__init__(self, logger)
         self.server = EdgeDriverServer(self.logger,
                                        binary=webdriver_binary,
diff --git a/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/browsers/edgechromium.py b/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/browsers/edgechromium.py
index 1d45983..c2275bd1 100644
--- a/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/browsers/edgechromium.py
+++ b/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/browsers/edgechromium.py
@@ -85,7 +85,7 @@
     """
 
     def __init__(self, logger, binary, webdriver_binary="msedgedriver",
-                 webdriver_args=None):
+                 webdriver_args=None, **kwargs):
         """Creates a new representation of MicrosoftEdge.  The `binary` argument gives
         the browser binary to use for testing."""
         Browser.__init__(self, logger)
diff --git a/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/browsers/epiphany.py b/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/browsers/epiphany.py
index a338443..f378170f 100644
--- a/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/browsers/epiphany.py
+++ b/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/browsers/epiphany.py
@@ -72,6 +72,6 @@
 
 class EpiphanyBrowser(WebKitBrowser):
     def __init__(self, logger, binary=None, webdriver_binary=None,
-                 webdriver_args=None):
+                 webdriver_args=None, **kwargs):
         WebKitBrowser.__init__(self, logger, binary, webdriver_binary,
                                webdriver_args)
diff --git a/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/browsers/firefox.py b/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/browsers/firefox.py
index 83a883b..3a42298 100644
--- a/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/browsers/firefox.py
+++ b/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/browsers/firefox.py
@@ -196,7 +196,8 @@
           "fission": kwargs.get("enable_fission") or get_bool_pref("fission.autostart"),
           "sessionHistoryInParent": (kwargs.get("enable_fission") or
                                      get_bool_pref("fission.autostart") or
-                                     get_bool_pref("fission.sessionHistoryInParent"))}
+                                     get_bool_pref("fission.sessionHistoryInParent")),
+          "swgl": get_bool_pref("gfx.webrender.software")}
 
     # The value of `sw-e10s` defaults to whether the "parent_intercept"
     # implementation is enabled for the current build. This value, however,
@@ -226,7 +227,7 @@
 
 
 def update_properties():
-    return (["os", "debug", "webrender", "fission", "e10s", "sw-e10s", "processor"],
+    return (["os", "debug", "webrender", "fission", "e10s", "sw-e10s", "processor", "swgl"],
             {"os": ["version"], "processor": ["bits"]})
 
 
diff --git a/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/browsers/ie.py b/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/browsers/ie.py
index 3a86c1b6..5fce29a 100644
--- a/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/browsers/ie.py
+++ b/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/browsers/ie.py
@@ -43,7 +43,7 @@
 class InternetExplorerBrowser(Browser):
     used_ports = set()
 
-    def __init__(self, logger, webdriver_binary, webdriver_args=None):
+    def __init__(self, logger, webdriver_binary, webdriver_args=None, **kwargs):
         Browser.__init__(self, logger)
         self.server = InternetExplorerDriverServer(self.logger,
                                                    binary=webdriver_binary,
diff --git a/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/browsers/opera.py b/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/browsers/opera.py
index a34f419..80e5a31 100644
--- a/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/browsers/opera.py
+++ b/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/browsers/opera.py
@@ -71,7 +71,7 @@
     """
 
     def __init__(self, logger, binary, webdriver_binary="operadriver",
-                 webdriver_args=None):
+                 webdriver_args=None, **kwargs):
         """Creates a new representation of Opera.  The `binary` argument gives
         the browser binary to use for testing."""
         Browser.__init__(self, logger)
diff --git a/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/browsers/safari.py b/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/browsers/safari.py
index 312d4db9..9565f1c 100644
--- a/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/browsers/safari.py
+++ b/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/browsers/safari.py
@@ -1,3 +1,9 @@
+import os
+import plistlib
+from distutils.spawn import find_executable
+
+import psutil
+
 from .base import Browser, ExecutorBrowser, require_arg
 from .base import get_timeout_multiplier   # noqa: F401
 from ..webdriver_server import SafariDriverServer
@@ -28,7 +34,8 @@
 
 def browser_kwargs(logger, test_type, run_info_data, config, **kwargs):
     return {"webdriver_binary": kwargs["webdriver_binary"],
-            "webdriver_args": kwargs.get("webdriver_args")}
+            "webdriver_args": kwargs.get("webdriver_args"),
+            "kill_safari": kwargs.get("kill_safari", False)}
 
 
 def executor_kwargs(logger, test_type, server_config, cache_manager, run_info_data,
@@ -58,15 +65,60 @@
     ``wptrunner.webdriver.SafariDriverServer``.
     """
 
-    def __init__(self, logger, webdriver_binary, webdriver_args=None):
+    def __init__(self, logger, webdriver_binary, webdriver_args=None, kill_safari=False, **kwargs):
         """Creates a new representation of Safari.  The `webdriver_binary`
         argument gives the WebDriver binary to use for testing. (The browser
         binary location cannot be specified, as Safari and SafariDriver are
-        coupled.)"""
+        coupled.) If `kill_safari` is True, then `Browser.stop` will stop Safari."""
         Browser.__init__(self, logger)
         self.server = SafariDriverServer(self.logger,
                                          binary=webdriver_binary,
                                          args=webdriver_args)
+        if "/" not in webdriver_binary:
+            wd_path = find_executable(webdriver_binary)
+        else:
+            wd_path = webdriver_binary
+        self.safari_path = self._findAssociatedSafariExecutable(wd_path)
+
+        logger.debug("WebDriver executable path: %s" % wd_path)
+        logger.debug("Safari executable path: %s" % self.safari_path)
+
+        self.kill_safari = kill_safari
+
+    def _findAssociatedSafariExecutable(self, wd_path):
+        bundle_paths = [
+            os.path.join(os.path.dirname(wd_path), "..", ".."),  # bundled Safari (e.g. STP)
+            os.path.join(os.path.dirname(wd_path), "Safari.app"),  # local Safari build
+            "/Applications/Safari.app",  # system Safari
+        ]
+
+        for bundle_path in bundle_paths:
+            info_path = os.path.join(bundle_path, "Contents", "Info.plist")
+            if not os.path.isfile(info_path):
+                continue
+
+            with open(info_path, "rb") as fp:
+                info = plistlib.load(fp)
+
+            # check we have a Safari family bundle
+            if "CFBundleIdentifier" not in info:
+                continue
+            ident = info["CFBundleIdentifier"]
+            if not isinstance(ident, str) or not ident.startswith("com.apple.Safari"):
+                continue
+
+            # get the executable name
+            if "CFBundleExecutable" not in info:
+                continue
+            exe = info["CFBundleExecutable"]
+            if not isinstance(exe, str):
+                continue
+
+            exe_path = os.path.join(bundle_path, "Contents", "MacOS", exe)
+            if not os.path.isfile(exe_path):
+                continue
+
+            return exe_path
 
     def start(self, **kwargs):
         self.server.start(block=False)
@@ -74,6 +126,20 @@
     def stop(self, force=False):
         self.server.stop(force=force)
 
+        if self.kill_safari:
+            self.logger.debug("Going to stop Safari")
+            for proc in psutil.process_iter(attrs=["exe"]):
+                if proc.info["exe"] is not None and os.path.samefile(proc.info["exe"], self.safari_path):
+                    self.logger.debug("Stopping Safari %s" % proc.pid)
+                    try:
+                        proc.terminate()
+                        try:
+                            proc.wait(10)
+                        except psutil.TimeoutExpired:
+                            proc.kill()
+                    except psutil.NoSuchProcess:
+                        pass
+
     def pid(self):
         return self.server.pid
 
diff --git a/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/browsers/sauce.py b/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/browsers/sauce.py
index 3497c5c..99ece89d 100644
--- a/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/browsers/sauce.py
+++ b/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/browsers/sauce.py
@@ -12,7 +12,7 @@
 
 import requests
 
-from six.moves import cStringIO as StringIO
+from io import StringIO
 
 from .base import Browser, ExecutorBrowser, require_arg
 from .base import get_timeout_multiplier   # noqa: F401
@@ -228,7 +228,7 @@
 class SauceBrowser(Browser):
     init_timeout = 300
 
-    def __init__(self, logger, sauce_config):
+    def __init__(self, logger, sauce_config, **kwargs):
         Browser.__init__(self, logger)
         self.sauce_config = sauce_config
 
diff --git a/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/browsers/servo.py b/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/browsers/servo.py
index a65ed5ea..4f934aed 100644
--- a/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/browsers/servo.py
+++ b/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/browsers/servo.py
@@ -71,7 +71,7 @@
 
 class ServoBrowser(NullBrowser):
     def __init__(self, logger, binary, debug_info=None, binary_args=None,
-                 user_stylesheets=None, ca_certificate_path=None):
+                 user_stylesheets=None, ca_certificate_path=None, **kwargs):
         NullBrowser.__init__(self, logger)
         self.binary = binary
         self.debug_info = debug_info
diff --git a/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/browsers/servodriver.py b/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/browsers/servodriver.py
index ed85cbf..83b9423 100644
--- a/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/browsers/servodriver.py
+++ b/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/browsers/servodriver.py
@@ -77,7 +77,8 @@
     init_timeout = 300  # Large timeout for cases where we're booting an Android emulator
 
     def __init__(self, logger, binary, debug_info=None, webdriver_host="127.0.0.1",
-                 server_config=None, binary_args=None, user_stylesheets=None, headless=None):
+                 server_config=None, binary_args=None,
+                 user_stylesheets=None, headless=None, **kwargs):
         Browser.__init__(self, logger)
         self.binary = binary
         self.binary_args = binary_args or []
diff --git a/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/browsers/webkit.py b/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/browsers/webkit.py
index 590e472..f83de29 100644
--- a/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/browsers/webkit.py
+++ b/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/browsers/webkit.py
@@ -82,7 +82,7 @@
     """
 
     def __init__(self, logger, binary, webdriver_binary=None,
-                 webdriver_args=None):
+                 webdriver_args=None, **kwargs):
         Browser.__init__(self, logger)
         self.binary = binary
         self.server = WebKitDriverServer(self.logger, binary=webdriver_binary,
diff --git a/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/browsers/webkitgtk_minibrowser.py b/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/browsers/webkitgtk_minibrowser.py
index 5b7a360f..6c1001e 100644
--- a/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/browsers/webkitgtk_minibrowser.py
+++ b/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/browsers/webkitgtk_minibrowser.py
@@ -76,6 +76,6 @@
 
 class WebKitGTKMiniBrowser(WebKitBrowser):
     def __init__(self, logger, binary=None, webdriver_binary=None,
-                 webdriver_args=None):
+                 webdriver_args=None, **kwargs):
         WebKitBrowser.__init__(self, logger, binary, webdriver_binary,
                                webdriver_args)
diff --git a/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/config.py b/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/config.py
index d46beb8e..3f5e934 100644
--- a/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/config.py
+++ b/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/config.py
@@ -1,4 +1,4 @@
-from six.moves.configparser import SafeConfigParser
+from configparser import SafeConfigParser
 import os
 import sys
 from collections import OrderedDict
diff --git a/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/environment.py b/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/environment.py
index b68fce0..86dcb93 100644
--- a/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/environment.py
+++ b/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/environment.py
@@ -5,7 +5,6 @@
 import socket
 import sys
 import time
-from six import iteritems
 
 from mozlog import get_default_logger, handlers, proxy
 
@@ -108,7 +107,7 @@
     def __exit__(self, exc_type, exc_val, exc_tb):
         self.process_interrupts()
 
-        for scheme, servers in iteritems(self.servers):
+        for scheme, servers in self.servers.items():
             for port, server in servers:
                 server.kill()
         for cm in self.env_extras_cms:
@@ -215,7 +214,7 @@
         route_builder.add_handler("GET", "/resources/testdriver.js",
                                   StringHandler(data, "text/javascript"))
 
-        for url_base, paths in iteritems(self.test_paths):
+        for url_base, paths in self.test_paths.items():
             if url_base == "/":
                 continue
             route_builder.add_mount_point(url_base, paths["tests_path"])
@@ -247,13 +246,13 @@
         failed = []
         pending = []
         host = self.config["server_host"]
-        for scheme, servers in iteritems(self.servers):
+        for scheme, servers in self.servers.items():
             for port, server in servers:
                 if not server.is_alive():
                     failed.append((scheme, port))
 
         if not failed and self.test_server_port:
-            for scheme, servers in iteritems(self.servers):
+            for scheme, servers in self.servers.items():
                 # TODO(Hexcles): Find a way to test QUIC's UDP port.
                 if scheme == "quic-transport":
                     continue
diff --git a/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/executors/base.py b/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/executors/base.py
index 1b754219..b47d0ad 100644
--- a/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/executors/base.py
+++ b/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/executors/base.py
@@ -8,9 +8,8 @@
 import socket
 import sys
 from abc import ABCMeta, abstractmethod
-from six import text_type
-from six.moves.http_client import HTTPConnection
-from six.moves.urllib.parse import urljoin, urlsplit, urlunsplit
+from http.client import HTTPConnection
+from urllib.parse import urljoin, urlsplit, urlunsplit
 
 from .actions import actions
 from .protocol import Protocol, BaseProtocolPart
@@ -287,9 +286,9 @@
         """Run a particular test.
 
         :param test: The test to run"""
-        if test.environment != self.last_environment:
-            self.on_environment_change(test.environment)
         try:
+            if test.environment != self.last_environment:
+                self.on_environment_change(test.environment)
             result = self.do_test(test)
         except Exception as e:
             exception_string = traceback.format_exc()
@@ -333,7 +332,7 @@
             status = e.status
         else:
             status = "INTERNAL-ERROR"
-        message = text_type(getattr(e, "message", ""))
+        message = str(getattr(e, "message", ""))
         if message:
             message += "\n"
         message += exception_string
diff --git a/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/executors/executorchrome.py b/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/executors/executorchrome.py
index 3a70475..431d398 100644
--- a/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/executors/executorchrome.py
+++ b/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/executors/executorchrome.py
@@ -1,7 +1,7 @@
 import os
 import traceback
 
-from six.moves.urllib.parse import urljoin
+from urllib.parse import urljoin
 
 from .base import WdspecProtocol, WdspecExecutor, get_pages
 from .executorwebdriver import WebDriverProtocol, WebDriverRefTestExecutor, WebDriverRun
diff --git a/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/executors/executormarionette.py b/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/executors/executormarionette.py
index d61e1f2..81be731b 100644
--- a/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/executors/executormarionette.py
+++ b/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/executors/executormarionette.py
@@ -5,8 +5,7 @@
 import traceback
 import uuid
 
-from six import iteritems, iterkeys
-from six.moves.urllib.parse import urljoin
+from urllib.parse import urljoin
 
 errors = None
 marionette = None
@@ -727,14 +726,14 @@
 
     def on_environment_change(self, old_environment, new_environment):
         #Unset all the old prefs
-        for name in iterkeys(old_environment.get("prefs", {})):
+        for name in old_environment.get("prefs", {}).keys():
             value = self.executor.original_pref_values[name]
             if value is None:
                 self.prefs.clear(name)
             else:
                 self.prefs.set(name, value)
 
-        for name, value in iteritems(new_environment.get("prefs", {})):
+        for name, value in new_environment.get("prefs", {}).items():
             self.executor.original_pref_values[name] = self.prefs.get(name)
             self.prefs.set(name, value)
 
@@ -1000,7 +999,7 @@
                 result["extra"]["assertion_count"] = assertion_count
 
         if self.debug_test and result["status"] in ["PASS", "FAIL", "ERROR"] and "extra" in result:
-            self.parent.base.set_window(self.parent.base.window_handles()[0])
+            self.protocol.base.set_window(self.protocol.base.window_handles()[0])
             self.protocol.debug.load_reftest_analyzer(test, result)
 
         return self.convert_result(test, result)
@@ -1048,8 +1047,7 @@
         data = {"screenshot": screenshot, "isPrint": self.executor.is_print}
         if self.executor.group_metadata is not None:
             data["urlCount"] = {urljoin(self.executor.server_url(key[0]), key[1]):value
-                                for key, value in iteritems(
-                                    self.executor.group_metadata.get("url_count", {}))
+                                for key, value in self.executor.group_metadata.get("url_count", {}).items()
                                 if value > 1}
         self.chrome_scope = chrome_scope
         if chrome_scope:
diff --git a/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/executors/executorselenium.py b/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/executors/executorselenium.py
index 6070007..783903b9 100644
--- a/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/executors/executorselenium.py
+++ b/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/executors/executorselenium.py
@@ -6,7 +6,7 @@
 import time
 import traceback
 import uuid
-from six.moves.urllib.parse import urljoin
+from urllib.parse import urljoin
 
 from .base import (CallbackHandler,
                    RefTestExecutor,
diff --git a/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/executors/executorservo.py b/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/executors/executorservo.py
index efe93ab..597c879 100644
--- a/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/executors/executorservo.py
+++ b/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/executors/executorservo.py
@@ -7,7 +7,7 @@
 import threading
 import traceback
 import uuid
-from six import ensure_str, iteritems
+from six import ensure_str
 
 from mozprocess import ProcessHandler
 
@@ -48,7 +48,7 @@
         args += ["-Z", debug_opts]
     for stylesheet in browser.user_stylesheets:
         args += ["--user-stylesheet", stylesheet]
-    for pref, value in iteritems(test.environment.get('prefs', {})):
+    for pref, value in test.environment.get('prefs', {}).items():
         args += ["--pref", "%s=%s" % (pref, value)]
     if browser.ca_certificate_path:
         args += ["--certificate-path", browser.ca_certificate_path]
diff --git a/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/executors/executorwebdriver.py b/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/executors/executorwebdriver.py
index 0a968bf3..a04a55a 100644
--- a/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/executors/executorwebdriver.py
+++ b/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/executors/executorwebdriver.py
@@ -6,7 +6,7 @@
 import time
 import traceback
 import uuid
-from six.moves.urllib.parse import urljoin
+from urllib.parse import urljoin
 
 from .base import (CallbackHandler,
                    CrashtestExecutor,
diff --git a/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/executors/protocol.py b/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/executors/protocol.py
index 05e251c..ab16b0ef 100644
--- a/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/executors/protocol.py
+++ b/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/executors/protocol.py
@@ -531,7 +531,7 @@
     def load_reftest_analyzer(self, test, result):
         import io
         import mozlog
-        from six.moves.urllib.parse import quote, urljoin
+        from urllib.parse import quote, urljoin
 
         debug_test_logger = mozlog.structuredlog.StructuredLogger("debug_test")
         output = io.StringIO()
diff --git a/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/expectedtree.py b/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/expectedtree.py
index 7521f25..76ade95f 100644
--- a/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/expectedtree.py
+++ b/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/expectedtree.py
@@ -1,6 +1,5 @@
 from math import log
 from collections import defaultdict
-from six import iteritems, itervalues
 
 class Node(object):
     def __init__(self, prop, value):
@@ -34,7 +33,7 @@
 
     result_counts = defaultdict(int)
     total = float(len(results))
-    for values in itervalues(results):
+    for values in results.values():
         # Not sure this is right, possibly want to treat multiple values as
         # distinct from multiple of the same value?
         for value in values:
@@ -42,7 +41,7 @@
 
     entropy_sum = 0
 
-    for count in itervalues(result_counts):
+    for count in result_counts.values():
         prop = float(count) / total
         entropy_sum -= prop * log(prop, 2)
 
@@ -53,7 +52,7 @@
     """Split a dictionary of results into a dictionary of dictionaries where
     each sub-dictionary has a specific value of the given property"""
     by_prop = defaultdict(dict)
-    for run_info, value in iteritems(results):
+    for run_info, value in results.items():
         by_prop[run_info[prop]][run_info] = value
 
     return by_prop
@@ -78,13 +77,13 @@
     prop_index = {prop: i for i, prop in enumerate(properties)}
 
     all_results = defaultdict(int)
-    for result_values in itervalues(results):
-        for result_value, count in iteritems(result_values):
+    for result_values in results.values():
+        for result_value, count in result_values.items():
             all_results[result_value] += count
 
     # If there is only one result we are done
     if not properties or len(all_results) == 1:
-        for value, count in iteritems(all_results):
+        for value, count in all_results.items():
             tree.result_values[value] += count
         tree.run_info |= set(results.keys())
         return tree
@@ -100,7 +99,7 @@
             continue
         new_entropy = 0.
         results_sets_entropy = []
-        for prop_value, result_set in iteritems(result_sets):
+        for prop_value, result_set in result_sets.items():
             results_sets_entropy.append((entropy(result_set), prop_value, result_set))
             new_entropy += (float(len(result_set)) / len(results)) * results_sets_entropy[-1][0]
 
@@ -110,7 +109,7 @@
 
     # In the case that no properties partition the space
     if not results_partitions:
-        for value, count in iteritems(all_results):
+        for value, count in all_results.items():
             tree.result_values[value] += count
         tree.run_info |= set(results.keys())
         return tree
diff --git a/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/formatters/chromium.py b/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/formatters/chromium.py
index 2ff2bd1..ae8d96a1 100644
--- a/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/formatters/chromium.py
+++ b/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/formatters/chromium.py
@@ -1,6 +1,5 @@
 import json
 import time
-import six
 
 from collections import defaultdict
 from mozlog.formatters import base
@@ -88,7 +87,7 @@
         :param str artifact_name: the name of the artifact
         :param str artifact_value: the value of the artifact
         """
-        assert isinstance(artifact_value, six.string_types), "artifact_value must be a str"
+        assert isinstance(artifact_value, str), "artifact_value must be a str"
         if "artifacts" not in cur_dict.keys():
             cur_dict["artifacts"] = defaultdict(list)
         cur_dict["artifacts"][artifact_name].append(artifact_value)
@@ -272,5 +271,6 @@
         return json.dumps(final_result)
 
     def process_output(self, data):
-        if 'command' in data and 'chromedriver' in data['command']:
+        cmd = data.get("command", "")
+        if any(c in cmd for c in ["chromedriver", "logcat"]):
             self.browser_log.append(data['data'])
diff --git a/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/formatters/tests/test_chromium.py b/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/formatters/tests/test_chromium.py
index 55a12b1d7..53d64df7 100644
--- a/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/formatters/tests/test_chromium.py
+++ b/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/formatters/tests/test_chromium.py
@@ -1,7 +1,7 @@
 import json
 import sys
 from os.path import dirname, join
-from six.moves import cStringIO as StringIO
+from io import StringIO
 
 from mozlog import handlers, structuredlog
 
diff --git a/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/instruments.py b/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/instruments.py
index a887cf4..4e3e013 100644
--- a/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/instruments.py
+++ b/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/instruments.py
@@ -1,6 +1,6 @@
 import time
 import threading
-from six.moves.queue import Queue
+from queue import Queue
 
 """Instrumentation for measuring high-level time spent on various tasks inside the runner.
 
diff --git a/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/manifestexpected.py b/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/manifestexpected.py
index 31c57e9..2f7f533 100644
--- a/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/manifestexpected.py
+++ b/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/manifestexpected.py
@@ -1,8 +1,6 @@
 import os
 from collections import deque
-from six import string_types, text_type
-from six.moves.urllib.parse import urljoin
-
+from urllib.parse import urljoin
 
 from .wptmanifest.backends import static
 from .wptmanifest.backends.base import ManifestItem
@@ -49,7 +47,7 @@
     """List property"""
     try:
         list_prop = node.get(name)
-        if isinstance(list_prop, string_types):
+        if isinstance(list_prop, str):
             return [list_prop]
         return list(list_prop)
     except KeyError:
@@ -59,7 +57,7 @@
 def str_prop(name, node):
     try:
         prop = node.get(name)
-        if not isinstance(prop, string_types):
+        if not isinstance(prop, str):
             raise ValueError
         return prop
     except KeyError:
@@ -70,7 +68,7 @@
     """Set of tags that have been applied to the test"""
     try:
         value = node.get("tags")
-        if isinstance(value, text_type):
+        if isinstance(value, str):
             return {value}
         return set(value)
     except KeyError:
@@ -79,7 +77,7 @@
 
 def prefs(node):
     def value(ini_value):
-        if isinstance(ini_value, text_type):
+        if isinstance(ini_value, str):
             return tuple(pref_piece.strip() for pref_piece in ini_value.split(':', 1))
         else:
             # this should be things like @Reset, which are apparently type 'object'
@@ -87,7 +85,7 @@
 
     try:
         node_prefs = node.get("prefs")
-        if isinstance(node_prefs, text_type):
+        if isinstance(node_prefs, str):
             rv = dict(value(node_prefs))
         else:
             rv = dict(value(item) for item in node_prefs)
@@ -99,7 +97,7 @@
 def set_prop(name, node):
     try:
         node_items = node.get(name)
-        if isinstance(node_items, text_type):
+        if isinstance(node_items, str):
             rv = {node_items}
         else:
             rv = set(node_items)
@@ -112,7 +110,7 @@
     rv = {}
     try:
         node_items = node.get("leak-threshold")
-        if isinstance(node_items, text_type):
+        if isinstance(node_items, str):
             node_items = [node_items]
         for item in node_items:
             process, value = item.rsplit(":", 1)
@@ -169,7 +167,7 @@
     if not isinstance(value, list):
         value = [value]
     for item in value:
-        if not isinstance(item, text_type):
+        if not isinstance(item, str):
             rv.append(item)
             continue
         parts = item.rsplit(":", 1)
diff --git a/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/manifestinclude.py b/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/manifestinclude.py
index 79b5b19..97348e6 100644
--- a/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/manifestinclude.py
+++ b/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/manifestinclude.py
@@ -6,8 +6,7 @@
 """
 import glob
 import os
-from six import iteritems
-from six.moves.urllib.parse import urlparse, urlsplit
+from urllib.parse import urlparse, urlsplit
 
 from .wptmanifest.node import DataNode
 from .wptmanifest.backends import conditional
@@ -95,7 +94,7 @@
         if paths:
             urls = []
             for path in paths:
-                for manifest, data in iteritems(test_manifests):
+                for manifest, data in test_manifests.items():
                     found = False
                     rel_path = os.path.relpath(path, data["tests_path"])
                     iterator = manifest.iterpath if os.path.isfile(path) else manifest.iterdir
diff --git a/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/manifestupdate.py b/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/manifestupdate.py
index c74632b0..5bccaa2d 100644
--- a/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/manifestupdate.py
+++ b/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/manifestupdate.py
@@ -1,9 +1,8 @@
 from __future__ import print_function
 import os
-from six.moves.urllib.parse import urljoin, urlsplit
+from urllib.parse import urljoin, urlsplit
 from collections import namedtuple, defaultdict, deque
 from math import ceil
-from six import integer_types, iterkeys, itervalues, iteritems, string_types, text_type
 
 from .wptmanifest import serialize
 from .wptmanifest.node import (DataNode, ConditionalNode, BinaryExpressionNode,
@@ -76,7 +75,7 @@
         return name in self._classes
 
     def __iter__(self):
-        for name in iterkeys(self._classes):
+        for name in self._classes.keys():
             yield getattr(self, name)
 
 
@@ -313,8 +312,8 @@
 
 def build_unconditional_tree(_, run_info_properties, results):
     root = expectedtree.Node(None, None)
-    for run_info, values in iteritems(results):
-        for value, count in iteritems(values):
+    for run_info, values in results.items():
+        for value, count in values.items():
             root.result_values[value] += count
         root.run_info.add(run_info)
     return root
@@ -411,7 +410,7 @@
         for e in errors:
             if disable_intermittent:
                 condition = e.cond.children[0] if e.cond else None
-                msg = disable_intermittent if isinstance(disable_intermittent, string_types+(text_type,)) else "unstable"
+                msg = disable_intermittent if isinstance(disable_intermittent, str) else "unstable"
                 self.node.set("disabled", msg, condition)
                 self.node.new_disabled = True
             else:
@@ -499,7 +498,7 @@
                           for run_info in node.run_info}
 
         node_by_run_info = {run_info: node
-                            for (run_info, node) in iteritems(run_info_index)
+                            for (run_info, node) in run_info_index.items()
                             if node.result_values}
 
         run_info_by_condition = self.run_info_by_condition(run_info_index,
@@ -512,7 +511,7 @@
             # using the properties we've specified and not matching any run_info
             top_level_props, dependent_props = self.node.root.run_info_properties
             update_properties = set(top_level_props)
-            for item in itervalues(dependent_props):
+            for item in dependent_props.values():
                 update_properties |= set(item)
             for condition in current_conditions:
                 if ((not condition.variables.issubset(update_properties) and
@@ -695,7 +694,7 @@
             raise ConditionError
 
         counts = {}
-        for status, count in iteritems(new):
+        for status, count in new.items():
             if isinstance(status, tuple):
                 counts[status[0]] = count
                 counts.update({intermittent: 0 for intermittent in status[1:] if intermittent not in counts})
@@ -709,9 +708,9 @@
         # Counts with 0 are considered intermittent.
         statuses = ["OK", "PASS", "FAIL", "ERROR", "TIMEOUT", "CRASH"]
         status_priority = {value: i for i, value in enumerate(statuses)}
-        sorted_new = sorted(iteritems(counts), key=lambda x:(-1 * x[1],
-                                                           status_priority.get(x[0],
-                                                           len(status_priority))))
+        sorted_new = sorted(counts.items(), key=lambda x:(-1 * x[1],
+                                                        status_priority.get(x[0],
+                                                        len(status_priority))))
         expected = []
         for status, count in sorted_new:
             # If we are not removing existing recorded intermittents, with a count of 0,
@@ -777,7 +776,7 @@
         for item in new:
             if item is None:
                 continue
-            elif isinstance(item, text_type):
+            elif isinstance(item, str):
                 rv.add(item)
             else:
                 rv |= item
@@ -825,7 +824,7 @@
         return result
 
     def to_ini_value(self, data):
-        return ["%s:%s" % item for item in sorted(iteritems(data))]
+        return ["%s:%s" % item for item in sorted(data.items())]
 
     def from_ini_value(self, data):
         rv = {}
@@ -900,10 +899,10 @@
 
 
 def make_node(value):
-    if isinstance(value, integer_types+(float,)):
+    if isinstance(value, (int, float,)):
         node = NumberNode(value)
-    elif isinstance(value, text_type):
-        node = StringNode(text_type(value))
+    elif isinstance(value, str):
+        node = StringNode(str(value))
     elif hasattr(value, "__iter__"):
         node = ListNode()
         for item in value:
@@ -912,10 +911,10 @@
 
 
 def make_value_node(value):
-    if isinstance(value, integer_types+(float,)):
+    if isinstance(value, (int, float,)):
         node = ValueNode(value)
-    elif isinstance(value, text_type):
-        node = ValueNode(text_type(value))
+    elif isinstance(value, str):
+        node = ValueNode(str(value))
     elif hasattr(value, "__iter__"):
         node = ListNode()
         for item in value:
diff --git a/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/metadata.py b/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/metadata.py
index ab8d474..ddc433d 100644
--- a/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/metadata.py
+++ b/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/metadata.py
@@ -5,8 +5,8 @@
 from collections import defaultdict, namedtuple
 
 from mozlog import structuredlog
-from six import ensure_str, ensure_text, iteritems, iterkeys, itervalues, text_type
-from six.moves import intern, range
+from six import ensure_str, ensure_text
+from sys import intern
 
 from . import manifestupdate
 from . import testloader
@@ -45,11 +45,11 @@
         return self.canonical_repr == other.canonical_repr
 
     def iteritems(self):
-        for key, value in iteritems(self.data):
+        for key, value in self.data.items():
             yield key, value
 
     def items(self):
-        return list(iteritems(self))
+        return list(self.items())
 
 
 def update_expected(test_paths, serve_root, log_file_names,
@@ -129,7 +129,7 @@
     files_changed = set(files_changed)
 
     root_manifest = None
-    for manifest, paths in iteritems(manifests):
+    for manifest, paths in manifests.items():
         if paths["url_base"] == "/":
             root_manifest = manifest
             break
@@ -240,7 +240,7 @@
 def unpack_result(data):
     if isinstance(data, int):
         return (status_intern.get(data), None)
-    if isinstance(data, text_type):
+    if isinstance(data, str):
         return (data, None)
     # Unpack multiple statuses into a tuple to be used in the Results named tuple below,
     # separating `status` and `known_intermittent`.
@@ -259,7 +259,7 @@
     manifests = manifest_loader.load()
 
     id_test_map = {}
-    for test_manifest, paths in iteritems(manifests):
+    for test_manifest, paths in manifests.items():
         id_test_map.update(create_test_tree(paths["metadata_path"],
                                             test_manifest))
     return id_test_map
@@ -287,10 +287,10 @@
                    disable_intermittent,
                    update_intermittent,
                    remove_intermittent):
-    test_file_items = set(itervalues(id_test_map))
+    test_file_items = set(id_test_map.values())
 
     default_expected_by_type = {}
-    for test_type, test_cls in iteritems(wpttest.manifest_test_cls):
+    for test_type, test_cls in wpttest.manifest_test_cls.items():
         if test_cls.result_cls:
             default_expected_by_type[(test_type, False)] = test_cls.result_cls.default_expected
         if test_cls.subtest_result_cls:
@@ -431,7 +431,7 @@
             action_map["lsan_leak"](item)
 
         mozleak_data = data.get("mozleak", {})
-        for scope, scope_data in iteritems(mozleak_data):
+        for scope, scope_data in mozleak_data.items():
             for key, action in [("objects", "mozleak_object"),
                                 ("total", "mozleak_total")]:
                 for item in scope_data.get(key, []):
@@ -668,11 +668,11 @@
         # Return subtest nodes present in the expected file, but missing from the data
         rv = []
 
-        for test_id, subtests in iteritems(self.data):
+        for test_id, subtests in self.data.items():
             test = expected.get_test(ensure_text(test_id))
             if not test:
                 continue
-            seen_subtests = set(ensure_text(item) for item in iterkeys(subtests) if item is not None)
+            seen_subtests = set(ensure_text(item) for item in subtests.keys() if item is not None)
             missing_subtests = set(test.subtests.keys()) - seen_subtests
             for item in missing_subtests:
                 expected_subtest = test.get_subtest(item)
@@ -691,7 +691,7 @@
         # since removing these may be inappropriate
         top_level_props, dependent_props = update_properties
         all_properties = set(top_level_props)
-        for item in itervalues(dependent_props):
+        for item in dependent_props.values():
             all_properties |= set(item)
 
         filtered = []
@@ -741,9 +741,9 @@
             test_expected = expected.get_test(test_id)
             expected_by_test[test_id] = test_expected
 
-        for test_id, test_data in iteritems(self.data):
+        for test_id, test_data in self.data.items():
             test_id = ensure_str(test_id)
-            for subtest_id, results_list in iteritems(test_data):
+            for subtest_id, results_list in test_data.items():
                 for prop, run_info, value in results_list:
                     # Special case directory metadata
                     if subtest_id is None and test_id.endswith("__dir__"):
diff --git a/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/mpcontext.py b/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/mpcontext.py
index daade10..a50c0478 100644
--- a/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/mpcontext.py
+++ b/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/mpcontext.py
@@ -1,21 +1,11 @@
 import multiprocessing
 
-import six
-
 _context = None
 
 
-class MpContext(object):
-    def __getattr__(self, name):
-        return getattr(multiprocessing, name)
-
-
 def get_context():
     global _context
 
     if _context is None:
-        if six.PY2:
-            _context = MpContext()
-        else:
-            _context = multiprocessing.get_context("spawn")
+        _context = multiprocessing.get_context("spawn")
     return _context
diff --git a/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/process.py b/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/process.py
index 8a2b894..d3ff380a 100644
--- a/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/process.py
+++ b/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/process.py
@@ -1,11 +1,8 @@
-import sys
-
 import six
 
 
 def cast_env(env):
-    """Encode all the environment values as the appropriate type for each Python version
+    """Encode all the environment values as the appropriate type.
     This assumes that all the data is or can be represented as UTF8"""
 
-    env_type = six.ensure_binary if sys.version_info[0] < 3 else six.ensure_str
-    return {env_type(key): env_type(value) for key, value in six.iteritems(env)}
+    return {six.ensure_str(key): six.ensure_str(value) for key, value in env.items()}
diff --git a/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/products.py b/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/products.py
index abd8409..7ba30537 100644
--- a/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/products.py
+++ b/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/products.py
@@ -1,6 +1,5 @@
 import importlib
 import imp
-from six import iteritems
 
 from .browsers import product_list
 
@@ -45,7 +44,7 @@
         self.get_timeout_multiplier = getattr(module, data["timeout_multiplier"])
 
         self.executor_classes = {}
-        for test_type, cls_name in iteritems(data["executor"]):
+        for test_type, cls_name in data["executor"].items():
             cls = getattr(module, cls_name)
             self.executor_classes[test_type] = cls
 
diff --git a/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/stability.py b/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/stability.py
index 88d1c23..2265523 100644
--- a/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/stability.py
+++ b/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/stability.py
@@ -5,7 +5,6 @@
 import os
 from collections import OrderedDict, defaultdict
 from datetime import datetime
-from six import iteritems
 
 from mozlog import reader
 from mozlog.formatters import JSONFormatter
@@ -142,10 +141,10 @@
     handler = LogHandler()
     reader.handle_log(reader.read(log), handler)
     results = handler.results
-    for test_name, test in iteritems(results):
+    for test_name, test in results.items():
         if is_inconsistent(test["status"], iterations):
             inconsistent.append((test_name, None, test["status"], []))
-        for subtest_name, subtest in iteritems(test["subtests"]):
+        for subtest_name, subtest in test["subtests"].items():
             if is_inconsistent(subtest["status"], iterations):
                 inconsistent.append((test_name, subtest_name, subtest["status"], subtest["messages"]))
 
@@ -235,7 +234,7 @@
                                                   "tests" if len(results) > 1
                                                   else "test"))
 
-    for test_name, test in iteritems(results):
+    for test_name, test in results.items():
         baseurl = "http://w3c-test.org/submissions"
         if "https" in os.path.splitext(test_name)[0].split(".")[1:]:
             baseurl = "https://w3c-test.org/submissions"
@@ -305,7 +304,7 @@
     for kwargs_extra in kwargs_extras:
         if kwargs_extra:
             flags_string = " with flags %s" % " ".join(
-                "%s=%s" % item for item in iteritems(kwargs_extra))
+                "%s=%s" % item for item in kwargs_extra.items())
         else:
             flags_string = ""
 
diff --git a/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/testloader.py b/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/testloader.py
index e57619b4..346fe2a2 100644
--- a/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/testloader.py
+++ b/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/testloader.py
@@ -1,12 +1,11 @@
 import hashlib
 import json
 import os
-from six.moves.urllib.parse import urlsplit
+from urllib.parse import urlsplit
 from abc import ABCMeta, abstractmethod
-from six.moves.queue import Empty
+from queue import Empty
 from collections import defaultdict, deque
-from six import ensure_binary, iteritems
-from six.moves import range
+from six import ensure_binary
 
 from . import manifestinclude
 from . import manifestexpected
@@ -41,7 +40,7 @@
             raise
 
         self.group_by_test = {}
-        for group, test_ids in iteritems(self._data):
+        for group, test_ids in self._data.items():
             for test_id in test_ids:
                 self.group_by_test[test_id] = group
 
@@ -51,6 +50,16 @@
     def __getitem__(self, key):
         return self._data[key]
 
+def read_include_from_file(file):
+    new_include = []
+    with open(file) as f:
+        for line in f:
+            line = line.strip()
+            # Allow whole-line comments;
+            # fragments mean we can't have partial line #-based comments
+            if len(line) > 0 and not line.startswith("#"):
+                new_include.append(line)
+    return new_include
 
 def update_include_for_groups(test_groups, include):
     if include is None:
@@ -173,7 +182,7 @@
 
     def load(self):
         rv = {}
-        for url_base, paths in iteritems(self.test_paths):
+        for url_base, paths in self.test_paths.items():
             manifest_file = self.load_manifest(url_base=url_base,
                                                **paths)
             path_data = {"url_base": url_base}
@@ -472,7 +481,7 @@
         mp = mpcontext.get_context()
         test_queue = mp.Queue()
 
-        for group_name, test_ids in iteritems(tests_by_group):
+        for group_name, test_ids in tests_by_group.items():
             group_metadata = {"scope": group_name}
             group = deque()
 
diff --git a/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/testrunner.py b/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/testrunner.py
index 8dd9341..04221cb 100644
--- a/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/testrunner.py
+++ b/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/testrunner.py
@@ -2,7 +2,7 @@
 
 import threading
 import traceback
-from six.moves.queue import Empty
+from queue import Empty
 from collections import namedtuple
 
 from mozlog import structuredlog, capture
@@ -339,6 +339,7 @@
 
         self.test_count = 0
         self.unexpected_count = 0
+        self.unexpected_pass_count = 0
 
         # This may not really be what we want
         self.daemon = True
@@ -361,7 +362,8 @@
         spins."""
         self.recording.set(["testrunner", "startup"])
         self.logger = structuredlog.StructuredLogger(self.suite_name)
-        with self.browser_cls(self.logger, **self.browser_kwargs) as browser:
+        with self.browser_cls(self.logger, remote_queue=self.command_queue,
+                              **self.browser_kwargs) as browser:
             self.browser = BrowserManager(self.logger,
                                           browser,
                                           self.command_queue,
@@ -614,6 +616,9 @@
             return
         if self.timer is not None:
             self.timer.cancel()
+
+        self.browser.browser.maybe_parse_tombstone()
+
         # Write the result of each subtest
         file_result, test_results = results
         subtest_unexpected = False
@@ -634,6 +639,11 @@
                 self.unexpected_count += 1
                 self.logger.debug("Unexpected count in this thread %i" % self.unexpected_count)
                 subtest_unexpected = True
+
+            is_unexpected_pass = is_unexpected and result.status == "PASS"
+            if is_unexpected_pass:
+                self.unexpected_pass_count += 1
+
             self.logger.test_status(test.id,
                                     result.name,
                                     result.status,
@@ -667,6 +677,10 @@
             self.unexpected_count += 1
             self.logger.debug("Unexpected count in this thread %i" % self.unexpected_count)
 
+        is_unexpected_pass = is_unexpected and status == "OK"
+        if is_unexpected_pass:
+            self.unexpected_pass_count += 1
+
         if "assertion_count" in file_result.extra:
             assertion_count = file_result.extra["assertion_count"]
             if assertion_count is not None and assertion_count > 0:
@@ -775,14 +789,19 @@
             # This might leak a file handle from the queue
             self.logger.warning("Forcibly terminating runner process")
             self.test_runner_proc.terminate()
+            self.logger.debug("After terminating runner process")
 
             # Multiprocessing queues are backed by operating system pipes. If
             # the pipe in the child process had buffered data at the time of
             # forced termination, the queue is no longer in a usable state
             # (subsequent attempts to retrieve items may block indefinitely).
             # Discard the potentially-corrupted queue and create a new one.
+            self.logger.debug("Recreating command queue")
+            self.command_queue.cancel_join_thread()
             self.command_queue.close()
             self.command_queue = mp.Queue()
+            self.logger.debug("Recreating remote queue")
+            self.remote_queue.cancel_join_thread()
             self.remote_queue.close()
             self.remote_queue = mp.Queue()
         else:
@@ -925,3 +944,6 @@
 
     def unexpected_count(self):
         return sum(manager.unexpected_count for manager in self.pool)
+
+    def unexpected_pass_count(self):
+        return sum(manager.unexpected_pass_count for manager in self.pool)
diff --git a/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/update/state.py b/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/update/state.py
index 6b6ff1a..a526c24 100644
--- a/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/update/state.py
+++ b/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/update/state.py
@@ -1,5 +1,5 @@
 import os
-from six.moves import cPickle as pickle  # noqa: N813
+import pickle
 
 here = os.path.abspath(os.path.dirname(__file__))
 
diff --git a/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/update/sync.py b/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/update/sync.py
index f878752..4ace28f 100644
--- a/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/update/sync.py
+++ b/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/update/sync.py
@@ -67,7 +67,7 @@
                          state.local_branch)
         sync_path = os.path.abspath(sync_tree.root)
         if sync_path not in sys.path:
-            from update import setup_paths
+            from .update import setup_paths
             setup_paths(sync_path)
 
     def restore(self, state):
diff --git a/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/update/tree.py b/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/update/tree.py
index f362770..2f43bac 100644
--- a/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/update/tree.py
+++ b/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/update/tree.py
@@ -3,8 +3,6 @@
 import subprocess
 import tempfile
 
-from six.moves import range
-
 from .. import vcs
 from ..vcs import git, hg
 
@@ -87,7 +85,7 @@
 
     @property
     def is_clean(self):
-        return self.hg("status").strip() == ""
+        return self.hg("status").strip() == b""
 
     def add_new(self, prefix=None):
         if prefix is not None:
@@ -105,7 +103,7 @@
         except subprocess.CalledProcessError:
             pass
 
-        patch_names = [item.strip() for item in self.hg("qseries").split("\n") if item.strip()]
+        patch_names = [item.strip() for item in self.hg("qseries").split(b"\n") if item.strip()]
 
         suffix = 0
         test_name = patch_name
@@ -142,7 +140,7 @@
 
     def __init__(self, root=None, log_error=True):
         if root is None:
-            root = git("rev-parse", "--show-toplevel", log_error=log_error).strip()
+            root = git("rev-parse", "--show-toplevel", log_error=log_error).strip().decode('utf-8')
         self.root = root
         self.git = vcs.bind_to_repo(git, self.root, log_error=log_error)
         self.message = None
@@ -178,7 +176,7 @@
 
     @property
     def is_clean(self):
-        return self.git("status").strip() == ""
+        return self.git("status").strip() == b""
 
     def add_new(self, prefix=None):
         """Add files to the staging area.
@@ -203,7 +201,7 @@
             f.seek(0)
             ignored_files = sync_tree.git("check-ignore", "--no-index", "--stdin", "-z", stdin=f)
         args = []
-        for entry in ignored_files.split('\0'):
+        for entry in ignored_files.decode('utf-8').split('\0'):
             args.append(os.path.join(prefix, entry))
         if args:
             self.git("add", "--force", *args)
@@ -219,7 +217,7 @@
             args.append(ref_filter)
         data = self.git("show-ref", *args)
         rv = []
-        for line in data.split("\n"):
+        for line in data.split(b"\n"):
             if not line.strip():
                 continue
             sha1, ref = line.split()
@@ -237,7 +235,7 @@
             args.append(ref_filter)
         data = self.git("ls-remote", remote, *args)
         rv = []
-        for line in data.split("\n"):
+        for line in data.split(b"\n"):
             if not line.strip():
                 continue
             sha1, ref = line.split()
@@ -250,8 +248,8 @@
         :param remote: the remote URL
         :param branch: the branch name"""
         for sha1, ref in self.list_remote(remote, branch):
-            if ref == "refs/heads/%s" % branch:
-                return self.commit_cls(self, sha1)
+            if ref.decode('utf-8') == "refs/heads/%s" % branch:
+                return self.commit_cls(self, sha1.decode('utf-8'))
         assert False
 
     def create_patch(self, patch_name, message):
@@ -299,8 +297,8 @@
 
         args = []
         if branch:
-            branches = [ref[len("refs/heads/"):] for sha1, ref in self.list_refs()
-                        if ref.startswith("refs/heads/")]
+            branches = [ref[len("refs/heads/"):].decode('utf-8') for sha1, ref in self.list_refs()
+                        if ref.startswith(b"refs/heads/")]
             branch = get_unique_name(branches, branch)
 
             args += ["-b", branch]
@@ -336,8 +334,8 @@
         rv = []
 
         for repo_path in repo_paths:
-            paths = vcs.git("ls-tree", "-r", "--name-only", "HEAD", repo=repo_path).split("\n")
-            rv.extend(os.path.relpath(os.path.join(repo_path, item), self.root) for item in paths
+            paths = vcs.git("ls-tree", "-r", "--name-only", "HEAD", repo=repo_path).split(b"\n")
+            rv.extend(os.path.relpath(os.path.join(repo_path, item.decode('utf-8')), self.root) for item in paths
                       if item.strip())
         return rv
 
@@ -345,11 +343,11 @@
         """List submodule directories"""
         output = self.git("submodule", "status", "--recursive")
         rv = []
-        for line in output.split("\n"):
+        for line in output.split(b"\n"):
             line = line.strip()
             if not line:
                 continue
-            parts = line.split(" ")
+            parts = line.split(b" ")
             rv.append(parts[1])
         return rv
 
@@ -403,5 +401,5 @@
         self.git = self.tree.git
 
     def _get_meta(self):
-        author, email, message = self.git("show", "-s", "--format=format:%an\n%ae\n%B", self.sha1).split("\n", 2)
+        author, email, message = self.git("show", "-s", "--format=format:%an\n%ae\n%B", self.sha1).decode('utf-8').split("\n", 2)
         return author, email, self.msg_cls(message)
diff --git a/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/update/update.py b/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/update/update.py
index 80e509d..265a331 100644
--- a/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/update/update.py
+++ b/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/update/update.py
@@ -1,8 +1,6 @@
 import os
 import sys
 
-from six import itervalues
-
 from .metadata import MetadataUpdateRunner
 from .sync import SyncFromUpstreamRunner
 from .tree import GitTree, HgTree, NoVCSTree
@@ -111,7 +109,7 @@
         state.tests_path = state.paths["/"]["tests_path"]
         state.metadata_path = state.paths["/"]["metadata_path"]
 
-        for url_paths in itervalues(paths):
+        for url_paths in paths.values():
             tests_path = url_paths["tests_path"]
             metadata_path = url_paths["metadata_path"]
             for dirpath, dirnames, filenames in os.walk(metadata_path):
diff --git a/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/wptcommandline.py b/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/wptcommandline.py
index 2b6d4b35..90edd64 100644
--- a/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/wptcommandline.py
+++ b/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/wptcommandline.py
@@ -5,7 +5,7 @@
 from collections import OrderedDict
 from distutils.spawn import find_executable
 from datetime import timedelta
-from six import ensure_text, iterkeys, itervalues, iteritems
+from six import ensure_text
 
 from . import config
 from . import wpttest
@@ -16,7 +16,7 @@
 
 
 def url_or_path(path):
-    from six.moves.urllib.parse import urlparse
+    from urllib.parse import urlparse
 
     parsed = urlparse(path)
     if len(parsed.scheme) > 2:
@@ -72,6 +72,10 @@
                         default=True,
                         dest="fail_on_unexpected",
                         help="Exit with status code 0 when test expectations are violated")
+    parser.add_argument("--no-fail-on-unexpected-pass", action="store_false",
+                        default=True,
+                        dest="fail_on_unexpected_pass",
+                        help="Exit with status code 0 when all unexpected results are PASS")
 
     mode_group = parser.add_argument_group("Mode")
     mode_group.add_argument("--list-test-groups", action="store_true",
@@ -131,6 +135,8 @@
                                       help="Test types to run")
     test_selection_group.add_argument("--include", action="append",
                                       help="URL prefix to include")
+    test_selection_group.add_argument("--include-file", action="store",
+                                      help="A file listing URL prefix for tests")
     test_selection_group.add_argument("--exclude", action="append",
                                       help="URL prefix to exclude")
     test_selection_group.add_argument("--include-manifest", type=abs_path,
@@ -183,7 +189,10 @@
                                  help="Path or url to symbols file used to analyse crash minidumps.")
     debugging_group.add_argument("--stackwalk-binary", action="store", type=abs_path,
                                  help="Path to stackwalker program used to analyse minidumps.")
-
+    debugging_group.add_argument("--output-directory", action="store",
+                                 help="Path to chromium output directory.")
+    debugging_group.add_argument("--stackparser-script", action="store", type=abs_path,
+                                 help="Path to stack parser script used to analyse tombstones.")
     debugging_group.add_argument("--pdb", action="store_true",
                                  help="Drop into pdb on python exception")
 
@@ -367,6 +376,10 @@
     webkit_group.add_argument("--webkit-port", dest="webkit_port",
                               help="WebKit port")
 
+    safari_group = parser.add_argument_group("Safari-specific")
+    safari_group.add_argument("--kill-safari", dest="kill_safari", action="store_true", default=False,
+                              help="Kill Safari when stopping the browser")
+
     parser.add_argument("test_list", nargs="*",
                         help="List of URLs for tests to run, or paths including tests to run. "
                              "(equivalent to --include)")
@@ -408,7 +421,7 @@
                     ("host_cert_path", "host_cert_path", True),
                     ("host_key_path", "host_key_path", True)]}
 
-    for section, values in iteritems(keys):
+    for section, values in keys.items():
         for config_value, kw_value, is_path in values:
             if kw_value in kwargs and kwargs[kw_value] is None:
                 if not is_path:
@@ -444,7 +457,7 @@
     # Set up test_paths
     test_paths = OrderedDict()
 
-    for section in iterkeys(config):
+    for section in config.keys():
         if section.startswith("manifest:"):
             manifest_opts = config.get(section)
             url_base = manifest_opts.get("url_base", "/")
@@ -470,7 +483,7 @@
 
 
 def check_paths(kwargs):
-    for test_paths in itervalues(kwargs["test_paths"]):
+    for test_paths in kwargs["test_paths"].values():
         if not ("tests_path" in test_paths and
                 "metadata_path" in test_paths):
             print("Fatal: must specify both a test path and metadata path")
@@ -478,7 +491,7 @@
         if "manifest_path" not in test_paths:
             test_paths["manifest_path"] = os.path.join(test_paths["metadata_path"],
                                                        "MANIFEST.json")
-        for key, path in iteritems(test_paths):
+        for key, path in test_paths.items():
             name = key.split("_", 1)[0]
 
             if name == "manifest":
diff --git a/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/wptmanifest/backends/base.py b/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/wptmanifest/backends/base.py
index c539d43..3069e4c 100644
--- a/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/wptmanifest/backends/base.py
+++ b/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/wptmanifest/backends/base.py
@@ -1,5 +1,4 @@
 import abc
-from six import iteritems, iterkeys, itervalues
 
 from ..node import NodeVisitor
 from ..parser import parse
@@ -187,21 +186,21 @@
     def _flatten(self):
         rv = {}
         for node in [self, self.root]:
-            for name, value in iteritems(node._data):
+            for name, value in node._data.items():
                 if name not in rv:
                     rv[name] = value
         return rv
 
     def iteritems(self):
-        for item in iteritems(self._flatten()):
+        for item in self._flatten().items():
             yield item
 
     def iterkeys(self):
-        for item in iterkeys(self._flatten()):
+        for item in self._flatten().keys():
             yield item
 
     def itervalues(self):
-        for item in itervalues(self._flatten()):
+        for item in self._flatten().values():
             yield item
 
     def append(self, child):
diff --git a/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/wptmanifest/backends/conditional.py b/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/wptmanifest/backends/conditional.py
index 3b11e83..30dd144 100644
--- a/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/wptmanifest/backends/conditional.py
+++ b/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/wptmanifest/backends/conditional.py
@@ -1,5 +1,5 @@
 import operator
-from six import ensure_text, iteritems, iterkeys, text_type
+from six import ensure_text
 
 from ..node import NodeVisitor, DataNode, ConditionalNode, KeyValueNode, ListNode, ValueNode, BinaryExpressionNode, VariableNode
 from ..parser import parse
@@ -300,9 +300,9 @@
         if isinstance(value, list):
             value_node = ListNode()
             for item in value:
-                value_node.append(ValueNode(text_type(item)))
+                value_node.append(ValueNode(str(item)))
         else:
-            value_node = ValueNode(text_type(value))
+            value_node = ValueNode(str(value))
         if condition is not None:
             if not isinstance(condition, ConditionalNode):
                 conditional_node = ConditionalNode()
@@ -368,17 +368,17 @@
     def _flatten(self):
         rv = {}
         for node in [self, self.root]:
-            for name, value in iteritems(node._data):
+            for name, value in node._data.items():
                 if name not in rv:
                     rv[name] = value
         return rv
 
     def iteritems(self):
-        for item in iteritems(self._flatten()):
+        for item in self._flatten().items():
             yield item
 
     def iterkeys(self):
-        for item in iterkeys(self._flatten()):
+        for item in self._flatten().keys():
             yield item
 
     def iter_properties(self):
diff --git a/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/wptmanifest/node.py b/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/wptmanifest/node.py
index 0f82d70..5e9d2b6 100644
--- a/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/wptmanifest/node.py
+++ b/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/wptmanifest/node.py
@@ -1,5 +1,3 @@
-from six.moves import range
-
 class NodeVisitor(object):
     def visit(self, node):
         # This is ugly as hell, but we don't have multimethods and
diff --git a/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/wptmanifest/parser.py b/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/wptmanifest/parser.py
index 911efac80..f6ae1e2 100644
--- a/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/wptmanifest/parser.py
+++ b/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/wptmanifest/parser.py
@@ -14,8 +14,7 @@
 
 from __future__ import unicode_literals
 
-from six import binary_type, text_type, BytesIO, unichr
-from six.moves import range
+from io import BytesIO
 
 from .node import (Node, AtomNode, BinaryExpressionNode, BinaryOperatorNode,
                    ConditionalNode, DataNode, IndexNode, KeyValueNode, ListNode,
@@ -50,7 +49,7 @@
          "Reset": object()}
 
 def decode(s):
-    assert isinstance(s, text_type)
+    assert isinstance(s, str)
     return s
 
 
@@ -79,7 +78,7 @@
 
     def tokenize(self, stream):
         self.reset()
-        assert not isinstance(stream, text_type)
+        assert not isinstance(stream, str)
         if isinstance(stream, bytes):
             stream = BytesIO(stream)
         if not hasattr(stream, "name"):
@@ -89,7 +88,7 @@
 
         self.next_line_state = self.line_start_state
         for i, line in enumerate(stream):
-            assert isinstance(line, binary_type)
+            assert isinstance(line, bytes)
             self.state = self.next_line_state
             assert self.state is not None
             states = []
@@ -97,7 +96,7 @@
             self.line_number = i + 1
             self.index = 0
             self.line = line.decode('utf-8').rstrip()
-            assert isinstance(self.line, text_type)
+            assert isinstance(self.line, str)
             while self.state != self.eol_state:
                 states.append(self.state)
                 tokens = self.state()
@@ -505,7 +504,7 @@
             value += self.escape_value(c)
             self.consume()
 
-        return unichr(value)
+        return chr(value)
 
     def escape_value(self, c):
         if '0' <= c <= '9':
diff --git a/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/wptrunner.py b/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/wptrunner.py
index 7182510..bb1e570e 100644
--- a/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/wptrunner.py
+++ b/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/wptrunner.py
@@ -4,8 +4,6 @@
 import os
 import sys
 
-from six import iteritems, itervalues
-
 import wptserve
 from wptserve import sslutils
 
@@ -67,6 +65,8 @@
     manifest_filters = []
 
     include = kwargs["include"]
+    if kwargs["include_file"]:
+        include.extend(testloader.read_include_from_file(kwargs["include_file"]))
     if test_groups:
         include = testloader.update_include_for_groups(test_groups, include)
 
@@ -117,7 +117,7 @@
     run_info, test_loader = get_loader(test_paths, product,
                                        run_info_extras=run_info_extras, **kwargs)
 
-    for test_type, tests in iteritems(test_loader.disabled_tests):
+    for test_type, tests in test_loader.disabled_tests.items():
         for test in tests:
             rv.append({"test": test.id, "reason": test.disabled()})
     print(json.dumps(rv, indent=2))
@@ -144,7 +144,7 @@
         if kwargs["debug_test"]:
             return True
         tests = test_loader.tests
-        is_single_testharness = (sum(len(item) for item in itervalues(tests)) == 1 and
+        is_single_testharness = (sum(len(item) for item in tests.values()) == 1 and
                                  len(tests.get("testharness", [])) == 1)
         if kwargs["repeat"] == 1 and kwargs["rerun"] == 1 and is_single_testharness:
             return True
@@ -201,6 +201,7 @@
         skipped_tests = 0
         test_total = 0
         unexpected_total = 0
+        unexpected_pass_total = 0
 
         if len(test_loader.test_ids) == 0 and kwargs["test_list"]:
             logger.critical("Unable to find any tests at the path(s):")
@@ -255,6 +256,7 @@
 
                 test_count = 0
                 unexpected_count = 0
+                unexpected_pass_count = 0
 
                 tests = []
                 for test_type in test_loader.test_types:
@@ -345,10 +347,13 @@
                             raise
                         test_count += manager_group.test_count()
                         unexpected_count += manager_group.unexpected_count()
+                        unexpected_pass_count += manager_group.unexpected_pass_count()
                 recording.set(["after-end"])
                 test_total += test_count
                 unexpected_total += unexpected_count
-                logger.info("Got %i unexpected results" % unexpected_count)
+                unexpected_pass_total += unexpected_pass_count
+                logger.info("Got %i unexpected results, with %i unexpected passes" %
+                            (unexpected_count, unexpected_pass_count))
                 logger.suite_end()
                 if repeat_until_unexpected and unexpected_total > 0:
                     break
@@ -370,6 +375,13 @@
         logger.info("Tolerating %s unexpected results" % unexpected_total)
         return True
 
+    all_unexpected_passed = (unexpected_total and
+                             unexpected_total == unexpected_pass_total)
+    if all_unexpected_passed and not kwargs["fail_on_unexpected_pass"]:
+        logger.info("Tolerating %i unexpected results because they all PASS" %
+                    unexpected_pass_total)
+        return True
+
     return unexpected_total == 0
 
 
diff --git a/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/wpttest.py b/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/wpttest.py
index ed9184e..b7a7cec1 100644
--- a/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/wpttest.py
+++ b/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/wpttest.py
@@ -1,9 +1,8 @@
 import os
 import subprocess
 import sys
-from six.moves.urllib.parse import urljoin
 from collections import defaultdict
-from six import iteritems, string_types
+from urllib.parse import urljoin
 
 from .wptmanifest.parser import atoms
 
@@ -307,7 +306,7 @@
         rv = {}
         for meta in self.itermeta(None):
             threshold = meta.leak_threshold
-            for key, value in iteritems(threshold):
+            for key, value in threshold.items():
                 if key not in rv:
                     rv[key] = value
         return rv
@@ -349,7 +348,7 @@
 
         try:
             expected = metadata.get("expected")
-            if isinstance(expected, string_types):
+            if isinstance(expected, str):
                 return expected
             elif isinstance(expected, list):
                 return expected[0]
diff --git a/third_party/wpt_tools/wpt/tools/wptserve/wptserve/config.py b/third_party/wpt_tools/wpt/tools/wptserve/wptserve/config.py
index 4d653f5..843f6e6 100644
--- a/third_party/wpt_tools/wpt/tools/wptserve/wptserve/config.py
+++ b/third_party/wpt_tools/wpt/tools/wptserve/wptserve/config.py
@@ -2,9 +2,7 @@
 import logging
 import os
 from collections import defaultdict
-
-from six.moves.collections_abc import Mapping
-from six import integer_types, iteritems, itervalues, string_types
+from collections.abc import Mapping
 
 from . import sslutils
 from .utils import get_port
@@ -20,7 +18,7 @@
 
 def _merge_dict(base_dict, override_dict):
     rv = base_dict.copy()
-    for key, value in iteritems(base_dict):
+    for key, value in base_dict.items():
         if key in override_dict:
             if isinstance(value, dict):
                 rv[key] = _merge_dict(value, override_dict[key])
@@ -89,7 +87,7 @@
                 target = target[part]
             value = target[key[-1]]
             if isinstance(value, dict):
-                target[key[-1]] = {k:v for (k,v) in iteritems(value) if not k.startswith("op")}
+                target[key[-1]] = {k:v for (k,v) in value.items() if not k.startswith("op")}
             else:
                 target[key[-1]] = [x for x in value if not x.startswith("op")]
 
@@ -98,9 +96,9 @@
 
 def json_types(obj):
     if isinstance(obj, dict):
-        return {key: json_types(value) for key, value in iteritems(obj)}
-    if (isinstance(obj, string_types) or
-        isinstance(obj, integer_types) or
+        return {key: json_types(value) for key, value in obj.items()}
+    if (isinstance(obj, str) or
+        isinstance(obj, int) or
         isinstance(obj, float) or
         isinstance(obj, bool) or
         obj is None):
@@ -203,13 +201,13 @@
                 self.log_level = level_name
             self._logger_name = logger.name
 
-        for k, v in iteritems(self._default):
+        for k, v in self._default.items():
             self._data[k] = kwargs.pop(k, v)
 
         self._data["subdomains"] = subdomains
         self._data["not_subdomains"] = not_subdomains
 
-        for k, new_k in iteritems(_renamed_props):
+        for k, new_k in _renamed_props.items():
             if k in kwargs:
                 self.logger.warning(
                     "%s in config is deprecated; use %s instead" % (
@@ -242,7 +240,7 @@
             if k in override:
                 self._set_override(k, override.pop(k))
 
-        for k, new_k in iteritems(_renamed_props):
+        for k, new_k in _renamed_props.items():
             if k in override:
                 self.logger.warning(
                     "%s in config is deprecated; use %s instead" % (
@@ -287,7 +285,7 @@
 
     def _get_ports(self, data):
         new_ports = defaultdict(list)
-        for scheme, ports in iteritems(data["ports"]):
+        for scheme, ports in data["ports"].items():
             if scheme in ["wss", "https"] and not sslutils.get_cls(data["ssl"]["type"]).ssl_enabled:
                 continue
             for i, port in enumerate(ports):
@@ -301,7 +299,7 @@
         hosts[""] = data["browser_host"]
 
         rv = {}
-        for name, host in iteritems(hosts):
+        for name, host in hosts.items():
             rv[name] = {subdomain: (subdomain.encode("idna").decode("ascii") + u"." + host)
                         for subdomain in data["subdomains"]}
             rv[name][""] = host
@@ -313,7 +311,7 @@
         hosts[""] = data["browser_host"]
 
         rv = {}
-        for name, host in iteritems(hosts):
+        for name, host in hosts.items():
             rv[name] = {subdomain: (subdomain.encode("idna").decode("ascii") + u"." + host)
                         for subdomain in data["not_subdomains"]}
         return rv
@@ -327,13 +325,13 @@
 
     def _get_domains_set(self, data):
         return {domain
-                for per_host_domains in itervalues(data["domains"])
-                for domain in itervalues(per_host_domains)}
+                for per_host_domains in data["domains"].values()
+                for domain in per_host_domains.values()}
 
     def _get_not_domains_set(self, data):
         return {domain
-                for per_host_domains in itervalues(data["not_domains"])
-                for domain in itervalues(per_host_domains)}
+                for per_host_domains in data["not_domains"].values()
+                for domain in per_host_domains.values()}
 
     def _get_all_domains_set(self, data):
         return data["domains_set"] | data["not_domains_set"]
diff --git a/third_party/wpt_tools/wpt/tools/wptserve/wptserve/handlers.py b/third_party/wpt_tools/wpt/tools/wptserve/wptserve/handlers.py
index 0fc11c19..9eb4a44c 100644
--- a/third_party/wpt_tools/wpt/tools/wptserve/wptserve/handlers.py
+++ b/third_party/wpt_tools/wpt/tools/wptserve/wptserve/handlers.py
@@ -3,8 +3,7 @@
 import traceback
 from collections import defaultdict
 
-from six.moves.urllib.parse import quote, unquote, urljoin
-from six import iteritems
+from urllib.parse import quote, unquote, urljoin
 
 from .constants import content_types
 from .pipes import Pipeline, template
@@ -482,7 +481,7 @@
         self.data = data
 
         self.resp_headers = [("Content-Type", content_type)]
-        for k, v in iteritems(headers):
+        for k, v in headers.items():
             self.resp_headers.append((k.replace("_", "-"), v))
 
         self.handler = handler(self.handle_request)
diff --git a/third_party/wpt_tools/wpt/tools/wptserve/wptserve/pipes.py b/third_party/wpt_tools/wpt/tools/wptserve/wptserve/pipes.py
index bbf25e6f..6845c33 100644
--- a/third_party/wpt_tools/wpt/tools/wptserve/wptserve/pipes.py
+++ b/third_party/wpt_tools/wpt/tools/wptserve/wptserve/pipes.py
@@ -7,8 +7,7 @@
 import time
 import uuid
 
-from six.moves import StringIO
-from six import text_type, binary_type
+from io import BytesIO
 
 try:
     from html import escape
@@ -305,7 +304,7 @@
         return ("var", token)
 
     def tokenize(self, string):
-        assert isinstance(string, binary_type)
+        assert isinstance(string, bytes)
         return self.scanner.scan(string)[0]
 
     scanner = re.Scanner([(br"\$\w+:", var),
@@ -320,7 +319,7 @@
 
     def __getitem__(self, key):
         try:
-            if isinstance(key, text_type):
+            if isinstance(key, str):
                 key = key.encode('iso-8859-1')
             return self.params.first(key)
         except KeyError:
@@ -408,7 +407,7 @@
 
     @staticmethod
     def file_hash(request, algorithm, path):
-        assert isinstance(algorithm, text_type)
+        assert isinstance(algorithm, str)
         if algorithm not in SubFunctions.supported_algorithms:
             raise ValueError("Unsupported encryption algorithm: '%s'" % algorithm)
 
@@ -460,12 +459,12 @@
         tokens = deque(tokens)
 
         token_type, field = tokens.popleft()
-        assert isinstance(field, text_type)
+        assert isinstance(field, str)
 
         if token_type == "var":
             variable = field
             token_type, field = tokens.popleft()
-            assert isinstance(field, text_type)
+            assert isinstance(field, str)
         else:
             variable = None
 
@@ -516,7 +515,7 @@
                     "unexpected token type %s (token '%r'), expected ident or arguments" % (ttype, field)
                 )
 
-        assert isinstance(value, (int, (binary_type, text_type))), tokens
+        assert isinstance(value, (int, (bytes, str))), tokens
 
         if variable is not None:
             variables[variable] = value
@@ -527,10 +526,10 @@
         # Should possibly support escaping for other contexts e.g. script
         # TODO: read the encoding of the response
         # cgi.escape() only takes text strings in Python 3.
-        if isinstance(value, binary_type):
+        if isinstance(value, bytes):
             value = value.decode("utf-8")
         elif isinstance(value, int):
-            value = text_type(value)
+            value = str(value)
         return escape_func(value).encode("utf-8")
 
     template_regexp = re.compile(br"{{([^}]*)}}")
@@ -549,7 +548,7 @@
     content = resolve_content(response)
     response.headers.set("Content-Encoding", "gzip")
 
-    out = StringIO()
+    out = BytesIO()
     with gzip_module.GzipFile(fileobj=out, mode="w") as f:
         f.write(content)
     response.content = out.getvalue()
diff --git a/third_party/wpt_tools/wpt/tools/wptserve/wptserve/request.py b/third_party/wpt_tools/wpt/tools/wptserve/wptserve/request.py
index dbfe067..76644cc49 100644
--- a/third_party/wpt_tools/wpt/tools/wptserve/wptserve/request.py
+++ b/third_party/wpt_tools/wpt/tools/wptserve/wptserve/request.py
@@ -2,9 +2,9 @@
 import cgi
 import tempfile
 
-from six import BytesIO, binary_type, iteritems, PY3
-from six.moves.http_cookies import BaseCookie
-from six.moves.urllib.parse import parse_qsl, urlsplit
+from http.cookies import BaseCookie
+from io import BytesIO
+from urllib.parse import parse_qsl, urlsplit
 
 from . import stash
 from .utils import HTTPException, isomorphic_encode, isomorphic_decode
@@ -301,9 +301,8 @@
         if self._GET is None:
             kwargs = {
                 "keep_blank_values": True,
+                "encoding": "iso-8859-1",
             }
-            if PY3:
-                kwargs["encoding"] = "iso-8859-1"
             params = parse_qsl(self.url_parts.query, **kwargs)
             self._GET = MultiDict()
             for key, value in params:
@@ -321,9 +320,8 @@
                 "environ": {"REQUEST_METHOD": self.method},
                 "headers": self.raw_headers,
                 "keep_blank_values": True,
+                "encoding": "iso-8859-1",
             }
-            if PY3:
-                kwargs["encoding"] = "iso-8859-1"
             fs = cgi.FieldStorage(**kwargs)
             self._POST = MultiDict.from_field_storage(fs)
             self.raw_input.seek(pos)
@@ -336,7 +334,7 @@
             cookie_headers = self.headers.get("cookie", b"")
             parser.load(cookie_headers)
             cookies = Cookies()
-            for key, value in iteritems(parser):
+            for key, value in parser.items():
                 cookies[isomorphic_encode(key)] = CookieValue(value)
             self._cookies = cookies
         return self._cookies
@@ -630,11 +628,9 @@
         This overrides and calls BaseCookie.load. Unlike BaseCookie.load, it
         does not accept dictionaries.
         """
-        assert isinstance(rawdata, binary_type)
-        if PY3:
-            # BaseCookie.load expects a native string, which in Python 3 is text.
-            rawdata = isomorphic_decode(rawdata)
-        super(BinaryCookieParser, self).load(rawdata)
+        assert isinstance(rawdata, bytes)
+        # BaseCookie.load expects a native string
+        super(BinaryCookieParser, self).load(isomorphic_decode(rawdata))
 
 
 class Cookies(MultiDict):
@@ -675,7 +671,7 @@
 
         if "authorization" in headers:
             header = headers.get("authorization")
-            assert isinstance(header, binary_type)
+            assert isinstance(header, bytes)
             auth_type, data = header.split(b" ", 1)
             if auth_type in auth_schemes:
                 self.username, self.password = auth_schemes[auth_type](data)
@@ -683,6 +679,6 @@
                 raise HTTPException(400, "Unsupported authentication scheme %s" % auth_type)
 
     def decode_basic(self, data):
-        assert isinstance(data, binary_type)
+        assert isinstance(data, bytes)
         decoded_data = base64.b64decode(data)
         return decoded_data.split(b":", 1)
diff --git a/third_party/wpt_tools/wpt/tools/wptserve/wptserve/response.py b/third_party/wpt_tools/wpt/tools/wptserve/wptserve/response.py
index 6e5ee11..8763cca 100644
--- a/third_party/wpt_tools/wpt/tools/wptserve/wptserve/response.py
+++ b/third_party/wpt_tools/wpt/tools/wptserve/wptserve/response.py
@@ -6,9 +6,8 @@
 import uuid
 
 from hpack.struct import HeaderTuple
+from http.cookies import BaseCookie, Morsel
 from hyperframe.frame import HeadersFrame, DataFrame, ContinuationFrame
-from six import binary_type, text_type, integer_types, itervalues, PY3
-from six.moves.http_cookies import BaseCookie, Morsel
 
 from .constants import response_codes, h2_headers
 from .logger import get_logger
@@ -91,7 +90,7 @@
                 message = value[1]
                 # Only call str() if message is not a string type, so that we
                 # don't get `str(b"foo") == "b'foo'"` in Python 3.
-                if not isinstance(message, (binary_type, text_type)):
+                if not isinstance(message, (bytes, str)):
                     message = str(message)
                 self._status = (code, message)
         else:
@@ -122,9 +121,8 @@
             max_age = 0
             expires = timedelta(days=-1)
 
-        if PY3:
-            name = isomorphic_decode(name)
-            value = isomorphic_decode(value)
+        name = isomorphic_decode(name)
+        value = isomorphic_decode(value)
 
         days = {i+1: name for i, name in enumerate(["jan", "feb", "mar",
                                                     "apr", "may", "jun",
@@ -163,15 +161,11 @@
 
     def unset_cookie(self, name):
         """Remove a cookie from those that are being sent with the response"""
-        if PY3:
-            name = isomorphic_decode(name)
+        name = isomorphic_decode(name)
         cookies = self.headers.get("Set-Cookie")
         parser = BaseCookie()
         for cookie in cookies:
-            if PY3:
-                # BaseCookie.load expects a text string.
-                cookie = isomorphic_decode(cookie)
-            parser.load(cookie)
+            parser.load(isomorphic_decode(cookie))
 
         if name in parser.keys():
             del self.headers["Set-Cookie"]
@@ -199,9 +193,9 @@
                           string facilitating non-streaming operations like
                           template substitution.
         """
-        if isinstance(self.content, binary_type):
+        if isinstance(self.content, bytes):
             yield self.content
-        elif isinstance(self.content, text_type):
+        elif isinstance(self.content, str):
             yield self.content.encode(self.encoding)
         elif hasattr(self.content, "read"):
             if read_file:
@@ -256,7 +250,7 @@
     def __init__(self, boundary=None, default_content_type=None):
         self.items = []
         if boundary is None:
-            boundary = text_type(uuid.uuid4())
+            boundary = str(uuid.uuid4())
         self.boundary = boundary
         self.default_content_type = default_content_type
 
@@ -284,7 +278,7 @@
 
 class MultipartPart(object):
     def __init__(self, data, content_type=None, headers=None):
-        assert isinstance(data, binary_type), data
+        assert isinstance(data, bytes), data
         self.headers = ResponseHeaders()
 
         if content_type is not None:
@@ -303,8 +297,8 @@
     def to_bytes(self):
         rv = []
         for key, value in self.headers:
-            assert isinstance(key, binary_type)
-            assert isinstance(value, binary_type)
+            assert isinstance(key, bytes)
+            assert isinstance(value, bytes)
             rv.append(b"%s: %s" % (key, value))
         rv.append(b"")
         rv.append(self.data)
@@ -313,7 +307,7 @@
 
 def _maybe_encode(s):
     """Encode a string or an int into binary data using isomorphic_encode()."""
-    if isinstance(s, integer_types):
+    if isinstance(s, int):
         return b"%i" % (s,)
     return isomorphic_encode(s)
 
@@ -377,7 +371,7 @@
         self.set(key, value)
 
     def __iter__(self):
-        for key, values in itervalues(self.data):
+        for key, values in self.data.values():
             for value in values:
                 yield key, value
 
@@ -447,10 +441,10 @@
         for header, value in headers:
             # h2_headers are native strings
             # header field names are strings of ASCII
-            if isinstance(header, binary_type):
+            if isinstance(header, bytes):
                 header = header.decode('ascii')
             # value in headers can be either string or integer
-            if isinstance(value, binary_type):
+            if isinstance(value, bytes):
                 value = self.decode(value)
             if header in h2_headers:
                 header = ':' + header
@@ -482,7 +476,7 @@
         :param last: Flag to signal if this is the last frame in stream.
         :param stream_id: Id of stream to send frame on. Will use the request stream ID if None
         """
-        if isinstance(item, (text_type, binary_type)):
+        if isinstance(item, (str, bytes)):
             data = BytesIO(self.encode(item))
         else:
             data = item
@@ -638,18 +632,18 @@
 
     def decode(self, data):
         """Convert bytes to unicode according to response.encoding."""
-        if isinstance(data, binary_type):
+        if isinstance(data, bytes):
             return data.decode(self._response.encoding)
-        elif isinstance(data, text_type):
+        elif isinstance(data, str):
             return data
         else:
             raise ValueError(type(data))
 
     def encode(self, data):
         """Convert unicode to bytes according to response.encoding."""
-        if isinstance(data, binary_type):
+        if isinstance(data, bytes):
             return data
-        elif isinstance(data, text_type):
+        elif isinstance(data, str):
             return data.encode(self._response.encoding)
         else:
             raise ValueError
@@ -707,7 +701,7 @@
         if not self.write(b": "):
             return False
         if isinstance(value, int):
-            if not self.write(text_type(value)):
+            if not self.write(str(value)):
                 return False
         elif not self.write(value):
             return False
@@ -720,7 +714,7 @@
                 if not self.write_header(name, f()):
                     return False
 
-        if (isinstance(self._response.content, (binary_type, text_type)) and
+        if (isinstance(self._response.content, (bytes, str)) and
             not self._seen_header("content-length")):
             #Would be nice to avoid double-encoding here
             if not self.write_header("Content-Length", len(self.encode(self._response.content))):
@@ -767,7 +761,7 @@
         """Writes the data 'as is'"""
         if data is None:
             raise ValueError('data cannot be None')
-        if isinstance(data, (text_type, binary_type)):
+        if isinstance(data, (str, bytes)):
             # Deliberately allows both text and binary types. See `self.encode`.
             return self.write(data)
         else:
@@ -805,9 +799,9 @@
 
     def encode(self, data):
         """Convert unicode to bytes according to response.encoding."""
-        if isinstance(data, binary_type):
+        if isinstance(data, bytes):
             return data
-        elif isinstance(data, text_type):
+        elif isinstance(data, str):
             return data.encode(self._response.encoding)
         else:
             raise ValueError("data %r should be text or binary, but is %s" % (data, type(data)))
diff --git a/third_party/wpt_tools/wpt/tools/wptserve/wptserve/router.py b/third_party/wpt_tools/wpt/tools/wptserve/wptserve/router.py
index d1704a7..5a91de30 100644
--- a/third_party/wpt_tools/wpt/tools/wptserve/wptserve/router.py
+++ b/third_party/wpt_tools/wpt/tools/wptserve/wptserve/router.py
@@ -3,7 +3,6 @@
 import sys
 
 from .logger import get_logger
-from six import binary_type, text_type
 
 any_method = object()
 
@@ -146,7 +145,7 @@
                         object and the response object.
 
         """
-        if isinstance(methods, (binary_type, text_type)) or methods is any_method:
+        if isinstance(methods, (bytes, str)) or methods is any_method:
             methods = [methods]
         for method in methods:
             self.routes.append((method, compile_path_match(path), handler))
diff --git a/third_party/wpt_tools/wpt/tools/wptserve/wptserve/server.py b/third_party/wpt_tools/wpt/tools/wptserve/wptserve/server.py
index 2b5ed4a2..cfa86f99 100644
--- a/third_party/wpt_tools/wpt/tools/wptserve/wptserve/server.py
+++ b/third_party/wpt_tools/wpt/tools/wptserve/wptserve/server.py
@@ -1,18 +1,16 @@
-from six.moves import BaseHTTPServer
 import errno
+import http.server
 import os
 import socket
-from six.moves.socketserver import ThreadingMixIn
+from socketserver import ThreadingMixIn
 import ssl
 import sys
 import threading
 import time
 import traceback
-from six import binary_type, text_type
 import uuid
 from collections import OrderedDict
-
-from six.moves.queue import Queue
+from queue import Queue
 
 from h2.config import H2Configuration
 from h2.connection import H2Connection
@@ -21,7 +19,7 @@
 from h2.settings import SettingCodes
 from h2.utilities import extract_method_header
 
-from six.moves.urllib.parse import urlsplit, urlunsplit
+from urllib.parse import urlsplit, urlunsplit
 
 from mod_pywebsocket import dispatch
 from mod_pywebsocket.handshake import HandshakeException
@@ -38,13 +36,12 @@
 
 # We need to stress test that browsers can send/receive many headers (there is
 # no specified limit), but the Python stdlib has an arbitrary limit of 100
-# headers. Hitting the limit would produce an exception that is silently caught
-# in Python 2 but leads to HTTP 431 in Python 3, so we monkey patch it higher.
+# headers. Hitting the limit leads to HTTP 431, so we monkey patch it higher.
 # https://bugs.python.org/issue26586
 # https://github.com/web-platform-tests/wpt/pull/24451
-from six.moves import http_client
-assert isinstance(getattr(http_client, '_MAXHEADERS'), int)
-setattr(http_client, '_MAXHEADERS', 512)
+import http.client
+assert isinstance(getattr(http.client, '_MAXHEADERS'), int)
+setattr(http.client, '_MAXHEADERS', 512)
 
 """
 HTTP server designed for testing purposes.
@@ -106,7 +103,7 @@
         :param output_path: Path to replace the input path with in
                             the request.
         """
-        if isinstance(methods, (binary_type, text_type)):
+        if isinstance(methods, (bytes, str)):
             methods = [methods]
         self.rules[input_path] = (methods, output_path)
 
@@ -129,7 +126,7 @@
                 request_handler.path = new_url
 
 
-class WebTestServer(ThreadingMixIn, BaseHTTPServer.HTTPServer):
+class WebTestServer(ThreadingMixIn, http.server.HTTPServer):
     allow_reuse_address = True
     acceptable_errors = (errno.EPIPE, errno.ECONNABORTED)
     request_queue_size = 2000
@@ -190,8 +187,7 @@
         else:
             hostname_port = ("",server_address[1])
 
-        #super doesn't work here because BaseHTTPServer.HTTPServer is old-style
-        BaseHTTPServer.HTTPServer.__init__(self, hostname_port, request_handler_cls, **kwargs)
+        http.server.HTTPServer.__init__(self, hostname_port, request_handler_cls, **kwargs)
 
         if config is not None:
             Server.config = config
@@ -236,12 +232,12 @@
             self.logger.error(traceback.format_exc())
 
 
-class BaseWebTestRequestHandler(BaseHTTPServer.BaseHTTPRequestHandler):
+class BaseWebTestRequestHandler(http.server.BaseHTTPRequestHandler):
     """RequestHandler for WebTestHttpd"""
 
     def __init__(self, *args, **kwargs):
         self.logger = get_logger()
-        BaseHTTPServer.BaseHTTPRequestHandler.__init__(self, *args, **kwargs)
+        http.server.BaseHTTPRequestHandler.__init__(self, *args, **kwargs)
 
     def finish_handling_h1(self, request_line_is_valid):
 
@@ -482,14 +478,19 @@
         try:
             handshaker.do_handshake()
         except HandshakeException as e:
-            self.logger.info('Handshake failed for error: %s', e)
+            self.logger.info('Handshake failed for error: %s' % e)
             h2response.set_error(e.status)
             h2response.write()
             return
 
         # h2 Handshaker prepares the headers but does not send them down the
         # wire. Flush the headers here.
-        h2response.write_status_headers()
+        try:
+            h2response.write_status_headers()
+        except StreamClosedError:
+            # work around https://github.com/web-platform-tests/wpt/issues/27786
+            # The stream was already closed.
+            return
 
         request_wrapper._dispatcher = dispatcher
 
@@ -522,7 +523,13 @@
 
     def _stream_ws_sub_thread(self, request, stream_handler, queue):
         dispatcher = request._dispatcher
-        dispatcher.transfer_data(request)
+        try:
+            dispatcher.transfer_data(request)
+        except StreamClosedError:
+            # work around https://github.com/web-platform-tests/wpt/issues/27786
+            # The stream was already closed.
+            queue.put(None)
+            return
 
         stream_id = stream_handler.h2_stream_id
         with stream_handler.conn as connection:
diff --git a/third_party/wpt_tools/wpt/tools/wptserve/wptserve/sslutils/openssl.py b/third_party/wpt_tools/wpt/tools/wptserve/wptserve/sslutils/openssl.py
index 64f6d5f..87a8cc9 100644
--- a/third_party/wpt_tools/wpt/tools/wptserve/wptserve/sslutils/openssl.py
+++ b/third_party/wpt_tools/wpt/tools/wptserve/wptserve/sslutils/openssl.py
@@ -6,8 +6,6 @@
 import tempfile
 from datetime import datetime, timedelta
 
-from six import iteritems, PY2
-
 # Amount of time beyond the present to consider certificates "expired." This
 # allows certificates to be proactively re-generated in the "buffer" period
 # prior to their exact expiration time.
@@ -18,11 +16,7 @@
     """makes sure s is an instance of str, converting with encoding if needed"""
     if isinstance(s, str):
         return s
-
-    if PY2:
-        return s.encode(encoding)
-    else:
-        return s.decode(encoding)
+    return s.decode(encoding)
 
 
 class OpenSSL(object):
@@ -79,7 +73,7 @@
         # Copy the environment, converting to plain strings. Win32 StartProcess
         # is picky about all the keys/values being str (on both Py2/3).
         env = {}
-        for k, v in iteritems(os.environ):
+        for k, v in os.environ.items():
             env[_ensure_str(k, "utf8")] = _ensure_str(v, "utf8")
 
         if self.base_conf_path is not None:
@@ -324,7 +318,7 @@
             end_date_str = openssl("x509",
                                    "-noout",
                                    "-enddate",
-                                   "-in", cert_path).split("=", 1)[1].strip()
+                                   "-in", cert_path).decode("utf8").split("=", 1)[1].strip()
             # Not sure if this works in other locales
             end_date = datetime.strptime(end_date_str, "%b %d %H:%M:%S %Y %Z")
             time_buffer = timedelta(**CERT_EXPIRY_BUFFER)
diff --git a/third_party/wpt_tools/wpt/tools/wptserve/wptserve/stash.py b/third_party/wpt_tools/wpt/tools/wptserve/wptserve/stash.py
index bf6e5992..66c2713 100644
--- a/third_party/wpt_tools/wpt/tools/wptserve/wptserve/stash.py
+++ b/third_party/wpt_tools/wpt/tools/wptserve/wptserve/stash.py
@@ -1,12 +1,10 @@
 import base64
 import json
 import os
-import six
 import threading
 import uuid
 
 from multiprocessing.managers import AcquirerProxy, BaseManager, DictProxy
-from six import text_type, binary_type
 
 from .utils import isomorphic_encode
 
@@ -67,10 +65,10 @@
 
 
 def start_server(address=None, authkey=None, mp_context=None):
-    if isinstance(authkey, text_type):
+    if isinstance(authkey, str):
         authkey = authkey.encode("ascii")
     kwargs = {}
-    if six.PY3 and mp_context is not None:
+    if mp_context is not None:
         kwargs["ctx"] = mp_context
     manager = ServerDictManager(address, authkey, **kwargs)
     manager.start()
@@ -160,7 +158,7 @@
         # This key format is required to support using the path. Since the data
         # passed into the stash can be a DictProxy which wouldn't detect
         # changes when writing to a subdict.
-        if isinstance(key, binary_type):
+        if isinstance(key, bytes):
             # UUIDs are within the ASCII charset.
             key = key.decode('ascii')
         return (isomorphic_encode(path), uuid.UUID(key).bytes)
diff --git a/third_party/wpt_tools/wpt/tools/wptserve/wptserve/utils.py b/third_party/wpt_tools/wpt/tools/wptserve/wptserve/utils.py
index df0b3f3..ed74b73 100644
--- a/third_party/wpt_tools/wpt/tools/wptserve/wptserve/utils.py
+++ b/third_party/wpt_tools/wpt/tools/wptserve/wptserve/utils.py
@@ -1,6 +1,5 @@
 import socket
 
-from six import binary_type, text_type
 
 from .logger import get_logger
 
@@ -8,18 +7,18 @@
 def isomorphic_decode(s):
     """Decodes a binary string into a text string using iso-8859-1.
 
-    Returns `unicode` in Python 2 and `str` in Python 3. The function is a
-    no-op if the argument already has a text type. iso-8859-1 is chosen because
-    it is an 8-bit encoding whose code points range from 0x0 to 0xFF and the
-    values are the same as the binary representations, so any binary string can
-    be decoded into and encoded from iso-8859-1 without any errors or data
-    loss. Python 3 also uses iso-8859-1 (or latin-1) extensively in http:
+    Returns `str`. The function is a no-op if the argument already has a text
+    type. iso-8859-1 is chosen because it is an 8-bit encoding whose code
+    points range from 0x0 to 0xFF and the values are the same as the binary
+    representations, so any binary string can be decoded into and encoded from
+    iso-8859-1 without any errors or data loss. Python 3 also uses iso-8859-1
+    (or latin-1) extensively in http:
     https://github.com/python/cpython/blob/273fc220b25933e443c82af6888eb1871d032fb8/Lib/http/client.py#L213
     """
-    if isinstance(s, text_type):
+    if isinstance(s, str):
         return s
 
-    if isinstance(s, binary_type):
+    if isinstance(s, bytes):
         return s.decode("iso-8859-1")
 
     raise TypeError("Unexpected value (expecting string-like): %r" % s)
@@ -28,14 +27,13 @@
 def isomorphic_encode(s):
     """Encodes a text-type string into binary data using iso-8859-1.
 
-    Returns `str` in Python 2 and `bytes` in Python 3. The function is a no-op
-    if the argument already has a binary type. This is the counterpart of
-    isomorphic_decode.
+    Returns `bytes`. The function is a no-op if the argument already has a
+    binary type. This is the counterpart of isomorphic_decode.
     """
-    if isinstance(s, binary_type):
+    if isinstance(s, bytes):
         return s
 
-    if isinstance(s, text_type):
+    if isinstance(s, str):
         return s.encode("iso-8859-1")
 
     raise TypeError("Unexpected value (expecting string-like): %r" % s)
@@ -88,6 +86,7 @@
         42,    # name
         43,    # nicname
         53,    # domain
+        69,    # tftp
         77,    # priv-rjs
         79,    # finger
         87,    # ttylink
@@ -105,8 +104,10 @@
         119,   # nntp
         123,   # ntp
         135,   # loc-srv / epmap
-        139,   # netbios
+        137,   # netbios-ns
+        139,   # netbios-ssn
         143,   # imap2
+        161,   # snmp
         179,   # bgp
         389,   # ldap
         427,   # afp (alternate)
@@ -129,6 +130,7 @@
         636,   # ldap+ssl
         993,   # ldap+ssl
         995,   # pop3+ssl
+        1719,  # h323gatestat
         1720,  # h323hostcall
         1723,  # pptp
         2049,  # nfs
@@ -137,6 +139,7 @@
         5060,  # sip
         5061,  # sips
         6000,  # x11
+        6566,  # sane-port
         6665,  # irc (alternate)
         6666,  # irc (alternate)
         6667,  # irc (default)
diff --git a/third_party/wpt_tools/wpt/tools/wptserve/wptserve/ws_h2_handshake.py b/third_party/wpt_tools/wpt/tools/wptserve/wptserve/ws_h2_handshake.py
index 98796c0..c813ecb 100644
--- a/third_party/wpt_tools/wpt/tools/wptserve/wptserve/ws_h2_handshake.py
+++ b/third_party/wpt_tools/wpt/tools/wptserve/wptserve/ws_h2_handshake.py
@@ -11,8 +11,6 @@
 from mod_pywebsocket.stream import Stream
 from mod_pywebsocket.stream import StreamOptions
 from mod_pywebsocket import util
-from six.moves import map
-from six.moves import range
 
 # TODO: We are using "private" methods of pywebsocket. We might want to
 # refactor pywebsocket to expose those methods publicly. Also, _get_origin
diff --git a/third_party/wpt_tools/wpt/wpt b/third_party/wpt_tools/wpt/wpt
index 36d0bed4..eac84b6 100755
--- a/third_party/wpt_tools/wpt/wpt
+++ b/third_party/wpt_tools/wpt/wpt
@@ -1,34 +1,11 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
 
 if __name__ == "__main__":
     import sys
-    from tools.wpt import wpt
-    args, extra = wpt.parse_args(sys.argv[1:])
-    commands = wpt.load_commands()
-    py3only = commands[args.command]["py3only"]
+    if (sys.version_info.major < 3 or
+            (sys.version_info.major == 3 and sys.version_info.minor < 6)):
+        sys.stderr.write("wpt requires Python 3.6 or higher\n")
+        sys.exit(1)
 
-    if (args.py2) and sys.version_info.major > 2:
-        if py3only:
-            sys.stderr.write("This command only works with Python 3\n")
-            sys.exit(1)
-        from subprocess import call
-        try:
-            sys.exit(call(['python2'] + sys.argv))
-        except OSError as e:
-            if e.errno == 2:
-                sys.stderr.write("python2 is needed to run this command\n")
-                sys.exit(1)
-            else:
-                raise
-    elif (not args.py2) and sys.version_info.major < 3:
-        from subprocess import call
-        try:
-            sys.exit(call(['python3'] + sys.argv))
-        except OSError as e:
-            if e.errno == 2:
-                sys.stderr.write("python3 is needed to run this command\n")
-                sys.exit(1)
-            else:
-                raise
-    else:
-        wpt.main()
+    from tools.wpt import wpt
+    wpt.main()
diff --git a/tools/licenses.py b/tools/licenses.py
index c548ac8f..aaa520b 100755
--- a/tools/licenses.py
+++ b/tools/licenses.py
@@ -122,7 +122,6 @@
     os.path.join('chrome', 'common', 'extensions', 'docs', 'examples'),
     os.path.join('chrome', 'test', 'chromeos', 'autotest'),
     os.path.join('chrome', 'test', 'data'),
-    os.path.join('clank', 'third_party', 'elements'),
     os.path.join('native_client'),
     os.path.join('testing', 'gmock'),
     os.path.join('testing', 'gtest'),
@@ -506,7 +505,7 @@
       third_party_dirs.update([os.path.join(dirname, p) for p in extra_paths])
 
 
-def FindThirdPartyDirs(prune_paths, root):
+def FindThirdPartyDirs(prune_paths, root, extra_third_party_dirs=None):
   """Find all third_party directories underneath the source root."""
   third_party_dirs = set()
   for path, dirs, files in os.walk(root):
@@ -542,7 +541,11 @@
     if path in ADDITIONAL_PATHS:
       dirs[:] = []
 
-  for dir in ADDITIONAL_PATHS:
+  extra_paths = set(ADDITIONAL_PATHS)
+  if extra_third_party_dirs:
+    extra_paths.update(extra_third_party_dirs)
+
+  for dir in extra_paths:
     if dir not in prune_paths:
       third_party_dirs.add(dir)
       ProcessAdditionalReadmePathsJson(root, dir, third_party_dirs)
@@ -647,9 +650,14 @@
   return len(errors) == 0
 
 
-def GenerateCredits(
-        file_template_file, entry_template_file, output_file, target_os,
-        gn_out_dir, gn_target, depfile=None):
+def GenerateCredits(file_template_file,
+                    entry_template_file,
+                    output_file,
+                    target_os,
+                    gn_out_dir,
+                    gn_target,
+                    extra_third_party_dirs=None,
+                    depfile=None):
   """Generate about:credits."""
 
   def EvaluateTemplate(template, env, escape=True):
@@ -681,7 +689,8 @@
     if not third_party_dirs:
       raise RuntimeError("No deps found.")
   else:
-    third_party_dirs = FindThirdPartyDirs(PRUNE_PATHS, _REPOSITORY_ROOT)
+    third_party_dirs = FindThirdPartyDirs(PRUNE_PATHS, _REPOSITORY_ROOT,
+                                          extra_third_party_dirs)
 
   if not file_template_file:
     file_template_file = os.path.join(_REPOSITORY_ROOT, 'components',
@@ -821,6 +830,9 @@
       '--file-template', help='Template HTML to use for the license page.')
   parser.add_argument(
       '--entry-template', help='Template HTML to use for each license.')
+  parser.add_argument(
+      '--extra-third-party-dirs',
+      help='Gn list of additional third_party dirs to look through.')
   parser.add_argument('--target-os', help='OS that this build is targeting.')
   parser.add_argument(
       '--gn-out-dir', help='GN output directory for scanning dependencies.')
@@ -830,6 +842,8 @@
   parser.add_argument('output_file', nargs='?')
   build_utils.AddDepfileOption(parser)
   args = parser.parse_args()
+  args.extra_third_party_dirs = build_utils.ParseGnList(
+      args.extra_third_party_dirs)
 
   if args.command == 'scan':
     if not ScanThirdPartyDirs():
@@ -837,7 +851,8 @@
   elif args.command == 'credits':
     if not GenerateCredits(args.file_template, args.entry_template,
                            args.output_file, args.target_os, args.gn_out_dir,
-                           args.gn_target, args.depfile):
+                           args.gn_target, args.extra_third_party_dirs,
+                           args.depfile):
       return 1
   elif args.command == 'license_file':
     try:
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
index 08693954..9487f93 100644
--- a/tools/metrics/histograms/enums.xml
+++ b/tools/metrics/histograms/enums.xml
@@ -10096,6 +10096,16 @@
   <int value="0" label="Unknown"/>
 </enum>
 
+<enum name="CellularConnectResult">
+  <int value="0" label="Unknown"/>
+  <int value="1" label="Invalid GUID"/>
+  <int value="2" label="Invalid state"/>
+  <int value="3" label="Canceled"/>
+  <int value="4" label="Not configured"/>
+  <int value="5" label="Blocked"/>
+  <int value="6" label="Success"/>
+</enum>
+
 <enum name="CertificateChainPosition">
   <obsolete>
     Deprecated as of 01/2016. CertCacheTrial has been removed.
@@ -46378,6 +46388,7 @@
   <int value="408190863" label="OmniboxDisableInstantExtendedLimit:disabled"/>
   <int value="409566604" label="IntentPickerPWAPersistence:enabled"/>
   <int value="409622437" label="disable-buffer-bw-compression"/>
+  <int value="410400234" label="WebAppEnableProtocolHandlers:disabled"/>
   <int value="411250226" label="AutoplayMutedVideos:disabled"/>
   <int value="412957264" label="tab-close-buttons-hidden-with-touch"/>
   <int value="413062443" label="MessagesForAndroidInfrastructure:disabled"/>
@@ -47746,6 +47757,7 @@
   <int value="1645479440" label="HistoryManipulationIntervention:disabled"/>
   <int value="1646498561" label="OfflineBookmarks:disabled"/>
   <int value="1649121568" label="DynamicTcmallocTuning:disabled"/>
+  <int value="1651030066" label="WebAppEnableProtocolHandlers:enabled"/>
   <int value="1651141490" label="SaveEditedPDFForm:enabled"/>
   <int value="1652798696"
       label="InterestFeedV1ClickAndViewActionsConditionalUpload:disabled"/>
diff --git a/tools/metrics/histograms/histograms_xml/gpu/histograms.xml b/tools/metrics/histograms/histograms_xml/gpu/histograms.xml
index 8b118a4..63f9eff 100644
--- a/tools/metrics/histograms/histograms_xml/gpu/histograms.xml
+++ b/tools/metrics/histograms/histograms_xml/gpu/histograms.xml
@@ -1100,6 +1100,32 @@
   </summary>
 </histogram>
 
+<histogram name="GPU.Scheduler.TaskDependencyTime" units="microseconds"
+    expires_after="2022-03-31">
+  <owner>berlu@chromium.org</owner>
+  <owner>chrome-gpu-metrics@google.com</owner>
+  <summary>
+    Records the wall time taken spent by each GPU scheduler task waiting on it's
+    dependencies to resolve. The time delta starts when the waiting fence is
+    added and finishes when the last fence is removed. Only recorded for clients
+    with high resolution clocks.
+  </summary>
+</histogram>
+
+<histogram name="GPU.Scheduler.TaskSchedulingDelayTime" units="microseconds"
+    expires_after="2022-03-31">
+  <owner>berlu@chromium.org</owner>
+  <owner>chrome-gpu-metrics@google.com</owner>
+  <summary>
+    Records the wall time taken spent by each GPU scheduler task waiting on to
+    be scheduled once all its dependencies have been resolved. The time delta
+    starts when the last waiting fence is passed and stops when the tasks starts
+    running. If the schedulers is working properly, for a high priority task,
+    this metric should be minimal. Only recorded for clients with high
+    resolution clocks.
+  </summary>
+</histogram>
+
 <histogram name="GPU.setIsAcceleratedCompositingActive"
     enum="GPUsetIsAcceleratedCompositingActive" expires_after="M85">
   <owner>vmiura@chromium.org</owner>
diff --git a/tools/metrics/histograms/histograms_xml/histogram_suffixes_list.xml b/tools/metrics/histograms/histograms_xml/histogram_suffixes_list.xml
index 33dcbaa..f912fe81 100644
--- a/tools/metrics/histograms/histograms_xml/histogram_suffixes_list.xml
+++ b/tools/metrics/histograms/histograms_xml/histogram_suffixes_list.xml
@@ -875,6 +875,10 @@
   <owner>boliu@google.com</owner>
   <owner>ssid@chromium.org</owner>
   <suffix name="Browser" label="Recorded for Browser crashes."/>
+  <suffix name="Browser.Background"
+      label="Recorded for background Browser crashes."/>
+  <suffix name="Browser.Foreground"
+      label="Recorded for foregound Browser crashes."/>
   <suffix name="GpuForegroundOom"
       label="Recorded for foreground GPU process OOM crashes."/>
   <suffix name="UtilityForegroundOom"
diff --git a/tools/metrics/histograms/histograms_xml/network/histograms.xml b/tools/metrics/histograms/histograms_xml/network/histograms.xml
index f59cfa49..25d62f1 100644
--- a/tools/metrics/histograms/histograms_xml/network/histograms.xml
+++ b/tools/metrics/histograms/histograms_xml/network/histograms.xml
@@ -104,6 +104,10 @@
 
 <histogram name="Network.Cellular.Connection.Disconnections"
     enum="NetworkCellularConnectionState" expires_after="2021-08-29">
+  <obsolete>
+    Split into Network.Cellular.PSim.Disconnections and
+    Network.Cellular.ESim.Disconnections on 03/2021.
+  </obsolete>
   <owner>azeemarshad@chromium.org</owner>
   <owner>cros-system-services-networking@google.com</owner>
   <summary>
@@ -114,6 +118,10 @@
 
 <histogram name="Network.Cellular.Connection.TimeToConnected" units="ms"
     expires_after="2021-08-29">
+  <obsolete>
+    Split into Network.Cellular.ESim.TimeToConnected and
+    Network.Cellular.ESim.TimeToConnected on 03/2021.
+  </obsolete>
   <owner>azeemarshad@chromium.org</owner>
   <owner>cros-connectivity@google.com</owner>
   <summary>
@@ -270,6 +278,53 @@
   </summary>
 </histogram>
 
+<histogram name="Network.Cellular.{SimType}.ConnectionSuccess"
+    enum="CellularConnectResult" expires_after="2022-03-01">
+  <owner>azeemarshad@chromium.org</owner>
+  <owner>cros-connectivity@google.com</owner>
+  <owner>hsuregan@chromium.org</owner>
+  <summary>
+    Tracks the result of connecting to a cellular network. Logged when a
+    {SimType} connection succeeds or fails. In the case of failure, which can
+    occur in the Chrome or Shill layers, the error reason is emitted. Note that
+    Shill errors are mapped to Unknown. Refer to
+    go/cros-cellular-success-metrics for details.
+  </summary>
+  <token key="SimType">
+    <variant name="ESim"/>
+    <variant name="PSim"/>
+  </token>
+</histogram>
+
+<histogram name="Network.Cellular.{SimType}.Disconnections"
+    enum="NetworkCellularConnectionState" expires_after="2022-03-01">
+  <owner>azeemarshad@chromium.org</owner>
+  <owner>cros-system-services-networking@google.com</owner>
+  <summary>
+    Tracks when cellular {SimType} network is connected and when cellular
+    network is disconnected without explicit user action.
+  </summary>
+  <token key="SimType">
+    <variant name="ESim"/>
+    <variant name="PSim"/>
+  </token>
+</histogram>
+
+<histogram name="Network.Cellular.{SimType}.TimeToConnected" units="ms"
+    expires_after="2022-03-01">
+  <owner>azeemarshad@chromium.org</owner>
+  <owner>cros-connectivity@google.com</owner>
+  <owner>hsuregan@chromium.org</owner>
+  <summary>
+    Tracks the amount fo time taken between when cellular device starts and
+    finishes connecting for {SimType} networks.
+  </summary>
+  <token key="SimType">
+    <variant name="ESim"/>
+    <variant name="PSim"/>
+  </token>
+</histogram>
+
 <histogram name="Network.Cellular.{SimType}.Usage.Duration" units="seconds"
     expires_after="2022-03-01">
   <owner>azeemarshad@chromium.org</owner>
diff --git a/tools/metrics/histograms/histograms_xml/new_tab_page/histograms.xml b/tools/metrics/histograms/histograms_xml/new_tab_page/histograms.xml
index 0a4bf624..dc83ae8 100644
--- a/tools/metrics/histograms/histograms_xml/new_tab_page/histograms.xml
+++ b/tools/metrics/histograms/histograms_xml/new_tab_page/histograms.xml
@@ -742,9 +742,7 @@
   </summary>
 </histogram>
 
-<histogram name="NewTabPage.LoadTime" units="ms" expires_after="never">
-<!-- expires-never: "heartbeat" metric (internal: go/uma-heartbeats) -->
-
+<histogram name="NewTabPage.LoadTime" units="ms" expires_after="2022-01-01">
   <owner>tiborg@chromium.org</owner>
   <owner>yyushkina@chromium.org</owner>
   <owner>chrome-desktop-ntp@google.com</owner>
diff --git a/tools/metrics/histograms/histograms_xml/page/histograms.xml b/tools/metrics/histograms/histograms_xml/page/histograms.xml
index f36fce2..82fc66c2 100644
--- a/tools/metrics/histograms/histograms_xml/page/histograms.xml
+++ b/tools/metrics/histograms/histograms_xml/page/histograms.xml
@@ -1866,7 +1866,8 @@
 </histogram>
 
 <histogram name="PageLoad.Internal.Renderer.PresentationTime.DeltaFromSwapTime"
-    units="ms" expires_after="2020-09-27">
+    units="ms" expires_after="2021-12-31">
+  <owner>jonross@chromium.org</owner>
   <owner>sadrul@chromium.org</owner>
   <summary>
     'Swap time' is the timestamp of the renderer submitting a CompositorFrame,
@@ -1878,17 +1879,24 @@
     compositor-frames that report a paint-timing metric (e.g.
     FirstContentfulPaint etc.), and after that frame has been displayed on
     screen.
+
+    The metric had expired on 2020-09-27, and re-enabled on 2021-03-17. So the
+    data within this period is incomplete.
   </summary>
 </histogram>
 
 <histogram name="PageLoad.Internal.Renderer.PresentationTime.Valid"
-    enum="Boolean" expires_after="2020-09-27">
+    enum="Boolean" expires_after="2021-12-31">
+  <owner>jonross@chromium.org</owner>
   <owner>sadrul@chromium.org</owner>
   <summary>
     This boolean keeps track of whether a valid presentation-timestamp was
     received or not. This is reported only for compositor-frames that report a
     paint-timing metric (e.g. FirstContentfulPaint etc.), and after that frame
     has been displayed on screen.
+
+    The metric had expired on 2020-09-27, and re-enabled on 2021-03-17. So the
+    data within this period is incomplete.
   </summary>
 </histogram>
 
diff --git a/tools/metrics/histograms/histograms_xml/phonehub/histograms.xml b/tools/metrics/histograms/histograms_xml/phonehub/histograms.xml
index de6d5369..8e25bdb 100644
--- a/tools/metrics/histograms/histograms_xml/phonehub/histograms.xml
+++ b/tools/metrics/histograms/histograms_xml/phonehub/histograms.xml
@@ -105,21 +105,31 @@
     enum="PhoneHubInterstitialScreenEvent" expires_after="2021-10-31">
   <owner>tengs@chromium.org</owner>
   <owner>khorimoto@chromium.org</owner>
-  <summary>Events for the given PhoneHub interstitial screen.</summary>
+  <summary>Events for the given PhoneHub interstitial screen. {Screen}</summary>
 <!-- The entries below should be a subset of the PhoneHubScreen enum -->
 
   <token key="Screen">
-    <variant name="BluetoothOrWifiDisabled"/>
-    <variant name="InitialConnecting">
+    <variant name="BluetoothOrWifiDisabled" summary=""/>
+    <variant name="InitialConnecting" summary="">
       <obsolete>
         Combined into PhoneConnecting on Nov 2020.
       </obsolete>
     </variant>
-    <variant name="Onboarding.ExistingMultideviceUser"/>
-    <variant name="Onboarding.NewMultideviceUser"/>
-    <variant name="OnboardingDismissPrompt"/>
-    <variant name="PhoneConnecting"/>
-    <variant name="PhoneDisconnected"/>
+    <variant name="Onboarding.ExistingMultideviceUser"
+        summary="NOTE(https://crbug.com/1187255): This metric is actually
+                 logging new multi-device users. For continuity, we continue
+                 to log this metric in reverse. See ExistingMultideviceUser2
+                 for the correct logging."/>
+    <variant name="Onboarding.ExistingMultideviceUser2" summary=""/>
+    <variant name="Onboarding.NewMultideviceUser"
+        summary="NOTE(https://crbug.com/1187255): This metric is actually
+                 logging existing multi-device users. For continuity, we
+                 continue to log this metric in reverse. See
+                 NewMultideviceUser2 for the correct logging."/>
+    <variant name="Onboarding.NewMultideviceUser2" summary=""/>
+    <variant name="OnboardingDismissPrompt" summary=""/>
+    <variant name="PhoneConnecting" summary=""/>
+    <variant name="PhoneDisconnected" summary=""/>
   </token>
 </histogram>
 
diff --git a/tools/metrics/histograms/histograms_xml/stability/histograms.xml b/tools/metrics/histograms/histograms_xml/stability/histograms.xml
index f5ecd5e..4f223298 100644
--- a/tools/metrics/histograms/histograms_xml/stability/histograms.xml
+++ b/tools/metrics/histograms/histograms_xml/stability/histograms.xml
@@ -129,7 +129,9 @@
   <owner>ssid@chromium.org</owner>
   <owner>wnwen@chromium.org</owner>
   <summary>
-    Reason given by Android ActivityManager for the exit of the process.
+    Reason given by Android ActivityManager for the exit of the process, only
+    recorded in Android R+. Recorded on the next browser startup for browser
+    exits, or right after the process died for child processes.
   </summary>
 </histogram>
 
diff --git a/tools/metrics/histograms/histograms_xml/translate/histograms.xml b/tools/metrics/histograms/histograms_xml/translate/histograms.xml
index 3d5f53e..ec0081f 100644
--- a/tools/metrics/histograms/histograms_xml/translate/histograms.xml
+++ b/tools/metrics/histograms/histograms_xml/translate/histograms.xml
@@ -1062,7 +1062,7 @@
   <owner>megjablon@google.com</owner>
   <owner>chrome-language@google.com</owner>
   <summary>
-    Records the target language used for a translation. This is the langauge
+    Records the target language used for a translation. This is the language
     being translated to.
 
     This is recorded when a translation (both manual and automatic) is started.
diff --git a/tools/perf/core/perfetto_binary_roller/binary_deps.json b/tools/perf/core/perfetto_binary_roller/binary_deps.json
index cad2baac..b7c915f5 100644
--- a/tools/perf/core/perfetto_binary_roller/binary_deps.json
+++ b/tools/perf/core/perfetto_binary_roller/binary_deps.json
@@ -1,16 +1,16 @@
 {
     "trace_processor_shell": {
         "win": {
-            "hash": "c80b858c77b82ec7e89933de81938d53be761d22",
-            "remote_path": "perfetto_binaries/trace_processor_shell/win/e7a3007adf25b4c59b99aef58ff41b179b3b66ef/trace_processor_shell.exe"
+            "hash": "70109c7140b950fc50cfb6b7b0064f361c5a767d",
+            "remote_path": "perfetto_binaries/trace_processor_shell/win/371d8955a1cbc43fc7b2ec19b30c250d6466291c/trace_processor_shell.exe"
         },
         "mac": {
-            "hash": "59667644a3ec6354704a6933d5df2dfb903fd734",
-            "remote_path": "perfetto_binaries/trace_processor_shell/mac/e7a3007adf25b4c59b99aef58ff41b179b3b66ef/trace_processor_shell"
+            "hash": "8c080f6337a2f1e880197daf242690a6980093be",
+            "remote_path": "perfetto_binaries/trace_processor_shell/mac/371d8955a1cbc43fc7b2ec19b30c250d6466291c/trace_processor_shell"
         },
         "linux": {
             "hash": "b053a22b4b34b7da28445e2e26d19695a6d8edbf",
-            "remote_path": "perfetto_binaries/trace_processor_shell/linux/375eac05933d48f729014597f8f34e5733d7cedb/trace_processor_shell"
+            "remote_path": "perfetto_binaries/trace_processor_shell/linux/371d8955a1cbc43fc7b2ec19b30c250d6466291c/trace_processor_shell"
         }
     },
     "power_profile.sql": {
diff --git a/ui/accessibility/ax_enum_util_unittest.cc b/ui/accessibility/ax_enum_util_unittest.cc
index 34be7b3..284b68cf 100644
--- a/ui/accessibility/ax_enum_util_unittest.cc
+++ b/ui/accessibility/ax_enum_util_unittest.cc
@@ -103,8 +103,17 @@
 
 TEST(AXEnumUtilTest, StringAttribute) {
   TestEnumStringConversion<ax::mojom::StringAttribute>();
-  TestAXNodeDataSetter<ax::mojom::StringAttribute>(
-      &AXNodeData::AddStringAttribute, std::string());
+
+  AXNodeData node_data;
+  for (int i = static_cast<int>(ax::mojom::StringAttribute::kMinValue) + 1;
+       i <= static_cast<int>(ax::mojom::StringAttribute::kMaxValue); ++i) {
+    ax::mojom::StringAttribute attr =
+        static_cast<ax::mojom::StringAttribute>(i);
+    if (attr == ax::mojom::StringAttribute::kChildTreeId)
+      continue;
+    node_data.AddStringAttribute(attr, std::string());
+  }
+  EXPECT_TRUE(!node_data.ToString().empty());
 }
 
 TEST(AXEnumUtilTest, IntAttribute) {
diff --git a/ui/accessibility/ax_node_data.cc b/ui/accessibility/ax_node_data.cc
index 2880aa94..9c38702 100644
--- a/ui/accessibility/ax_node_data.cc
+++ b/ui/accessibility/ax_node_data.cc
@@ -19,6 +19,7 @@
 #include "ui/accessibility/ax_enum_util.h"
 #include "ui/accessibility/ax_enums.mojom.h"
 #include "ui/accessibility/ax_role_properties.h"
+#include "ui/accessibility/ax_tree_id.h"
 #include "ui/gfx/transform.h"
 
 namespace ui {
@@ -450,11 +451,21 @@
 void AXNodeData::AddStringAttribute(ax::mojom::StringAttribute attribute,
                                     const std::string& value) {
   DCHECK_NE(attribute, ax::mojom::StringAttribute::kNone);
+  DCHECK_NE(attribute, ax::mojom::StringAttribute::kChildTreeId)
+      << "Use AddChildTreeId";
   if (HasStringAttribute(attribute))
     RemoveStringAttribute(attribute);
   string_attributes.push_back(std::make_pair(attribute, value));
 }
 
+void AXNodeData::AddChildTreeId(const ui::AXTreeID& tree_id) {
+  ax::mojom::StringAttribute attribute =
+      ax::mojom::StringAttribute::kChildTreeId;
+  if (HasStringAttribute(attribute))
+    RemoveStringAttribute(attribute);
+  string_attributes.push_back(std::make_pair(attribute, tree_id.ToString()));
+}
+
 void AXNodeData::AddIntAttribute(ax::mojom::IntAttribute attribute, int value) {
   DCHECK_NE(attribute, ax::mojom::IntAttribute::kNone);
   if (HasIntAttribute(attribute))
diff --git a/ui/accessibility/ax_node_data.h b/ui/accessibility/ax_node_data.h
index 31dd8efc..0dabcb6 100644
--- a/ui/accessibility/ax_node_data.h
+++ b/ui/accessibility/ax_node_data.h
@@ -22,6 +22,8 @@
 
 namespace ui {
 
+class AXTreeID;
+
 // Defines the type used for AXNode IDs.
 using AXNodeID = int32_t;
 
@@ -117,8 +119,11 @@
   // have wanted or what existing code already assumes.
   //
 
+  // This method cannot be used to set kChildTreeId due to a common
+  // misuse of base::UnguessableToken serialization. Use AddChildTreeId instead.
   void AddStringAttribute(ax::mojom::StringAttribute attribute,
                           const std::string& value);
+  void AddChildTreeId(const ui::AXTreeID& tree_id);
   void AddIntAttribute(ax::mojom::IntAttribute attribute, int32_t value);
   void AddFloatAttribute(ax::mojom::FloatAttribute attribute, float value);
   void AddBoolAttribute(ax::mojom::BoolAttribute attribute, bool value);
diff --git a/ui/accessibility/ax_node_position_unittest.cc b/ui/accessibility/ax_node_position_unittest.cc
index 4f2acc0..c9c8e81 100644
--- a/ui/accessibility/ax_node_position_unittest.cc
+++ b/ui/accessibility/ax_node_position_unittest.cc
@@ -417,8 +417,7 @@
   SetTree(std::make_unique<AXTree>(initial_state));
 
   AXTreeUpdate views_tree_update;
-  web_view.AddStringAttribute(ax::mojom::StringAttribute::kChildTreeId,
-                              GetTreeID().ToString());
+  web_view.AddChildTreeId(GetTreeID());
   views_tree_update.nodes = {web_view};
   ASSERT_TRUE(views_tree->Unserialize(views_tree_update));
   views_tree_manager_ = TestAXTreeManager(std::move(views_tree));
@@ -587,14 +586,12 @@
       CreateAXTree({iframe_root}, webpage_tree->GetAXTreeID());
 
   AXTreeUpdate views_tree_update;
-  web_view.AddStringAttribute(ax::mojom::StringAttribute::kChildTreeId,
-                              webpage_tree->GetAXTreeID().ToString());
+  web_view.AddChildTreeId(webpage_tree->GetAXTreeID());
   views_tree_update.nodes = {web_view};
   ASSERT_TRUE(views_tree->Unserialize(views_tree_update));
 
   AXTreeUpdate webpage_tree_update;
-  iframe.AddStringAttribute(ax::mojom::StringAttribute::kChildTreeId,
-                            iframe_tree->GetAXTreeID().ToString());
+  iframe.AddChildTreeId(iframe_tree->GetAXTreeID());
   webpage_tree_update.nodes = {iframe};
   ASSERT_TRUE(webpage_tree->Unserialize(webpage_tree_update));
 
@@ -11122,8 +11119,7 @@
   root.child_ids = {embed_object.id};
 
   embed_object.role = ax::mojom::Role::kEmbeddedObject;
-  embed_object.AddStringAttribute(ax::mojom::StringAttribute::kChildTreeId,
-                                  child_tree_id.ToString());
+  embed_object.AddChildTreeId(child_tree_id);
   SetTree(CreateAXTree({root, embed_object}));
 
   // Create tree manager for child tree.
diff --git a/ui/accessibility/ax_tree_combiner_unittest.cc b/ui/accessibility/ax_tree_combiner_unittest.cc
index 0bf8893..36889a7d 100644
--- a/ui/accessibility/ax_tree_combiner_unittest.cc
+++ b/ui/accessibility/ax_tree_combiner_unittest.cc
@@ -55,8 +55,7 @@
   parent_tree.nodes[1].role = ax::mojom::Role::kButton;
   parent_tree.nodes[2].id = 3;
   parent_tree.nodes[2].role = ax::mojom::Role::kIframe;
-  parent_tree.nodes[2].AddStringAttribute(
-      ax::mojom::StringAttribute::kChildTreeId, tree_id_2.ToString());
+  parent_tree.nodes[2].AddChildTreeId(tree_id_2);
 
   AXTreeUpdate child_tree;
   child_tree.root_id = 1;
@@ -181,8 +180,7 @@
   parent_tree.nodes[1].role = ax::mojom::Role::kButton;
   parent_tree.nodes[2].id = 3;
   parent_tree.nodes[2].role = ax::mojom::Role::kIframe;
-  parent_tree.nodes[2].AddStringAttribute(
-      ax::mojom::StringAttribute::kChildTreeId, tree_id_2.ToString());
+  parent_tree.nodes[2].AddChildTreeId(tree_id_2);
 
   AXTreeUpdate child_tree;
   child_tree.has_tree_data = true;
diff --git a/ui/accessibility/ax_tree_unittest.cc b/ui/accessibility/ax_tree_unittest.cc
index 22f87bf..e169e7d 100644
--- a/ui/accessibility/ax_tree_unittest.cc
+++ b/ui/accessibility/ax_tree_unittest.cc
@@ -3114,14 +3114,11 @@
   initial_state.nodes[0].child_ids.push_back(3);
   initial_state.nodes[0].child_ids.push_back(4);
   initial_state.nodes[1].id = 2;
-  initial_state.nodes[1].AddStringAttribute(
-      ax::mojom::StringAttribute::kChildTreeId, tree_id_2.ToString());
+  initial_state.nodes[1].AddChildTreeId(tree_id_2);
   initial_state.nodes[2].id = 3;
-  initial_state.nodes[2].AddStringAttribute(
-      ax::mojom::StringAttribute::kChildTreeId, tree_id_3.ToString());
+  initial_state.nodes[2].AddChildTreeId(tree_id_3);
   initial_state.nodes[3].id = 4;
-  initial_state.nodes[3].AddStringAttribute(
-      ax::mojom::StringAttribute::kChildTreeId, tree_id_3.ToString());
+  initial_state.nodes[3].AddChildTreeId(tree_id_3);
   AXTree tree(initial_state);
 
   auto child_tree_1_nodes = tree.GetNodeIdsForChildTreeId(tree_id_1);
@@ -3138,8 +3135,7 @@
 
   AXTreeUpdate update = initial_state;
   update.nodes[2].string_attributes.clear();
-  update.nodes[2].AddStringAttribute(ax::mojom::StringAttribute::kChildTreeId,
-                                     tree_id_2.ToString());
+  update.nodes[2].AddChildTreeId(tree_id_2);
   update.nodes[3].string_attributes.clear();
 
   EXPECT_TRUE(tree.Unserialize(update));
diff --git a/ui/color/color_id.h b/ui/color/color_id.h
index 7163d04d..3cc431c 100644
--- a/ui/color/color_id.h
+++ b/ui/color/color_id.h
@@ -18,21 +18,27 @@
   E(kColorAlertLowSeverity, NativeTheme::kColorId_AlertSeverityLow) \
   E(kColorAlertMediumSeverity, NativeTheme::kColorId_AlertSeverityMedium) \
   E_CPONLY(kColorDisabledForeground) \
+  E_CPONLY(kColorEndpointBackground) \
+  E_CPONLY(kColorEndpointForeground) \
   E_CPONLY(kColorItemSelectionBackground) \
   E_CPONLY(kColorMidground) \
   E_CPONLY(kColorPrimaryBackground) \
   E_CPONLY(kColorPrimaryForeground) \
   E_CPONLY(kColorSecondaryForeground) \
+  E_CPONLY(kColorSubtleAccent) \
   E_CPONLY(kColorSubtleEmphasisBackground) \
   E_CPONLY(kColorTextSelectionBackground) \
+  E_CPONLY(kColorTextSelectionForeground) \
   \
   /* Further UI element colors */ \
   E(kColorAvatarHeaderArt, NativeTheme::kColorId_AvatarHeaderArt) \
   E(kColorAvatarIconGuest, NativeTheme::kColorId_AvatarIconGuest) \
   E(kColorAvatarIconIncognito, NativeTheme::kColorId_AvatarIconIncognito) \
   E(kColorBubbleBackground, NativeTheme::kColorId_BubbleBackground) \
+  E(kColorBubbleBorder, NativeTheme::kColorId_BubbleBorder) \
   E(kColorBubbleFooterBackground, \
     NativeTheme::kColorId_BubbleFooterBackground) \
+  E(kColorBubbleFooterBorder, NativeTheme::kColorId_BubbleFooterBorder) \
   E(kColorButtonBackground, NativeTheme::kColorId_ButtonColor) \
   /* TODO(https://crbug.com/1003612): Map this to old color id. */ \
   E_CPONLY(kColorButtonBackgroundPressed) \
@@ -46,6 +52,7 @@
   E(kColorButtonBorderDisabled, \
     NativeTheme::kColorId_DisabledButtonBorderColor) \
   E(kColorButtonForeground, NativeTheme::kColorId_ButtonEnabledColor) \
+  E(kColorButtonForegroundChecked, NativeTheme::kColorId_ButtonCheckedColor) \
   E(kColorButtonForegroundDisabled, NativeTheme::kColorId_ButtonDisabledColor) \
   E(kColorButtonForegroundProminent, \
     NativeTheme::kColorId_TextOnProminentButtonColor) \
@@ -53,10 +60,21 @@
     NativeTheme::kColorId_ButtonUncheckedColor) \
   E(kColorDialogBackground, NativeTheme::kColorId_DialogBackground) \
   E(kColorDialogForeground, NativeTheme::kColorId_DialogForeground) \
+  E(kColorDropdownBackground, NativeTheme::kColorId_DropdownBackgroundColor) \
+  E(kColorDropdownBackgroundSelected, \
+    NativeTheme::kColorId_DropdownSelectedBackgroundColor) \
+  E(kColorDropdownForeground, NativeTheme::kColorId_DropdownForegroundColor) \
+  E(kColorDropdownForegroundSelected, \
+    NativeTheme::kColorId_DropdownSelectedForegroundColor) \
   E(kColorFocusableBorderFocused, NativeTheme::kColorId_FocusedBorderColor) \
   E(kColorFocusableBorderUnfocused, \
     NativeTheme::kColorId_UnfocusedBorderColor) \
+  E(kColorFrameActive, NativeTheme::kColorId_CustomFrameActiveColor) \
+  E(kColorFrameInactive, NativeTheme::kColorId_CustomFrameInactiveColor) \
+  E(kColorHelpIconActive, NativeTheme::kColorId_TooltipIconHovered) \
+  E(kColorHelpIconInactive, NativeTheme::kColorId_TooltipIcon) \
   E(kColorIcon, NativeTheme::kColorId_DefaultIconColor) \
+  E(kColorIconDisabled, NativeTheme::kColorId_DisabledIconColor) \
   E(kColorLabelForeground, NativeTheme::kColorId_LabelEnabledColor) \
   E(kColorLabelForegroundDisabled, NativeTheme::kColorId_LabelDisabledColor) \
   E(kColorLabelForegroundSecondary, NativeTheme::kColorId_LabelSecondaryColor) \
@@ -69,6 +87,7 @@
   E(kColorLinkForegroundPressed, NativeTheme::kColorId_LinkPressed) \
   E(kColorMenuBackground, NativeTheme::kColorId_MenuBackgroundColor) \
   E(kColorMenuBorder, NativeTheme::kColorId_MenuBorderColor) \
+  E(kColorMenuDropmarker, NativeTheme::kColorId_MenuDropIndicator) \
   E(kColorMenuIcon, NativeTheme::kColorId_MenuIconColor) \
   E(kColorMenuItemBackgroundAlertedInitial, \
     NativeTheme::kColorId_MenuItemInitialAlertBackgroundColor) \
@@ -89,6 +108,61 @@
   E(kColorMenuItemForegroundSelected, \
     NativeTheme::kColorId_SelectedMenuItemForegroundColor) \
   E(kColorMenuSeparator, NativeTheme::kColorId_MenuSeparatorColor) \
+  E(kColorNotificationActionsBackground, \
+    NativeTheme::kColorId_NotificationActionsRowBackground) \
+  E(kColorNotificationBackgroundActive, \
+    NativeTheme::kColorId_NotificationBackgroundActive) \
+  E(kColorNotificationBackgroundInactive, \
+    NativeTheme::kColorId_NotificationBackground) \
+  E(kColorNotificationHeaderForeground, \
+    NativeTheme::kColorId_NotificationDefaultAccentColor) \
+  E(kColorNotificationIconBackground, \
+    NativeTheme::kColorId_MessageCenterSmallImageMaskBackground) \
+  E(kColorNotificationIconForeground, \
+    NativeTheme::kColorId_MessageCenterSmallImageMaskForeground) \
+  E(kColorNotificationImageBackground, \
+    NativeTheme::kColorId_NotificationLargeImageBackground) \
+  E(kColorNotificationInputBackground, \
+    NativeTheme::kColorId_NotificationInkDropBase) \
+  E(kColorNotificationInputForeground, \
+    NativeTheme::kColorId_NotificationColor) \
+  E(kColorNotificationInputPlaceholderForeground, \
+    NativeTheme::kColorId_NotificationPlaceholderColor) \
+  E(kColorOverlayScrollbarFill, \
+    NativeTheme::kColorId_OverlayScrollbarThumbFill) \
+  E(kColorOverlayScrollbarFillHovered, \
+    NativeTheme::kColorId_OverlayScrollbarThumbHoveredFill) \
+  E(kColorOverlayScrollbarStroke, \
+    NativeTheme::kColorId_OverlayScrollbarThumbStroke) \
+  E(kColorOverlayScrollbarStrokeHovered, \
+    NativeTheme::kColorId_OverlayScrollbarThumbHoveredStroke) \
+  E(kColorPwaSecurityChipForeground, \
+    NativeTheme::kColorId_CustomTabBarSecurityChipDefaultColor) \
+  E(kColorPwaSecurityChipForegroundDangerous, \
+    NativeTheme::kColorId_CustomTabBarSecurityChipDangerousColor) \
+  E(kColorPwaSecurityChipForegroundPolicyCert, \
+    NativeTheme::kColorId_CustomTabBarSecurityChipWithCertColor) \
+  E(kColorPwaSecurityChipForegroundSecure, \
+    NativeTheme::kColorId_CustomTabBarSecurityChipSecureColor) \
+  E(kColorPwaToolbarBackground, \
+    NativeTheme::kColorId_CustomTabBarBackgroundColor) \
+  E(kColorPwaToolbarForeground, \
+    NativeTheme::kColorId_CustomTabBarForegroundColor) \
+  E(kColorSeparator, NativeTheme::kColorId_SeparatorColor) \
+  E(kColorSliderThumb, NativeTheme::kColorId_SliderThumbDefault) \
+  E(kColorSliderThumbMinimal, NativeTheme::kColorId_SliderThumbMinimal) \
+  E(kColorSliderTrack, NativeTheme::kColorId_SliderTroughDefault) \
+  E(kColorSliderTrackMinimal, NativeTheme::kColorId_SliderTroughMinimal) \
+  E(kColorSyncInfoBackground, \
+    NativeTheme::kColorId_SyncInfoContainerNoPrimaryAccount) \
+  E(kColorSyncInfoBackgroundError, \
+    NativeTheme::kColorId_SyncInfoContainerError) \
+  E(kColorSyncInfoBackgroundPaused, \
+    NativeTheme::kColorId_SyncInfoContainerPaused) \
+  E(kColorTabBackgroundHighlighted, \
+    NativeTheme::kColorId_TabHighlightBackground) \
+  E(kColorTabBackgroundHighlightedFocused, \
+    NativeTheme::kColorId_TabHighlightFocusedBackground) \
   E(kColorTabBorderSelected, NativeTheme::kColorId_TabSelectedBorderColor) \
   E(kColorTabContentSeparator, NativeTheme::kColorId_TabBottomBorder) \
   E(kColorTabForeground, NativeTheme::kColorId_TabTitleColorInactive) \
@@ -123,6 +197,14 @@
   E(kColorTextfieldSelectionForeground, \
     NativeTheme::kColorId_TextfieldSelectionColor) \
   E(kColorThrobber, NativeTheme::kColorId_ThrobberSpinningColor) \
+  E(kColorThrobberPreconnect, NativeTheme::kColorId_ThrobberWaitingColor) \
+  E(kColorToggleButtonShadow, NativeTheme::kColorId_ToggleButtonShadowColor) \
+  E(kColorToggleButtonThumbOff, \
+    NativeTheme::kColorId_ToggleButtonThumbColorOff) \
+  E(kColorToggleButtonThumbOn, NativeTheme::kColorId_ToggleButtonThumbColorOn) \
+  E(kColorToggleButtonTrackOff, \
+    NativeTheme::kColorId_ToggleButtonTrackColorOff) \
+  E(kColorToggleButtonTrackOn, NativeTheme::kColorId_ToggleButtonTrackColorOn) \
   E(kColorTooltipBackground, NativeTheme::kColorId_TooltipBackground) \
   E(kColorTooltipForeground, NativeTheme::kColorId_TooltipText) \
   E(kColorTreeBackground, NativeTheme::kColorId_TreeBackground) \
diff --git a/ui/color/color_mixers.h b/ui/color/color_mixers.h
index 48ae995..062ac353 100644
--- a/ui/color/color_mixers.h
+++ b/ui/color/color_mixers.h
@@ -33,7 +33,10 @@
 
 // Adds a color mixer to |provider| that combine the above color sets with
 // recipes as necessary to produce all colors needed by ui/.
-COMPONENT_EXPORT(COLOR) void AddUiColorMixer(ColorProvider* provider);
+COMPONENT_EXPORT(COLOR)
+void AddUiColorMixer(ColorProvider* provider,
+                     bool dark_window,
+                     bool high_contrast);
 
 // Adds a color mixer to |provider| that can add to kColorSetNative.
 // Intended for colors needed by ui/ that this platform overrides but
diff --git a/ui/color/color_provider_manager.cc b/ui/color/color_provider_manager.cc
index 56cfa51..15ab8e57 100644
--- a/ui/color/color_provider_manager.cc
+++ b/ui/color/color_provider_manager.cc
@@ -60,7 +60,7 @@
               contrast_mode == ColorProviderManager::ContrastMode::kHigh;
           ui::AddCoreDefaultColorMixer(provider, dark_mode, high_contrast);
           ui::AddNativeCoreColorMixer(provider, dark_mode, high_contrast);
-          ui::AddUiColorMixer(provider);
+          ui::AddUiColorMixer(provider, dark_mode, high_contrast);
           ui::AddNativeUiColorMixer(provider, dark_mode, high_contrast);
 #if defined(OS_MAC)
           // Always keep this mixer at the last so the system tint will be
diff --git a/ui/color/core_default_color_mixer.cc b/ui/color/core_default_color_mixer.cc
index 0e443fe3..178717a2 100644
--- a/ui/color/core_default_color_mixer.cc
+++ b/ui/color/core_default_color_mixer.cc
@@ -22,20 +22,18 @@
 
 ColorMixer& AddMixerForDarkMode(ColorProvider* provider, bool high_contrast) {
   ColorMixer& mixer = provider->AddMixer();
-  mixer.AddSet(
-      {kColorSetCoreDefaults,
-       {
-           {kColorAccent, gfx::kGoogleBlue300},
-           {kColorAlertHighSeverity, gfx::kGoogleRed300},
-           {kColorAlertLowSeverity, gfx::kGoogleGreen300},
-           {kColorAlertMediumSeverity, gfx::kGoogleYellow300},
-           {kColorMidground, gfx::kGoogleGrey800},
-           {kColorPrimaryBackground, SkColorSetRGB(0x29, 0x2A, 0x2D)},
-           {kColorPrimaryForeground, gfx::kGoogleGrey200},
-           {kColorSecondaryForeground, gfx::kGoogleGrey500},
-           {kColorSubtleEmphasisBackground, SkColorSetRGB(0x32, 0x36, 0x39)},
-           {kColorTextSelectionBackground, gfx::kGoogleBlue800},
-       }});
+  mixer.AddSet({kColorSetCoreDefaults,
+                {
+                    {kColorAccent, gfx::kGoogleBlue300},
+                    {kColorAlertHighSeverity, gfx::kGoogleRed300},
+                    {kColorAlertLowSeverity, gfx::kGoogleGreen300},
+                    {kColorAlertMediumSeverity, gfx::kGoogleYellow300},
+                    {kColorMidground, gfx::kGoogleGrey800},
+                    {kColorPrimaryBackground, SkColorSetRGB(0x29, 0x2A, 0x2D)},
+                    {kColorPrimaryForeground, gfx::kGoogleGrey200},
+                    {kColorSecondaryForeground, gfx::kGoogleGrey500},
+                    {kColorTextSelectionBackground, gfx::kGoogleBlue800},
+                }});
   return mixer;
 }
 
@@ -51,7 +49,6 @@
                     {kColorPrimaryBackground, SK_ColorWHITE},
                     {kColorPrimaryForeground, gfx::kGoogleGrey900},
                     {kColorSecondaryForeground, gfx::kGoogleGrey700},
-                    {kColorSubtleEmphasisBackground, gfx::kGoogleGrey050},
                     {kColorTextSelectionBackground, gfx::kGoogleBlue200},
                 }});
 
@@ -71,8 +68,18 @@
                           : AddMixerForLightMode(provider, high_contrast);
   mixer[kColorDisabledForeground] = BlendForMinContrast(
       gfx::kGoogleGrey600, kColorPrimaryBackground, kColorPrimaryForeground);
+  mixer[kColorEndpointBackground] =
+      GetColorWithMaxContrast(kColorEndpointForeground);
+  mixer[kColorEndpointForeground] =
+      GetColorWithMaxContrast(kColorPrimaryBackground);
   mixer[kColorItemSelectionBackground] =
       BlendForMinContrastWithSelf(kColorPrimaryBackground, 1.67f);
+  mixer[kColorSubtleAccent] = AlphaBlend(kColorAccent, kColorPrimaryBackground,
+                                         gfx::kGoogleGreyAlpha400);
+  mixer[kColorSubtleEmphasisBackground] =
+      BlendTowardMaxContrast(kColorPrimaryBackground, gfx::kGoogleGreyAlpha100);
+  mixer[kColorTextSelectionForeground] =
+      GetColorWithMaxContrast(kColorTextSelectionBackground);
 }
 
 }  // namespace ui
diff --git a/ui/color/mac/native_color_mixers.mm b/ui/color/mac/native_color_mixers.mm
index 4cc08cd..f4be384c 100644
--- a/ui/color/mac/native_color_mixers.mm
+++ b/ui/color/mac/native_color_mixers.mm
@@ -26,9 +26,8 @@
     ui::kColorMenuItemForegroundDisabled,
     ui::kColorMenuItemForeground,
     ui::kColorMenuSeparator,
-    ui::kColorTextSelectionBackground,
-    ui::kColorTextfieldSelectionBackground,
-    ui::kColorTableBackgroundAlternate});
+    ui::kColorTableBackgroundAlternate,
+    ui::kColorTextfieldSelectionBackground});
 // clang-format on
 }
 
@@ -64,14 +63,8 @@
             skia::NSSystemColorToSkColor([NSColor disabledControlTextColor])},
            {kColorMenuItemForeground,
             skia::NSSystemColorToSkColor([NSColor controlTextColor])},
-           {kColorTextSelectionBackground,
-            skia::NSSystemColorToSkColor(
-                [NSColor selectedTextBackgroundColor])},
        }});
 
-  mixer[kColorMenuItemForegroundHighlighted] = {kColorPrimaryForeground};
-  mixer[kColorMenuItemForegroundSelected] = {kColorPrimaryForeground};
-
   if (@available(macOS 10.14, *)) {
     mixer[kColorTableBackgroundAlternate] = {skia::NSSystemColorToSkColor(
         NSColor.alternatingContentBackgroundColors[1])};
diff --git a/ui/color/ui_color_mixer.cc b/ui/color/ui_color_mixer.cc
index ecf814bb..23686a8 100644
--- a/ui/color/ui_color_mixer.cc
+++ b/ui/color/ui_color_mixer.cc
@@ -12,90 +12,163 @@
 
 namespace ui {
 
-void AddUiColorMixer(ColorProvider* provider) {
+void AddUiColorMixer(ColorProvider* provider,
+                     bool dark_window,
+                     bool high_contrast) {
   ColorMixer& mixer = provider->AddMixer();
-  const auto button_disabled_background =
-      BlendForMinContrastWithSelf(kColorButtonBackground, 1.2f);
-
   mixer[kColorAvatarHeaderArt] = {kColorMidground};
   mixer[kColorAvatarIconGuest] = {kColorSecondaryForeground};
   mixer[kColorAvatarIconIncognito] = {kColorPrimaryForeground};
   mixer[kColorBubbleBackground] = {kColorPrimaryBackground};
+  mixer[kColorBubbleBorder] = {kColorMidground};
   mixer[kColorBubbleFooterBackground] = {kColorSubtleEmphasisBackground};
+  mixer[kColorBubbleFooterBorder] = {kColorMidground};
   mixer[kColorButtonBackground] = {kColorPrimaryBackground};
-  mixer[kColorButtonBorder] = {kColorMidground};
-  mixer[kColorButtonBorderDisabled] = button_disabled_background;
-  mixer[kColorButtonForegroundDisabled] = {kColorDisabledForeground};
-  mixer[kColorButtonForeground] = {kColorAccent};
   mixer[kColorButtonBackgroundPressed] = {kColorButtonBackground};
   mixer[kColorButtonBackgroundProminent] = {kColorAccent};
-  mixer[kColorButtonBackgroundProminentDisabled] = button_disabled_background;
+  mixer[kColorButtonBackgroundProminentDisabled] = {
+      kColorSubtleEmphasisBackground};
   mixer[kColorButtonBackgroundProminentFocused] =
       BlendForMinContrastWithSelf(kColorButtonBackgroundProminent, 1.3f);
+  mixer[kColorButtonBorder] = {kColorMidground};
+  mixer[kColorButtonBorderDisabled] = {kColorSubtleEmphasisBackground};
+  mixer[kColorButtonForeground] = {kColorAccent};
+  mixer[kColorButtonForegroundChecked] = {kColorButtonForeground};
+  mixer[kColorButtonForegroundDisabled] = {kColorDisabledForeground};
   mixer[kColorButtonForegroundProminent] =
       GetColorWithMaxContrast(kColorButtonBackgroundProminent);
   mixer[kColorButtonForegroundUnchecked] = {kColorSecondaryForeground};
   mixer[kColorDialogBackground] = {kColorPrimaryBackground};
   mixer[kColorDialogForeground] = {kColorSecondaryForeground};
+  mixer[kColorDropdownBackground] = {kColorPrimaryBackground};
+  mixer[kColorDropdownBackgroundSelected] = {kColorItemSelectionBackground};
+  mixer[kColorDropdownForeground] = {kColorPrimaryForeground};
+  mixer[kColorDropdownForegroundSelected] = {kColorPrimaryForeground};
   mixer[kColorFocusableBorderFocused] = SetAlpha(kColorAccent, 0x4D);
   mixer[kColorFocusableBorderUnfocused] = {kColorMidground};
+  mixer[kColorFrameActive] = {dark_window ? gfx::kGoogleGrey900
+                                          : SkColorSetRGB(0xDE, 0xE1, 0xE6)};
+  mixer[kColorFrameInactive] = {dark_window ? gfx::kGoogleGrey800
+                                            : gfx::kGoogleGrey200};
+  mixer[kColorHelpIconActive] = {kColorPrimaryForeground};
+  mixer[kColorHelpIconInactive] = {kColorSecondaryForeground};
   mixer[kColorIcon] = {kColorSecondaryForeground};
-  mixer[kColorMenuIcon] = {kColorIcon};
-  mixer[kColorLabelForegroundDisabled] = {kColorDisabledForeground};
+  mixer[kColorIconDisabled] = SetAlpha(kColorIcon, gfx::kDisabledControlAlpha);
   mixer[kColorLabelForeground] = {kColorPrimaryForeground};
+  mixer[kColorLabelForegroundDisabled] = {kColorDisabledForeground};
   mixer[kColorLabelForegroundSecondary] = {kColorSecondaryForeground};
   mixer[kColorLabelSelectionBackground] = {kColorTextSelectionBackground};
-  mixer[kColorLabelSelectionForeground] = {kColorLabelForeground};
+  mixer[kColorLabelSelectionForeground] = {kColorTextSelectionForeground};
+  mixer[kColorLinkForeground] = {kColorAccent};
   mixer[kColorLinkForegroundDisabled] = {kColorDisabledForeground};
   mixer[kColorLinkForegroundPressed] = {kColorLinkForeground};
-  mixer[kColorLinkForeground] = {kColorAccent};
   mixer[kColorMenuBackground] = {kColorPrimaryBackground};
   mixer[kColorMenuBorder] = {kColorMidground};
+  mixer[kColorMenuDropmarker] = {kColorPrimaryForeground};
+  mixer[kColorMenuIcon] = {kColorIcon};
   mixer[kColorMenuItemBackgroundAlertedInitial] = SetAlpha(kColorAccent, 0x4D);
-  mixer[kColorMenuItemBackgroundAlertedTarget] = SetAlpha(kColorAccent, 0x1A);
-  mixer[kColorMenuItemForegroundDisabled] = {kColorDisabledForeground};
-  mixer[kColorMenuItemForeground] = {kColorPrimaryForeground};
+  mixer[kColorMenuItemBackgroundAlertedTarget] =
+      SetAlpha(kColorAccent, gfx::kGoogleGreyAlpha200);
   mixer[kColorMenuItemBackgroundHighlighted] = {kColorSubtleEmphasisBackground};
+  mixer[kColorMenuItemBackgroundSelected] = {kColorItemSelectionBackground};
+  mixer[kColorMenuItemForeground] = {kColorPrimaryForeground};
+  mixer[kColorMenuItemForegroundDisabled] = {kColorDisabledForeground};
   mixer[kColorMenuItemForegroundHighlighted] = {kColorMenuItemForeground};
   mixer[kColorMenuItemForegroundSecondary] = {kColorSecondaryForeground};
-  mixer[kColorMenuItemBackgroundSelected] = {kColorItemSelectionBackground};
   mixer[kColorMenuItemForegroundSelected] = {kColorMenuItemForeground};
   mixer[kColorMenuSeparator] = {kColorMidground};
+  mixer[kColorNotificationActionsBackground] = {
+      kColorNotificationBackgroundActive};
+  mixer[kColorNotificationBackgroundActive] = {kColorSubtleEmphasisBackground};
+  mixer[kColorNotificationBackgroundInactive] = {kColorPrimaryBackground};
+  mixer[kColorNotificationHeaderForeground] = {kColorSecondaryForeground};
+  mixer[kColorNotificationIconBackground] = {
+      kColorNotificationHeaderForeground};
+  mixer[kColorNotificationIconForeground] = {
+      kColorNotificationBackgroundInactive};
+  mixer[kColorNotificationImageBackground] = {
+      kColorNotificationBackgroundActive};
+  mixer[kColorNotificationInputBackground] = {kColorAccent};
+  mixer[kColorNotificationInputForeground] =
+      GetColorWithMaxContrast(kColorNotificationInputBackground);
+  mixer[kColorNotificationInputPlaceholderForeground] =
+      SetAlpha(kColorNotificationInputForeground, gfx::kGoogleGreyAlpha700);
+  mixer[kColorOverlayScrollbarFill] =
+      SetAlpha(kColorEndpointForeground, gfx::kGoogleGreyAlpha700);
+  mixer[kColorOverlayScrollbarFillHovered] =
+      SetAlpha(kColorEndpointForeground, gfx::kGoogleGreyAlpha800);
+  mixer[kColorOverlayScrollbarStroke] =
+      SetAlpha(kColorEndpointBackground, gfx::kGoogleGreyAlpha400);
+  mixer[kColorOverlayScrollbarStrokeHovered] =
+      SetAlpha(kColorEndpointBackground, gfx::kGoogleGreyAlpha500);
+  mixer[kColorPwaSecurityChipForeground] = {kColorSecondaryForeground};
+  mixer[kColorPwaSecurityChipForegroundDangerous] = {kColorAlertHighSeverity};
+  mixer[kColorPwaSecurityChipForegroundPolicyCert] = {kColorDisabledForeground};
+  mixer[kColorPwaSecurityChipForegroundSecure] = {
+      kColorPwaSecurityChipForeground};
+  mixer[kColorPwaToolbarBackground] = {kColorEndpointBackground};
+  mixer[kColorPwaToolbarForeground] = {kColorEndpointForeground};
+  mixer[kColorSeparator] = {kColorMidground};
+  mixer[kColorSliderThumb] = {kColorAccent};
+  mixer[kColorSliderThumbMinimal] = {kColorSecondaryForeground};
+  mixer[kColorSliderTrack] = {kColorSubtleAccent};
+  mixer[kColorSliderTrackMinimal] = {kColorMidground};
+  mixer[kColorSyncInfoBackground] = {kColorSubtleEmphasisBackground};
+  mixer[kColorSyncInfoBackgroundError] =
+      SetAlpha(kColorAlertHighSeverity, gfx::kGoogleGreyAlpha100);
+  mixer[kColorSyncInfoBackgroundPaused] =
+      SetAlpha(kColorAccent, gfx::kGoogleGreyAlpha100);
+  mixer[kColorTabBackgroundHighlighted] = SetAlpha(gfx::kGoogleBlue300, 0x2B);
+  mixer[kColorTabBackgroundHighlightedFocused] =
+      SetAlpha(gfx::kGoogleBlue300, 0x53);
+  mixer[kColorTabBorderSelected] = {kColorAccent};
   mixer[kColorTabContentSeparator] = {kColorMidground};
   mixer[kColorTabForeground] = {kColorSecondaryForeground};
-  mixer[kColorTabBorderSelected] = {kColorAccent};
   mixer[kColorTabForegroundSelected] = {kColorAccent};
   mixer[kColorTableBackground] = {kColorPrimaryBackground};
+  mixer[kColorTableBackgroundSelectedFocused] = {kColorItemSelectionBackground};
+  mixer[kColorTableBackgroundSelectedUnfocused] = {
+      kColorTableBackgroundSelectedFocused};
   mixer[kColorTableForeground] = {kColorPrimaryForeground};
+  mixer[kColorTableForegroundSelectedFocused] = {kColorTableForeground};
+  mixer[kColorTableForegroundSelectedUnfocused] = {
+      kColorTableForegroundSelectedFocused};
   mixer[kColorTableGroupingIndicator] = {kColorTableBackgroundSelectedFocused};
   mixer[kColorTableHeaderBackground] = {kColorTableBackground};
   mixer[kColorTableHeaderForeground] = {kColorTableForeground};
   mixer[kColorTableHeaderSeparator] = {kColorMidground};
-  mixer[kColorTableBackgroundSelectedFocused] = {kColorItemSelectionBackground};
-  mixer[kColorTableForegroundSelectedFocused] = {kColorTableForeground};
-  mixer[kColorTableBackgroundSelectedUnfocused] = {
-      kColorTableBackgroundSelectedFocused};
-  mixer[kColorTableForegroundSelectedUnfocused] = {
-      kColorTableForegroundSelectedFocused};
-  mixer[kColorTextfieldBackground] =
-      GetColorWithMaxContrast(kColorTextfieldForeground);
+  mixer[kColorTextfieldBackground] = {kColorEndpointBackground};
   mixer[kColorTextfieldBackgroundDisabled] = {kColorPrimaryBackground};
+  mixer[kColorTextfieldForeground] = {kColorPrimaryForeground};
   mixer[kColorTextfieldForegroundDisabled] = {kColorDisabledForeground};
   mixer[kColorTextfieldForegroundPlaceholder] = {
       kColorTextfieldForegroundDisabled};
-  mixer[kColorTextfieldForeground] = {kColorPrimaryForeground};
   mixer[kColorTextfieldSelectionBackground] = {kColorTextSelectionBackground};
-  mixer[kColorTextfieldSelectionForeground] = {kColorTextfieldForeground};
+  mixer[kColorTextfieldSelectionForeground] = {kColorTextSelectionForeground};
   mixer[kColorThrobber] = {kColorAccent};
+  mixer[kColorThrobberPreconnect] = {kColorSubtleAccent};
+  mixer[kColorToggleButtonShadow] = {kColorMidground};
+  mixer[kColorToggleButtonThumbOff] = {kColorSecondaryForeground};
+  mixer[kColorToggleButtonThumbOn] = {kColorAccent};
+  if (dark_window) {
+    mixer[kColorToggleButtonThumbOff] +=
+        AlphaBlend(kColorPrimaryForeground, FromTransformInput(), 0x0D);
+    mixer[kColorToggleButtonThumbOn] +=
+        AlphaBlend(kColorPrimaryForeground, FromTransformInput(), 0x0D);
+  }
+  mixer[kColorToggleButtonTrackOff] = {
+      dark_window ? ColorTransform(gfx::kGoogleGrey700) : kColorMidground};
+  mixer[kColorToggleButtonTrackOn] = {dark_window ? gfx::kGoogleBlue600
+                                                  : gfx::kGoogleBlue300};
   mixer[kColorTooltipBackground] = SetAlpha(kColorPrimaryBackground, 0xCC);
   mixer[kColorTooltipForeground] = SetAlpha(kColorPrimaryForeground, 0xDE);
   mixer[kColorTreeBackground] = {kColorPrimaryBackground};
-  mixer[kColorTreeNodeForeground] = {kColorPrimaryForeground};
   mixer[kColorTreeNodeBackgroundSelectedFocused] = {
       kColorItemSelectionBackground};
-  mixer[kColorTreeNodeForegroundSelectedFocused] = {kColorTreeNodeForeground};
   mixer[kColorTreeNodeBackgroundSelectedUnfocused] = {
       kColorTreeNodeBackgroundSelectedFocused};
+  mixer[kColorTreeNodeForeground] = {kColorPrimaryForeground};
+  mixer[kColorTreeNodeForegroundSelectedFocused] = {kColorTreeNodeForeground};
   mixer[kColorTreeNodeForegroundSelectedUnfocused] = {
       kColorTreeNodeForegroundSelectedFocused};
   mixer[kColorWindowBackground] = {kColorPrimaryBackground};
diff --git a/ui/message_center/views/notification_view_md_unittest.cc b/ui/message_center/views/notification_view_md_unittest.cc
index cd00246..c67b0e2 100644
--- a/ui/message_center/views/notification_view_md_unittest.cc
+++ b/ui/message_center/views/notification_view_md_unittest.cc
@@ -994,8 +994,10 @@
 }
 
 TEST_F(NotificationViewMDTest, TestAccentColor) {
+  // TODO(pkasting): These hardcoded colors are fragile and should be obtained
+  // dynamically.
   const SkColor kNotificationBackgroundColor = SK_ColorWHITE;
-  const SkColor kActionButtonBackgroundColor = SkColorSetRGB(0xEE, 0xEE, 0xEE);
+  const SkColor kActionButtonBackgroundColor = SkColorSetRGB(0xF2, 0xF2, 0xF2);
   const SkColor kActionButtonTextColor =
       DeriveMinContrastColor(gfx::kGoogleBlue600, kActionButtonBackgroundColor);
 
@@ -1004,7 +1006,12 @@
 
   std::unique_ptr<Notification> notification = CreateSimpleNotification();
   notification->set_buttons(CreateButtons(2));
+
+  // The code below is not prepared to deal with dark mode.
+  notification_view()->GetWidget()->GetNativeTheme()->set_use_dark_colors(
+      false);
   UpdateNotificationViews(*notification);
+
   notification_view()->GetWidget()->Show();
 
   // Action buttons are hidden by collapsed state.
diff --git a/ui/native_theme/common_theme.cc b/ui/native_theme/common_theme.cc
index ec16386..8b361bb 100644
--- a/ui/native_theme/common_theme.cc
+++ b/ui/native_theme/common_theme.cc
@@ -60,10 +60,6 @@
     case NativeTheme::kColorId_AlertSeverityMedium:
       return GetAlertSeverityColor(color_id, true);
 
-    // Bubble
-    case NativeTheme::kColorId_FootnoteContainerBorder:
-      return gfx::kGoogleGrey900;
-
     // Button
     case NativeTheme::kColorId_ProminentButtonColor:
       return gfx::kGoogleBlue300;
@@ -82,29 +78,33 @@
     case NativeTheme::kColorId_LabelTextSelectionBackgroundFocused:
       return gfx::kGoogleBlue800;
 
-    // Menu
-    case NativeTheme::kColorId_HighlightedMenuItemBackgroundColor:
-      return SkColorSetRGB(0x32, 0x36, 0x39);
-
     // Separator
     case NativeTheme::kColorId_SeparatorColor:
       return gfx::kGoogleGrey800;
 
-    // Tabbed pane
-    case NativeTheme::kColorId_TabHighlightFocusedBackground:
-      return SkColorSetRGB(0x32, 0x36, 0x39);
-    case NativeTheme::kColorId_TabHighlightBackground:
-      return gfx::kGoogleGrey800;
-
-    // Tooltip
-    case NativeTheme::kColorId_TooltipIcon:
-      return SkColorSetA(gfx::kGoogleGrey200, 0xBD);
-    case NativeTheme::kColorId_TooltipIconHovered:
-      return SK_ColorWHITE;
+    // Toggle button
+    case ui::NativeTheme::kColorId_ToggleButtonThumbColorOff: {
+      const SkColor enabled =
+          *GetDarkSchemeColor(NativeTheme::kColorId_LabelEnabledColor);
+      const SkColor secondary =
+          *GetDarkSchemeColor(NativeTheme::kColorId_LabelSecondaryColor);
+      return color_utils::AlphaBlend(enabled, secondary, 0.05f);
+    }
+    case ui::NativeTheme::kColorId_ToggleButtonThumbColorOn: {
+      const SkColor enabled =
+          *GetDarkSchemeColor(NativeTheme::kColorId_LabelEnabledColor);
+      const SkColor prominent =
+          *GetDarkSchemeColor(NativeTheme::kColorId_ProminentButtonColor);
+      return color_utils::AlphaBlend(enabled, prominent, 0.05f);
+    }
+    case ui::NativeTheme::kColorId_ToggleButtonTrackColorOff:
+      return gfx::kGoogleGrey700;
+    case ui::NativeTheme::kColorId_ToggleButtonTrackColorOn:
+      return gfx::kGoogleBlue600;
 
     // Window
     case NativeTheme::kColorId_WindowBackground:
-      return color_utils::AlphaBlend(SK_ColorWHITE, gfx::kGoogleGrey900, 0.04f);
+      return color_utils::BlendTowardMaxContrast(gfx::kGoogleGrey900, 0x0A);
 
     default:
       return base::nullopt;
@@ -151,7 +151,6 @@
           NativeTheme::kColorId_HighlightedMenuItemBackgroundColor,
           color_scheme);
     case NativeTheme::kColorId_FootnoteContainerBorder:
-      return gfx::kGoogleGrey200;
     case NativeTheme::kColorId_BubbleBorder:
       return base_theme->GetUnprocessedSystemColor(
           NativeTheme::kColorId_SeparatorColor, color_scheme);
@@ -165,12 +164,10 @@
           base_theme->GetUnprocessedSystemColor(
               NativeTheme::kColorId_ProminentButtonColor, color_scheme));
     case NativeTheme::kColorId_ProminentButtonDisabledColor:
-    case NativeTheme::kColorId_DisabledButtonBorderColor: {
-      const SkColor bg = base_theme->GetUnprocessedSystemColor(
-          NativeTheme::kColorId_WindowBackground, color_scheme);
-      return color_utils::BlendForMinContrast(bg, bg, base::nullopt, 1.2f)
-          .color;
-    }
+    case NativeTheme::kColorId_DisabledButtonBorderColor:
+      return base_theme->GetUnprocessedSystemColor(
+          NativeTheme::kColorId_HighlightedMenuItemBackgroundColor,
+          color_scheme);
     case NativeTheme::kColorId_ButtonBorderColor:
       return base_theme->GetUnprocessedSystemColor(
           NativeTheme::kColorId_SeparatorColor, color_scheme);
@@ -240,7 +237,7 @@
     case NativeTheme::kColorId_CustomFrameActiveColor:
       return SkColorSetRGB(0xDE, 0xE1, 0xE6);
     case NativeTheme::kColorId_CustomFrameInactiveColor:
-      return SkColorSetRGB(0xE7, 0xEA, 0xED);
+      return gfx::kGoogleGrey200;
 
     // Icon
     case NativeTheme::kColorId_DefaultIconColor:
@@ -265,9 +262,12 @@
       return gfx::kGoogleGrey700;
     case NativeTheme::kColorId_LabelEnabledColor:
       return gfx::kGoogleGrey900;
-    case NativeTheme::kColorId_LabelTextSelectionColor:
-      return base_theme->GetUnprocessedSystemColor(
-          NativeTheme::kColorId_LabelEnabledColor, color_scheme);
+    case NativeTheme::kColorId_LabelTextSelectionColor: {
+      const SkColor bg = base_theme->GetUnprocessedSystemColor(
+          NativeTheme::kColorId_LabelTextSelectionBackgroundFocused,
+          color_scheme);
+      return color_utils::GetColorWithMaxContrast(bg);
+    }
     case NativeTheme::kColorId_LabelTextSelectionBackgroundFocused:
       return gfx::kGoogleBlue200;
 
@@ -284,8 +284,11 @@
     case NativeTheme::kColorId_MenuBackgroundColor:
       return base_theme->GetUnprocessedSystemColor(
           NativeTheme::kColorId_WindowBackground, color_scheme);
-    case NativeTheme::kColorId_HighlightedMenuItemBackgroundColor:
-      return gfx::kGoogleGrey050;
+    case NativeTheme::kColorId_HighlightedMenuItemBackgroundColor: {
+      const SkColor bg = base_theme->GetUnprocessedSystemColor(
+          NativeTheme::kColorId_WindowBackground, color_scheme);
+      return color_utils::BlendTowardMaxContrast(bg, gfx::kGoogleGreyAlpha100);
+    }
     case NativeTheme::kColorId_MenuBorderColor:
     case NativeTheme::kColorId_MenuSeparatorColor:
       return base_theme->GetUnprocessedSystemColor(
@@ -316,57 +319,55 @@
           accent, (color_id == kInitial) ? 0x4D : gfx::kGoogleGreyAlpha200);
     }
 
-    // Message Center
-    case NativeTheme::kColorId_MessageCenterSmallImageMaskForeground:
-      return SK_ColorWHITE;
-    case NativeTheme::kColorId_MessageCenterSmallImageMaskBackground:
-      return SkColorSetRGB(0xa3, 0xa3, 0xa3);
-
     // Notification
+    case NativeTheme::kColorId_MessageCenterSmallImageMaskForeground:
     case NativeTheme::kColorId_NotificationBackground:
-    case NativeTheme::kColorId_NotificationColor:
       return base_theme->GetUnprocessedSystemColor(
           NativeTheme::kColorId_WindowBackground, color_scheme);
 #if BUILDFLAG(IS_CHROMEOS_ASH)
     case NativeTheme::kColorId_NotificationButtonBackground:
       return SkColorSetA(SK_ColorWHITE, 0.9 * 0xff);
 #endif
-    case NativeTheme::kColorId_NotificationPlaceholderColor:
-      return SkColorSetA(SK_ColorWHITE, gfx::kDisabledControlAlpha);
+    case NativeTheme::kColorId_NotificationPlaceholderColor: {
+      const SkColor color = base_theme->GetUnprocessedSystemColor(
+          NativeTheme::kColorId_TextOnProminentButtonColor, color_scheme);
+      return SkColorSetA(color, gfx::kGoogleGreyAlpha700);
+    }
+    case NativeTheme::kColorId_NotificationColor:
+      return base_theme->GetUnprocessedSystemColor(
+          NativeTheme::kColorId_TextOnProminentButtonColor, color_scheme);
     case NativeTheme::kColorId_NotificationActionsRowBackground:
-    case NativeTheme::kColorId_NotificationBackgroundActive: {
-      const SkColor bg = base_theme->GetUnprocessedSystemColor(
-          NativeTheme::kColorId_WindowBackground, color_scheme);
-      return color_utils::BlendTowardMaxContrast(bg, 0x14);
-    }
-    case NativeTheme::kColorId_NotificationLargeImageBackground: {
-      const SkColor bg = base_theme->GetUnprocessedSystemColor(
-          NativeTheme::kColorId_WindowBackground, color_scheme);
-      return color_utils::BlendTowardMaxContrast(bg, 0x0C);
-    }
+    case NativeTheme::kColorId_NotificationBackgroundActive:
+    case NativeTheme::kColorId_NotificationLargeImageBackground:
+      return base_theme->GetUnprocessedSystemColor(
+          NativeTheme::kColorId_HighlightedMenuItemBackgroundColor,
+          color_scheme);
+    case NativeTheme::kColorId_MessageCenterSmallImageMaskBackground:
     case NativeTheme::kColorId_NotificationDefaultAccentColor:
-      return gfx::kGoogleGrey700;
+      return base_theme->GetUnprocessedSystemColor(
+          NativeTheme::kColorId_LabelSecondaryColor, color_scheme);
     case NativeTheme::kColorId_NotificationInkDropBase:
-      return gfx::kGoogleBlue600;
+      return base_theme->GetUnprocessedSystemColor(
+          NativeTheme::kColorId_ProminentButtonColor, color_scheme);
 
     // Scrollbar
     case NativeTheme::kColorId_OverlayScrollbarThumbFill:
     case NativeTheme::kColorId_OverlayScrollbarThumbHoveredFill: {
-      SkColor fill = base_theme->GetUnprocessedSystemColor(
+      const SkColor fill = base_theme->GetUnprocessedSystemColor(
           NativeTheme::kColorId_CustomTabBarForegroundColor, color_scheme);
-      fill = color_utils::IsDark(fill) ? SK_ColorBLACK : SK_ColorWHITE;
       const bool hovered =
           color_id == NativeTheme::kColorId_OverlayScrollbarThumbHoveredFill;
-      return SkColorSetA(fill, (hovered ? 0.7 : 0.5) * SK_AlphaOPAQUE);
+      return SkColorSetA(
+          fill, hovered ? gfx::kGoogleGreyAlpha800 : gfx::kGoogleGreyAlpha700);
     }
     case NativeTheme::kColorId_OverlayScrollbarThumbStroke:
     case NativeTheme::kColorId_OverlayScrollbarThumbHoveredStroke: {
-      SkColor stroke = base_theme->GetUnprocessedSystemColor(
+      const SkColor stroke = base_theme->GetUnprocessedSystemColor(
           NativeTheme::kColorId_CustomTabBarBackgroundColor, color_scheme);
-      stroke = color_utils::IsDark(stroke) ? SK_ColorBLACK : SK_ColorWHITE;
       const bool hovered =
           color_id == NativeTheme::kColorId_OverlayScrollbarThumbHoveredStroke;
-      return SkColorSetA(stroke, (hovered ? 0.5 : 0.3) * SK_AlphaOPAQUE);
+      return SkColorSetA(stroke, hovered ? gfx::kGoogleGreyAlpha500
+                                         : gfx::kGoogleGreyAlpha400);
     }
 
     // Separator
@@ -375,29 +376,37 @@
 
     // Slider
     case NativeTheme::kColorId_SliderThumbMinimal:
-      return SkColorSetA(gfx::kGoogleGrey100, gfx::kGoogleGreyAlpha500);
+      return base_theme->GetUnprocessedSystemColor(
+          NativeTheme::kColorId_LabelSecondaryColor, color_scheme);
     case NativeTheme::kColorId_SliderTroughMinimal:
-      return SkColorSetA(gfx::kGoogleGrey100, 0x19);
+      return base_theme->GetUnprocessedSystemColor(
+          NativeTheme::kColorId_SeparatorColor, color_scheme);
     case NativeTheme::kColorId_SliderThumbDefault:
-      return gfx::kGoogleBlueDark600;
-    case NativeTheme::kColorId_SliderTroughDefault:
-      return SkColorSetA(gfx::kGoogleBlueDark600, 0x40);
+      return base_theme->GetUnprocessedSystemColor(
+          NativeTheme::kColorId_ProminentButtonColor, color_scheme);
+    case NativeTheme::kColorId_SliderTroughDefault: {
+      const SkColor bg = base_theme->GetUnprocessedSystemColor(
+          NativeTheme::kColorId_WindowBackground, color_scheme);
+      const SkColor fg = base_theme->GetUnprocessedSystemColor(
+          NativeTheme::kColorId_ProminentButtonColor, color_scheme);
+      return color_utils::AlphaBlend(fg, bg, gfx::kGoogleGreyAlpha400);
+    }
 
     // Sync info container
     case NativeTheme::kColorId_SyncInfoContainerNoPrimaryAccount:
       return base_theme->GetUnprocessedSystemColor(
           NativeTheme::kColorId_HighlightedMenuItemBackgroundColor,
           color_scheme);
-    case NativeTheme::kColorId_SyncInfoContainerPaused:
-      return SkColorSetA(
-          base_theme->GetUnprocessedSystemColor(
-              NativeTheme::kColorId_ProminentButtonColor, color_scheme),
-          0x10);
-    case NativeTheme::kColorId_SyncInfoContainerError:
-      return SkColorSetA(
-          base_theme->GetUnprocessedSystemColor(
-              NativeTheme::kColorId_AlertSeverityHigh, color_scheme),
-          0x10);
+    case NativeTheme::kColorId_SyncInfoContainerPaused: {
+      const SkColor fg = base_theme->GetUnprocessedSystemColor(
+          NativeTheme::kColorId_ProminentButtonColor, color_scheme);
+      return SkColorSetA(fg, gfx::kGoogleGreyAlpha100);
+    }
+    case NativeTheme::kColorId_SyncInfoContainerError: {
+      const SkColor fg = base_theme->GetUnprocessedSystemColor(
+          NativeTheme::kColorId_AlertSeverityHigh, color_scheme);
+      return SkColorSetA(fg, gfx::kGoogleGreyAlpha100);
+    }
 
     // Tabbed pane
     case NativeTheme::kColorId_TabBottomBorder:
@@ -407,9 +416,9 @@
       return base_theme->GetUnprocessedSystemColor(
           NativeTheme::kColorId_LabelSecondaryColor, color_scheme);
     case NativeTheme::kColorId_TabHighlightBackground:
-      return gfx::kGoogleBlue050;
+      return SkColorSetA(gfx::kGoogleBlue300, 0x2B);
     case NativeTheme::kColorId_TabHighlightFocusedBackground:
-      return gfx::kGoogleBlue100;
+      return SkColorSetA(gfx::kGoogleBlue300, 0x53);
     case NativeTheme::kColorId_TabTitleColorActive:
     case NativeTheme::kColorId_TabSelectedBorderColor:
       return base_theme->GetUnprocessedSystemColor(
@@ -450,9 +459,11 @@
       return base_theme->GetUnprocessedSystemColor(
           NativeTheme::kColorId_LabelDisabledColor, color_scheme);
     case NativeTheme::kColorId_TextfieldDefaultColor:
-    case NativeTheme::kColorId_TextfieldSelectionColor:
       return base_theme->GetUnprocessedSystemColor(
           NativeTheme::kColorId_LabelEnabledColor, color_scheme);
+    case NativeTheme::kColorId_TextfieldSelectionColor:
+      return base_theme->GetUnprocessedSystemColor(
+          NativeTheme::kColorId_LabelTextSelectionColor, color_scheme);
     case NativeTheme::kColorId_TextfieldSelectionBackgroundFocused:
       return base_theme->GetUnprocessedSystemColor(
           NativeTheme::kColorId_LabelTextSelectionBackgroundFocused,
@@ -461,28 +472,26 @@
     // Throbber
     case NativeTheme::kColorId_ThrobberWaitingColor:
       return base_theme->GetUnprocessedSystemColor(
-          NativeTheme::kColorId_LabelTextSelectionBackgroundFocused,
-          color_scheme);
+          NativeTheme::kColorId_SliderTroughDefault, color_scheme);
     case NativeTheme::kColorId_ThrobberSpinningColor:
       return base_theme->GetUnprocessedSystemColor(
           NativeTheme::kColorId_ProminentButtonColor, color_scheme);
 
     // Toggle button
     case NativeTheme::kColorId_ToggleButtonShadowColor:
-      return SkColorSetA(
-          base_theme->GetUnprocessedSystemColor(
-              NativeTheme::kColorId_LabelEnabledColor, color_scheme),
-          0x99);
+      return base_theme->GetUnprocessedSystemColor(
+          NativeTheme::kColorId_SeparatorColor, color_scheme);
+    case ui::NativeTheme::kColorId_ToggleButtonThumbColorOff:
+      return base_theme->GetUnprocessedSystemColor(
+          NativeTheme::kColorId_LabelSecondaryColor, color_scheme);
+    case ui::NativeTheme::kColorId_ToggleButtonThumbColorOn:
+      return base_theme->GetUnprocessedSystemColor(
+          NativeTheme::kColorId_ProminentButtonColor, color_scheme);
     case ui::NativeTheme::kColorId_ToggleButtonTrackColorOff:
-    case ui::NativeTheme::kColorId_ToggleButtonTrackColorOn: {
-      const ui::NativeTheme::ColorId base_color_id =
-          color_id == ui::NativeTheme::kColorId_ToggleButtonTrackColorOff
-              ? ui::NativeTheme::kColorId_LabelEnabledColor
-              : ui::NativeTheme::kColorId_ProminentButtonColor;
-      return SkColorSetA(
-          base_theme->GetUnprocessedSystemColor(base_color_id, color_scheme),
-          0x66);
-    }
+      return base_theme->GetUnprocessedSystemColor(
+          NativeTheme::kColorId_SeparatorColor, color_scheme);
+    case ui::NativeTheme::kColorId_ToggleButtonTrackColorOn:
+      return gfx::kGoogleBlue300;
 
     // Tooltip
     case NativeTheme::kColorId_TooltipBackground: {
@@ -490,24 +499,31 @@
           NativeTheme::kColorId_WindowBackground, color_scheme);
       return SkColorSetA(bg, 0xCC);
     }
-    case NativeTheme::kColorId_TooltipIcon:
-      return SkColorSetA(gfx::kGoogleGrey800, 0xBD);
     case NativeTheme::kColorId_TooltipText: {
       const SkColor text = base_theme->GetUnprocessedSystemColor(
           NativeTheme::kColorId_LabelEnabledColor, color_scheme);
       return SkColorSetA(text, 0xDE);
     }
+
+    // Tooltip icon
+    case NativeTheme::kColorId_TooltipIcon:
+      return base_theme->GetUnprocessedSystemColor(
+          NativeTheme::kColorId_LabelSecondaryColor, color_scheme);
     case NativeTheme::kColorId_TooltipIconHovered:
-      return SkColorSetA(SK_ColorBLACK, 0xBD);
+      return base_theme->GetUnprocessedSystemColor(
+          NativeTheme::kColorId_LabelEnabledColor, color_scheme);
 
     // Tree
     case NativeTheme::kColorId_TreeBackground:
       return base_theme->GetUnprocessedSystemColor(
           NativeTheme::kColorId_WindowBackground, color_scheme);
     case NativeTheme::kColorId_TreeSelectionBackgroundFocused:
+      return base_theme->GetUnprocessedSystemColor(
+          NativeTheme::kColorId_TableSelectionBackgroundFocused, color_scheme);
     case NativeTheme::kColorId_TreeSelectionBackgroundUnfocused:
       return base_theme->GetUnprocessedSystemColor(
-          NativeTheme::kColorId_DropdownSelectedBackgroundColor, color_scheme);
+          NativeTheme::kColorId_TableSelectionBackgroundUnfocused,
+          color_scheme);
     case NativeTheme::kColorId_TreeSelectedText:
     case NativeTheme::kColorId_TreeSelectedTextUnfocused:
     case NativeTheme::kColorId_TreeText:
diff --git a/ui/native_theme/native_theme.cc b/ui/native_theme/native_theme.cc
index 3396f06..e56022d 100644
--- a/ui/native_theme/native_theme.cc
+++ b/ui/native_theme/native_theme.cc
@@ -37,31 +37,54 @@
         {NTCID::kColorId_AvatarIconGuest, kColorAvatarIconGuest},
         {NTCID::kColorId_AvatarIconIncognito, kColorAvatarIconIncognito},
         {NTCID::kColorId_BubbleBackground, kColorBubbleBackground},
+        {NTCID::kColorId_BubbleBorder, kColorBubbleBorder},
         {NTCID::kColorId_BubbleFooterBackground,
           kColorBubbleFooterBackground},
-        {NTCID::kColorId_ButtonColor, kColorButtonBackground},
         {NTCID::kColorId_ButtonBorderColor, kColorButtonBorder},
-        {NTCID::kColorId_DisabledButtonBorderColor, kColorButtonBorderDisabled},
+        {NTCID::kColorId_ButtonCheckedColor, kColorButtonForegroundChecked},
+        {NTCID::kColorId_ButtonColor, kColorButtonBackground},
         {NTCID::kColorId_ButtonDisabledColor,
           kColorButtonForegroundDisabled},
         {NTCID::kColorId_ButtonEnabledColor, kColorButtonForeground},
-        {NTCID::kColorId_ProminentButtonColor,
-          kColorButtonBackgroundProminent},
-        {NTCID::kColorId_ProminentButtonDisabledColor,
-          kColorButtonBackgroundProminentDisabled},
-        {NTCID::kColorId_ProminentButtonFocusedColor,
-          kColorButtonBackgroundProminentFocused},
-        {NTCID::kColorId_TextOnProminentButtonColor,
-          kColorButtonForegroundProminent},
         {NTCID::kColorId_ButtonUncheckedColor,
           kColorButtonForegroundUnchecked},
+        {NTCID::kColorId_CustomFrameActiveColor, kColorFrameActive},
+        {NTCID::kColorId_CustomFrameInactiveColor, kColorFrameInactive},
+        {NTCID::kColorId_CustomTabBarBackgroundColor,
+          kColorPwaToolbarBackground},
+        {NTCID::kColorId_CustomTabBarForegroundColor,
+          kColorPwaToolbarForeground},
+        {NTCID::kColorId_CustomTabBarSecurityChipDangerousColor,
+          kColorPwaSecurityChipForegroundDangerous},
+        {NTCID::kColorId_CustomTabBarSecurityChipDefaultColor,
+          kColorPwaSecurityChipForeground},
+        {NTCID::kColorId_CustomTabBarSecurityChipSecureColor,
+          kColorPwaSecurityChipForegroundSecure},
+        {NTCID::kColorId_CustomTabBarSecurityChipWithCertColor,
+          kColorPwaSecurityChipForegroundPolicyCert},
+        {NTCID::kColorId_DefaultIconColor, kColorIcon},
         {NTCID::kColorId_DialogBackground, kColorDialogBackground},
         {NTCID::kColorId_DialogForeground, kColorDialogForeground},
+        {NTCID::kColorId_DisabledButtonBorderColor, kColorButtonBorderDisabled},
+        {NTCID::kColorId_DisabledIconColor, kColorIconDisabled},
+        {NTCID::kColorId_DisabledMenuItemForegroundColor,
+          kColorMenuItemForegroundDisabled},
+        {NTCID::kColorId_DropdownBackgroundColor, kColorDropdownBackground},
+        {NTCID::kColorId_DropdownForegroundColor, kColorDropdownForeground},
+        {NTCID::kColorId_DropdownSelectedBackgroundColor,
+          kColorDropdownBackgroundSelected},
+        {NTCID::kColorId_DropdownSelectedForegroundColor,
+          kColorDropdownForegroundSelected},
+        {NTCID::kColorId_EnabledMenuItemForegroundColor,
+          kColorMenuItemForeground},
         {NTCID::kColorId_FocusedBorderColor, kColorFocusableBorderFocused},
-        {NTCID::kColorId_UnfocusedBorderColor,
-          kColorFocusableBorderUnfocused},
-        {NTCID::kColorId_MenuIconColor, kColorMenuIcon},
-        {NTCID::kColorId_DefaultIconColor, kColorIcon},
+        {NTCID::kColorId_FocusedMenuItemBackgroundColor,
+          kColorMenuItemBackgroundSelected},
+        {NTCID::kColorId_FootnoteContainerBorder, kColorBubbleFooterBorder},
+        {NTCID::kColorId_HighlightedMenuItemBackgroundColor,
+          kColorMenuItemBackgroundHighlighted},
+        {NTCID::kColorId_HighlightedMenuItemForegroundColor,
+          kColorMenuItemForegroundHighlighted},
         {NTCID::kColorId_LabelDisabledColor, kColorLabelForegroundDisabled},
         {NTCID::kColorId_LabelEnabledColor, kColorLabelForeground},
         {NTCID::kColorId_LabelSecondaryColor,
@@ -75,76 +98,123 @@
         {NTCID::kColorId_LinkPressed, kColorLinkForegroundPressed},
         {NTCID::kColorId_MenuBackgroundColor, kColorMenuBackground},
         {NTCID::kColorId_MenuBorderColor, kColorMenuBorder},
+        {NTCID::kColorId_MenuDropIndicator, kColorMenuDropmarker},
+        {NTCID::kColorId_MenuIconColor, kColorMenuIcon},
         {NTCID::kColorId_MenuItemInitialAlertBackgroundColor,
           kColorMenuItemBackgroundAlertedInitial},
-        {NTCID::kColorId_MenuItemTargetAlertBackgroundColor,
-          kColorMenuItemBackgroundAlertedTarget},
-        {NTCID::kColorId_DisabledMenuItemForegroundColor,
-          kColorMenuItemForegroundDisabled},
-        {NTCID::kColorId_EnabledMenuItemForegroundColor,
-          kColorMenuItemForeground},
-        {NTCID::kColorId_HighlightedMenuItemBackgroundColor,
-          kColorMenuItemBackgroundHighlighted},
-        {NTCID::kColorId_HighlightedMenuItemForegroundColor,
-          kColorMenuItemForegroundHighlighted},
         {NTCID::kColorId_MenuItemMinorTextColor,
           kColorMenuItemForegroundSecondary},
-        {NTCID::kColorId_FocusedMenuItemBackgroundColor,
-          kColorMenuItemBackgroundSelected},
+        {NTCID::kColorId_MenuItemTargetAlertBackgroundColor,
+          kColorMenuItemBackgroundAlertedTarget},
+        {NTCID::kColorId_MenuSeparatorColor, kColorMenuSeparator},
+        {NTCID::kColorId_MessageCenterSmallImageMaskBackground,
+          kColorNotificationIconBackground},
+        {NTCID::kColorId_MessageCenterSmallImageMaskForeground,
+          kColorNotificationIconForeground},
+        {NTCID::kColorId_NotificationActionsRowBackground,
+          kColorNotificationActionsBackground},
+        {NTCID::kColorId_NotificationBackground,
+          kColorNotificationBackgroundInactive},
+        {NTCID::kColorId_NotificationBackgroundActive,
+          kColorNotificationBackgroundActive},
+        {NTCID::kColorId_NotificationColor, kColorNotificationInputForeground},
+        {NTCID::kColorId_NotificationDefaultAccentColor,
+          kColorNotificationHeaderForeground},
+        {NTCID::kColorId_NotificationInkDropBase,
+          kColorNotificationInputBackground},
+        {NTCID::kColorId_NotificationLargeImageBackground,
+          kColorNotificationImageBackground},
+        {NTCID::kColorId_NotificationPlaceholderColor,
+          kColorNotificationInputPlaceholderForeground},
+        {NTCID::kColorId_OverlayScrollbarThumbFill, kColorOverlayScrollbarFill},
+        {NTCID::kColorId_OverlayScrollbarThumbHoveredFill,
+          kColorOverlayScrollbarFillHovered},
+        {NTCID::kColorId_OverlayScrollbarThumbHoveredStroke,
+          kColorOverlayScrollbarStrokeHovered},
+        {NTCID::kColorId_OverlayScrollbarThumbStroke,
+          kColorOverlayScrollbarStroke},
+        {NTCID::kColorId_ProminentButtonColor,
+          kColorButtonBackgroundProminent},
+        {NTCID::kColorId_ProminentButtonDisabledColor,
+          kColorButtonBackgroundProminentDisabled},
+        {NTCID::kColorId_ProminentButtonFocusedColor,
+          kColorButtonBackgroundProminentFocused},
         {NTCID::kColorId_SelectedMenuItemForegroundColor,
           kColorMenuItemForegroundSelected},
-        {NTCID::kColorId_MenuSeparatorColor, kColorMenuSeparator},
+        {NTCID::kColorId_SeparatorColor, kColorSeparator},
+        {NTCID::kColorId_SliderThumbDefault, kColorSliderThumb},
+        {NTCID::kColorId_SliderThumbMinimal, kColorSliderThumbMinimal},
+        {NTCID::kColorId_SliderTroughDefault, kColorSliderTrack},
+        {NTCID::kColorId_SliderTroughMinimal, kColorSliderTrackMinimal},
+        {NTCID::kColorId_SyncInfoContainerError, kColorSyncInfoBackgroundError},
+        {NTCID::kColorId_SyncInfoContainerNoPrimaryAccount,
+          kColorSyncInfoBackground},
+        {NTCID::kColorId_SyncInfoContainerPaused,
+          kColorSyncInfoBackgroundPaused},
         {NTCID::kColorId_TabBottomBorder, kColorTabContentSeparator},
-        {NTCID::kColorId_TabTitleColorInactive, kColorTabForeground},
-        {NTCID::kColorId_TabSelectedBorderColor, kColorTabBorderSelected},
-        {NTCID::kColorId_TabTitleColorActive, kColorTabForegroundSelected},
+        {NTCID::kColorId_TabHighlightBackground,
+          kColorTabBackgroundHighlighted},
+        {NTCID::kColorId_TabHighlightFocusedBackground,
+          kColorTabBackgroundHighlightedFocused},
         {NTCID::kColorId_TableBackground, kColorTableBackground},
 #if defined(OS_APPLE)
         {NTCID::kColorId_TableBackgroundAlternate,
           kColorTableBackgroundAlternate},
 #endif
-        {NTCID::kColorId_TableText, kColorTableForeground},
         {NTCID::kColorId_TableGroupingIndicatorColor,
           kColorTableGroupingIndicator},
-        {NTCID::kColorId_TableHeaderBackground,
-          kColorTableHeaderBackground},
+        {NTCID::kColorId_TableHeaderBackground, kColorTableHeaderBackground},
+        {NTCID::kColorId_TableHeaderSeparator, kColorTableHeaderSeparator},
         {NTCID::kColorId_TableHeaderText, kColorTableHeaderForeground},
-        // TODO(http://crbug.com/1057754): kColorId_TableHeaderSeparator,
-        // which is implemented as a native theme override on Mac.
-        {NTCID::kColorId_TableSelectionBackgroundFocused,
-          kColorTableBackgroundSelectedFocused},
         {NTCID::kColorId_TableSelectedText,
           kColorTableForegroundSelectedFocused},
-        {NTCID::kColorId_TableSelectionBackgroundUnfocused,
-          kColorTableBackgroundSelectedUnfocused},
         {NTCID::kColorId_TableSelectedTextUnfocused,
           kColorTableForegroundSelectedUnfocused},
+        {NTCID::kColorId_TableSelectionBackgroundFocused,
+          kColorTableBackgroundSelectedFocused},
+        {NTCID::kColorId_TableSelectionBackgroundUnfocused,
+          kColorTableBackgroundSelectedUnfocused},
+        {NTCID::kColorId_TableText, kColorTableForeground},
+        {NTCID::kColorId_TabSelectedBorderColor, kColorTabBorderSelected},
+        {NTCID::kColorId_TabTitleColorActive, kColorTabForegroundSelected},
+        {NTCID::kColorId_TabTitleColorInactive, kColorTabForeground},
         {NTCID::kColorId_TextfieldDefaultBackground,
           kColorTextfieldBackground},
+        {NTCID::kColorId_TextfieldDefaultColor, kColorTextfieldForeground},
+        {NTCID::kColorId_TextfieldPlaceholderColor,
+          kColorTextfieldForegroundPlaceholder},
         {NTCID::kColorId_TextfieldReadOnlyBackground,
           kColorTextfieldBackgroundDisabled},
         {NTCID::kColorId_TextfieldReadOnlyColor,
           kColorTextfieldForegroundDisabled},
-        {NTCID::kColorId_TextfieldPlaceholderColor,
-          kColorTextfieldForegroundPlaceholder},
-        {NTCID::kColorId_TextfieldDefaultColor, kColorTextfieldForeground},
         {NTCID::kColorId_TextfieldSelectionBackgroundFocused,
           kColorTextfieldSelectionBackground},
         {NTCID::kColorId_TextfieldSelectionColor,
           kColorTextfieldSelectionForeground},
+        {NTCID::kColorId_TextOnProminentButtonColor,
+          kColorButtonForegroundProminent},
         {NTCID::kColorId_ThrobberSpinningColor, kColorThrobber},
+        {NTCID::kColorId_ThrobberWaitingColor, kColorThrobberPreconnect},
+        {NTCID::kColorId_ToggleButtonShadowColor, kColorToggleButtonShadow},
+        {NTCID::kColorId_ToggleButtonThumbColorOff, kColorToggleButtonThumbOff},
+        {NTCID::kColorId_ToggleButtonThumbColorOn, kColorToggleButtonThumbOn},
+        {NTCID::kColorId_ToggleButtonTrackColorOff, kColorToggleButtonTrackOff},
+        {NTCID::kColorId_ToggleButtonTrackColorOn, kColorToggleButtonTrackOn},
         {NTCID::kColorId_TooltipBackground, kColorTooltipBackground},
+        {NTCID::kColorId_TooltipIcon, kColorHelpIconInactive},
+        {NTCID::kColorId_TooltipIconHovered, kColorHelpIconActive},
         {NTCID::kColorId_TooltipText, kColorTooltipForeground},
         {NTCID::kColorId_TreeBackground, kColorTreeBackground},
-        {NTCID::kColorId_TreeText, kColorTreeNodeForeground},
-        {NTCID::kColorId_TreeSelectionBackgroundFocused,
-          kColorTreeNodeBackgroundSelectedFocused},
         {NTCID::kColorId_TreeSelectedText,
           kColorTreeNodeForegroundSelectedFocused},
-        {NTCID::kColorId_TreeSelectionBackgroundUnfocused,
-          kColorTreeNodeBackgroundSelectedUnfocused},
         {NTCID::kColorId_TreeSelectedTextUnfocused,
           kColorTreeNodeForegroundSelectedUnfocused},
+        {NTCID::kColorId_TreeSelectionBackgroundFocused,
+          kColorTreeNodeBackgroundSelectedFocused},
+        {NTCID::kColorId_TreeSelectionBackgroundUnfocused,
+          kColorTreeNodeBackgroundSelectedUnfocused},
+        {NTCID::kColorId_TreeText, kColorTreeNodeForeground},
+        {NTCID::kColorId_UnfocusedBorderColor, kColorFocusableBorderUnfocused},
         {NTCID::kColorId_WindowBackground, kColorWindowBackground},
       });
   auto* color_it = map.find(native_theme_color_id);
diff --git a/ui/native_theme/native_theme_color_id.h b/ui/native_theme/native_theme_color_id.h
index 3117e88..6019757 100644
--- a/ui/native_theme/native_theme_color_id.h
+++ b/ui/native_theme/native_theme_color_id.h
@@ -38,6 +38,8 @@
   OP(kColorId_TextOnProminentButtonColor),                                     \
   /* ToggleButton */                                                           \
   OP(kColorId_ToggleButtonShadowColor),                                        \
+  OP(kColorId_ToggleButtonThumbColorOff),                                      \
+  OP(kColorId_ToggleButtonThumbColorOn),                                       \
   OP(kColorId_ToggleButtonTrackColorOff),                                      \
   OP(kColorId_ToggleButtonTrackColorOn),                                       \
   /* MenuItem */                                                               \
diff --git a/ui/views/accessibility/ax_window_obj_wrapper.cc b/ui/views/accessibility/ax_window_obj_wrapper.cc
index 869e5ae7..d58593a 100644
--- a/ui/views/accessibility/ax_window_obj_wrapper.cc
+++ b/ui/views/accessibility/ax_window_obj_wrapper.cc
@@ -145,25 +145,27 @@
   out_node_data->relative_bounds.bounds =
       gfx::RectF(window_->GetBoundsInScreen());
   std::string* child_ax_tree_id_ptr = window_->GetProperty(ui::kChildAXTreeID);
-  if (child_ax_tree_id_ptr && ui::AXTreeID::FromString(*child_ax_tree_id_ptr) !=
-                                  ui::AXTreeIDUnknown()) {
-    // Most often, child AX trees are parented to Views. We need to handle
-    // the case where they're not here, but we don't want the same AX tree
-    // to be a child of two different parents.
-    //
-    // To avoid this double-parenting, only add the child tree ID of this
-    // window if the top-level window doesn't have an associated Widget.
-    //
-    // Also, if this window is not visible, its child tree should also be
-    // non-visible so prune it.
-    if (!window_->GetToplevelWindow() ||
-        GetWidgetForWindow(window_->GetToplevelWindow()) ||
-        !window_->IsVisible()) {
-      return;
-    }
+  if (child_ax_tree_id_ptr) {
+    ui::AXTreeID child_ax_tree_id =
+        ui::AXTreeID::FromString(*child_ax_tree_id_ptr);
+    if (child_ax_tree_id != ui::AXTreeIDUnknown()) {
+      // Most often, child AX trees are parented to Views. We need to handle
+      // the case where they're not here, but we don't want the same AX tree
+      // to be a child of two different parents.
+      //
+      // To avoid this double-parenting, only add the child tree ID of this
+      // window if the top-level window doesn't have an associated Widget.
+      //
+      // Also, if this window is not visible, its child tree should also be
+      // non-visible so prune it.
+      if (!window_->GetToplevelWindow() ||
+          GetWidgetForWindow(window_->GetToplevelWindow()) ||
+          !window_->IsVisible()) {
+        return;
+      }
 
-    out_node_data->AddStringAttribute(ax::mojom::StringAttribute::kChildTreeId,
-                                      *child_ax_tree_id_ptr);
+      out_node_data->AddChildTreeId(child_ax_tree_id);
+    }
   }
 
   out_node_data->AddStringAttribute(ax::mojom::StringAttribute::kClassName,
diff --git a/ui/views/accessibility/view_accessibility.cc b/ui/views/accessibility/view_accessibility.cc
index 5fd22a8..8cfb389a 100644
--- a/ui/views/accessibility/view_accessibility.cc
+++ b/ui/views/accessibility/view_accessibility.cc
@@ -276,8 +276,7 @@
       << "Please annotate child tree ids using "
          "ViewAccessibility::OverrideChildTreeID.";
   if (child_tree_id_) {
-    data->AddStringAttribute(ax::mojom::StringAttribute::kChildTreeId,
-                             child_tree_id_->ToString());
+    data->AddChildTreeId(child_tree_id_.value());
   }
 }
 
diff --git a/ui/views/controls/button/toggle_button.cc b/ui/views/controls/button/toggle_button.cc
index e64e389..82a636b 100644
--- a/ui/views/controls/button/toggle_button.cc
+++ b/ui/views/controls/button/toggle_button.cc
@@ -92,10 +92,12 @@
     cc::PaintFlags thumb_flags;
     thumb_flags.setLooper(gfx::CreateShadowDrawLooper(shadows));
     thumb_flags.setAntiAlias(true);
-    const SkColor thumb_on_color = thumb_on_color_.value_or(
-        theme->GetSystemColor(ui::NativeTheme::kColorId_ProminentButtonColor));
-    const SkColor thumb_off_color = thumb_off_color_.value_or(
-        theme->GetSystemColor(ui::NativeTheme::kColorId_ButtonColor));
+    const SkColor thumb_on_color =
+        thumb_on_color_.value_or(theme->GetSystemColor(
+            ui::NativeTheme::kColorId_ToggleButtonThumbColorOn));
+    const SkColor thumb_off_color =
+        thumb_off_color_.value_or(theme->GetSystemColor(
+            ui::NativeTheme::kColorId_ToggleButtonThumbColorOff));
     thumb_flags.setColor(
         color_utils::AlphaBlend(thumb_on_color, thumb_off_color, color_ratio_));
 
diff --git a/ui/webui/resources/cr_components/chromeos/cellular_setup/activation_code_page.html b/ui/webui/resources/cr_components/chromeos/cellular_setup/activation_code_page.html
index 85b5832..747a4f1d 100644
--- a/ui/webui/resources/cr_components/chromeos/cellular_setup/activation_code_page.html
+++ b/ui/webui/resources/cr_components/chromeos/cellular_setup/activation_code_page.html
@@ -76,6 +76,14 @@
         cursor: default;
       }
 
+      .scan-error-header {
+        --iron-icon-fill-color: #D93025;
+      }
+
+      .scan-error-message {
+        color: var(--google-red-600);
+      }
+
       .blue-icon {
         --iron-icon-fill-color: #1A73E8;
       }
@@ -91,14 +99,9 @@
       }
 
       #scanFailureHeader {
-        --iron-icon-fill-color: #D93025;
         margin-bottom: 4px;
       }
 
-      #scanFailureMessage {
-        color: var(--google-red-600);
-      }
-
       #useCameraAgainButton {
         display: block;
         font-weight: 500;
@@ -177,12 +180,20 @@
               <div>
                 <div id="scanSuccessContainer"
                     hidden$="[[isUiElementHidden_(UiElement.SCAN_SUCCESS, state_)]]">
-                  <div id=scanSucessHeader>
+                  <div id=scanSucessHeader
+                      hidden$="[[isUiElementHidden_(UiElement.CODE_DETECTED, state_)]]">
                     <iron-icon class="scan-finish-image" icon="cellular-setup:checked"></iron-icon>
                     <span class="label scan-finish-message" id="scanSuccessMessage">
                       [[i18n('scanQRCodeSuccess')]]
                     </span>
                   </div>
+                  <div id="scanInstallFailureHeader" class="scan-error-header"
+                      hidden$="[[isUiElementHidden_(UiElement.SCAN_INSTALL_FAILURE, state_)]]">
+                    <iron-icon class="scan-finish-image" icon="cellular-setup:error"></iron-icon>
+                    <span class="label scan-finish-message scan-error-message">
+                      [[i18n('scanQrCodeInvalid')]]
+                    </span>
+                  </div>
                   <cr-button id="useCameraAgainButton" class="blue-icon"
                       on-click="startScanning_"
                       disabled="[[isUiElementDisabled_(UiElement.SCAN_SUCCESS, state_, showBusy)]]">
@@ -192,9 +203,9 @@
                 </div>
                 <div id="scanFailureContainer"
                     hidden$="[[isUiElementHidden_(UiElement.SCAN_FAILURE, state_)]]">
-                  <div id="scanFailureHeader" class="blue-icon">
+                  <div id="scanFailureHeader" class="scan-error-header">
                     <iron-icon class="scan-finish-image" icon="cellular-setup:error"></iron-icon>
-                    <span class="label scan-finish-message" id="scanFailureMessage">
+                    <span class="label scan-finish-message scan-error-message">
                       [[i18n('scanQrCodeError')]]
                     </span>
                   </div>
@@ -215,7 +226,7 @@
             value="{{activationCode}}"
             disabled="[[showBusy]]"
             on-keydown="onKeyDown_"
-            invalid="[[showError]]"
+            invalid="[[shouldActivationCodeInputBeInvalid_(state_)]]"
             error-message="[[i18n('scanQrCodeInvalid')]]">
           </cr-input>
           <paper-spinner-lite active
diff --git a/ui/webui/resources/cr_components/chromeos/cellular_setup/activation_code_page.js b/ui/webui/resources/cr_components/chromeos/cellular_setup/activation_code_page.js
index 0dfb6bb..86a0df9 100644
--- a/ui/webui/resources/cr_components/chromeos/cellular_setup/activation_code_page.js
+++ b/ui/webui/resources/cr_components/chromeos/cellular_setup/activation_code_page.js
@@ -18,6 +18,7 @@
   SCANNING_SUCCESS: 6,
   SCANNING_FAILURE: 7,
   MANUAL_ENTRY_INSTALL_FAILURE: 8,
+  SCANNING_INSTALL_FAILURE: 9,
 };
 
 /** @enum {number} */
@@ -28,6 +29,8 @@
   SCAN_FINISH: 4,
   SCAN_SUCCESS: 5,
   SCAN_FAILURE: 6,
+  CODE_DETECTED: 7,
+  SCAN_INSTALL_FAILURE: 8,
 };
 
 /**
@@ -395,18 +398,22 @@
 
   /** @private */
   onShowErrorChanged_() {
-    // TODO(crbug.com/1093185) Handle install failure from scanning.
     if (this.showError) {
-      this.state_ = PageState.MANUAL_ENTRY_INSTALL_FAILURE;
-      Polymer.RenderStatus.afterNextRender(this, () => {
-        cr.ui.focusWithoutInk(this.$.activationCode);
-      });
+      if (this.state_ === PageState.MANUAL_ENTRY) {
+        this.state_ = PageState.MANUAL_ENTRY_INSTALL_FAILURE;
+        Polymer.RenderStatus.afterNextRender(this, () => {
+          cr.ui.focusWithoutInk(this.$.activationCode);
+        });
+      } else if (this.state_ === PageState.SCANNING_SUCCESS) {
+        this.state_ = PageState.SCANNING_INSTALL_FAILURE;
+      }
     }
   },
 
   /** @private */
   onStateChanged_() {
-    if (this.state_ !== PageState.MANUAL_ENTRY_INSTALL_FAILURE) {
+    if (this.state_ !== PageState.MANUAL_ENTRY_INSTALL_FAILURE &&
+        this.state_ !== PageState.SCANNING_INSTALL_FAILURE) {
       this.showError = false;
     }
     if (this.state_ === PageState.MANUAL_ENTRY) {
@@ -451,11 +458,17 @@
         return !(isScanning && this.cameraCount_ > 1);
       case UiElement.SCAN_FINISH:
         return state !== PageState.SCANNING_SUCCESS &&
-            state !== PageState.SCANNING_FAILURE;
+            state !== PageState.SCANNING_FAILURE &&
+            state !== PageState.SCANNING_INSTALL_FAILURE;
       case UiElement.SCAN_SUCCESS:
-        return state !== PageState.SCANNING_SUCCESS;
+        return state !== PageState.SCANNING_SUCCESS &&
+            state !== PageState.SCANNING_INSTALL_FAILURE;
       case UiElement.SCAN_FAILURE:
         return state !== PageState.SCANNING_FAILURE;
+      case UiElement.CODE_DETECTED:
+        return state !== PageState.SCANNING_SUCCESS;
+      case UiElement.SCAN_INSTALL_FAILURE:
+        return state !== PageState.SCANNING_INSTALL_FAILURE;
     }
   },
 
@@ -489,4 +502,13 @@
     return this.showNoProfilesMessage ? this.i18n('scanQRCodeNoProfiles') :
                                         this.i18n('scanQRCode');
   },
+
+  /**
+   * @param {PageState} state
+   * @return {boolean}
+   * @private
+   */
+  shouldActivationCodeInputBeInvalid_(state) {
+    return state === PageState.MANUAL_ENTRY_INSTALL_FAILURE;
+  }
 });
diff --git a/weblayer/browser/background_fetch/background_fetch_download.cc b/weblayer/browser/background_fetch/background_fetch_download.cc
index c2a5937..c4ef437 100644
--- a/weblayer/browser/background_fetch/background_fetch_download.cc
+++ b/weblayer/browser/background_fetch/background_fetch_download.cc
@@ -6,6 +6,7 @@
 
 #include "base/strings/utf_string_conversions.h"
 #include "content/public/browser/background_fetch_description.h"
+#include "url/origin.h"
 #include "weblayer/browser/background_fetch/background_fetch_delegate_impl.h"
 #include "weblayer/browser/background_fetch/job_details.h"
 
@@ -92,4 +93,12 @@
   return true;
 }
 
+GURL BackgroundFetchDownload::GetSourceUrl() {
+  return job_->fetch_description->origin.GetURL();
+}
+
+const SkBitmap* BackgroundFetchDownload::GetLargeIcon() {
+  return &job_->fetch_description->icon;
+}
+
 }  // namespace weblayer
diff --git a/weblayer/browser/background_fetch/background_fetch_download.h b/weblayer/browser/background_fetch/background_fetch_download.h
index 69c69d7..6fc7506 100644
--- a/weblayer/browser/background_fetch/background_fetch_download.h
+++ b/weblayer/browser/background_fetch/background_fetch_download.h
@@ -40,6 +40,8 @@
   // DownloadImpl:
   int GetNotificationId() override;
   bool IsTransient() override;
+  GURL GetSourceUrl() override;
+  const SkBitmap* GetLargeIcon() override;
 
  private:
   BackgroundFetchDelegateImpl* controller_;
diff --git a/weblayer/browser/download_callback_proxy.cc b/weblayer/browser/download_callback_proxy.cc
index 6574412..0b9effd 100644
--- a/weblayer/browser/download_callback_proxy.cc
+++ b/weblayer/browser/download_callback_proxy.cc
@@ -5,6 +5,7 @@
 #include "weblayer/browser/download_callback_proxy.h"
 
 #include "base/android/jni_string.h"
+#include "url/android/gurl_android.h"
 #include "url/gurl.h"
 #include "weblayer/browser/download_impl.h"
 #include "weblayer/browser/java/jni/DownloadCallbackProxy_jni.h"
@@ -78,7 +79,8 @@
   JNIEnv* env = AttachCurrentThread();
   Java_DownloadCallbackProxy_createDownload(
       env, java_delegate_, reinterpret_cast<jlong>(download_impl),
-      download_impl->GetNotificationId());
+      download_impl->GetNotificationId(), download_impl->IsTransient(),
+      url::GURLAndroid::FromNativeGURL(env, download_impl->GetSourceUrl()));
   Java_DownloadCallbackProxy_downloadStarted(env, java_delegate_,
                                              download_impl->java_download());
 }
diff --git a/weblayer/browser/download_impl.cc b/weblayer/browser/download_impl.cc
index f30720816..c785111 100644
--- a/weblayer/browser/download_impl.cc
+++ b/weblayer/browser/download_impl.cc
@@ -12,6 +12,7 @@
 
 #if defined(OS_ANDROID)
 #include "base/android/jni_string.h"
+#include "ui/gfx/android/java_bitmap.h"
 #include "weblayer/browser/java/jni/DownloadImpl_jni.h"
 #endif
 
@@ -51,6 +52,17 @@
   return base::android::ScopedJavaLocalRef<jstring>(
       base::android::ConvertUTF8ToJavaString(env, GetMimeType()));
 }
+
+base::android::ScopedJavaLocalRef<jobject> DownloadImpl::GetLargeIconImpl(
+    JNIEnv* env) {
+  base::android::ScopedJavaLocalRef<jobject> j_icon;
+  const SkBitmap* icon = GetLargeIcon();
+
+  if (icon && !icon->drawsNothing())
+    j_icon = gfx::ConvertToJavaBitmap(*icon);
+
+  return j_icon;
+}
 #endif
 
 DownloadImpl::DownloadImpl() = default;
diff --git a/weblayer/browser/download_impl.h b/weblayer/browser/download_impl.h
index 7df2af2..ca92f08 100644
--- a/weblayer/browser/download_impl.h
+++ b/weblayer/browser/download_impl.h
@@ -9,12 +9,15 @@
 #include "base/memory/weak_ptr.h"
 #include "base/supports_user_data.h"
 #include "build/build_config.h"
+#include "url/gurl.h"
 #include "weblayer/public/download.h"
 
 #if defined(OS_ANDROID)
 #include "base/android/scoped_java_ref.h"
 #endif
 
+class SkBitmap;
+
 namespace weblayer {
 
 // Base class for downloads that should be represented in the UI.
@@ -39,7 +42,7 @@
       JNIEnv* env);
   base::android::ScopedJavaLocalRef<jstring> GetMimeTypeImpl(JNIEnv* env);
   int GetErrorImpl(JNIEnv* env) { return static_cast<int>(GetError()); }
-  bool IsTransientImpl(JNIEnv* env) { return IsTransient(); }
+  base::android::ScopedJavaLocalRef<jobject> GetLargeIconImpl(JNIEnv* env);
 
   base::android::ScopedJavaGlobalRef<jobject> java_download() {
     return java_download_;
@@ -50,10 +53,17 @@
   // unique across all DownloadImpls.
   virtual int GetNotificationId() = 0;
 
-  // A transient download is not persisted to disk, which will affect its UI
+  // A transient download is not persisted to disk, which affects its UI
   // treatment.
   virtual bool IsTransient() = 0;
 
+  // Returns the originating URL for this download.
+  virtual GURL GetSourceUrl() = 0;
+
+  // Gets the icon to display. If the return value is null or draws nothing, no
+  // icon will be displayed.
+  virtual const SkBitmap* GetLargeIcon() = 0;
+
   // Returns whether this download has been added to the UI via
   // DownloadDelegate::OnDownloadStarted.
   bool HasBeenAddedToUi();
diff --git a/weblayer/browser/java/org/chromium/weblayer_private/DownloadCallbackProxy.java b/weblayer/browser/java/org/chromium/weblayer_private/DownloadCallbackProxy.java
index 347a551..9235cc26 100644
--- a/weblayer/browser/java/org/chromium/weblayer_private/DownloadCallbackProxy.java
+++ b/weblayer/browser/java/org/chromium/weblayer_private/DownloadCallbackProxy.java
@@ -14,6 +14,7 @@
 import org.chromium.base.annotations.JNINamespace;
 import org.chromium.base.annotations.NativeMethods;
 import org.chromium.ui.base.WindowAndroid;
+import org.chromium.url.GURL;
 import org.chromium.weblayer_private.interfaces.IDownloadCallbackClient;
 import org.chromium.weblayer_private.interfaces.ObjectWrapper;
 
@@ -99,9 +100,10 @@
     }
 
     @CalledByNative
-    private DownloadImpl createDownload(long nativeDownloadImpl, int id) {
-        return new DownloadImpl(
-                mProfile.getName(), mProfile.isIncognito(), mClient, nativeDownloadImpl, id);
+    private DownloadImpl createDownload(
+            long nativeDownloadImpl, int id, boolean isTransient, GURL sourceUrl) {
+        return new DownloadImpl(mProfile.getName(), mProfile.isIncognito(), mClient,
+                nativeDownloadImpl, id, isTransient, sourceUrl);
     }
 
     @CalledByNative
diff --git a/weblayer/browser/java/org/chromium/weblayer_private/DownloadImpl.java b/weblayer/browser/java/org/chromium/weblayer_private/DownloadImpl.java
index 490cf760..7bcb103 100644
--- a/weblayer/browser/java/org/chromium/weblayer_private/DownloadImpl.java
+++ b/weblayer/browser/java/org/chromium/weblayer_private/DownloadImpl.java
@@ -8,7 +8,9 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.res.Resources;
+import android.graphics.Bitmap;
 import android.net.Uri;
+import android.os.Build;
 import android.os.RemoteException;
 import android.text.TextUtils;
 
@@ -25,6 +27,7 @@
 import org.chromium.components.browser_ui.notifications.NotificationMetadata;
 import org.chromium.components.browser_ui.notifications.PendingIntentProvider;
 import org.chromium.components.browser_ui.util.DownloadUtils;
+import org.chromium.url.GURL;
 import org.chromium.weblayer_private.interfaces.APICallException;
 import org.chromium.weblayer_private.interfaces.DownloadError;
 import org.chromium.weblayer_private.interfaces.DownloadState;
@@ -73,6 +76,18 @@
     private long mNativeDownloadImpl;
     private boolean mDisableNotification;
 
+    // The time this download started, in milliseconds.
+    private final long mStartTime;
+
+    // A transient download is not persisted to disk, which affects its UI treatment.
+    private final boolean mIsTransient;
+
+    // The originating URL for this download.
+    private final GURL mSourceUrl;
+
+    // The large icon to show. Once this is successfully fetched from native, it won't be updated.
+    private Bitmap mLargeIcon;
+
     private final int mNotificationId;
     private static final HashMap<Integer, DownloadImpl> sMap = new HashMap<Integer, DownloadImpl>();
 
@@ -149,12 +164,16 @@
     }
 
     public DownloadImpl(String profileName, boolean isIncognito, IDownloadCallbackClient client,
-            long nativeDownloadImpl, int id) {
+            long nativeDownloadImpl, int id, boolean isTransient, GURL sourceUrl) {
         mProfileName = profileName;
         mIsIncognito = isIncognito;
-        mClient = DownloadImplJni.get().isTransientImpl(nativeDownloadImpl) ? null : client;
+        mClient = isTransient ? null : client;
         mNativeDownloadImpl = nativeDownloadImpl;
         mNotificationId = id;
+        mStartTime = System.currentTimeMillis();
+        mIsTransient = isTransient;
+        mSourceUrl = sourceUrl;
+
         if (mClient == null) {
             mClientDownload = null;
         } else {
@@ -350,6 +369,12 @@
 
         @DownloadState
         int state = getState();
+        if (state == DownloadState.CANCELLED) {
+            notificationManager.cancel(NOTIFICATION_TAG, mNotificationId);
+            mDisableNotification = true;
+            return;
+        }
+
         String channelId = state == DownloadState.COMPLETE
                 ? WebLayerNotificationChannels.ChannelId.COMPLETED_DOWNLOADS
                 : WebLayerNotificationChannels.ChannelId.ACTIVE_DOWNLOADS;
@@ -357,6 +382,8 @@
         WebLayerNotificationWrapperBuilder builder = WebLayerNotificationWrapperBuilder.create(
                 channelId, new NotificationMetadata(0, NOTIFICATION_TAG, mNotificationId));
         builder.setOngoing(true)
+                .setWhen(mStartTime)
+                .setShowWhen(true)
                 .setDeleteIntent(deletePendingIntent)
                 .setPriorityBeforeO(NotificationCompat.PRIORITY_DEFAULT);
 
@@ -366,27 +393,40 @@
             builder.setContentTitle(name);
         }
 
-        if (state == DownloadState.CANCELLED) {
-            notificationManager.cancel(NOTIFICATION_TAG, mNotificationId);
-            mDisableNotification = true;
-            return;
+        // Set the large icon/thumbnail, except when incognito.
+        if (!mIsIncognito && mLargeIcon == null) {
+            mLargeIcon = DownloadImplJni.get().getLargeIconImpl(mNativeDownloadImpl);
         }
+        if (mLargeIcon != null) {
+            builder.setLargeIcon(mLargeIcon);
+        }
+
+        // As with Chrome, transient downloads "promote" the source URL.
+        if (!mIsIncognito && mIsTransient) {
+            String formattedUrl = DownloadUtils.formatUrlForDisplayInNotification(mSourceUrl);
+            if (formattedUrl != null) setSubText(builder, formattedUrl);
+        }
+        // TODO(estade): In incognito, Chrome uses a subtext of "Incognito tab". Should WL display
+        // something similar?
 
         Resources resources = context.getResources();
 
         if (state == DownloadState.COMPLETE) {
-            String contextText =
-                    resources.getString(R.string.download_notification_completed_with_size,
-                            DownloadUtils.getStringForBytes(context, getTotalBytes()));
-            builder.setContentText(contextText)
-                    .setOngoing(false)
+            builder.setOngoing(false)
                     .setSmallIcon(android.R.drawable.stat_sys_download_done)
                     .setAutoCancel(true)
                     .setProgress(0, 0, false);
 
-            // TODO(estade): for transient downloads, create an intent that delegates back to native
-            // code.
-            if (!DownloadImplJni.get().isTransientImpl(mNativeDownloadImpl)) {
+            if (mIsTransient) {
+                builder.setContentText(
+                        resources.getString(R.string.download_notification_completed));
+                // TODO(estade): for transient downloads, create an intent that delegates back to
+                // native code.
+            } else {
+                builder.setContentText(
+                        resources.getString(R.string.download_notification_completed_with_size,
+                                DownloadUtils.getStringForBytes(context, getTotalBytes())));
+
                 Intent openIntent = createIntent(OPEN_INTENT);
                 openIntent.putExtra(EXTRA_NOTIFICATION_LOCATION, getLocation());
                 openIntent.putExtra(EXTRA_NOTIFICATION_MIME_TYPE, getMimeType());
@@ -454,6 +494,19 @@
     }
 
     /**
+     * Helper method to set the sub text on different versions of Android.
+     * @param builder The builder to build notification.
+     * @param subText A string shown as sub text on the notification.
+     */
+    private static void setSubText(WebLayerNotificationWrapperBuilder builder, String subText) {
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
+            builder.setSubText(subText);
+        } else {
+            builder.setContentInfo(subText);
+        }
+    }
+
+    /**
      * Returns the notification manager.
      */
     private static NotificationManagerProxy getNotificationManager() {
@@ -485,6 +538,6 @@
         String getFileNameToReportToUserImpl(long nativeDownloadImpl);
         String getMimeTypeImpl(long nativeDownloadImpl);
         int getErrorImpl(long nativeDownloadImpl);
-        boolean isTransientImpl(long nativeDownloadImpl);
+        Bitmap getLargeIconImpl(long nativeDownloadImpl);
     }
 }
diff --git a/weblayer/browser/persistent_download.cc b/weblayer/browser/persistent_download.cc
index bd27ca2531..316e2ecb 100644
--- a/weblayer/browser/persistent_download.cc
+++ b/weblayer/browser/persistent_download.cc
@@ -140,6 +140,14 @@
   return false;
 }
 
+GURL PersistentDownload::GetSourceUrl() {
+  return {};
+}
+
+const SkBitmap* PersistentDownload::GetLargeIcon() {
+  return nullptr;
+}
+
 void PersistentDownload::ResumeInternal() {
   if (resume_pending_) {
     resume_pending_ = false;
diff --git a/weblayer/browser/persistent_download.h b/weblayer/browser/persistent_download.h
index 8d45e380..4a6f961a 100644
--- a/weblayer/browser/persistent_download.h
+++ b/weblayer/browser/persistent_download.h
@@ -40,6 +40,8 @@
   // DownloadImpl:
   int GetNotificationId() override;
   bool IsTransient() override;
+  GURL GetSourceUrl() override;
+  const SkBitmap* GetLargeIcon() override;
 
  private:
   explicit PersistentDownload(download::DownloadItem* item);
diff --git a/weblayer/grit_strings_allowlist.txt b/weblayer/grit_strings_allowlist.txt
index 4a32bebae0..f8f75765 100644
--- a/weblayer/grit_strings_allowlist.txt
+++ b/weblayer/grit_strings_allowlist.txt
@@ -81,6 +81,7 @@
 IDS_CLOCK_ERROR_UPDATE_DATE_AND_TIME
 IDS_DEFAULT_ENCODING
 IDS_DOWNLOAD_NOTIFICATION_CANCEL_BUTTON
+IDS_DOWNLOAD_NOTIFICATION_COMPLETED
 IDS_DOWNLOAD_NOTIFICATION_COMPLETED_WITH_SIZE
 IDS_DOWNLOAD_NOTIFICATION_FAILED
 IDS_DOWNLOAD_NOTIFICATION_PAUSED