diff --git a/DEPS b/DEPS
index 66c1818..9bec2070 100644
--- a/DEPS
+++ b/DEPS
@@ -172,11 +172,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': 'c4420ef3dc6c5688193da062d6ce81e84eb385ab',
+  'skia_revision': '12cea8d6c429d06425be97af31db7c31655e5290',
   # 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': '67d4bf7ade77f8a3910c6861ad512b6f0fcbe4fa',
+  'v8_revision': '500af51db17fa412e5913c8f81c0d729ad5668e4',
   # 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.
@@ -184,7 +184,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling ANGLE
   # and whatever else without interference from each other.
-  'angle_revision': '8542baee0645c9f1787c2412690209e19d2c1b0e',
+  'angle_revision': '652dbfc63e70f7af1b4d3d9beff5ae69b1699c14',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling SwiftShader
   # and whatever else without interference from each other.
@@ -235,7 +235,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling catapult
   # and whatever else without interference from each other.
-  'catapult_revision': '6d6b9a1ca930357ace8e8ebeebc01d69824c12f1',
+  'catapult_revision': 'f9004ee81c432e30474b6e87216b0fb038216ccf',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling libFuzzer
   # and whatever else without interference from each other.
@@ -243,7 +243,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': 'e66080e9a1ff79cba8130169c16ebcd222747510',
+  'devtools_frontend_revision': 'b47c6e25f94ebbc276390b621980c0e956490861',
   # 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.
@@ -307,7 +307,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
-  'dawn_revision': '64cfaeac4c5d9eaf3876a648258a1eefce912410',
+  'dawn_revision': 'c3284fa40ec6b12731ed66c2f2a9256ae3fa692e',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
@@ -871,7 +871,7 @@
 
   # Build tools for Chrome OS. Note: This depends on third_party/pyelftools.
   'src/third_party/chromite': {
-      'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + 'c2ef0f867b79e344bcbab6466a07297d75519598',
+      'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + '48dc2da07f5bb8fd2353d5790f172fa5b4c4f9fe',
       'condition': 'checkout_linux',
   },
 
@@ -896,7 +896,7 @@
   },
 
   'src/third_party/depot_tools':
-    Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + '23247b99321549c24e62ad45200409419423695d',
+    Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + '9ab047e78be34f3345b00c3d71cbfcbbe110ab3b',
 
   'src/third_party/devtools-frontend/src':
     Var('chromium_git') + '/devtools/devtools-frontend' + '@' + Var('devtools_frontend_revision'),
@@ -1311,7 +1311,7 @@
   },
 
   'src/third_party/perfetto':
-    Var('android_git') + '/platform/external/perfetto.git' + '@' + '1af507800d72e284c139ba42cc8cedec27e63bfd',
+    Var('android_git') + '/platform/external/perfetto.git' + '@' + '50d2ff30828b5155f3e1f19d98f66def2dd97b09',
 
   'src/third_party/perl': {
       'url': Var('chromium_git') + '/chromium/deps/perl.git' + '@' + '6f3e5028eb65d0b4c5fdd792106ac4c84eee1eb3',
@@ -1512,7 +1512,7 @@
     Var('chromium_git') + '/external/khronosgroup/webgl.git' + '@' + 'dd55f3ca8f2ea716ca917a4aaf36f0729fe902b1',
 
   'src/third_party/webrtc':
-    Var('webrtc_git') + '/src.git' + '@' + '70ec48ca281c360be46e093c4c2cc5f801e8224e',
+    Var('webrtc_git') + '/src.git' + '@' + 'e9f663c8cb8c08501472e2fef8c84dfa98f52b16',
 
   # Wuffs' canonical repository is at github.com/google/wuffs, but we use
   # Skia's mirror of Wuffs, the same as in upstream Skia's DEPS file.
diff --git a/WATCHLISTS b/WATCHLISTS
index c699f91..6845545e 100644
--- a/WATCHLISTS
+++ b/WATCHLISTS
@@ -1948,9 +1948,11 @@
                       'dmazzoni+watch@chromium.org',
                       'dougt+watch@chromium.org',
                       'dtseng+watch@chromium.org',
+                      'hirokisato+watch@chromium.org',
                       'je_julie.kim@chromium.org',
                       'kbabbitt@microsoft.com',
                       'nektar+watch@chromium.org',
+                      'sarakato+watch@chromium.org',
                       'yuzo+watch@chromium.org'],
     'add_to_homescreen': ['dominickn+watch-a2hs@chromium.org',
                           'hanxi+watch@chromium.org',
diff --git a/android_webview/BUILD.gn b/android_webview/BUILD.gn
index 44126338..faa86ed 100644
--- a/android_webview/BUILD.gn
+++ b/android_webview/BUILD.gn
@@ -778,20 +778,20 @@
   srcjar_deps = [ ":common_aidl" ]
 }
 
-# Generate LocaleConfig.java so that android_webview_locale_config_java's
+# Generate ProductConfig.java so that android_webview_product_config_java's
 # compile step works.
-generate_locale_config_srcjar("webview_locale_config") {
-  java_package = webview_locale_config_java_package
+generate_product_config_srcjar("webview_product_config") {
+  java_package = webview_product_config_java_package
 }
 
-# LocaleConfig.java is excluded from the generated .jar
+# ProductConfig.java is excluded from the generated .jar
 # (via. jar_excluded_patterns) and the final version is inserted at the APK
 # level - with the list of pak locales populated by looking at the assets that
 # are listed in the final APK's .build_config.
-android_library("android_webview_locale_config_java") {
+android_library("android_webview_product_config_java") {
   java_files = [ "java/src/org/chromium/android_webview/AwLocaleConfig.java" ]
-  srcjar_deps = [ ":webview_locale_config" ]
-  jar_excluded_patterns = [ "*/LocaleConfig.class" ]
+  srcjar_deps = [ ":webview_product_config" ]
+  jar_excluded_patterns = [ "*/ProductConfig.class" ]
 }
 
 if (public_android_sdk) {
diff --git a/android_webview/apk/BUILD.gn b/android_webview/apk/BUILD.gn
index 1d032c6..2ffcbb4f 100644
--- a/android_webview/apk/BUILD.gn
+++ b/android_webview/apk/BUILD.gn
@@ -24,7 +24,7 @@
   deps = [
     ":devui_java",
     ":services_java",
-    "//android_webview:android_webview_locale_config_java",
+    "//android_webview:android_webview_product_config_java",
     "//android_webview:common_commandline_java",
     "//base:base_java",
     "//base:jni_java",
diff --git a/android_webview/apk/java/src/org/chromium/android_webview/devui/CrashesListActivity.java b/android_webview/apk/java/src/org/chromium/android_webview/devui/CrashesListActivity.java
index e42b396..a1a018a 100644
--- a/android_webview/apk/java/src/org/chromium/android_webview/devui/CrashesListActivity.java
+++ b/android_webview/apk/java/src/org/chromium/android_webview/devui/CrashesListActivity.java
@@ -142,7 +142,7 @@
             }
             if (crashInfo.uploadTime >= 0) {
                 builder.append("upload time: ", new StyleSpan(android.graphics.Typeface.BOLD), 0)
-                        .append(new Date(crashInfo.uploadTime * 1000).toString())
+                        .append(new Date(crashInfo.uploadTime).toString())
                         .append("\n");
             }
             if (crashInfo.captureTime >= 0) {
diff --git a/android_webview/apk/java/src/org/chromium/android_webview/devui/util/UploadedCrashesInfoLoader.java b/android_webview/apk/java/src/org/chromium/android_webview/devui/util/UploadedCrashesInfoLoader.java
index c768f23f..f766b8ba 100644
--- a/android_webview/apk/java/src/org/chromium/android_webview/devui/util/UploadedCrashesInfoLoader.java
+++ b/android_webview/apk/java/src/org/chromium/android_webview/devui/util/UploadedCrashesInfoLoader.java
@@ -12,6 +12,7 @@
 import java.io.IOException;
 import java.util.ArrayList;
 import java.util.List;
+import java.util.concurrent.TimeUnit;
 
 /**
  * Parses upload log file in crash directory where crash upload id and time are written.
@@ -67,7 +68,8 @@
         CrashInfo info = new CrashInfo(components[2]);
         info.uploadState = CrashInfo.UploadState.UPLOADED;
         try {
-            info.uploadTime = Long.parseLong(components[0]);
+            // Log file has upload time in sec, convert it back to millisec.
+            info.uploadTime = TimeUnit.SECONDS.toMillis(Long.parseLong(components[0]));
         } catch (NumberFormatException e) {
             return null;
         }
diff --git a/android_webview/browser/aw_settings.cc b/android_webview/browser/aw_settings.cc
index 52d36c96..9e52feb68 100644
--- a/android_webview/browser/aw_settings.cc
+++ b/android_webview/browser/aw_settings.cc
@@ -45,6 +45,7 @@
   web_prefs->picture_in_picture_enabled = false;
   web_prefs->disable_features_depending_on_viz = true;
   web_prefs->disable_accelerated_small_canvases = true;
+  web_prefs->reenable_web_components_v0 = true;
 }
 
 const void* const kAwSettingsUserDataKey = &kAwSettingsUserDataKey;
diff --git a/android_webview/common/crash_reporter/aw_crash_reporter_client.cc b/android_webview/common/crash_reporter/aw_crash_reporter_client.cc
index c369ac3..2e932823 100644
--- a/android_webview/common/crash_reporter/aw_crash_reporter_client.cc
+++ b/android_webview/common/crash_reporter/aw_crash_reporter_client.cc
@@ -97,10 +97,16 @@
   }
 
   bool GetBrowserProcessType(std::string* ptype) override {
-    *ptype = base::CommandLine::ForCurrentProcess()->HasSwitch(
-                 switches::kWebViewSandboxedRenderer)
-                 ? "browser"
-                 : "webview";
+    if (!base::CommandLine::ForCurrentProcess()->HasSwitch(
+            switches::kWebViewSandboxedRenderer)) {
+      // In single process mode the renderer and browser are in the same
+      // process. The process type is "webview" to distinguish this case,
+      // and for backwards compatibility.
+      *ptype = "webview";
+    } else {
+      // Otherwise, in multi process mode, "browser" suffices.
+      *ptype = "browser";
+    }
     return true;
   }
 
diff --git a/android_webview/glue/BUILD.gn b/android_webview/glue/BUILD.gn
index 3b81953..dae45d9 100644
--- a/android_webview/glue/BUILD.gn
+++ b/android_webview/glue/BUILD.gn
@@ -10,7 +10,7 @@
 android_library("glue") {
   deps = [
     "//android_webview:android_webview_java",
-    "//android_webview:android_webview_locale_config_java",
+    "//android_webview:android_webview_product_config_java",
     "//android_webview:common_commandline_java",
     "//android_webview:common_java",
     "//android_webview/apk:system_webview_manifest",
diff --git a/android_webview/java/src/org/chromium/android_webview/AwLocaleConfig.java b/android_webview/java/src/org/chromium/android_webview/AwLocaleConfig.java
index a383ef0..d14456c 100644
--- a/android_webview/java/src/org/chromium/android_webview/AwLocaleConfig.java
+++ b/android_webview/java/src/org/chromium/android_webview/AwLocaleConfig.java
@@ -12,6 +12,6 @@
     private AwLocaleConfig() {}
 
     public static String[] getWebViewSupportedPakLocales() {
-        return LocaleConfig.UNCOMPRESSED_LOCALES;
+        return ProductConfig.UNCOMPRESSED_LOCALES;
     }
 }
diff --git a/android_webview/java/src/org/chromium/android_webview/common/crash/CrashInfo.java b/android_webview/java/src/org/chromium/android_webview/common/crash/CrashInfo.java
index 9e59155..f41b60bb 100644
--- a/android_webview/java/src/org/chromium/android_webview/common/crash/CrashInfo.java
+++ b/android_webview/java/src/org/chromium/android_webview/common/crash/CrashInfo.java
@@ -39,7 +39,8 @@
     @NonNull
     public String localId;
     /**
-     * The time the data was captured. This is useful if the data is stored locally when
+     * The time the data was captured in millisecs since epoch.
+     * This is useful if the data is stored locally when
      * captured and uploaded at a later time.
      */
     public long captureTime = -1;
@@ -58,7 +59,7 @@
      */
     public String uploadId;
     /**
-     * The time when the crash report is uploaded.
+     * The time when the crash report is uploaded in millisecs since epoch.
      * Only valid when |uploadState| == Uploaded.
      */
     public long uploadTime = -1;
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/AwSettingsTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/AwSettingsTest.java
index 4612ca8..2d017ce 100644
--- a/android_webview/javatests/src/org/chromium/android_webview/test/AwSettingsTest.java
+++ b/android_webview/javatests/src/org/chromium/android_webview/test/AwSettingsTest.java
@@ -3123,6 +3123,35 @@
         Assert.assertEquals(expectedTitle, actualTitle);
     }
 
+    @Test
+    @SmallTest
+    @Feature({"AndroidWebView", "Preferences"})
+    public void testWebComponentsV0Reenabled() throws Throwable {
+        // TODO(1021631): This test should be removed once Android Webview
+        // disables Web Components v0 by default.
+        final TestAwContentsClient client = new TestAwContentsClient();
+        final AwTestContainerView view =
+                mActivityTestRule.createAwTestContainerViewOnMainSync(client);
+        final AwContents awContents = view.getAwContents();
+        CallbackHelper onPageFinishedHelper = client.getOnPageFinishedHelper();
+        AwActivityTestRule.enableJavaScriptOnUiThread(awContents);
+        final String expectedTitle = "enabled"; // https://crbug.com/1021631
+        final String page = "<!doctype html>"
+                + "<script>"
+                + "const htmlImportsEnabled = 'import' in document.createElement('link');"
+                + "const customElementsV0Enabled = 'registerElement' in document;"
+                + "const shadowDomV0Enabled = 'createShadowRoot' in document.createElement('div');"
+                + "if (htmlImportsEnabled && customElementsV0Enabled && shadowDomV0Enabled) {"
+                + "  document.title = 'enabled';"
+                + "} else {"
+                + "  document.title = 'disabled';"
+                + "}"
+                + "</script>";
+        mActivityTestRule.loadDataSync(awContents, onPageFinishedHelper, page, "text/html", false);
+        String actualTitle = mActivityTestRule.getTitleOnUiThread(awContents);
+        Assert.assertEquals(expectedTitle, actualTitle);
+    }
+
     private static class SelectionRangeTestDependencyFactory extends TestDependencyFactory {
         private boolean mDoNotUpdate;
         public SelectionRangeTestDependencyFactory(boolean doNotUpdate) {
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 e64456f7..1f8bf9d29 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
@@ -131,6 +131,32 @@
     @Test
     @SmallTest
     @Feature({"AndroidWebView", "JsJavaInteraction"})
+    public void testPostMessage_LoadData_MessageHasStringNullOrigin() throws Throwable {
+        addWebMessageListenerOnUiThread(mAwContents, JS_OBJECT_NAME, new String[] {"*"}, mListener);
+
+        final String html = "<html><head><script>myObject.postMessage('Hello');</script></head>"
+                + "<body></body></html>";
+
+        // This uses loadDataAsync() which is equivalent to WebView#loadData(...).
+        mActivityTestRule.loadHtmlSync(
+                mAwContents, mContentsClient.getOnPageFinishedHelper(), html);
+
+        TestWebMessageListener.Data data = mListener.waitForOnPostMessage();
+
+        // Note that the source origin is a non-null string of n, u, l, l.
+        Assert.assertNotNull(data.mSourceOrigin);
+        Assert.assertEquals("null", data.mSourceOrigin.toString());
+
+        Assert.assertEquals(HELLO, data.mMessage);
+        Assert.assertTrue(data.mIsMainFrame);
+        Assert.assertEquals(0, data.mPorts.length);
+
+        Assert.assertTrue(mListener.hasNoMoreOnPostMessage());
+    }
+
+    @Test
+    @SmallTest
+    @Feature({"AndroidWebView", "JsJavaInteraction"})
     public void testPostMessageWithPorts() throws Throwable {
         addWebMessageListenerOnUiThread(mAwContents, JS_OBJECT_NAME, new String[] {"*"}, mListener);
 
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/devui/util/UploadedCrashesInfoLoaderTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/devui/util/UploadedCrashesInfoLoaderTest.java
index c67344a..a457437 100644
--- a/android_webview/javatests/src/org/chromium/android_webview/test/devui/util/UploadedCrashesInfoLoaderTest.java
+++ b/android_webview/javatests/src/org/chromium/android_webview/test/devui/util/UploadedCrashesInfoLoaderTest.java
@@ -26,6 +26,7 @@
 import java.io.IOException;
 import java.util.ArrayList;
 import java.util.List;
+import java.util.concurrent.TimeUnit;
 
 /**
  * Unit tests for UploadedCrashesInfoLoader.
@@ -33,8 +34,9 @@
 @RunWith(AwJUnit4ClassRunner.class)
 @OnlyRunIn(SINGLE_PROCESS)
 public class UploadedCrashesInfoLoaderTest {
-    private static final String TEST_UPLOAD_TIME_STR = "1234567890";
-    private static final long TEST_UPLOAD_TIME = Long.parseLong(TEST_UPLOAD_TIME_STR);
+    private static final String TEST_UPLOAD_TIME_SEC_STR = "1234567890";
+    private static final long TEST_UPLOAD_TIME_MILLI =
+            TimeUnit.SECONDS.toMillis(Long.parseLong(TEST_UPLOAD_TIME_SEC_STR));
     private static final String TEST_UPLOAD_ID = "0123456789abcdef";
     private static final String TEST_LOCAL_ID = "fedcba9876543210";
 
@@ -64,7 +66,7 @@
     @SmallTest
     public void testParseSingleEntry() throws IOException {
         List<String> logs = new ArrayList<>();
-        logs.add(TEST_UPLOAD_TIME_STR + "," + TEST_UPLOAD_ID + "," + TEST_LOCAL_ID);
+        logs.add(TEST_UPLOAD_TIME_SEC_STR + "," + TEST_UPLOAD_ID + "," + TEST_LOCAL_ID);
         writeUploadLogs(mLogFile, logs);
 
         UploadedCrashesInfoLoader crashesInfoLoader = new UploadedCrashesInfoLoader(mLogFile);
@@ -73,7 +75,7 @@
         Assert.assertEquals(1, infoList.size());
 
         CrashInfo crashInfo = infoList.get(0);
-        Assert.assertEquals(TEST_UPLOAD_TIME, crashInfo.uploadTime);
+        Assert.assertEquals(TEST_UPLOAD_TIME_MILLI, crashInfo.uploadTime);
         Assert.assertEquals(TEST_UPLOAD_ID, crashInfo.uploadId);
         Assert.assertEquals(TEST_LOCAL_ID, crashInfo.localId);
         Assert.assertEquals(UploadState.UPLOADED, crashInfo.uploadState);
@@ -84,7 +86,7 @@
     public void testParseMultipleEntries() throws IOException {
         List<String> logs = new ArrayList<>();
         for (int i = 1; i <= 4; ++i) {
-            String testEntry = TEST_UPLOAD_TIME_STR + ","
+            String testEntry = TEST_UPLOAD_TIME_SEC_STR + ","
                     + "upload" + Integer.toString(i) + ","
                     + "local" + Integer.toString(i);
             logs.add(testEntry);
@@ -97,7 +99,7 @@
         Assert.assertEquals(4, infoList.size());
         for (int i = 1; i <= 4; ++i) {
             CrashInfo crashInfo = infoList.get(i - 1);
-            Assert.assertEquals(TEST_UPLOAD_TIME, crashInfo.uploadTime);
+            Assert.assertEquals(TEST_UPLOAD_TIME_MILLI, crashInfo.uploadTime);
             Assert.assertEquals("upload" + Integer.toString(i), crashInfo.uploadId);
             Assert.assertEquals("local" + Integer.toString(i), crashInfo.localId);
             Assert.assertEquals(UploadState.UPLOADED, crashInfo.uploadState);
@@ -110,7 +112,7 @@
         List<String> logs = new ArrayList<>();
         // Valid logs
         for (int i = 1; i <= 2; ++i) {
-            String testEntry = TEST_UPLOAD_TIME_STR + ","
+            String testEntry = TEST_UPLOAD_TIME_SEC_STR + ","
                     + "upload" + Integer.toString(i) + ","
                     + "local" + Integer.toString(i);
             logs.add(testEntry);
@@ -131,7 +133,7 @@
         // too many components
         logs.add("123456789,1a2b3c,4d5e6f,1011121314");
         for (int i = 3; i <= 4; ++i) {
-            String testEntry = TEST_UPLOAD_TIME_STR + ","
+            String testEntry = TEST_UPLOAD_TIME_SEC_STR + ","
                     + "upload" + Integer.toString(i) + ","
                     + "local" + Integer.toString(i);
             logs.add(testEntry);
@@ -144,7 +146,7 @@
         Assert.assertEquals(4, infoList.size());
         for (int i = 1; i <= 4; ++i) {
             CrashInfo crashInfo = infoList.get(i - 1);
-            Assert.assertEquals(TEST_UPLOAD_TIME, crashInfo.uploadTime);
+            Assert.assertEquals(TEST_UPLOAD_TIME_MILLI, crashInfo.uploadTime);
             Assert.assertEquals("upload" + Integer.toString(i), crashInfo.uploadId);
             Assert.assertEquals("local" + Integer.toString(i), crashInfo.localId);
             Assert.assertEquals(UploadState.UPLOADED, crashInfo.uploadState);
diff --git a/android_webview/system_webview_apk_tmpl.gni b/android_webview/system_webview_apk_tmpl.gni
index d76eeb8..9ae48d4 100644
--- a/android_webview/system_webview_apk_tmpl.gni
+++ b/android_webview/system_webview_apk_tmpl.gni
@@ -34,9 +34,9 @@
     ]
 
     target_sdk_version = android_sdk_version
-    locale_config_java_packages = [
-      webview_locale_config_java_package,
-      weblayer_locale_config_java_package,
+    product_config_java_packages = [
+      webview_product_config_java_package,
+      weblayer_product_config_java_package,
     ]
 
     if (!defined(alternative_android_sdk_dep)) {
@@ -87,14 +87,14 @@
           }
         } else {
           shared_libraries = [ "//android_webview:monochrome" ]
-          deps += [
-            "//third_party/crashpad/crashpad/handler:crashpad_handler_trampoline",
-          ]
-          loadable_modules = [ "$root_out_dir/libcrashpad_handler_trampoline.so" ]
+          deps += [ "//third_party/crashpad/crashpad/handler:crashpad_handler_trampoline" ]
+          loadable_modules =
+              [ "$root_out_dir/libcrashpad_handler_trampoline.so" ]
           if (build_apk_secondary_abi) {
             secondary_native_lib_placeholders = [ "libdummy.so" ]
             if (use_v8_context_snapshot) {
-              deps += [ "//tools/v8_context_snapshot:v8_context_snapshot_assets" ]
+              deps +=
+                  [ "//tools/v8_context_snapshot:v8_context_snapshot_assets" ]
             } else {
               deps += [ "//v8:v8_external_startup_data_assets" ]
             }
diff --git a/android_webview/test/BUILD.gn b/android_webview/test/BUILD.gn
index 4755eb0..0b60621d 100644
--- a/android_webview/test/BUILD.gn
+++ b/android_webview/test/BUILD.gn
@@ -61,7 +61,7 @@
     ":webview_instrumentation_apk_resources",
     ":webview_instrumentation_test_utils_java",
     "//android_webview:android_webview_java",
-    "//android_webview:android_webview_locale_config_java",
+    "//android_webview:android_webview_product_config_java",
     "//android_webview:common_java",
     "//android_webview:locale_pak_assets",
     "//android_webview:platform_service_bridge_upstream_implementation_java",
@@ -92,7 +92,7 @@
     "shell/src/org/chromium/android_webview/test/OnlyRunIn.java",
     "shell/src/org/chromium/android_webview/test/TestContentProvider.java",
   ]
-  locale_config_java_packages = [ webview_locale_config_java_package ]
+  product_config_java_packages = [ webview_product_config_java_package ]
 
   shared_libraries = [ ":libstandalonelibwebviewchromium" ]
 
diff --git a/android_webview/variables.gni b/android_webview/variables.gni
index 36653c6..8bbf434 100644
--- a/android_webview/variables.gni
+++ b/android_webview/variables.gni
@@ -11,12 +11,11 @@
     "$root_gen_dir/android_webview/system_webview_apk/AndroidManifest.xml"
 trichrome_webview_android_manifest =
     "$root_gen_dir/android_webview/trichrome_webview_apk/AndroidManifest.xml"
-trichrome_webview_64_32_android_manifest =
-    "$root_gen_dir/android_webview/trichrome_webview_64_32_apk/AndroidManifest.xml"
+trichrome_webview_64_32_android_manifest = "$root_gen_dir/android_webview/trichrome_webview_64_32_apk/AndroidManifest.xml"
 
 upstream_only_webview_deps = [
   "//android_webview:platform_service_bridge_upstream_implementation_java",
   "//android_webview/apk:icon_resources",
 ]
 
-webview_locale_config_java_package = "org.chromium.android_webview"
+webview_product_config_java_package = "org.chromium.android_webview"
diff --git a/ash/BUILD.gn b/ash/BUILD.gn
index 48d0297..3b48226 100644
--- a/ash/BUILD.gn
+++ b/ash/BUILD.gn
@@ -1985,6 +1985,7 @@
     "wm/window_mirror_view_unittest.cc",
     "wm/window_modality_controller_unittest.cc",
     "wm/window_positioner_unittest.cc",
+    "wm/window_positioning_utils_unittest.cc",
     "wm/window_preview_view_unittest.cc",
     "wm/window_state_unittest.cc",
     "wm/window_transient_descendant_iterator_unittest.cc",
diff --git a/ash/home_screen/drag_window_from_shelf_controller_unittest.cc b/ash/home_screen/drag_window_from_shelf_controller_unittest.cc
index f166b5e6..6d1802a 100644
--- a/ash/home_screen/drag_window_from_shelf_controller_unittest.cc
+++ b/ash/home_screen/drag_window_from_shelf_controller_unittest.cc
@@ -448,8 +448,9 @@
 }
 
 // Check the split view drag indicators window dragging states.
+// Flaky on ChromeOS. https://crbug.com/1022320
 TEST_F(DragWindowFromShelfControllerTest,
-       SplitViewDragIndicatorsWindowDraggingStates) {
+       DISABLED_SplitViewDragIndicatorsWindowDraggingStates) {
   UpdateDisplay("400x400");
   const gfx::Rect shelf_bounds =
       Shelf::ForWindow(Shell::GetPrimaryRootWindow())->GetIdealBounds();
@@ -517,7 +518,8 @@
 
 // Test that if drag is cancelled, overview should be dismissed and other
 // hidden windows should restore to its previous visibility state.
-TEST_F(DragWindowFromShelfControllerTest, CancelDragDismissOverview) {
+// Flaky on ChromeOS. https://crbug.com/1022319
+TEST_F(DragWindowFromShelfControllerTest, DISABLED_CancelDragDismissOverview) {
   auto window3 = CreateTestWindow();
   auto window2 = CreateTestWindow();
   auto window1 = CreateTestWindow();
diff --git a/ash/system/bluetooth/tray_bluetooth_helper_legacy.cc b/ash/system/bluetooth/tray_bluetooth_helper_legacy.cc
index 79a9a65..0ef3f24 100644
--- a/ash/system/bluetooth/tray_bluetooth_helper_legacy.cc
+++ b/ash/system/bluetooth/tray_bluetooth_helper_legacy.cc
@@ -77,20 +77,11 @@
 // Converts a MAC Address string e.g. "00:11:22:33:44:55" into an
 // BluetoothAddress e.g. {0x00, 0x11, 0x22, 0x33, 0x44, 0x55}.
 BluetoothAddress AddressStrToBluetoothAddress(const std::string& address_str) {
-  std::string numbers;
-  bool success = base::ReplaceChars(address_str, ":", "", &numbers);
-  DCHECK(success);
-
-  std::vector<uint8_t> address_vector;
-  success = base::HexStringToBytes(numbers, &address_vector);
-  DCHECK(success);
-
-  // If the size is not 6, then the underlying Bluetooth API returned an
-  // incorrect value.
-  CHECK_EQ(6u, address_vector.size());
-
   BluetoothAddress address_array;
-  std::copy_n(address_vector.begin(), 6, address_array.begin());
+
+  // If the string is not a valid encoding of a Bluetooth address, then the
+  // underlying Bluetooth API returned an incorrect value.
+  CHECK(device::BluetoothDevice::ParseAddress(address_str, address_array));
 
   return address_array;
 }
diff --git a/ash/wm/window_positioning_utils.cc b/ash/wm/window_positioning_utils.cc
index ccee00d..f216f57 100644
--- a/ash/wm/window_positioning_utils.cc
+++ b/ash/wm/window_positioning_utils.cc
@@ -31,16 +31,11 @@
 
 namespace {
 
-// Returns the default width of a snapped window.
-int GetDefaultSnappedWindowWidth(aura::Window* window) {
-  const float kSnappedWidthWorkspaceRatio = 0.5f;
-
-  int work_area_width =
+int GetSnappedWindowWidth(int ideal_width, aura::Window* window) {
+  const int work_area_width =
       screen_util::GetDisplayWorkAreaBoundsInParent(window).width();
-  int min_width =
+  const int min_width =
       window->delegate() ? window->delegate()->GetMinimumSize().width() : 0;
-  int ideal_width =
-      static_cast<int>(work_area_width * kSnappedWidthWorkspaceRatio);
   return base::ClampToRange(ideal_width, min_width, work_area_width);
 }
 
@@ -93,15 +88,19 @@
 gfx::Rect GetDefaultLeftSnappedWindowBoundsInParent(aura::Window* window) {
   gfx::Rect work_area_in_parent(
       screen_util::GetDisplayWorkAreaBoundsInParent(window));
-  return gfx::Rect(work_area_in_parent.x(), work_area_in_parent.y(),
-                   GetDefaultSnappedWindowWidth(window),
-                   work_area_in_parent.height());
+  const int middle = work_area_in_parent.CenterPoint().x();
+  return gfx::Rect(
+      work_area_in_parent.x(), work_area_in_parent.y(),
+      GetSnappedWindowWidth(middle - work_area_in_parent.x(), window),
+      work_area_in_parent.height());
 }
 
 gfx::Rect GetDefaultRightSnappedWindowBoundsInParent(aura::Window* window) {
   gfx::Rect work_area_in_parent(
       screen_util::GetDisplayWorkAreaBoundsInParent(window));
-  int width = GetDefaultSnappedWindowWidth(window);
+  const int middle = work_area_in_parent.CenterPoint().x();
+  const int width =
+      GetSnappedWindowWidth(work_area_in_parent.right() - middle, window);
   return gfx::Rect(work_area_in_parent.right() - width, work_area_in_parent.y(),
                    width, work_area_in_parent.height());
 }
diff --git a/ash/wm/window_positioning_utils_unittest.cc b/ash/wm/window_positioning_utils_unittest.cc
new file mode 100644
index 0000000..682ed76
--- /dev/null
+++ b/ash/wm/window_positioning_utils_unittest.cc
@@ -0,0 +1,62 @@
+// 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 "ash/wm/window_positioning_utils.h"
+
+#include "ash/test/ash_test_base.h"
+#include "ui/aura/test/test_window_delegate.h"
+#include "ui/aura/window.h"
+
+namespace ash {
+
+using WindowPositioningUtilsTest = AshTestBase;
+
+TEST_F(WindowPositioningUtilsTest, SnapBoundsWithOddNumberedScreenWidth) {
+  UpdateDisplay("999x700");
+
+  auto window = CreateToplevelTestWindow();
+  gfx::Rect left_bounds =
+      GetDefaultLeftSnappedWindowBoundsInParent(window.get());
+  gfx::Rect right_bounds =
+      GetDefaultRightSnappedWindowBoundsInParent(window.get());
+  EXPECT_EQ(left_bounds.x(), 0);
+  EXPECT_EQ(left_bounds.y(), 0);
+  EXPECT_EQ(right_bounds.right(), 999);
+  EXPECT_EQ(right_bounds.y(), 0);
+  EXPECT_EQ(left_bounds.right(), right_bounds.x());
+  EXPECT_NEAR(left_bounds.width(), 499, 1);
+  EXPECT_NEAR(right_bounds.width(), 499, 1);
+}
+
+TEST_F(WindowPositioningUtilsTest, SnapBoundsWithMinimumSize) {
+  UpdateDisplay("800x600");
+
+  auto window = CreateToplevelTestWindow();
+  auto* test_delegate =
+      static_cast<aura::test::TestWindowDelegate*>(window->delegate());
+  test_delegate->set_minimum_size(gfx::Size(300, 200));
+  gfx::Rect left_bounds =
+      GetDefaultLeftSnappedWindowBoundsInParent(window.get());
+  EXPECT_EQ(left_bounds.width(), 400);
+  gfx::Rect right_bounds =
+      GetDefaultRightSnappedWindowBoundsInParent(window.get());
+  EXPECT_EQ(right_bounds.width(), 400);
+  EXPECT_EQ(right_bounds.right(), 800);
+
+  test_delegate->set_minimum_size(gfx::Size(600, 200));
+  left_bounds = GetDefaultLeftSnappedWindowBoundsInParent(window.get());
+  EXPECT_EQ(left_bounds.width(), 600);
+  right_bounds = GetDefaultRightSnappedWindowBoundsInParent(window.get());
+  EXPECT_EQ(right_bounds.width(), 600);
+  EXPECT_EQ(right_bounds.right(), 800);
+
+  test_delegate->set_minimum_size(gfx::Size(1200, 200));
+  left_bounds = GetDefaultLeftSnappedWindowBoundsInParent(window.get());
+  EXPECT_EQ(left_bounds.width(), 800);
+  right_bounds = GetDefaultRightSnappedWindowBoundsInParent(window.get());
+  EXPECT_EQ(right_bounds.width(), 800);
+  EXPECT_EQ(right_bounds.right(), 800);
+}
+
+}  // namespace ash
diff --git a/base/BUILD.gn b/base/BUILD.gn
index 8399302..1608b7e 100644
--- a/base/BUILD.gn
+++ b/base/BUILD.gn
@@ -3363,7 +3363,6 @@
       "android/java/src/org/chromium/base/compat/ApiHelperForP.java",
       "android/java/src/org/chromium/base/library_loader/LegacyLinker.java",
       "android/java/src/org/chromium/base/library_loader/LibraryLoader.java",
-      "android/java/src/org/chromium/base/library_loader/LibraryLoaderConfig.java",
       "android/java/src/org/chromium/base/library_loader/LibraryPrefetcher.java",
       "android/java/src/org/chromium/base/library_loader/Linker.java",
       "android/java/src/org/chromium/base/library_loader/LoaderErrors.java",
diff --git a/base/android/java/src/org/chromium/base/library_loader/LibraryLoader.java b/base/android/java/src/org/chromium/base/library_loader/LibraryLoader.java
index 43824f9..1f55bdca 100644
--- a/base/android/java/src/org/chromium/base/library_loader/LibraryLoader.java
+++ b/base/android/java/src/org/chromium/base/library_loader/LibraryLoader.java
@@ -62,9 +62,6 @@
 public class LibraryLoader {
     private static final String TAG = "LibraryLoader";
 
-    // Set to true to enable debug logs.
-    private static final boolean DEBUG = false;
-
     // Experience shows that on some devices, the PackageManager fails to properly extract
     // native shared libraries to the /data partition at installation or upgrade time,
     // which creates all kind of chaos (https://crbug.com/806998).
@@ -104,6 +101,15 @@
     private NativeLibraryPreloader mLibraryPreloader;
     private boolean mLibraryPreloaderCalled;
 
+    // Whether to use the Chromium linker vs system linker.
+    private boolean mUseChromiumLinker;
+
+    // Whether to use ModernLinker, vs LegacyLinker.
+    private boolean mUseModernLinker;
+
+    // Whether the configuration has been set.
+    private boolean mConfigurationSet;
+
     // One-way switch becomes true when the libraries are loaded.
     private boolean mLoaded;
 
@@ -150,6 +156,17 @@
         }
     }
 
+    @GuardedBy("mLock")
+    private void setConfigurationIfNeeded() {
+        if (mConfigurationSet) return;
+
+        // Cannot use initializers for the variables below, as this makes roboelectric tests fail,
+        // since they don't have a NativeLibraries class.
+        mUseChromiumLinker = NativeLibraries.sUseLinker;
+        mUseModernLinker = NativeLibraries.sUseModernLinker;
+        mConfigurationSet = true;
+    }
+
     public static LibraryLoader getInstance() {
         return sInstance;
     }
@@ -157,6 +174,39 @@
     private LibraryLoader() {}
 
     /**
+     * Sets the configuration for library loading.
+     *
+     * Must be called before loading the library.
+     *
+     * @param useChromiumLinker Whether to use the chromium linker.
+     * @param useModernLinker Whether to use ModernLinker.
+     */
+    public void setConfiguration(boolean useChromiumLinker, boolean useModernLinker) {
+        synchronized (mLock) {
+            assert !mInitialized;
+
+            mUseChromiumLinker = useChromiumLinker;
+            mUseModernLinker = useModernLinker;
+
+            Log.d(TAG, "Configuration, useChromiumLinker = %b, useModernLinker = %b",
+                    mUseChromiumLinker, mUseModernLinker);
+            mConfigurationSet = true;
+        }
+    }
+
+    public boolean useChromiumLinker() {
+        return mUseChromiumLinker;
+    }
+
+    public boolean useModernLinker() {
+        return mUseModernLinker;
+    }
+
+    public boolean areTestsEnabled() {
+        return NativeLibraries.sEnableLinkerTests;
+    }
+
+    /**
      * Return if library is already loaded successfully by the zygote.
      */
     public boolean isLoadedByZygote() {
@@ -193,7 +243,8 @@
      */
     public void preloadNowOverrideApplicationContext(Context appContext) {
         synchronized (mLock) {
-            if (LibraryLoaderConfig.useChromiumLinker()) return;
+            setConfigurationIfNeeded();
+            if (mUseChromiumLinker) return;
             preloadAlreadyLocked(appContext.getApplicationInfo());
         }
     }
@@ -201,7 +252,7 @@
     private void preloadAlreadyLocked(ApplicationInfo appInfo) {
         try (TraceEvent te = TraceEvent.scoped("LibraryLoader.preloadAlreadyLocked")) {
             // Preloader uses system linker, we shouldn't preload if Chromium linker is used.
-            assert !LibraryLoaderConfig.useChromiumLinker();
+            assert !mUseChromiumLinker;
             if (mLibraryPreloader != null && !mLibraryPreloaderCalled) {
                 mLibraryPreloader.loadLibrary(appInfo);
                 mLibraryPreloaderCalled = true;
@@ -384,16 +435,19 @@
         try (TraceEvent te = TraceEvent.scoped("LibraryLoader.loadAlreadyLocked")) {
             if (mLoaded) return;
             assert !mInitialized;
+            setConfigurationIfNeeded();
 
             long startTime = SystemClock.uptimeMillis();
 
-            if (LibraryLoaderConfig.useChromiumLinker() && !inZygote) {
+            if (mUseChromiumLinker && !inZygote) {
+                Log.d(TAG, "Loading with the Chromium linker.");
                 // See base/android/linker/config.gni, the chromium linker is only enabled when
                 // we have a single library.
                 assert NativeLibraries.LIBRARIES.length == 1;
                 String library = NativeLibraries.LIBRARIES[0];
                 loadWithChromiumLinker(appInfo, library);
             } else {
+                Log.d(TAG, "Loading with the System linker.");
                 loadWithSystemLinkerAlreadyLocked(appInfo);
             }
 
@@ -552,7 +606,7 @@
 
     // Called after all native initializations are complete.
     public void onBrowserNativeInitializationComplete() {
-        if (LibraryLoaderConfig.useChromiumLinker()) {
+        if (mUseChromiumLinker) {
             RecordHistogram.recordTimesHistogram(
                     "ChromiumAndroidLinker.BrowserLoadTime", mLibraryLoadTimeMs);
         }
@@ -563,7 +617,7 @@
     // time they are captured. This function stores a pending value, so that a later call to
     // RecordChromiumAndroidLinkerRendererHistogram() will record it correctly.
     public void registerRendererProcessHistogram() {
-        if (!LibraryLoaderConfig.useChromiumLinker()) return;
+        if (!mUseChromiumLinker) return;
         synchronized (mLock) {
             LibraryLoaderJni.get().recordRendererLibraryLoadTime(mLibraryLoadTimeMs);
         }
diff --git a/base/android/java/src/org/chromium/base/library_loader/LibraryLoaderConfig.java b/base/android/java/src/org/chromium/base/library_loader/LibraryLoaderConfig.java
deleted file mode 100644
index 521ff1d..0000000
--- a/base/android/java/src/org/chromium/base/library_loader/LibraryLoaderConfig.java
+++ /dev/null
@@ -1,35 +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.
-
-package org.chromium.base.library_loader;
-
-/**
- * Build-time configuration of LibraryLoader.
- * These are in a separate class from LibraryLoader to ensure that they are inlined.
- */
-// TODO(agrieve): Add @CheckDiscard once trichrome no longer requires a -keep for NativeLibraries:
-//     https://cs.chromium.org/chromium/src/chrome/android/java/trichrome.flags?rcl=233c4879107285b519cb55b3e9a7f439b15a418f
-public class LibraryLoaderConfig {
-    private LibraryLoaderConfig() {}
-
-    /**
-     * Check that native library linker tests are enabled.
-     * If not enabled, calls to testing functions will fail with an assertion
-     * error.
-     *
-     * @return true if native library linker tests are enabled.
-     */
-    public static boolean areTestsEnabled() {
-        return NativeLibraries.sEnableLinkerTests;
-    }
-
-    /**
-     * Call this method to determine if this chromium project must
-     * use this linker. If not, System.loadLibrary() should be used to load
-     * libraries instead.
-     */
-    public static boolean useChromiumLinker() {
-        return NativeLibraries.sUseLinker;
-    }
-}
diff --git a/base/android/java/src/org/chromium/base/library_loader/Linker.java b/base/android/java/src/org/chromium/base/library_loader/Linker.java
index 7d3ad4b1..5e1ed8c 100644
--- a/base/android/java/src/org/chromium/base/library_loader/Linker.java
+++ b/base/android/java/src/org/chromium/base/library_loader/Linker.java
@@ -248,7 +248,7 @@
                         ContextUtils.getApplicationContext().getApplicationInfo().className;
                 boolean isIncrementalInstall =
                         appClass != null && appClass.contains("incrementalinstall");
-                if (NativeLibraries.sUseModernLinker && !isIncrementalInstall) {
+                if (LibraryLoader.getInstance().useModernLinker() && !isIncrementalInstall) {
                     sSingleton = new ModernLinker();
                 } else {
                     sSingleton = new LegacyLinker();
diff --git a/base/threading/thread_restrictions.h b/base/threading/thread_restrictions.h
index a405d67..e2b9fd0 100644
--- a/base/threading/thread_restrictions.h
+++ b/base/threading/thread_restrictions.h
@@ -211,6 +211,9 @@
 namespace rlz_lib {
 class FinancialPing;
 }
+namespace syncer {
+class GetLocalChangesRequest;
+}
 namespace ui {
 class CommandBufferClientImpl;
 class CommandBufferLocal;
@@ -418,6 +421,7 @@
   friend class rlz_lib::FinancialPing;
   friend class shell_integration_linux::
       LaunchXdgUtilityScopedAllowBaseSyncPrimitives;
+  friend class syncer::GetLocalChangesRequest;
   friend class webrtc::DesktopConfigurationMonitor;
 
   // Usage that should be fixed:
diff --git a/build/android/gyp/native_libraries_template.py b/build/android/gyp/native_libraries_template.py
index 0a70d95..cd966cd 100644
--- a/build/android/gyp/native_libraries_template.py
+++ b/build/android/gyp/native_libraries_template.py
@@ -10,16 +10,16 @@
 package org.chromium.base.library_loader;
 
 public class NativeLibraries {{
-    public static final int CPU_FAMILY_UNKNOWN = 0;
-    public static final int CPU_FAMILY_ARM = 1;
-    public static final int CPU_FAMILY_MIPS = 2;
-    public static final int CPU_FAMILY_X86 = 3;
+    static final int CPU_FAMILY_UNKNOWN = 0;
+    static final int CPU_FAMILY_ARM = 1;
+    static final int CPU_FAMILY_MIPS = 2;
+    static final int CPU_FAMILY_X86 = 3;
 
     // Set to true to enable the use of the Chromium Linker.
-    public static {MAYBE_FINAL}boolean sUseLinker{USE_LINKER};
-    public static {MAYBE_FINAL}boolean sUseLibraryInZipFile{USE_LIBRARY_IN_ZIP_FILE};
-    public static {MAYBE_FINAL}boolean sEnableLinkerTests{ENABLE_LINKER_TESTS};
-    public static {MAYBE_FINAL}boolean sUseModernLinker{USE_MODERN_LINKER};
+    static {MAYBE_FINAL}boolean sUseLinker{USE_LINKER};
+    static {MAYBE_FINAL}boolean sUseLibraryInZipFile{USE_LIBRARY_IN_ZIP_FILE};
+    static {MAYBE_FINAL}boolean sEnableLinkerTests{ENABLE_LINKER_TESTS};
+    static {MAYBE_FINAL}boolean sUseModernLinker{USE_MODERN_LINKER};
 
     // This is the list of native libraries to be loaded (in the correct order)
     // by LibraryLoader.java.
@@ -30,11 +30,8 @@
     // This is the expected version of the 'main' native library, which is the one that
     // implements the initial set of base JNI functions including
     // base::android::nativeGetVersionName()
-    // TODO(torne): This is public to work around classloader issues in Trichrome
-    // where NativeLibraries is not in the same dex as LibraryLoader.
-    // We should instead split up Java code along package boundaries.
-    public static {MAYBE_FINAL}String sVersionNumber = {VERSION_NUMBER};
+    static {MAYBE_FINAL}String sVersionNumber = {VERSION_NUMBER};
 
-    public static {MAYBE_FINAL}int sCpuFamily = {CPU_FAMILY};
+    static {MAYBE_FINAL}int sCpuFamily = {CPU_FAMILY};
 }}
 """
diff --git a/build/android/java/templates/LocaleConfig.template b/build/android/java/templates/ProductConfig.template
similarity index 62%
rename from build/android/java/templates/LocaleConfig.template
rename to build/android/java/templates/ProductConfig.template
index 95f6051..cf4ba125 100644
--- a/build/android/java/templates/LocaleConfig.template
+++ b/build/android/java/templates/ProductConfig.template
@@ -4,10 +4,20 @@
 
 package PACKAGE;
 
+#if defined(USE_FINAL)
+#define MAYBE_FINAL final
+#define MAYBE_USE_CHROMIUM_LINKER = USE_CHROMIUM_LINKER_VALUE
+#define MAYBE_USE_MODERN_LINKER = USE_MODERN_LINKER_VALUE
+#else
+#define MAYBE_FINAL
+#define MAYBE_USE_CHROMIUM_LINKER
+#define MAYBE_USE_MODERN_LINKER
+#endif
+
 /**
- *  Locale configuration. Generated on a per-target basis.
+ *  Product configuration. Generated on a per-target basis.
  */
-public class LocaleConfig {
+public class ProductConfig {
 
     // Sorted list of locales that have a compressed .pak within assets.
     // Stored as an array because AssetManager.list() is slow.
@@ -24,4 +34,7 @@
 #else
     public static final String[] UNCOMPRESSED_LOCALES = {};
 #endif
+
+   public static MAYBE_FINAL boolean USE_CHROMIUM_LINKER MAYBE_USE_CHROMIUM_LINKER;
+   public static MAYBE_FINAL boolean USE_MODERN_LINKER MAYBE_USE_MODERN_LINKER;
 }
diff --git a/build/config/android/rules.gni b/build/config/android/rules.gni
index bd90cea..b0ec557 100644
--- a/build/config/android/rules.gni
+++ b/build/config/android/rules.gni
@@ -1973,19 +1973,40 @@
     }
   }
 
-  # Creates LocaleConfig.java, a file containing the list of compressed and
-  # uncompressed locale .pak files in an APK.
+  # Creates ProductConfig.java, a file containing product-specific configuration.
+  #
+  # Currently, this includes the list of locales, both in their compressed and
+  # uncompressed format, as well as library loading
   #
   # Variables:
   #   build_config: Path to build_config used for locale lists.
   #   java_package: Java package for the generated class.
-  template("generate_locale_config_srcjar") {
+  #   use_chromium_linker:
+  #   use_modern_linker:
+  template("generate_product_config_srcjar") {
     java_cpp_template(target_name) {
+      defines = []
+      _use_final = defined(invoker.build_config) ||
+                   defined(invoker.use_chromium_linker) ||
+                   defined(invoker.use_modern_linker)
+      if (_use_final) {
+        defines += [ "USE_FINAL" ]
+      }
+
       package_path = string_replace(invoker.java_package, ".", "/")
       sources = [
-        "//build/android/java/templates/LocaleConfig.template",
+        "//build/android/java/templates/ProductConfig.template",
       ]
-      defines = [ "PACKAGE=${invoker.java_package}" ]
+      defines += [ "PACKAGE=${invoker.java_package}" ]
+
+      _use_chromium_linker =
+          defined(invoker.use_chromium_linker) && invoker.use_chromium_linker
+      _use_modern_linker =
+          defined(invoker.use_modern_linker) && invoker.use_modern_linker
+      defines += [
+        "USE_CHROMIUM_LINKER_VALUE=$_use_chromium_linker",
+        "USE_MODERN_LINKER_VALUE=$_use_modern_linker",
+      ]
       if (defined(invoker.build_config)) {
         forward_variables_from(invoker,
                                [
@@ -2099,9 +2120,8 @@
   #   enable_native_mocks: Allow native calls using
   #     org.chromium.base.annotations.NativeMethods to be mocked in tests
   #     (optional).
-  #   locale_config_java_packages: Optional list of java packages. If given, a
-  #     LocaleConfig.java file will be generated for each package, and will
-  #     contain the list of compressed and uncompressed locale pak files.
+  #   product_config_java_packages: Optional list of java packages. If given, a
+  #     ProductConfig.java file will be generated for each package.
   #   disable_r8_outlining: Turn off outlining during the proguard step.
   #   annotation_processor_deps: List of java_annotation_processor targets to
   #     use when compiling the java_files given to this target (optional).
@@ -2316,7 +2336,7 @@
       _generate_buildconfig_java = invoker.generate_buildconfig_java
     }
 
-    _generate_localeconfig_java = defined(invoker.locale_config_java_packages)
+    _generate_productconfig_java = defined(invoker.product_config_java_packages)
 
     # JNI generation usually goes hand-in-hand with buildconfig generation.
     _generate_final_jni = _generate_buildconfig_java
@@ -2699,13 +2719,15 @@
       _srcjar_deps += [ ":${_template_name}__build_config_srcjar" ]
     }
 
-    if (_generate_localeconfig_java) {
-      foreach(_package, invoker.locale_config_java_packages) {
+    if (_generate_productconfig_java) {
+      foreach(_package, invoker.product_config_java_packages) {
         _locale_target_name =
-            "${_template_name}_${_package}__locale_config_srcjar"
-        generate_locale_config_srcjar("$_locale_target_name") {
+            "${_template_name}_${_package}__product_config_srcjar"
+        generate_product_config_srcjar("$_locale_target_name") {
           build_config = _build_config
           java_package = _package
+          use_chromium_linker = _use_chromium_linker
+          use_modern_linker = _use_modern_linker
           deps = [
             ":$_build_config_target",
           ]
@@ -3404,7 +3426,7 @@
                                "loadable_modules",
                                "manifest_package",
                                "max_sdk_version",
-                               "locale_config_java_packages",
+                               "product_config_java_packages",
                                "min_sdk_version",
                                "native_lib_placeholders",
                                "native_lib_version_arg",
@@ -3528,7 +3550,7 @@
                                "jni_sources_blacklist",
                                "load_library_from_apk",
                                "loadable_modules",
-                               "locale_config_java_packages",
+                               "product_config_java_packages",
                                "manifest_package",
                                "max_sdk_version",
                                "min_sdk_version",
@@ -3565,6 +3587,7 @@
                                "testonly",
                                "uncompress_shared_libraries",
                                "use_chromium_linker",
+                               "use_modern_linker",
                                "verify_manifest",
                                "version_code",
                                "version_name",
diff --git a/build/fuchsia/linux.sdk.sha1 b/build/fuchsia/linux.sdk.sha1
index 1ca524b..cf7ba58 100644
--- a/build/fuchsia/linux.sdk.sha1
+++ b/build/fuchsia/linux.sdk.sha1
@@ -1 +1 @@
-8897481650150071824
\ No newline at end of file
+8897466458026657776
\ No newline at end of file
diff --git a/build/fuchsia/mac.sdk.sha1 b/build/fuchsia/mac.sdk.sha1
index 8d96a2a..e7f9e6f 100644
--- a/build/fuchsia/mac.sdk.sha1
+++ b/build/fuchsia/mac.sdk.sha1
@@ -1 +1 @@
-8897481882837156320
\ No newline at end of file
+8897472768194582624
\ No newline at end of file
diff --git a/chrome/android/BUILD.gn b/chrome/android/BUILD.gn
index e6d102f..25b9be88 100644
--- a/chrome/android/BUILD.gn
+++ b/chrome/android/BUILD.gn
@@ -354,7 +354,7 @@
     ":chrome_android_java_enums_srcjar",
     ":chrome_android_java_switches_srcjar",
     ":chrome_android_java_google_api_keys_srcjar",
-    ":chrome_locale_config",
+    ":chrome_product_config",
     ":photo_picker_aidl",
     ":resource_id_javagen",
     ":sync_user_settings_enums_java",
@@ -429,7 +429,7 @@
   # can provide their own implementations.
   jar_excluded_patterns = [
     "*/AppHooksImpl.class",
-    "*/LocaleConfig.class",
+    "*/ProductConfig.class",
   ]
 
   annotation_processor_deps = [
@@ -445,7 +445,7 @@
   java_files += share_java_sources
 }
 
-generate_locale_config_srcjar("chrome_locale_config") {
+generate_product_config_srcjar("chrome_product_config") {
   java_package = "org.chromium.chrome.browser"
 }
 
@@ -1738,6 +1738,8 @@
                              "resource_ids_provider_dep",
                              "static_library_provider",
                              "target_type",
+                             "use_chromium_linker",
+                             "use_modern_linker",
                              "use_trichrome_library",
                              "verify_manifest",
                              "failed_manifest_expectation_file",
diff --git a/chrome/android/chrome_public_apk_tmpl.gni b/chrome/android/chrome_public_apk_tmpl.gni
index a8d2d27a8..27223c0 100644
--- a/chrome/android/chrome_public_apk_tmpl.gni
+++ b/chrome/android/chrome_public_apk_tmpl.gni
@@ -164,7 +164,7 @@
     ]
 
     if (!_is_monochrome) {
-      locale_config_java_packages = [ "org.chromium.chrome.browser" ]
+      product_config_java_packages = [ "org.chromium.chrome.browser" ]
     }
 
     # Use zh-TW strings for zh-HK (https://crbug.com/780847).
@@ -339,10 +339,10 @@
     ]
 
     if (is_monochrome) {
-      locale_config_java_packages = [
+      product_config_java_packages = [
         "org.chromium.chrome.browser",
-        webview_locale_config_java_package,
-        weblayer_locale_config_java_package,
+        webview_product_config_java_package,
+        weblayer_product_config_java_package,
       ]
       if (android_64bit_target_cpu) {
         # Build //android_webview:monochrome with the opposite bitness that
diff --git a/chrome/android/features/start_surface/internal/javatests/src/org/chromium/chrome/features/start_surface/StartSurfaceLayoutTest.java b/chrome/android/features/start_surface/internal/javatests/src/org/chromium/chrome/features/start_surface/StartSurfaceLayoutTest.java
index d6d9a09..2f9bab8 100644
--- a/chrome/android/features/start_surface/internal/javatests/src/org/chromium/chrome/features/start_surface/StartSurfaceLayoutTest.java
+++ b/chrome/android/features/start_surface/internal/javatests/src/org/chromium/chrome/features/start_surface/StartSurfaceLayoutTest.java
@@ -43,6 +43,7 @@
 import org.chromium.base.metrics.RecordHistogram;
 import org.chromium.base.test.util.CommandLineFlags;
 import org.chromium.base.test.util.DisabledTest;
+import org.chromium.base.test.util.Feature;
 import org.chromium.base.test.util.MinAndroidSdkLevel;
 import org.chromium.base.test.util.Restriction;
 import org.chromium.chrome.browser.ChromeFeatureList;
@@ -55,12 +56,14 @@
 import org.chromium.chrome.browser.tabmodel.TabModel;
 import org.chromium.chrome.browser.tabmodel.TabSelectionType;
 import org.chromium.chrome.browser.tasks.tab_management.TabSwitcher;
+import org.chromium.chrome.browser.tasks.tab_management.TabUiTestHelper;
 import org.chromium.chrome.tab_ui.R;
 import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
 import org.chromium.chrome.test.ChromeTabbedActivityTestRule;
 import org.chromium.chrome.test.util.ApplicationTestUtils;
 import org.chromium.chrome.test.util.ChromeTabUtils;
 import org.chromium.chrome.test.util.MenuUtils;
+import org.chromium.chrome.test.util.RenderTestRule;
 import org.chromium.chrome.test.util.browser.Features;
 import org.chromium.content_public.browser.test.util.Criteria;
 import org.chromium.content_public.browser.test.util.CriteriaHelper;
@@ -70,6 +73,7 @@
 import org.chromium.ui.test.util.UiRestriction;
 
 import java.io.File;
+import java.io.IOException;
 import java.lang.ref.WeakReference;
 import java.util.LinkedList;
 import java.util.List;
@@ -92,6 +96,9 @@
     @Rule
     public TestRule mProcessor = new Features.InstrumentationProcessor();
 
+    @Rule
+    public RenderTestRule mRenderTestRule = new RenderTestRule();
+
     private StartSurfaceLayout mStartSurfaceLayout;
     private String mUrl;
     private int mRepeat;
@@ -130,7 +137,9 @@
                         .getCurrentTabModelFilter()::isTabModelRestored));
 
         assertEquals(0, mTabListDelegate.getBitmapFetchCountForTesting());
-        // Only skip thumbnail releasing assertion when "warm" (large soft-cleanup-delay).
+        // Only skip thumbnail releasing assertion when "warm" (large soft-cleanup-delay) or in
+        // RenderTest.
+        // TODO(wychen): figure out why thumbnails are not released in RenderTest.
         mSkipAssertThumbnailsAreReleased = false;
     }
 
@@ -141,6 +150,79 @@
 
     @Test
     @MediumTest
+    @Feature({"RenderTest"})
+    @CommandLineFlags.Add({BASE_PARAMS})
+    public void testRenderGrid_3WebTabs() throws InterruptedException, IOException {
+        mSkipAssertThumbnailsAreReleased = true;
+
+        prepareTabs(3, 0, mUrl);
+        TabUiTestHelper.enterTabSwitcher(mActivityTestRule.getActivity());
+        TabUiTestHelper.clickFirstCardFromTabSwitcher(mActivityTestRule.getActivity());
+
+        enterGTS();
+        mRenderTestRule.render(mActivityTestRule.getActivity().findViewById(
+                                       org.chromium.chrome.tab_ui.R.id.tab_list_view),
+                "3_web_tabs");
+    }
+
+    @Test
+    @MediumTest
+    @Feature({"RenderTest"})
+    @CommandLineFlags.Add({BASE_PARAMS})
+    public void testRenderGrid_10WebTabs() throws InterruptedException, IOException {
+        mSkipAssertThumbnailsAreReleased = true;
+
+        prepareTabs(10, 0, mUrl);
+        TabUiTestHelper.enterTabSwitcher(mActivityTestRule.getActivity());
+        TabUiTestHelper.clickFirstCardFromTabSwitcher(mActivityTestRule.getActivity());
+
+        enterGTS();
+        mRenderTestRule.render(mActivityTestRule.getActivity().findViewById(
+                                       org.chromium.chrome.tab_ui.R.id.tab_list_view),
+                "10_web_tabs");
+    }
+
+    @Test
+    @MediumTest
+    @Feature({"RenderTest"})
+    @CommandLineFlags.Add({BASE_PARAMS})
+    public void testRenderGrid_10WebTabs_InitialScroll() throws InterruptedException, IOException {
+        mSkipAssertThumbnailsAreReleased = true;
+
+        prepareTabs(10, 0, mUrl);
+        TabUiTestHelper.enterTabSwitcher(mActivityTestRule.getActivity());
+        TabUiTestHelper.clickNthCardFromTabSwitcher(mActivityTestRule.getActivity(),
+                mActivityTestRule.getActivity().getTabModelSelector().getCurrentModel().getCount()
+                        - 1);
+
+        enterGTS();
+        // Make sure the grid tab switcher is scrolled down to show the selected tab.
+        mRenderTestRule.render(mActivityTestRule.getActivity().findViewById(
+                                       org.chromium.chrome.tab_ui.R.id.tab_list_view),
+                "10_web_tabs-select_last");
+    }
+
+    @Test
+    @MediumTest
+    @Feature({"RenderTest"})
+    @CommandLineFlags.Add({BASE_PARAMS})
+    public void testRenderGrid_Incognito() throws InterruptedException, IOException {
+        mSkipAssertThumbnailsAreReleased = true;
+
+        // Prepare some incognito tabs and enter tab switcher.
+        prepareTabs(1, 3, mUrl);
+        assertTrue(mActivityTestRule.getActivity().getCurrentTabModel().isIncognito());
+        TabUiTestHelper.enterTabSwitcher(mActivityTestRule.getActivity());
+        TabUiTestHelper.clickFirstCardFromTabSwitcher(mActivityTestRule.getActivity());
+
+        enterGTS();
+        mRenderTestRule.render(mActivityTestRule.getActivity().findViewById(
+                                       org.chromium.chrome.tab_ui.R.id.tab_list_view),
+                "3_incognito_web_tabs");
+    }
+
+    @Test
+    @MediumTest
     // clang-format off
     @Features.DisableFeatures(ChromeFeatureList.TAB_TO_GTS_ANIMATION)
     @CommandLineFlags.Add({BASE_PARAMS})
@@ -730,7 +812,6 @@
         } else {
             // The final capture at StartSurfaceLayout#finishedShowing time.
             delta = 1;
-            // TODO(wychen): refactor areAnimatorsEnabled() to a util class.
             if (ChromeFeatureList.isEnabled(ChromeFeatureList.TAB_TO_GTS_ANIMATION)
                     && areAnimatorsEnabled()) {
                 // The faster capturing without writing back to cache.
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogMediator.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogMediator.java
index 8fbfcd1..3f7a49e 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogMediator.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogMediator.java
@@ -102,6 +102,7 @@
     private int mCurrentTabId = Tab.INVALID_TAB_ID;
     private boolean mIsUpdatingTitle;
     private String mCurrentGroupModifiedTitle;
+    private Callback<Integer> mToolbarMenuCallback;
 
     TabGridDialogMediator(Context context, DialogController dialogController, PropertyModel model,
             TabModelSelector tabModelSelector, TabCreatorManager tabCreatorManager,
@@ -198,6 +199,14 @@
         assert mTabModelSelector.getTabModelFilterProvider().getCurrentTabModelFilter()
                         instanceof TabGroupModelFilter;
 
+        mToolbarMenuCallback = result -> {
+            if (result == R.id.ungroup_tab) {
+                mModel.set(TabGridPanelProperties.IS_POPUP_WINDOW_FOCUSABLE, false);
+                List<Tab> tabs = getRelatedTabs(mCurrentTabId);
+                mTabSelectionEditorController.show(tabs);
+            }
+        };
+
         // Setup toolbar button click listeners.
         setupToolbarClickHandlers();
 
@@ -411,13 +420,8 @@
 
     private View.OnClickListener getMenuButtonClickListener() {
         assert mTabSelectionEditorController != null;
-        Callback<Integer> callback = result -> {
-            if (result == R.id.ungroup_tab) {
-                List<Tab> tabs = getRelatedTabs(mCurrentTabId);
-                mTabSelectionEditorController.show(tabs);
-            }
-        };
-        return TabGridDialogMenuCoordinator.getTabGridDialogMenuOnClickListener(callback);
+        return TabGridDialogMenuCoordinator.getTabGridDialogMenuOnClickListener(
+                mToolbarMenuCallback);
     }
 
     private List<Tab> getRelatedTabs(int tabId) {
@@ -502,4 +506,9 @@
     String getCurrentGroupModifiedTitleForTesting() {
         return mCurrentGroupModifiedTitle;
     }
+
+    @VisibleForTesting
+    Callback<Integer> getToolbarMenuCallbackForTesting() {
+        return mToolbarMenuCallback;
+    }
 }
diff --git a/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogTest.java b/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogTest.java
index b448a750..3c5f2e79 100644
--- a/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogTest.java
+++ b/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogTest.java
@@ -20,7 +20,7 @@
 import static org.junit.Assert.assertTrue;
 
 import static org.chromium.chrome.browser.tasks.tab_management.TabUiTestHelper.CardCountAssertion;
-import static org.chromium.chrome.browser.tasks.tab_management.TabUiTestHelper.clickFirstTabFromTabSwitcher;
+import static org.chromium.chrome.browser.tasks.tab_management.TabUiTestHelper.clickFirstCardFromTabSwitcher;
 import static org.chromium.chrome.browser.tasks.tab_management.TabUiTestHelper.createOverviewHideWatcher;
 import static org.chromium.chrome.browser.tasks.tab_management.TabUiTestHelper.createTabGroup;
 import static org.chromium.chrome.browser.tasks.tab_management.TabUiTestHelper.createTabs;
@@ -111,7 +111,7 @@
 
         // Enter first tab page.
         assertTrue(cta.getLayoutManager().overviewVisible());
-        clickFirstTabFromTabSwitcher(cta);
+        clickFirstCardFromTabSwitcher(cta);
         clickFirstTabFromDialog(cta);
         // Open dialog from tab strip and verify dialog is showing correct content.
         openDialogFromStripAndVerify(cta, 2);
@@ -176,7 +176,7 @@
     }
 
     private void openDialogFromTabSwitcherAndVerify(ChromeTabbedActivity cta, int tabCount) {
-        clickFirstTabFromTabSwitcher(cta);
+        clickFirstCardFromTabSwitcher(cta);
         CriteriaHelper.pollInstrumentationThread(() -> isDialogShowing(cta));
         verifyShowingDialog(cta, tabCount);
     }
diff --git a/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabSelectionEditorTest.java b/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabSelectionEditorTest.java
index 59eb2fe..1565ff4 100644
--- a/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabSelectionEditorTest.java
+++ b/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabSelectionEditorTest.java
@@ -4,6 +4,7 @@
 
 package org.chromium.chrome.browser.tasks.tab_management;
 
+import android.os.Build;
 import android.support.test.filters.MediumTest;
 
 import org.junit.Before;
@@ -13,6 +14,8 @@
 import org.junit.runner.RunWith;
 
 import org.chromium.base.test.util.CommandLineFlags;
+import org.chromium.base.test.util.DisableIf;
+import org.chromium.base.test.util.Restriction;
 import org.chromium.chrome.browser.ChromeSwitches;
 import org.chromium.chrome.browser.flags.FeatureUtilities;
 import org.chromium.chrome.browser.tab.Tab;
@@ -23,6 +26,7 @@
 import org.chromium.chrome.test.ChromeTabbedActivityTestRule;
 import org.chromium.chrome.test.util.browser.Features;
 import org.chromium.content_public.browser.test.util.TestThreadUtils;
+import org.chromium.ui.test.util.UiRestriction;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -31,6 +35,9 @@
  * End-to-end test for TabSelectionEditor.
  */
 @RunWith(ChromeJUnit4ClassRunner.class)
+@Restriction(UiRestriction.RESTRICTION_TYPE_PHONE)
+@DisableIf.Build(message = "crbug.com/1022200", sdk_is_greater_than = Build.VERSION_CODES.KITKAT,
+        sdk_is_less_than = Build.VERSION_CODES.M)
 @CommandLineFlags.Add({ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE})
 public class TabSelectionEditorTest {
     @Rule
diff --git a/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabUiTestHelper.java b/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabUiTestHelper.java
index 6dfb391..cf8a3254 100644
--- a/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabUiTestHelper.java
+++ b/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabUiTestHelper.java
@@ -61,7 +61,7 @@
      * Enter tab switcher from a tab page.
      * @param cta  The current running activity.
      */
-    static void enterTabSwitcher(ChromeTabbedActivity cta) {
+    public static void enterTabSwitcher(ChromeTabbedActivity cta) {
         OverviewModeBehaviorWatcher showWatcher = createOverviewShowWatcher(cta);
         assertFalse(cta.getLayoutManager().overviewVisible());
         // TODO(crbug.com/1019727) Figure out a better way to wait until isCompletelyDisplayed() is
@@ -76,11 +76,21 @@
      * group, this will open up the dialog; otherwise this will open up the tab page.
      * @param cta  The current running activity.
      */
-    static void clickFirstTabFromTabSwitcher(ChromeTabbedActivity cta) {
+    public static void clickFirstCardFromTabSwitcher(ChromeTabbedActivity cta) {
+        clickNthCardFromTabSwitcher(cta, 0);
+    }
+
+    /**
+     * Click the Nth card in grid tab switcher. When group is enabled and the Nth card is a
+     * group, this will open up the dialog; otherwise this will open up the tab page.
+     * @param cta  The current running activity.
+     * @param index The index of the target card.
+     */
+    public static void clickNthCardFromTabSwitcher(ChromeTabbedActivity cta, int index) {
         assertTrue(cta.getLayoutManager().overviewVisible());
         onView(allOf(withParent(withId(org.chromium.chrome.R.id.compositor_view_holder)),
                        withId(R.id.tab_list_view)))
-                .perform(RecyclerViewActions.actionOnItemAtPosition(0, click()));
+                .perform(RecyclerViewActions.actionOnItemAtPosition(index, click()));
     }
 
     /**
diff --git a/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogMediatorUnitTest.java b/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogMediatorUnitTest.java
index d1dfa27..344e5b74 100644
--- a/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogMediatorUnitTest.java
+++ b/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogMediatorUnitTest.java
@@ -6,6 +6,7 @@
 
 import static org.hamcrest.CoreMatchers.equalTo;
 import static org.hamcrest.CoreMatchers.instanceOf;
+import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertThat;
 import static org.mockito.ArgumentMatchers.any;
@@ -42,6 +43,7 @@
 import org.mockito.MockitoAnnotations;
 import org.robolectric.annotation.Config;
 
+import org.chromium.base.Callback;
 import org.chromium.base.metrics.RecordHistogram;
 import org.chromium.base.metrics.RecordUserAction;
 import org.chromium.chrome.browser.ChromeFeatureList;
@@ -919,6 +921,23 @@
     }
 
     @Test
+    @Features.EnableFeatures(ChromeFeatureList.TAB_GROUPS_CONTINUATION_ANDROID)
+    public void testDialogToolbarMenu_SelectionMode() {
+        Callback<Integer> callback = mMediator.getToolbarMenuCallbackForTesting();
+        // Mock that currently the popup window is focusable, and the current tab is tab1 which is
+        // in a group of {tab1, tab2}.
+        mModel.set(TabGridPanelProperties.IS_POPUP_WINDOW_FOCUSABLE, true);
+        mMediator.setCurrentTabIdForTest(TAB1_ID);
+        List<Tab> tabgroup = new ArrayList<>(Arrays.asList(mTab1, mTab2));
+        createTabGroup(tabgroup, TAB1_ID);
+
+        callback.onResult(R.id.ungroup_tab);
+
+        assertFalse(mModel.get(TabGridPanelProperties.IS_POPUP_WINDOW_FOCUSABLE));
+        verify(mTabSelectionEditorController).show(eq(tabgroup));
+    }
+
+    @Test
     public void destroy() {
         mMediator.destroy();
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ChromeApplication.java b/chrome/android/java/src/org/chromium/chrome/browser/ChromeApplication.java
index fa2f938..9a8fe59b 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ChromeApplication.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ChromeApplication.java
@@ -22,6 +22,7 @@
 import org.chromium.base.PathUtils;
 import org.chromium.base.TraceEvent;
 import org.chromium.base.annotations.MainDex;
+import org.chromium.base.library_loader.LibraryLoader;
 import org.chromium.base.memory.MemoryPressureMonitor;
 import org.chromium.base.multidex.ChromiumMultiDexInstaller;
 import org.chromium.base.task.AsyncTask;
@@ -139,7 +140,9 @@
         AsyncTask.takeOverAndroidThreadPool();
         JNIUtils.setClassLoader(getClassLoader());
         ResourceBundle.setAvailablePakLocales(
-                LocaleConfig.COMPRESSED_LOCALES, LocaleConfig.UNCOMPRESSED_LOCALES);
+                ProductConfig.COMPRESSED_LOCALES, ProductConfig.UNCOMPRESSED_LOCALES);
+        LibraryLoader.getInstance().setConfiguration(
+                ProductConfig.USE_CHROMIUM_LINKER, ProductConfig.USE_MODERN_LINKER);
 
         if (isBrowserProcess) {
             TraceEvent.end("ChromeApplication.attachBaseContext");
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/metrics/StartupLoadingMetricsTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/metrics/StartupLoadingMetricsTest.java
index 0a4ca70..0df99d9 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/metrics/StartupLoadingMetricsTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/metrics/StartupLoadingMetricsTest.java
@@ -19,7 +19,7 @@
 
 import org.chromium.base.Log;
 import org.chromium.base.compat.ApiHelperForM;
-import org.chromium.base.library_loader.LibraryLoaderConfig;
+import org.chromium.base.library_loader.LibraryLoader;
 import org.chromium.base.library_loader.LoadStatusRecorder.LoadLibraryStatus;
 import org.chromium.base.metrics.RecordHistogram;
 import org.chromium.base.test.util.CommandLineFlags;
@@ -158,7 +158,7 @@
         assertHistogramsRecorded(1, TABBED_SUFFIX);
 
         // LibraryLoader checks.
-        if (!LibraryLoaderConfig.useChromiumLinker()) {
+        if (!LibraryLoader.getInstance().useChromiumLinker()) {
             Log.w(TAG, "Skipping test because not using ChromiumLinker.");
             return;
         }
diff --git a/chrome/android/profiles/newest.txt b/chrome/android/profiles/newest.txt
index 5afc5c09..b50f8bbf 100644
--- a/chrome/android/profiles/newest.txt
+++ b/chrome/android/profiles/newest.txt
@@ -1 +1 @@
-chromeos-chrome-amd64-80.0.3960.0_rc-r1-merged.afdo.bz2
\ No newline at end of file
+chromeos-chrome-amd64-80.0.3961.0_rc-r1-merged.afdo.bz2
\ No newline at end of file
diff --git a/chrome/app/chromeos_strings.grdp b/chrome/app/chromeos_strings.grdp
index 7413a825..578a8f1 100644
--- a/chrome/app/chromeos_strings.grdp
+++ b/chrome/app/chromeos_strings.grdp
@@ -4103,7 +4103,7 @@
 
   <!-- Strings for login screen extension UI -->
   <message name="IDS_LOGIN_EXTENSION_UI_DIALOG_TITLE" desc="Dialog title for windows opened from a login screen extension. These windows will be displayed over the login screen.">
-    This authentication service is hosted by <ph name="EXTENSION_NAME">$1<ex>Identity Provider Name</ex></ph>
+    Login provided by <ph name="EXTENSION_NAME">$1<ex>Identity Provider Name</ex></ph>
   </message>
 
   <!-- Strings for notifications about features which aren't supported in Crostini -->
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn
index e3fa85b..8143e61 100644
--- a/chrome/browser/BUILD.gn
+++ b/chrome/browser/BUILD.gn
@@ -284,8 +284,6 @@
     "client_hints/client_hints.h",
     "client_hints/client_hints_factory.cc",
     "client_hints/client_hints_factory.h",
-    "client_hints/client_hints_observer.cc",
-    "client_hints/client_hints_observer.h",
     "clipboard/clipboard_read_permission_context.cc",
     "clipboard/clipboard_read_permission_context.h",
     "clipboard/clipboard_write_permission_context.cc",
@@ -3725,6 +3723,7 @@
     deps += [
       "//ash/public/cpp",
       "//chrome/browser/chromeos",
+      "//chrome/services/app_service/public/cpp:instance_update",
       "//chromeos/components/account_manager",
       "//chromeos/components/sync_wifi",
       "//chromeos/services/assistant/public:feature_flags",
@@ -3819,7 +3818,6 @@
       "//components/browser_watcher:browser_watcher_client",
       "//components/browser_watcher:stability_client",
       "//components/chrome_cleaner/public/constants",
-      "//components/chrome_cleaner/public/interfaces",
       "//components/download/quarantine",
       "//third_party/crashpad/crashpad/client:client",
       "//third_party/iaccessible2",
diff --git a/chrome/browser/accessibility/accessibility_ui.cc b/chrome/browser/accessibility/accessibility_ui.cc
index 927b4ad..110cca0 100644
--- a/chrome/browser/accessibility/accessibility_ui.cc
+++ b/chrome/browser/accessibility/accessibility_ui.cc
@@ -26,6 +26,7 @@
 #include "components/pref_registry/pref_registry_syncable.h"
 #include "components/prefs/pref_service.h"
 #include "content/public/browser/accessibility_tree_formatter.h"
+#include "content/public/browser/ax_event_notification_details.h"
 #include "content/public/browser/browser_accessibility_state.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/favicon_status.h"
@@ -348,11 +349,25 @@
   web_ui->AddMessageHandler(std::make_unique<AccessibilityUIMessageHandler>());
 }
 
-AccessibilityUI::~AccessibilityUI() {}
+AccessibilityUI::~AccessibilityUI() = default;
 
-AccessibilityUIMessageHandler::AccessibilityUIMessageHandler() {}
+AccessibilityUIObserver::AccessibilityUIObserver(
+    content::WebContents* web_contents,
+    std::vector<std::string>* event_logs)
+    : content::WebContentsObserver(web_contents), event_logs_(event_logs) {}
 
-AccessibilityUIMessageHandler::~AccessibilityUIMessageHandler() {}
+AccessibilityUIObserver::~AccessibilityUIObserver() = default;
+
+void AccessibilityUIObserver::AccessibilityEventReceived(
+    const content::AXEventNotificationDetails& details) {
+  for (const ui::AXEvent event : details.events) {
+    event_logs_->push_back(event.ToString());
+  }
+}
+
+AccessibilityUIMessageHandler::AccessibilityUIMessageHandler() = default;
+
+AccessibilityUIMessageHandler::~AccessibilityUIMessageHandler() = default;
 
 void AccessibilityUIMessageHandler::RegisterMessages() {
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
@@ -649,11 +664,15 @@
         base::BindRepeating(&AccessibilityUIMessageHandler::Callback,
                             base::Unretained(this)),
         true);
+    observer_ =
+        std::make_unique<AccessibilityUIObserver>(web_contents, &event_logs_);
   } else {
     web_contents->RecordAccessibilityEvents(
         base::BindRepeating(&AccessibilityUIMessageHandler::Callback,
                             base::Unretained(this)),
         false);
+    observer_.release();
+
     std::string event_logs_str;
     for (std::string log : event_logs_) {
       event_logs_str += log;
diff --git a/chrome/browser/accessibility/accessibility_ui.h b/chrome/browser/accessibility/accessibility_ui.h
index 545dffe6..e7ead6e 100644
--- a/chrome/browser/accessibility/accessibility_ui.h
+++ b/chrome/browser/accessibility/accessibility_ui.h
@@ -5,7 +5,12 @@
 #ifndef CHROME_BROWSER_ACCESSIBILITY_ACCESSIBILITY_UI_H_
 #define CHROME_BROWSER_ACCESSIBILITY_ACCESSIBILITY_UI_H_
 
+#include <memory>
+#include <string>
+#include <vector>
+
 #include "base/macros.h"
+#include "content/public/browser/web_contents_observer.h"
 #include "content/public/browser/web_ui_controller.h"
 #include "content/public/browser/web_ui_data_source.h"
 #include "content/public/browser/web_ui_message_handler.h"
@@ -14,16 +19,36 @@
 class ListValue;
 }  // namespace base
 
+namespace content {
+struct AXEventNotificationDetails;
+}  // namespace content
+
 namespace user_prefs {
 class PrefRegistrySyncable;
 }  // namespace user_prefs
 
+// Controls the accessibility web UI page.
 class AccessibilityUI : public content::WebUIController {
  public:
   explicit AccessibilityUI(content::WebUI* web_ui);
   ~AccessibilityUI() override;
 };
 
+// Observes accessibility events from web contents.
+class AccessibilityUIObserver : public content::WebContentsObserver {
+ public:
+  AccessibilityUIObserver(content::WebContents* web_contents,
+                          std::vector<std::string>* event_logs);
+  ~AccessibilityUIObserver() override;
+
+  void AccessibilityEventReceived(
+      const content::AXEventNotificationDetails& details) override;
+
+ private:
+  std::vector<std::string>* event_logs_;
+};
+
+// Manages messages sent from accessibility.js via json.
 class AccessibilityUIMessageHandler : public content::WebUIMessageHandler {
  public:
   AccessibilityUIMessageHandler();
@@ -35,6 +60,7 @@
 
  private:
   std::vector<std::string> event_logs_;
+  std::unique_ptr<AccessibilityUIObserver> observer_;
 
   void ToggleAccessibility(const base::ListValue* args);
   void SetGlobalFlag(const base::ListValue* args);
diff --git a/chrome/browser/app_mode/app_mode_utils.cc b/chrome/browser/app_mode/app_mode_utils.cc
index c831ea31..da7bad5 100644
--- a/chrome/browser/app_mode/app_mode_utils.cc
+++ b/chrome/browser/app_mode/app_mode_utils.cc
@@ -67,7 +67,9 @@
 bool IsRunningInForcedAppMode() {
   return GetForcedAppModeApp().has_value() ||
          base::CommandLine::ForCurrentProcess()->HasSwitch(
-             switches::kForceAndroidAppMode);
+             switches::kForceAndroidAppMode) ||
+         base::CommandLine::ForCurrentProcess()->HasSwitch(
+             switches::kForceWebAppMode);
 }
 
 bool IsRunningInForcedAppModeForApp(const std::string& app_id) {
diff --git a/chrome/browser/apps/app_service/app_service_proxy.cc b/chrome/browser/apps/app_service/app_service_proxy.cc
index 56eb47f..addf5e9 100644
--- a/chrome/browser/apps/app_service/app_service_proxy.cc
+++ b/chrome/browser/apps/app_service/app_service_proxy.cc
@@ -15,6 +15,7 @@
 #include "chrome/browser/apps/app_service/uninstall_dialog.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/services/app_service/app_service_impl.h"
+#include "chrome/services/app_service/public/cpp/instance_registry.h"
 #include "chrome/services/app_service/public/cpp/intent_filter_util.h"
 #include "chrome/services/app_service/public/cpp/intent_util.h"
 #include "chrome/services/app_service/public/mojom/types.mojom.h"
@@ -164,6 +165,12 @@
   return cache_;
 }
 
+#if defined(OS_CHROMEOS)
+apps::InstanceRegistry& AppServiceProxy::InstanceRegistry() {
+  return instance_registry_;
+}
+#endif
+
 apps::PreferredApps& AppServiceProxy::PreferredApps() {
   return preferred_apps_;
 }
diff --git a/chrome/browser/apps/app_service/app_service_proxy.h b/chrome/browser/apps/app_service/app_service_proxy.h
index c08d5f7..ca32f83 100644
--- a/chrome/browser/apps/app_service/app_service_proxy.h
+++ b/chrome/browser/apps/app_service/app_service_proxy.h
@@ -15,6 +15,7 @@
 #include "chrome/services/app_service/public/cpp/app_registry_cache.h"
 #include "chrome/services/app_service/public/cpp/icon_cache.h"
 #include "chrome/services/app_service/public/cpp/icon_coalescer.h"
+#include "chrome/services/app_service/public/cpp/instance_registry.h"
 #include "chrome/services/app_service/public/cpp/preferred_apps.h"
 #include "components/keyed_service/core/keyed_service.h"
 #include "mojo/public/cpp/bindings/pending_receiver.h"
@@ -54,6 +55,10 @@
   apps::AppRegistryCache& AppRegistryCache();
   apps::PreferredApps& PreferredApps();
 
+#if defined(OS_CHROMEOS)
+  apps::InstanceRegistry& InstanceRegistry();
+#endif
+
   // apps::IconLoader overrides.
   apps::mojom::IconKeyPtr GetIconKey(const std::string& app_id) override;
   std::unique_ptr<IconLoader::Releaser> LoadIconFromIconKey(
@@ -204,7 +209,10 @@
   std::unique_ptr<CrostiniApps> crostini_apps_;
   std::unique_ptr<ExtensionApps> extension_apps_;
   std::unique_ptr<ExtensionApps> extension_web_apps_;
+
   bool arc_is_registered_ = false;
+
+  apps::InstanceRegistry instance_registry_;
 #endif  // OS_CHROMEOS
 
   Profile* profile_;
diff --git a/chrome/browser/browsing_data/browsing_data_remover_browsertest.cc b/chrome/browser/browsing_data/browsing_data_remover_browsertest.cc
index ed1a2ed..26847ae 100644
--- a/chrome/browser/browsing_data/browsing_data_remover_browsertest.cc
+++ b/chrome/browser/browsing_data/browsing_data_remover_browsertest.cc
@@ -37,7 +37,6 @@
 #include "chrome/browser/profiles/profile_manager.h"
 #include "chrome/browser/signin/account_reconcilor_factory.h"
 #include "chrome/browser/signin/identity_manager_factory.h"
-#include "chrome/browser/signin/scoped_account_consistency.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
 #include "chrome/common/pref_names.h"
@@ -587,9 +586,6 @@
         identity_manager->HasAccountWithRefreshToken(account_info.account_id));
     return account_info;
   }
-
- private:
-  ScopedAccountConsistencyDice dice_;
 };
 #endif
 
diff --git a/chrome/browser/browsing_data/chrome_browsing_data_remover_delegate_unittest.cc b/chrome/browser/browsing_data/chrome_browsing_data_remover_delegate_unittest.cc
index f21bf87..8feb129 100644
--- a/chrome/browser/browsing_data/chrome_browsing_data_remover_delegate_unittest.cc
+++ b/chrome/browser/browsing_data/chrome_browsing_data_remover_delegate_unittest.cc
@@ -505,10 +505,11 @@
 
   void RegisterWithOSAsDefaultClient(
       const std::string& protocol,
-      ProtocolHandlerRegistry* registry) override {}
+      shell_integration::DefaultWebClientWorkerCallback callback) override {}
 
-  void CheckDefaultClientWithOS(const std::string& protocol,
-                                ProtocolHandlerRegistry* registry) override {}
+  void CheckDefaultClientWithOS(
+      const std::string& protocol,
+      shell_integration::DefaultWebClientWorkerCallback callback) override {}
 
  private:
   std::set<std::string> registered_protocols_;
diff --git a/chrome/browser/browsing_data/counters/site_settings_counter_unittest.cc b/chrome/browser/browsing_data/counters/site_settings_counter_unittest.cc
index 0411a06..190e731 100644
--- a/chrome/browser/browsing_data/counters/site_settings_counter_unittest.cc
+++ b/chrome/browser/browsing_data/counters/site_settings_counter_unittest.cc
@@ -5,6 +5,8 @@
 #include "chrome/browser/browsing_data/counters/site_settings_counter.h"
 
 #include <memory>
+#include <string>
+#include <utility>
 
 #include "base/bind.h"
 #include "base/containers/flat_set.h"
@@ -54,18 +56,17 @@
   }
   void RegisterWithOSAsDefaultClient(
       const std::string& protocol,
-      ProtocolHandlerRegistry* registry) override {
+      shell_integration::DefaultWebClientWorkerCallback callback) override {
     base::ThreadTaskRunnerHandle::Get()->PostTask(
         FROM_HERE,
-        base::BindOnce(registry->GetDefaultWebClientCallback(protocol),
-                       shell_integration::NOT_DEFAULT));
+        base::BindOnce(std::move(callback), shell_integration::NOT_DEFAULT));
   }
-  void CheckDefaultClientWithOS(const std::string& protocol,
-                                ProtocolHandlerRegistry* registry) override {
+  void CheckDefaultClientWithOS(
+      const std::string& protocol,
+      shell_integration::DefaultWebClientWorkerCallback callback) override {
     base::ThreadTaskRunnerHandle::Get()->PostTask(
         FROM_HERE,
-        base::BindOnce(registry->GetDefaultWebClientCallback(protocol),
-                       shell_integration::NOT_DEFAULT));
+        base::BindOnce(std::move(callback), shell_integration::NOT_DEFAULT));
   }
 
  private:
diff --git a/chrome/browser/certificate_manager_model_unittest.cc b/chrome/browser/certificate_manager_model_unittest.cc
index 69aba31..2b5ca9f 100644
--- a/chrome/browser/certificate_manager_model_unittest.cc
+++ b/chrome/browser/certificate_manager_model_unittest.cc
@@ -284,11 +284,6 @@
         *extension_client_certificates_));
   }
 
-  std::unique_ptr<CertificateProvider> Copy() override {
-    NOTREACHED();
-    return nullptr;
-  }
-
  private:
   const net::CertificateList* extension_client_certificates_;
 
diff --git a/chrome/browser/chrome_browser_interface_binders.cc b/chrome/browser/chrome_browser_interface_binders.cc
index 6f15b65..6b1cc8d 100644
--- a/chrome/browser/chrome_browser_interface_binders.cc
+++ b/chrome/browser/chrome_browser_interface_binders.cc
@@ -65,10 +65,13 @@
   auto* unhandled_tap_notifier_observer =
       contextual_search::UnhandledTapWebContentsObserver::FromWebContents(
           content::WebContents::FromRenderFrameHost(host));
-  contextual_search::CreateUnhandledTapNotifierImpl(
-      unhandled_tap_notifier_observer->device_scale_factor(),
-      unhandled_tap_notifier_observer->unhandled_tap_callback(),
-      std::move(receiver));
+
+  if (unhandled_tap_notifier_observer) {
+    contextual_search::CreateUnhandledTapNotifierImpl(
+        unhandled_tap_notifier_observer->device_scale_factor(),
+        unhandled_tap_notifier_observer->unhandled_tap_callback(),
+        std::move(receiver));
+  }
 }
 #endif  // BUILDFLAG(ENABLE_UNHANDLED_TAP)
 
diff --git a/chrome/browser/chrome_browser_main_win.cc b/chrome/browser/chrome_browser_main_win.cc
index a127640..b8ce71c 100644
--- a/chrome/browser/chrome_browser_main_win.cc
+++ b/chrome/browser/chrome_browser_main_win.cc
@@ -569,8 +569,7 @@
   // What truly controls if the blocking is enabled is the presence of the
   // module blacklist cache file. This means that to disable the feature, the
   // cache must be deleted and the browser relaunched.
-  if (base::IsMachineExternallyManaged() ||
-      !ModuleDatabase::IsThirdPartyBlockingPolicyEnabled() ||
+  if (!ModuleDatabase::IsThirdPartyBlockingPolicyEnabled() ||
       !ModuleBlacklistCacheUpdater::IsBlockingEnabled())
     ThirdPartyConflictsManager::DisableThirdPartyModuleBlocking(
         base::CreateTaskRunner(
diff --git a/chrome/browser/chrome_content_browser_client.cc b/chrome/browser/chrome_content_browser_client.cc
index d02057b..737ae4e 100644
--- a/chrome/browser/chrome_content_browser_client.cc
+++ b/chrome/browser/chrome_content_browser_client.cc
@@ -75,6 +75,8 @@
 #include "chrome/browser/metrics/chrome_feature_list_creator.h"
 #include "chrome/browser/nacl_host/nacl_browser_delegate_impl.h"
 #include "chrome/browser/net/chrome_network_delegate.h"
+#include "chrome/browser/net/profile_network_context_service.h"
+#include "chrome/browser/net/profile_network_context_service_factory.h"
 #include "chrome/browser/net/system_network_context_manager.h"
 #include "chrome/browser/notifications/platform_notification_service_factory.h"
 #include "chrome/browser/notifications/platform_notification_service_impl.h"
@@ -4732,10 +4734,8 @@
 
 std::unique_ptr<net::ClientCertStore>
 ChromeContentBrowserClient::CreateClientCertStore(
-    content::ResourceContext* resource_context) {
-  if (!resource_context)
-    return nullptr;
-  return ProfileIOData::FromResourceContext(resource_context)
+    content::BrowserContext* browser_context) {
+  return ProfileNetworkContextServiceFactory::GetForContext(browser_context)
       ->CreateClientCertStore();
 }
 
diff --git a/chrome/browser/chrome_content_browser_client.h b/chrome/browser/chrome_content_browser_client.h
index cb9812b..f0e1c292 100644
--- a/chrome/browser/chrome_content_browser_client.h
+++ b/chrome/browser/chrome_content_browser_client.h
@@ -538,7 +538,7 @@
       const GURL& url,
       base::OnceCallback<void(bool, int, int)> callback) override;
   std::unique_ptr<net::ClientCertStore> CreateClientCertStore(
-      content::ResourceContext* resource_context) override;
+      content::BrowserContext* browser_context) override;
   std::unique_ptr<content::LoginDelegate> CreateLoginDelegate(
       const net::AuthChallengeInfo& auth_info,
       content::WebContents* web_contents,
diff --git a/chrome/browser/chromeos/BUILD.gn b/chrome/browser/chromeos/BUILD.gn
index 17a00b8..7e6d1fbf 100644
--- a/chrome/browser/chromeos/BUILD.gn
+++ b/chrome/browser/chromeos/BUILD.gn
@@ -435,6 +435,8 @@
     "app_mode/startup_app_launcher_update_checker.h",
     "app_mode/web_app/web_kiosk_app_data.cc",
     "app_mode/web_app/web_kiosk_app_data.h",
+    "app_mode/web_app/web_kiosk_app_launcher.cc",
+    "app_mode/web_app/web_kiosk_app_launcher.h",
     "app_mode/web_app/web_kiosk_app_manager.cc",
     "app_mode/web_app/web_kiosk_app_manager.h",
     "apps/apk_web_app_installer.cc",
@@ -773,6 +775,8 @@
     "child_accounts/time_limit_notifier.h",
     "child_accounts/time_limit_override.cc",
     "child_accounts/time_limit_override.h",
+    "child_accounts/time_limits/web_time_limit_enforcer.cc",
+    "child_accounts/time_limits/web_time_limit_enforcer.h",
     "child_accounts/usage_time_limit_processor.cc",
     "child_accounts/usage_time_limit_processor.h",
     "child_accounts/usage_time_state_notifier.cc",
diff --git a/chrome/browser/chromeos/app_mode/web_app/web_kiosk_app_data.cc b/chrome/browser/chromeos/app_mode/web_app/web_kiosk_app_data.cc
index 2a01a7d0..c8c31da2 100644
--- a/chrome/browser/chromeos/app_mode/web_app/web_kiosk_app_data.cc
+++ b/chrome/browser/chromeos/app_mode/web_app/web_kiosk_app_data.cc
@@ -4,15 +4,22 @@
 
 #include "chrome/browser/chromeos/app_mode/web_app/web_kiosk_app_data.h"
 
+#include "base/strings/utf_string_conversions.h"
 #include "base/values.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/chromeos/app_mode/kiosk_app_data_delegate.h"
 #include "chrome/browser/chromeos/app_mode/web_app/web_kiosk_app_manager.h"
+#include "chrome/common/web_application_info.h"
 #include "components/prefs/pref_service.h"
 #include "content/public/browser/browser_thread.h"
 
 namespace chromeos {
 
+namespace {
+constexpr int kIconSize = 128;  // size of the icon in px.
+const char kKeyLaunchUrl[] = "launch_url";
+}  // namespace
+
 WebKioskAppData::WebKioskAppData(KioskAppDataDelegate* delegate,
                                  const std::string& app_id,
                                  const AccountId& account_id,
@@ -22,8 +29,8 @@
                        account_id),
       delegate_(delegate),
       status_(STATUS_INIT),
-      url_(url) {
-  name_ = url_.spec();
+      install_url_(url) {
+  name_ = install_url_.spec();
 }
 
 WebKioskAppData::~WebKioskAppData() = default;
@@ -35,26 +42,75 @@
   if (!LoadFromDictionary(base::Value::AsDictionaryValue(*dict)))
     return false;
 
+  if (LoadLaunchUrlFromDictionary(*dict)) {
+    SetStatus(STATUS_INSTALLED);
+    return true;
+  }
+
   // Wait while icon is loaded.
   if (status_ == STATUS_INIT)
     SetStatus(STATUS_LOADING);
   return true;
 }
 
+void WebKioskAppData::UpdateFromWebAppInfo(
+    std::unique_ptr<WebApplicationInfo> app_info) {
+  DCHECK(app_info);
+  name_ = base::UTF16ToUTF8(app_info->title);
+  base::FilePath cache_dir;
+  if (delegate_)
+    delegate_->GetKioskAppIconCacheDir(&cache_dir);
+
+  for (const WebApplicationIconInfo& icon_info : app_info->icons) {
+    if (icon_info.width == kIconSize) {
+      icon_ = gfx::ImageSkia::CreateFrom1xBitmap(icon_info.data);
+      icon_.MakeThreadSafe();
+      SaveIcon(icon_info.data, cache_dir);
+      break;
+    }
+  }
+
+  PrefService* local_state = g_browser_process->local_state();
+  DictionaryPrefUpdate dict_update(local_state, dictionary_name());
+
+  launch_url_ = GURL(app_info->app_url);
+  const std::string app_key =
+      std::string(KioskAppDataBase::kKeyApps) + '.' + app_id();
+  const std::string launch_url_key = app_key + '.' + kKeyLaunchUrl;
+  dict_update->SetString(launch_url_key, launch_url_.spec());
+  SaveToDictionary(dict_update);
+
+  SetStatus(STATUS_INSTALLED);
+}
+
 void WebKioskAppData::SetStatus(Status status) {
-  if (status_ == status)
-    return;
   status_ = status;
 
   if (delegate_)
     delegate_->OnKioskAppDataChanged(app_id());
 }
 
+bool WebKioskAppData::LoadLaunchUrlFromDictionary(const base::Value& dict) {
+  const std::string app_key =
+      std::string(KioskAppDataBase::kKeyApps) + '.' + app_id();
+  const std::string launch_url_key = app_key + '.' + kKeyLaunchUrl;
+
+  const std::string* launch_url_string = dict.FindStringKey(launch_url_key);
+  if (!launch_url_string)
+    return false;
+
+  launch_url_ = GURL(*launch_url_string);
+  return true;
+}
+
 void WebKioskAppData::OnIconLoadSuccess(const gfx::ImageSkia& icon) {
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
   kiosk_app_icon_loader_.reset();
   icon_ = icon;
-  SetStatus(STATUS_LOADED);
+  if (status_ != STATUS_INSTALLED)
+    SetStatus(STATUS_LOADED);
+  else
+    SetStatus(STATUS_INSTALLED);  // To notify menu controller.
 }
 
 void WebKioskAppData::OnIconLoadFailure() {
diff --git a/chrome/browser/chromeos/app_mode/web_app/web_kiosk_app_data.h b/chrome/browser/chromeos/app_mode/web_app/web_kiosk_app_data.h
index fedd70b..e40f98763 100644
--- a/chrome/browser/chromeos/app_mode/web_app/web_kiosk_app_data.h
+++ b/chrome/browser/chromeos/app_mode/web_app/web_kiosk_app_data.h
@@ -5,6 +5,7 @@
 #ifndef CHROME_BROWSER_CHROMEOS_APP_MODE_WEB_APP_WEB_KIOSK_APP_DATA_H_
 #define CHROME_BROWSER_CHROMEOS_APP_MODE_WEB_APP_WEB_KIOSK_APP_DATA_H_
 
+#include <memory>
 #include <string>
 
 #include "base/macros.h"
@@ -13,6 +14,8 @@
 #include "ui/gfx/image/image_skia.h"
 #include "url/gurl.h"
 
+struct WebApplicationInfo;
+
 namespace chromeos {
 
 class KioskAppDataDelegate;
@@ -20,9 +23,11 @@
 class WebKioskAppData : public KioskAppDataBase {
  public:
   enum Status {
-    STATUS_INIT,     // Data initialized with app id.
-    STATUS_LOADING,  // Loading data from cache or web store.
-    STATUS_LOADED,   // Data load finished.
+    STATUS_INIT,       // Data initialized with app id.
+    STATUS_LOADING,    // Loading data from cache or web store.
+    STATUS_LOADED,     // Data load finished.
+    STATUS_INSTALLED,  // Icon and launch url are fetched and app can be run
+                       // without them.
   };
   WebKioskAppData(KioskAppDataDelegate* delegate,
                   const std::string& app_id,
@@ -40,13 +45,19 @@
 
   void SetStatus(Status status);
 
+  void UpdateFromWebAppInfo(std::unique_ptr<WebApplicationInfo> app_info);
+
   Status status() const { return status_; }
-  const GURL& url() const { return url_; }
+  const GURL& install_url() const { return install_url_; }
+  const GURL& launch_url() const { return launch_url_; }
 
  private:
+  bool LoadLaunchUrlFromDictionary(const base::Value& dict);
+
   const KioskAppDataDelegate* delegate_;  // not owned.
   Status status_;
-  const GURL url_;
+  const GURL install_url_;  // installation url.
+  GURL launch_url_;         // app launch url.
 
   DISALLOW_COPY_AND_ASSIGN(WebKioskAppData);
 };
diff --git a/chrome/browser/chromeos/app_mode/web_app/web_kiosk_app_launcher.cc b/chrome/browser/chromeos/app_mode/web_app/web_kiosk_app_launcher.cc
new file mode 100644
index 0000000..226f1a5
--- /dev/null
+++ b/chrome/browser/chromeos/app_mode/web_app/web_kiosk_app_launcher.cc
@@ -0,0 +1,97 @@
+// 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/chromeos/app_mode/web_app/web_kiosk_app_launcher.h>
+#include <memory>
+
+#include "ash/public/cpp/window_pin_type.h"
+#include "ash/public/cpp/window_properties.h"
+#include "base/bind.h"
+#include "chrome/browser/chromeos/app_mode/web_app/web_kiosk_app_data.h"
+#include "chrome/browser/chromeos/app_mode/web_app/web_kiosk_app_manager.h"
+#include "chrome/browser/extensions/api/tabs/tabs_util.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/ui/browser.h"
+#include "chrome/browser/ui/browser_window.h"
+#include "chrome/browser/web_applications/components/web_app_data_retriever.h"
+#include "chrome/browser/web_applications/components/web_app_url_loader.h"
+#include "chrome/browser/web_applications/web_app_install_task.h"
+#include "components/account_id/account_id.h"
+#include "ui/aura/window.h"
+#include "ui/base/page_transition_types.h"
+
+namespace chromeos {
+
+WebKioskAppLauncher::WebKioskAppLauncher(
+    Profile* profile,
+    WebKioskAppLauncher::Delegate* delegate)
+    : profile_(profile),
+      delegate_(delegate),
+      url_loader_(std::make_unique<web_app::WebAppUrlLoader>()) {}
+
+WebKioskAppLauncher::~WebKioskAppLauncher() = default;
+
+void WebKioskAppLauncher::Initialize(const AccountId& account_id) {
+  account_id_ = account_id;
+  const WebKioskAppData* app =
+      WebKioskAppManager::Get()->GetAppByAccountId(account_id_);
+  DCHECK(app);
+  if (app->status() == WebKioskAppData::STATUS_INSTALLED) {
+    delegate_->OnAppPrepared();
+    return;
+  }
+  // If the app is not yet installed -- require network connection.
+  delegate_->InitializeNetwork();
+}
+
+void WebKioskAppLauncher::ContinueWithNetworkReady() {
+  delegate_->OnAppStartedInstalling();
+  DCHECK(!is_installed_);
+  install_task_.reset(new web_app::WebAppInstallTask(
+      profile_, /*shortcut_manager=*/nullptr, /*install_finalizer=*/nullptr,
+      std::make_unique<web_app::WebAppDataRetriever>()));
+  install_task_->LoadAndRetrieveWebApplicationInfoWithIcons(
+      WebKioskAppManager::Get()->GetAppByAccountId(account_id_)->install_url(),
+      url_loader_.get(),
+      base::BindOnce(&WebKioskAppLauncher::OnAppDataObtained,
+                     weak_ptr_factory_.GetWeakPtr()));
+}
+
+void WebKioskAppLauncher::OnAppDataObtained(
+    std::unique_ptr<WebApplicationInfo> app_info) {
+  if (app_info) {
+    WebKioskAppManager::Get()->UpdateAppByAccountId(account_id_,
+                                                    std::move(app_info));
+  }
+  // If we could not update the app data, we should still launch the app(we may
+  // be under captive portal, there can be redirect, etc).
+  delegate_->OnAppPrepared();
+}
+
+void WebKioskAppLauncher::LaunchApp() {
+  DCHECK(!browser_);
+  const WebKioskAppData* app =
+      WebKioskAppManager::Get()->GetAppByAccountId(account_id_);
+  DCHECK(app);
+
+  GURL url = app->status() == WebKioskAppData::STATUS_INSTALLED
+                 ? app->launch_url()
+                 : app->install_url();
+
+  Browser::CreateParams params(Browser::TYPE_APP, profile_, false);
+
+  browser_ = Browser::Create(params);
+  NavigateParams nav_params(browser_, url,
+                            ui::PageTransition::PAGE_TRANSITION_AUTO_TOPLEVEL);
+  Navigate(&nav_params);
+  CHECK(browser_);
+  CHECK(browser_->window());
+  CHECK(browser_->window()->GetNativeWindow());
+  browser_->window()->GetNativeWindow()->SetProperty(
+      ash::kWindowPinTypeKey, ash::WindowPinType::kTrustedPinned);
+  browser_->window()->Show();
+  delegate_->OnAppLaunched();
+}
+
+}  // namespace chromeos
diff --git a/chrome/browser/chromeos/app_mode/web_app/web_kiosk_app_launcher.h b/chrome/browser/chromeos/app_mode/web_app/web_kiosk_app_launcher.h
new file mode 100644
index 0000000..bd0589d
--- /dev/null
+++ b/chrome/browser/chromeos/app_mode/web_app/web_kiosk_app_launcher.h
@@ -0,0 +1,76 @@
+// 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_CHROMEOS_APP_MODE_WEB_APP_WEB_KIOSK_APP_LAUNCHER_H_
+#define CHROME_BROWSER_CHROMEOS_APP_MODE_WEB_APP_WEB_KIOSK_APP_LAUNCHER_H_
+
+#include <memory>
+
+#include "base/macros.h"
+#include "base/memory/weak_ptr.h"
+#include "chrome/browser/web_applications/components/web_app_constants.h"
+#include "chrome/browser/web_applications/components/web_app_helpers.h"
+#include "chrome/browser/web_applications/components/web_app_install_utils.h"
+#include "chrome/browser/web_applications/components/web_app_url_loader.h"
+#include "components/account_id/account_id.h"
+#include "content/public/browser/web_contents.h"
+#include "url/gurl.h"
+
+class Browser;
+class Profile;
+
+namespace web_app {
+class WebAppInstallTask;
+class WebAppUrlLoader;
+}  // namespace web_app
+
+namespace chromeos {
+
+// Object responsible for preparing and launching web kiosk app. Is destroyed
+// upon app launch.
+class WebKioskAppLauncher {
+ public:
+  class Delegate {
+   public:
+    virtual void InitializeNetwork() = 0;
+    virtual void OnAppStartedInstalling() = 0;
+    virtual void OnAppPrepared() = 0;
+    virtual void OnAppLaunched() = 0;
+    virtual void OnAppLaunchFailed() = 0;
+
+   protected:
+    virtual ~Delegate() {}
+  };
+
+  WebKioskAppLauncher(Profile* profile, Delegate* delegate);
+  ~WebKioskAppLauncher();
+
+  // Prepares the environment for an app launch.
+  void Initialize(const AccountId& account_id);
+  // Continues the installation when the network i
+  void ContinueWithNetworkReady();
+  // Launches the app after the initialization is done.
+  void LaunchApp();
+
+ private:
+  void OnAppDataObtained(std::unique_ptr<WebApplicationInfo> app_info);
+
+  bool is_installed_ = false;  // Whether the installation was completed.
+  AccountId account_id_;
+  Profile* const profile_;
+  Delegate* const delegate_;  // Not owned. Owns us.
+
+  Browser* browser_ = nullptr;  // Browser instance that runs the web kiosk app.
+
+  std::unique_ptr<web_app::WebAppInstallTask>
+      install_task_;  // task that is used to install the app.
+  std::unique_ptr<web_app::WebAppUrlLoader>
+      url_loader_;  // Loads the app to be installed.
+
+  base::WeakPtrFactory<WebKioskAppLauncher> weak_ptr_factory_{this};
+  DISALLOW_COPY_AND_ASSIGN(WebKioskAppLauncher);
+};
+}  // namespace chromeos
+
+#endif  // CHROME_BROWSER_CHROMEOS_APP_MODE_WEB_APP_WEB_KIOSK_APP_LAUNCHER_H_
diff --git a/chrome/browser/chromeos/app_mode/web_app/web_kiosk_app_manager.cc b/chrome/browser/chromeos/app_mode/web_app/web_kiosk_app_manager.cc
index a31beaa7..d9597c8e 100644
--- a/chrome/browser/chromeos/app_mode/web_app/web_kiosk_app_manager.cc
+++ b/chrome/browser/chromeos/app_mode/web_app/web_kiosk_app_manager.cc
@@ -10,6 +10,7 @@
 #include "chrome/browser/chromeos/app_mode/kiosk_cryptohome_remover.h"
 #include "chrome/browser/chromeos/policy/device_local_account.h"
 #include "chrome/browser/web_applications/components/web_app_helpers.h"
+#include "chrome/common/web_application_info.h"
 #include "chromeos/settings/cros_settings_names.h"
 #include "components/prefs/pref_registry_simple.h"
 
@@ -52,7 +53,7 @@
   apps->reserve(apps_.size());
   for (auto& web_app : apps_) {
     App app(*web_app);
-    app.url = web_app->url();
+    app.url = web_app->install_url();
     apps->push_back(std::move(app));
   }
 }
@@ -71,6 +72,18 @@
   return nullptr;
 }
 
+void WebKioskAppManager::UpdateAppByAccountId(
+    const AccountId& account_id,
+    std::unique_ptr<WebApplicationInfo> app_info) {
+  for (auto& web_app : apps_) {
+    if (web_app->account_id() == account_id) {
+      web_app->UpdateFromWebAppInfo(std::move(app_info));
+      return;
+    }
+  }
+  NOTREACHED();
+}
+
 void WebKioskAppManager::UpdateAppsFromPolicy() {
   // Store current apps. We will compare old and new apps to determine which
   // apps are new, and which were deleted.
diff --git a/chrome/browser/chromeos/app_mode/web_app/web_kiosk_app_manager.h b/chrome/browser/chromeos/app_mode/web_app/web_kiosk_app_manager.h
index 5e4a26f..02128ab 100644
--- a/chrome/browser/chromeos/app_mode/web_app/web_kiosk_app_manager.h
+++ b/chrome/browser/chromeos/app_mode/web_app/web_kiosk_app_manager.h
@@ -14,6 +14,7 @@
 #include "components/account_id/account_id.h"
 
 class PrefRegistrySimple;
+struct WebApplicationInfo;
 
 namespace chromeos {
 
@@ -39,6 +40,10 @@
   // Obtains an app associated with given |account_id|.
   const WebKioskAppData* GetAppByAccountId(const AccountId& account_id) const;
 
+  // Updates app by the data obtained during installation.
+  void UpdateAppByAccountId(const AccountId& account_id,
+                            std::unique_ptr<WebApplicationInfo> app_info);
+
  private:
   // KioskAppManagerBase:
   // Updates |apps_| based on CrosSettings.
diff --git a/chrome/browser/chromeos/arc/enterprise/cert_store/arc_smart_card_manager_bridge_unittest.cc b/chrome/browser/chromeos/arc/enterprise/cert_store/arc_smart_card_manager_bridge_unittest.cc
index 869eb95..90e1106b 100644
--- a/chrome/browser/chromeos/arc/enterprise/cert_store/arc_smart_card_manager_bridge_unittest.cc
+++ b/chrome/browser/chromeos/arc/enterprise/cert_store/arc_smart_card_manager_bridge_unittest.cc
@@ -71,11 +71,6 @@
     return true;
   }
 
-  std::unique_ptr<CertificateProvider> Copy() override {
-    NOTREACHED();
-    return nullptr;
-  }
-
  private:
   // Returns true if the certificate for |name| is created successfully.
   bool AddCert(const std::string& name) {
diff --git a/chrome/browser/chromeos/assistant/assistant_util.cc b/chrome/browser/chromeos/assistant/assistant_util.cc
index cd0e0445..c546f20 100644
--- a/chrome/browser/chromeos/assistant/assistant_util.cc
+++ b/chrome/browser/chromeos/assistant/assistant_util.cc
@@ -41,8 +41,7 @@
   if (user_manager::UserManager::Get()->IsLoggedInAsPublicAccount())
     return ash::mojom::AssistantAllowedState::DISALLOWED_BY_PUBLIC_SESSION;
 
-  if (user_manager::UserManager::Get()->IsLoggedInAsKioskApp() ||
-      user_manager::UserManager::Get()->IsLoggedInAsArcKioskApp()) {
+  if (user_manager::UserManager::Get()->IsLoggedInAsAnyKioskApp()) {
     return ash::mojom::AssistantAllowedState::DISALLOWED_BY_KIOSK_MODE;
   }
 
diff --git a/chrome/browser/chromeos/certificate_provider/certificate_provider.h b/chrome/browser/chromeos/certificate_provider/certificate_provider.h
index 2c8e00d8..af29efd1 100644
--- a/chrome/browser/chromeos/certificate_provider/certificate_provider.h
+++ b/chrome/browser/chromeos/certificate_provider/certificate_provider.h
@@ -20,8 +20,6 @@
   virtual void GetCertificates(
       base::OnceCallback<void(net::ClientCertIdentityList)> callback) = 0;
 
-  virtual std::unique_ptr<CertificateProvider> Copy() = 0;
-
  private:
   DISALLOW_COPY_AND_ASSIGN(CertificateProvider);
 };
diff --git a/chrome/browser/chromeos/certificate_provider/certificate_provider_service.cc b/chrome/browser/chromeos/certificate_provider/certificate_provider_service.cc
index 476a69f..0765b1e 100644
--- a/chrome/browser/chromeos/certificate_provider/certificate_provider_service.cc
+++ b/chrome/browser/chromeos/certificate_provider/certificate_provider_service.cc
@@ -29,20 +29,17 @@
 
 namespace {
 
-void PostSignResultToTaskRunner(
-    const scoped_refptr<base::TaskRunner>& target_task_runner,
-    net::SSLPrivateKey::SignCallback callback,
-    net::Error error,
-    const std::vector<uint8_t>& signature) {
-  target_task_runner->PostTask(
+void PostSignResult(net::SSLPrivateKey::SignCallback callback,
+                    net::Error error,
+                    const std::vector<uint8_t>& signature) {
+  base::ThreadTaskRunnerHandle::Get()->PostTask(
       FROM_HERE, base::BindOnce(std::move(callback), error, signature));
 }
 
-void PostIdentitiesToTaskRunner(
-    const scoped_refptr<base::TaskRunner>& target_task_runner,
+void PostIdentities(
     base::OnceCallback<void(net::ClientCertIdentityList)> callback,
     net::ClientCertIdentityList certs) {
-  target_task_runner->PostTask(
+  base::ThreadTaskRunnerHandle::Get()->PostTask(
       FROM_HERE, base::BindOnce(std::move(callback), std::move(certs)));
 }
 
@@ -51,43 +48,31 @@
 class CertificateProviderService::CertificateProviderImpl
     : public CertificateProvider {
  public:
-  // Any calls back to |service| will be posted to |service_task_runner|.
-  // |service| must be dereferenceable on |service_task_runner|.
-  // This provider is not thread safe, but can be used on any thread.
-  CertificateProviderImpl(
-      const scoped_refptr<base::SequencedTaskRunner>& service_task_runner,
+  // This provider must be used on the same thread as the
+  // CertificateProviderService.
+  explicit CertificateProviderImpl(
       const base::WeakPtr<CertificateProviderService>& service);
   ~CertificateProviderImpl() override;
 
   void GetCertificates(
       base::OnceCallback<void(net::ClientCertIdentityList)> callback) override;
 
-  std::unique_ptr<CertificateProvider> Copy() override;
-
  private:
-  static void GetCertificatesOnServiceThread(
-      const base::WeakPtr<CertificateProviderService>& service,
-      base::OnceCallback<void(net::ClientCertIdentityList)> callback);
 
-  const scoped_refptr<base::SequencedTaskRunner> service_task_runner_;
-  // Must be dereferenced on |service_task_runner_| only.
   const base::WeakPtr<CertificateProviderService> service_;
+  SEQUENCE_CHECKER(sequence_checker_);
 
   DISALLOW_COPY_AND_ASSIGN(CertificateProviderImpl);
 };
 
 // Implements an SSLPrivateKey backed by the signing function exposed by an
 // extension through the certificateProvider API.
-// Objects of this class must be used on a single thread. Any thread is allowed.
+// Objects of this class must be used the CertificateProviderService's sequence.
 class CertificateProviderService::SSLPrivateKey : public net::SSLPrivateKey {
  public:
-  // Any calls back to |service| will be posted to |service_task_runner|.
-  // |service| must be dereferenceable on |service_task_runner|.
-  SSLPrivateKey(
-      const std::string& extension_id,
-      const CertificateInfo& cert_info,
-      const scoped_refptr<base::SequencedTaskRunner>& service_task_runner,
-      const base::WeakPtr<CertificateProviderService>& service);
+  SSLPrivateKey(const std::string& extension_id,
+                const CertificateInfo& cert_info,
+                const base::WeakPtr<CertificateProviderService>& service);
 
   // net::SSLPrivateKey:
   std::string GetProviderName() override;
@@ -97,27 +82,12 @@
             SignCallback callback) override;
 
  private:
-  ~SSLPrivateKey() override;
-
-  static void SignDigestOnServiceTaskRunner(
-      const base::WeakPtr<CertificateProviderService>& service,
-      const std::string& extension_id,
-      const scoped_refptr<net::X509Certificate>& certificate,
-      uint16_t algorithm,
-      base::span<const uint8_t> input,
-      SignCallback callback);
-
-  void DidSignDigest(SignCallback callback,
-                     net::Error error,
-                     const std::vector<uint8_t>& signature);
+  ~SSLPrivateKey() override = default;
 
   const std::string extension_id_;
   const CertificateInfo cert_info_;
-  scoped_refptr<base::SequencedTaskRunner> service_task_runner_;
-  // Must be dereferenced on |service_task_runner_| only.
   const base::WeakPtr<CertificateProviderService> service_;
-  base::ThreadChecker thread_checker_;
-  base::WeakPtrFactory<SSLPrivateKey> weak_factory_{this};
+  SEQUENCE_CHECKER(sequence_checker_);
 
   DISALLOW_COPY_AND_ASSIGN(SSLPrivateKey);
 };
@@ -125,24 +95,16 @@
 class CertificateProviderService::ClientCertIdentity
     : public net::ClientCertIdentity {
  public:
-  ClientCertIdentity(
-      scoped_refptr<net::X509Certificate> cert,
-      const scoped_refptr<base::SequencedTaskRunner>& service_task_runner,
-      base::WeakPtr<CertificateProviderService> service)
-      : net::ClientCertIdentity(std::move(cert)),
-        service_task_runner_(service_task_runner),
-        service_(service) {}
+  ClientCertIdentity(scoped_refptr<net::X509Certificate> cert,
+                     base::WeakPtr<CertificateProviderService> service)
+      : net::ClientCertIdentity(std::move(cert)), service_(service) {}
 
   void AcquirePrivateKey(
       base::OnceCallback<void(scoped_refptr<net::SSLPrivateKey>)>
           private_key_callback) override;
 
  private:
-  scoped_refptr<net::SSLPrivateKey> AcquirePrivateKeyOnServiceThread(
-      net::X509Certificate* cert);
-
-  scoped_refptr<base::SequencedTaskRunner> service_task_runner_;
-  // Must be dereferenced on |service_task_runner_| only.
+  SEQUENCE_CHECKER(sequence_checker_);
   const base::WeakPtr<CertificateProviderService> service_;
 
   DISALLOW_COPY_AND_ASSIGN(ClientCertIdentity);
@@ -151,124 +113,74 @@
 void CertificateProviderService::ClientCertIdentity::AcquirePrivateKey(
     base::OnceCallback<void(scoped_refptr<net::SSLPrivateKey>)>
         private_key_callback) {
-  // The caller is responsible for keeping the ClientCertIdentity alive until
-  // |private_key_callback| is run, so it's safe to use Unretained here.
-  base::PostTaskAndReplyWithResult(
-      service_task_runner_.get(), FROM_HERE,
-      base::BindOnce(&ClientCertIdentity::AcquirePrivateKeyOnServiceThread,
-                     base::Unretained(this), base::Unretained(certificate())),
-      std::move(private_key_callback));
-}
-
-scoped_refptr<net::SSLPrivateKey> CertificateProviderService::
-    ClientCertIdentity::AcquirePrivateKeyOnServiceThread(
-        net::X509Certificate* cert) {
-  if (!service_)
-    return nullptr;
+  if (!service_) {
+    std::move(private_key_callback).Run(nullptr);
+    return;
+  }
 
   bool is_currently_provided = false;
   CertificateInfo info;
   std::string extension_id;
   // TODO(mattm): can the ClientCertIdentity store a handle directly to the
   // extension instead of having to go through service_->certificate_map_ ?
-  service_->certificate_map_.LookUpCertificate(*cert, &is_currently_provided,
-                                               &info, &extension_id);
-  if (!is_currently_provided)
-    return nullptr;
+  service_->certificate_map_.LookUpCertificate(
+      *certificate(), &is_currently_provided, &info, &extension_id);
+  if (!is_currently_provided) {
+    std::move(private_key_callback).Run(nullptr);
+    return;
+  }
 
-  return base::MakeRefCounted<SSLPrivateKey>(extension_id, info,
-                                             service_task_runner_, service_);
+  std::move(private_key_callback)
+      .Run(base::MakeRefCounted<SSLPrivateKey>(extension_id, info, service_));
 }
 
 CertificateProviderService::CertificateProviderImpl::CertificateProviderImpl(
-    const scoped_refptr<base::SequencedTaskRunner>& service_task_runner,
     const base::WeakPtr<CertificateProviderService>& service)
-    : service_task_runner_(service_task_runner), service_(service) {}
+    : service_(service) {}
 
 CertificateProviderService::CertificateProviderImpl::
     ~CertificateProviderImpl() {}
 
 void CertificateProviderService::CertificateProviderImpl::GetCertificates(
     base::OnceCallback<void(net::ClientCertIdentityList)> callback) {
-  const scoped_refptr<base::TaskRunner> source_task_runner =
-      base::ThreadTaskRunnerHandle::Get();
-  base::OnceCallback<void(net::ClientCertIdentityList)>
-      callback_from_service_thread = base::BindOnce(
-          &PostIdentitiesToTaskRunner, source_task_runner, std::move(callback));
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 
-  service_task_runner_->PostTask(
-      FROM_HERE, base::BindOnce(&GetCertificatesOnServiceThread, service_,
-                                std::move(callback_from_service_thread)));
-}
+  // Avoid running the callback reentrantly.
+  callback = base::BindOnce(&PostIdentities, std::move(callback));
 
-std::unique_ptr<CertificateProvider>
-CertificateProviderService::CertificateProviderImpl::Copy() {
-  return base::WrapUnique(
-      new CertificateProviderImpl(service_task_runner_, service_));
-}
-
-// static
-void CertificateProviderService::CertificateProviderImpl::
-    GetCertificatesOnServiceThread(
-        const base::WeakPtr<CertificateProviderService>& service,
-        base::OnceCallback<void(net::ClientCertIdentityList)> callback) {
-  if (!service) {
+  if (!service_) {
     std::move(callback).Run(net::ClientCertIdentityList());
     return;
   }
-  service->GetCertificatesFromExtensions(std::move(callback));
+  service_->GetCertificatesFromExtensions(std::move(callback));
 }
 
 CertificateProviderService::SSLPrivateKey::SSLPrivateKey(
     const std::string& extension_id,
     const CertificateInfo& cert_info,
-    const scoped_refptr<base::SequencedTaskRunner>& service_task_runner,
     const base::WeakPtr<CertificateProviderService>& service)
-    : extension_id_(extension_id),
-      cert_info_(cert_info),
-      service_task_runner_(service_task_runner),
-      service_(service) {
-  // This constructor is called on |service_task_runner|. Only subsequent calls
-  // to member functions have to be on a common thread.
-  thread_checker_.DetachFromThread();
-}
+    : extension_id_(extension_id), cert_info_(cert_info), service_(service) {}
 
 std::string CertificateProviderService::SSLPrivateKey::GetProviderName() {
-  DCHECK(thread_checker_.CalledOnValidThread());
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   return "extension \"" + extension_id_ + "\"";
 }
 
 std::vector<uint16_t>
 CertificateProviderService::SSLPrivateKey::GetAlgorithmPreferences() {
-  DCHECK(thread_checker_.CalledOnValidThread());
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   return cert_info_.supported_algorithms;
 }
 
-// static
-void CertificateProviderService::SSLPrivateKey::SignDigestOnServiceTaskRunner(
-    const base::WeakPtr<CertificateProviderService>& service,
-    const std::string& extension_id,
-    const scoped_refptr<net::X509Certificate>& certificate,
-    uint16_t algorithm,
-    base::span<const uint8_t> input,
-    SignCallback callback) {
-  if (!service) {
-    const std::vector<uint8_t> no_signature;
-    std::move(callback).Run(net::ERR_FAILED, no_signature);
-    return;
-  }
-  service->RequestSignatureFromExtension(
-      extension_id, certificate, algorithm, input,
-      /*authenticating_user_account_id=*/{}, std::move(callback));
-}
-
 void CertificateProviderService::SSLPrivateKey::Sign(
     uint16_t algorithm,
     base::span<const uint8_t> input,
     SignCallback callback) {
-  DCHECK(thread_checker_.CalledOnValidThread());
-  const scoped_refptr<base::TaskRunner> source_task_runner =
-      base::ThreadTaskRunnerHandle::Get();
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+  // The Sign() method should not call its callback reentrantly, so wrap it in
+  // PostSignResult().
+  callback = base::BindOnce(&PostSignResult, std::move(callback));
 
   // The extension expects the input to be hashed ahead of time.
   const EVP_MD* md = SSL_get_signature_algorithm_digest(algorithm);
@@ -276,52 +188,31 @@
   unsigned digest_len;
   if (!md || !EVP_Digest(input.data(), input.size(), digest, &digest_len, md,
                          nullptr)) {
-    source_task_runner->PostTask(
-        FROM_HERE, base::BindOnce(std::move(callback),
-                                  net::ERR_SSL_CLIENT_AUTH_SIGNATURE_FAILED,
-                                  std::vector<uint8_t>()));
+    std::move(callback).Run(net::ERR_SSL_CLIENT_AUTH_SIGNATURE_FAILED,
+                            /*signature=*/{});
     return;
   }
 
-  SignCallback bound_callback =
-      // The CertificateProviderService calls back on another thread, so post
-      // back to the current thread.
-      base::BindOnce(
-          &PostSignResultToTaskRunner, source_task_runner,
-          // Drop the result and don't call back if this key handle is destroyed
-          // in the meantime.
-          base::BindOnce(&SSLPrivateKey::DidSignDigest,
-                         weak_factory_.GetWeakPtr(), std::move(callback)));
+  if (!service_) {
+    std::move(callback).Run(net::ERR_FAILED, /*signature=*/{});
+    return;
+  }
 
-  service_task_runner_->PostTask(
-      FROM_HERE,
-      base::BindOnce(&SSLPrivateKey::SignDigestOnServiceTaskRunner, service_,
-                     extension_id_, cert_info_.certificate, algorithm,
-                     std::vector<uint8_t>(digest, digest + digest_len),
-                     std::move(bound_callback)));
-}
-
-CertificateProviderService::SSLPrivateKey::~SSLPrivateKey() {
-  DCHECK(thread_checker_.CalledOnValidThread());
-}
-
-void CertificateProviderService::SSLPrivateKey::DidSignDigest(
-    SignCallback callback,
-    net::Error error,
-    const std::vector<uint8_t>& signature) {
-  DCHECK(thread_checker_.CalledOnValidThread());
-  std::move(callback).Run(error, signature);
+  service_->RequestSignatureFromExtension(
+      extension_id_, cert_info_.certificate, algorithm,
+      base::make_span(digest, digest_len),
+      /*authenticating_user_account_id=*/{}, std::move(callback));
 }
 
 CertificateProviderService::CertificateProviderService() {}
 
 CertificateProviderService::~CertificateProviderService() {
-  DCHECK(thread_checker_.CalledOnValidThread());
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 }
 
 void CertificateProviderService::SetDelegate(
     std::unique_ptr<Delegate> delegate) {
-  DCHECK(thread_checker_.CalledOnValidThread());
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   DCHECK(!delegate_);
   DCHECK(delegate);
 
@@ -340,7 +231,7 @@
     const std::string& extension_id,
     int cert_request_id,
     const CertificateInfoList& certificate_infos) {
-  DCHECK(thread_checker_.CalledOnValidThread());
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 
   bool completed = false;
   if (!certificate_requests_.SetCertificates(extension_id, cert_request_id,
@@ -363,7 +254,7 @@
     const std::string& extension_id,
     int sign_request_id,
     const std::vector<uint8_t>& signature) {
-  DCHECK(thread_checker_.CalledOnValidThread());
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 
   scoped_refptr<net::X509Certificate> certificate;
   net::SSLPrivateKey::SignCallback callback;
@@ -388,7 +279,7 @@
     const net::X509Certificate& cert,
     bool* has_extension,
     std::string* extension_id) {
-  DCHECK(thread_checker_.CalledOnValidThread());
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 
   CertificateInfo unused_info;
   return certificate_map_.LookUpCertificate(cert, has_extension, &unused_info,
@@ -397,15 +288,14 @@
 
 std::unique_ptr<CertificateProvider>
 CertificateProviderService::CreateCertificateProvider() {
-  DCHECK(thread_checker_.CalledOnValidThread());
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 
-  return std::make_unique<CertificateProviderImpl>(
-      base::ThreadTaskRunnerHandle::Get(), weak_factory_.GetWeakPtr());
+  return std::make_unique<CertificateProviderImpl>(weak_factory_.GetWeakPtr());
 }
 
 void CertificateProviderService::OnExtensionUnloaded(
     const std::string& extension_id) {
-  DCHECK(thread_checker_.CalledOnValidThread());
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 
   for (const int cert_request_id :
        certificate_requests_.DropExtension(extension_id)) {
@@ -430,7 +320,7 @@
     base::span<const uint8_t> digest,
     const base::Optional<AccountId>& authenticating_user_account_id,
     net::SSLPrivateKey::SignCallback callback) {
-  DCHECK(thread_checker_.CalledOnValidThread());
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   bool is_currently_provided = false;
   CertificateInfo info;
   std::string extension_id;
@@ -450,7 +340,7 @@
 bool CertificateProviderService::GetSupportedAlgorithmsBySpki(
     const std::string& subject_public_key_info,
     std::vector<uint16_t>* supported_algorithms) {
-  DCHECK(thread_checker_.CalledOnValidThread());
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   bool is_currently_provided = false;
   CertificateInfo info;
   std::string extension_id;
@@ -490,7 +380,7 @@
 
 void CertificateProviderService::GetCertificatesFromExtensions(
     base::OnceCallback<void(net::ClientCertIdentityList)> callback) {
-  DCHECK(thread_checker_.CalledOnValidThread());
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 
   const std::vector<std::string> provider_extensions(
       delegate_->CertificateProviderExtensions());
@@ -514,7 +404,7 @@
 void CertificateProviderService::UpdateCertificatesAndRun(
     const std::map<std::string, CertificateInfoList>& extension_to_certificates,
     base::OnceCallback<void(net::ClientCertIdentityList)> callback) {
-  DCHECK(thread_checker_.CalledOnValidThread());
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 
   // Extensions are removed from the service's state when they're unloaded.
   // Any remaining extension is assumed to be enabled.
@@ -524,8 +414,7 @@
   for (const auto& entry : extension_to_certificates) {
     for (const CertificateInfo& cert_info : entry.second)
       all_certs.push_back(std::make_unique<ClientCertIdentity>(
-          cert_info.certificate, base::ThreadTaskRunnerHandle::Get(),
-          weak_factory_.GetWeakPtr()));
+          cert_info.certificate, weak_factory_.GetWeakPtr()));
   }
 
   std::move(callback).Run(std::move(all_certs));
@@ -533,7 +422,7 @@
 
 void CertificateProviderService::TerminateCertificateRequest(
     int cert_request_id) {
-  DCHECK(thread_checker_.CalledOnValidThread());
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 
   std::map<std::string, CertificateInfoList> certificates;
   base::OnceCallback<void(net::ClientCertIdentityList)> callback;
@@ -554,7 +443,7 @@
     base::span<const uint8_t> digest,
     const base::Optional<AccountId>& authenticating_user_account_id,
     net::SSLPrivateKey::SignCallback callback) {
-  DCHECK(thread_checker_.CalledOnValidThread());
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 
   const int sign_request_id = sign_requests_.AddRequest(
       extension_id, certificate, authenticating_user_account_id,
diff --git a/chrome/browser/chromeos/certificate_provider/certificate_provider_service.h b/chrome/browser/chromeos/certificate_provider/certificate_provider_service.h
index 9d43b2f..a3629df 100644
--- a/chrome/browser/chromeos/certificate_provider/certificate_provider_service.h
+++ b/chrome/browser/chromeos/certificate_provider/certificate_provider_service.h
@@ -19,7 +19,7 @@
 #include "base/memory/weak_ptr.h"
 #include "base/observer_list.h"
 #include "base/optional.h"
-#include "base/threading/thread_checker.h"
+#include "base/sequence_checker.h"
 #include "chrome/browser/chromeos/certificate_provider/certificate_info.h"
 #include "chrome/browser/chromeos/certificate_provider/certificate_requests.h"
 #include "chrome/browser/chromeos/certificate_provider/pin_dialog_manager.h"
@@ -159,7 +159,6 @@
   // is sufficient to create the CertificateProvider once and then repeatedly
   // call its |GetCertificates()|. The returned provider is valid even after the
   // destruction of this service.
-  // The returned provider can be used on any thread.
   std::unique_ptr<CertificateProvider> CreateCertificateProvider();
 
   // Must be called if extension with id |extension_id| is unloaded and cannot
@@ -258,7 +257,7 @@
   // after an extension doesn't report it anymore.
   certificate_provider::ThreadSafeCertificateMap certificate_map_;
 
-  base::ThreadChecker thread_checker_;
+  SEQUENCE_CHECKER(sequence_checker_);
   base::WeakPtrFactory<CertificateProviderService> weak_factory_{this};
 
   DISALLOW_COPY_AND_ASSIGN(CertificateProviderService);
diff --git a/chrome/browser/chromeos/child_accounts/time_limits/web_time_limit_enforcer.cc b/chrome/browser/chromeos/child_accounts/time_limits/web_time_limit_enforcer.cc
new file mode 100644
index 0000000..0b0e8aa
--- /dev/null
+++ b/chrome/browser/chromeos/child_accounts/time_limits/web_time_limit_enforcer.cc
@@ -0,0 +1,92 @@
+// 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/chromeos/child_accounts/time_limits/web_time_limit_enforcer.h"
+
+#include <memory>
+
+#include "base/feature_list.h"
+#include "base/stl_util.h"
+#include "chrome/browser/ui/browser.h"
+#include "chrome/browser/ui/browser_list.h"
+#include "chrome/browser/ui/tabs/tab_strip_model.h"
+#include "chrome/common/chrome_features.h"
+#include "content/public/browser/navigation_controller.h"
+#include "content/public/browser/reload_type.h"
+#include "content/public/browser/web_contents.h"
+
+namespace {
+
+// URL schemes not on this list:  (e.g., file:// and chrome://,
+// chrome-extension:// ...) will always be allowed.
+const char* const kFilteredSchemes[] = {"http", "https", "ftp", "ws", "wss"};
+
+bool IsSchemeFiltered(const GURL& url) {
+  for (const auto* i : kFilteredSchemes) {
+    if (i == url.scheme())
+      return true;
+  }
+  return false;
+}
+
+}  // namespace
+
+// static
+bool WebTimeLimitEnforcer::IsEnabled() {
+  return base::FeatureList::IsEnabled(features::kWebTimeLimits);
+}
+
+WebTimeLimitEnforcer::WebTimeLimitEnforcer() = default;
+WebTimeLimitEnforcer::~WebTimeLimitEnforcer() = default;
+
+void WebTimeLimitEnforcer::OnWebTimeLimitReached() {
+  if (chrome_blocked_)
+    return;
+
+  chrome_blocked_ = true;
+  ReloadAllWebContents();
+}
+
+void WebTimeLimitEnforcer::OnWebTimeLimitEnded() {
+  if (!chrome_blocked_)
+    return;
+
+  chrome_blocked_ = false;
+  ReloadAllWebContents();
+}
+
+void WebTimeLimitEnforcer::OnWhitelistAdded(const GURL& url) {
+  auto result = whitelisted_urls_.insert(url);
+  bool inserted = result.second;
+  if (inserted && blocked())
+    ReloadAllWebContents();
+}
+
+void WebTimeLimitEnforcer::OnWhitelistRemoved(const GURL& url) {
+  if (!base::Contains(whitelisted_urls_, url))
+    return;
+
+  whitelisted_urls_.erase(url);
+  if (blocked())
+    ReloadAllWebContents();
+}
+
+bool WebTimeLimitEnforcer::IsURLWhitelisted(const GURL& url) const {
+  if (!IsSchemeFiltered(url))
+    return true;
+
+  return base::Contains(whitelisted_urls_, url);
+}
+
+void WebTimeLimitEnforcer::ReloadAllWebContents() {
+  auto* browser_list = BrowserList::GetInstance();
+  for (auto* browser : *browser_list) {
+    auto* tab_strip_model = browser->tab_strip_model();
+    for (int i = 0; i < tab_strip_model->count(); i++) {
+      auto* web_content = tab_strip_model->GetWebContentsAt(i);
+      web_content->GetController().Reload(content::ReloadType::NORMAL,
+                                          /* check_for_repost */ false);
+    }
+  }
+}
diff --git a/chrome/browser/chromeos/child_accounts/time_limits/web_time_limit_enforcer.h b/chrome/browser/chromeos/child_accounts/time_limits/web_time_limit_enforcer.h
new file mode 100644
index 0000000..0cbe7cb6
--- /dev/null
+++ b/chrome/browser/chromeos/child_accounts/time_limits/web_time_limit_enforcer.h
@@ -0,0 +1,54 @@
+// 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_CHROMEOS_CHILD_ACCOUNTS_TIME_LIMITS_WEB_TIME_LIMIT_ENFORCER_H_
+#define CHROME_BROWSER_CHROMEOS_CHILD_ACCOUNTS_TIME_LIMITS_WEB_TIME_LIMIT_ENFORCER_H_
+
+#include <algorithm>
+#include <memory>
+#include <set>
+
+#include "base/time/time.h"
+
+class GURL;
+
+class WebTimeLimitEnforcer {
+ public:
+  static bool IsEnabled();
+
+  WebTimeLimitEnforcer();
+  ~WebTimeLimitEnforcer();
+
+  // Delete copy constructor and copy assignment operator.
+  WebTimeLimitEnforcer(const WebTimeLimitEnforcer& enforcer) = delete;
+  WebTimeLimitEnforcer& operator=(const WebTimeLimitEnforcer& enforcer) =
+      delete;
+
+  // Delete move constructor and move assignment operator.
+  WebTimeLimitEnforcer(WebTimeLimitEnforcer&& enforcer) = delete;
+  WebTimeLimitEnforcer& operator=(WebTimeLimitEnforcer&& enforcer) = delete;
+
+  // TODO(crbug/1015661) The following should be private observer calls once the
+  // observer pattern has been set up for this.
+  void OnWebTimeLimitReached();
+  void OnWebTimeLimitEnded();
+  void OnWhitelistAdded(const GURL& url);
+  void OnWhitelistRemoved(const GURL& url);
+
+  bool IsURLWhitelisted(const GURL& url) const;
+
+  void set_time_limit(base::TimeDelta time_limit) { time_limit_ = time_limit; }
+  bool blocked() const { return chrome_blocked_; }
+  base::TimeDelta time_limit() const { return time_limit_; }
+
+ private:
+  void ReloadAllWebContents();
+
+  bool chrome_blocked_ = false;
+  base::TimeDelta time_limit_;
+
+  std::set<GURL> whitelisted_urls_;
+};
+
+#endif  // CHROME_BROWSER_CHROMEOS_CHILD_ACCOUNTS_TIME_LIMITS_WEB_TIME_LIMIT_ENFORCER_H_
diff --git a/chrome/browser/chromeos/extensions/autotest_private/autotest_private_api.cc b/chrome/browser/chromeos/extensions/autotest_private/autotest_private_api.cc
index 031b054d..b77fbc2 100644
--- a/chrome/browser/chromeos/extensions/autotest_private/autotest_private_api.cc
+++ b/chrome/browser/chromeos/extensions/autotest_private/autotest_private_api.cc
@@ -1979,6 +1979,59 @@
   Respond(Error("Assistant service timed out"));
 }
 
+///////////////////////////////////////////////////////////////////////////////
+// AutotestPrivateEnableAssistantAndWaitForReadyFunction
+///////////////////////////////////////////////////////////////////////////////
+
+AutotestPrivateEnableAssistantAndWaitForReadyFunction::
+    AutotestPrivateEnableAssistantAndWaitForReadyFunction() = default;
+
+AutotestPrivateEnableAssistantAndWaitForReadyFunction::
+    ~AutotestPrivateEnableAssistantAndWaitForReadyFunction() {
+  ash::AssistantState::Get()->RemoveObserver(this);
+}
+
+ExtensionFunction::ResponseAction
+AutotestPrivateEnableAssistantAndWaitForReadyFunction::Run() {
+  DVLOG(1) << "AutotestPrivateEnableAssistantAndWaitForReadyFunction";
+
+  Profile* profile = Profile::FromBrowserContext(browser_context());
+  const std::string& err_msg =
+      SetWhitelistedPref(profile, chromeos::assistant::prefs::kAssistantEnabled,
+                         base::Value(true));
+  if (!err_msg.empty())
+    return RespondNow(Error(err_msg));
+
+  // Asynchronously subscribe to status changes to avoid a possible segmentation
+  // fault caused by Respond() in the subscriber callback being called before
+  // RespondLater() below.
+  PostTask(
+      FROM_HERE, {base::CurrentThread()},
+      base::BindOnce(&AutotestPrivateEnableAssistantAndWaitForReadyFunction::
+                         SubscribeToStatusChanges,
+                     this));
+
+  // Prevent |this| from being freed before we get a response from the
+  // Assistant.
+  self_ = this;
+
+  return RespondLater();
+}
+
+void AutotestPrivateEnableAssistantAndWaitForReadyFunction::
+    SubscribeToStatusChanges() {
+  // |AddObserver| will immediately trigger |OnAssistantStatusChanged|.
+  ash::AssistantState::Get()->AddObserver(this);
+}
+
+void AutotestPrivateEnableAssistantAndWaitForReadyFunction::
+    OnAssistantStatusChanged(ash::mojom::AssistantState state) {
+  if (state == ash::mojom::AssistantState::NEW_READY) {
+    Respond(NoArguments());
+    self_.reset();
+  }
+}
+
 // AssistantInteractionHelper is a helper class used to interact with Assistant
 // server and store interaction states for tests. It is shared by
 // |AutotestPrivateSendAssistantTextQueryFunction| and
diff --git a/chrome/browser/chromeos/extensions/autotest_private/autotest_private_api.h b/chrome/browser/chromeos/extensions/autotest_private/autotest_private_api.h
index bfb331b..10cb729 100644
--- a/chrome/browser/chromeos/extensions/autotest_private/autotest_private_api.h
+++ b/chrome/browser/chromeos/extensions/autotest_private/autotest_private_api.h
@@ -559,6 +559,29 @@
   base::OneShotTimer timeout_timer_;
 };
 
+// Bring up the Assistant service, and wait until the ready signal is received.
+class AutotestPrivateEnableAssistantAndWaitForReadyFunction
+    : public ExtensionFunction,
+      public ash::AssistantStateObserver {
+ public:
+  AutotestPrivateEnableAssistantAndWaitForReadyFunction();
+  DECLARE_EXTENSION_FUNCTION("autotestPrivate.enableAssistantAndWaitForReady",
+                             AUTOTESTPRIVATE_ENABLEASSISTANTANDWAITFORREADY)
+
+ private:
+  ~AutotestPrivateEnableAssistantAndWaitForReadyFunction() override;
+  ResponseAction Run() override;
+
+  void SubscribeToStatusChanges();
+
+  // ash::AssistantStateObserver overrides:
+  void OnAssistantStatusChanged(ash::mojom::AssistantState state) override;
+
+  // A reference to keep |this| alive while waiting for the Assistant to
+  // respond.
+  scoped_refptr<ExtensionFunction> self_;
+};
+
 // Send text query to Assistant and return response.
 class AutotestPrivateSendAssistantTextQueryFunction : public ExtensionFunction {
  public:
diff --git a/chrome/browser/chromeos/extensions/device_local_account_management_policy_provider.cc b/chrome/browser/chromeos/extensions/device_local_account_management_policy_provider.cc
index c4d12d3..5141056 100644
--- a/chrome/browser/chromeos/extensions/device_local_account_management_policy_provider.cc
+++ b/chrome/browser/chromeos/extensions/device_local_account_management_policy_provider.cc
@@ -866,6 +866,10 @@
         extension->GetType() == extensions::Manifest::TYPE_EXTENSION) {
       return true;
     }
+  } else if (account_type_ == policy::DeviceLocalAccount::TYPE_WEB_KIOSK_APP) {
+    if (extension->GetType() == extensions::Manifest::TYPE_EXTENSION) {
+      return true;
+    }
   }
 
   // Disallow all other extensions.
diff --git a/chrome/browser/chromeos/kerberos/kerberos_credentials_manager_test.cc b/chrome/browser/chromeos/kerberos/kerberos_credentials_manager_test.cc
index a77f2c9..dbe2471 100644
--- a/chrome/browser/chromeos/kerberos/kerberos_credentials_manager_test.cc
+++ b/chrome/browser/chromeos/kerberos/kerberos_credentials_manager_test.cc
@@ -5,6 +5,7 @@
 #include "chrome/browser/chromeos/kerberos/kerberos_credentials_manager.h"
 
 #include <memory>
+#include <set>
 #include <string>
 #include <vector>
 
@@ -36,6 +37,7 @@
 constexpr char kNormalizedPrincipal[] = "kerberos_user@EXAMPLE.COM";
 constexpr char kOtherPrincipal[] = "icebear_cloud@example.com";
 constexpr char kNormalizedOtherPrincipal[] = "icebear_cloud@EXAMPLE.COM";
+constexpr char kYetAnotherPrincipal[] = "yet_another_user@example.com";
 constexpr char kBadPrincipal1[] = "";
 constexpr char kBadPrincipal2[] = "kerbeROS_user";
 constexpr char kBadPrincipal3[] = "kerbeROS_user@";
@@ -55,6 +57,47 @@
 const bool kDontAllowExisting = false;
 const bool kAllowExisting = true;
 
+const int kNoNotification = 0;
+const int kOneNotification = 1;
+
+const int kNoAccount = 0;
+const int kOneAccount = 1;
+const int kTwoAccounts = 2;
+const int kThreeAccounts = 3;
+
+// Fake observer used to test notifications sent by KerberosCredentialsManager
+// on accounts changes.
+class FakeKerberosCredentialsManagerObserver
+    : public KerberosCredentialsManager::Observer {
+ public:
+  FakeKerberosCredentialsManagerObserver() = default;
+  ~FakeKerberosCredentialsManagerObserver() override = default;
+
+  int notifications_count() const { return notifications_count_; }
+
+  int accounts_count_at_last_notification() const {
+    return accounts_count_at_last_notification_;
+  }
+
+  // KerberosCredentialsManager::Observer:
+  void OnAccountsChanged() override {
+    notifications_count_++;
+    accounts_count_at_last_notification_ =
+        KerberosClient::Get()->GetTestInterface()->GetNumberOfAccounts();
+  }
+
+  void Reset() {
+    notifications_count_ = 0;
+    accounts_count_at_last_notification_ = 0;
+  }
+
+ private:
+  // Registers how many times the observer has been notified of account changes.
+  int notifications_count_ = 0;
+  // Registers the number of accounts saved before the most recent
+  // OnAccountsChanged() call.
+  int accounts_count_at_last_notification_ = 0;
+};
 }  // namespace
 
 class KerberosCredentialsManagerTest : public testing::Test {
@@ -80,10 +123,14 @@
 
     mgr_ = std::make_unique<KerberosCredentialsManager>(local_state_.Get(),
                                                         profile_.get());
+
+    mgr_->AddObserver(&observer_);
   }
 
   ~KerberosCredentialsManagerTest() override {
+    mgr_->RemoveObserver(&observer_);
     mgr_.reset();
+    display_service_.reset();
     profile_.reset();
     KerberosClient::Shutdown();
   }
@@ -103,37 +150,60 @@
     return KerberosClient::Get()->GetTestInterface();
   }
 
-  // Gets a callback that sets |actual_error_| to the passed-in error.
+  // Gets a callback that adds the passed-in error to |result_errors_|.
   KerberosCredentialsManager::ResultCallback GetResultCallback() {
-    EXPECT_FALSE(result_error_);
-    EXPECT_FALSE(result_run_loop_);
-    result_run_loop_ = std::make_unique<base::RunLoop>();
+    // If this is the first account addition, sets |result_run_loop_|.
+    if (accounts_addition_count_ == 0) {
+      EXPECT_TRUE(result_errors_.empty());
+      EXPECT_FALSE(result_run_loop_);
+      result_run_loop_ = std::make_unique<base::RunLoop>();
+    }
+    accounts_addition_count_++;
+
     return base::BindOnce(&KerberosCredentialsManagerTest::OnResult,
                           weak_ptr_factory_.GetWeakPtr());
   }
 
   void OnResult(kerberos::ErrorType error) {
-    result_error_ = error;
-    result_run_loop_->Quit();
+    DCHECK_LT(0, accounts_addition_count_);
+    accounts_addition_count_--;
+    result_errors_.insert(error);
+
+    // Stops |result_run_loop_| if all additions are finished.
+    if (accounts_addition_count_ == 0) {
+      result_run_loop_->Quit();
+    }
   }
 
-  void WaitAndVerifyResult(kerberos::ErrorType expected_error) {
+  void WaitAndVerifyResult(std::multiset<kerberos::ErrorType> expected_errors_,
+                           int expected_notifications_count,
+                           int expected_accounts_count) {
+    EXPECT_LT(0, accounts_addition_count_);
     ASSERT_TRUE(result_run_loop_);
     result_run_loop_->Run();
-    ASSERT_TRUE(result_error_);
-    EXPECT_EQ(*result_error_, expected_error);
+
+    EXPECT_EQ(expected_errors_, result_errors_);
+    EXPECT_EQ(expected_notifications_count, observer_.notifications_count());
+    EXPECT_EQ(expected_accounts_count,
+              observer_.accounts_count_at_last_notification());
+
+    EXPECT_EQ(0, accounts_addition_count_);
     result_run_loop_.reset();
-    result_error_.reset();
+    result_errors_.clear();
+    observer_.Reset();
   }
 
   // Calls |mgr_->AddAccountAndAuthenticate()| with |principal_name| and some
-  // default parameters, waits for the result and expects |expected_error|.
+  // default parameters, waits for the result and checks expectations.
   void AddAccountAndAuthenticate(const char* principal_name,
-                                 kerberos::ErrorType expected_error) {
+                                 kerberos::ErrorType expected_error,
+                                 int expected_notifications_count,
+                                 int expected_accounts_count) {
     mgr_->AddAccountAndAuthenticate(principal_name, kUnmanaged, kPassword,
                                     kDontRememberPassword, kConfig,
                                     kAllowExisting, GetResultCallback());
-    WaitAndVerifyResult(expected_error);
+    WaitAndVerifyResult({expected_error}, expected_notifications_count,
+                        expected_accounts_count);
   }
 
   // Calls |mgr_->ListAccounts()|, waits for the result and expects success.
@@ -168,9 +238,11 @@
   std::unique_ptr<TestingProfile> profile_;
   std::unique_ptr<NotificationDisplayServiceTester> display_service_;
   std::unique_ptr<KerberosCredentialsManager> mgr_;
+  FakeKerberosCredentialsManagerObserver observer_;
 
+  int accounts_addition_count_ = 0;
   std::unique_ptr<base::RunLoop> result_run_loop_;
-  base::Optional<kerberos::ErrorType> result_error_;
+  std::multiset<kerberos::ErrorType> result_errors_;
 
  private:
   base::WeakPtrFactory<KerberosCredentialsManagerTest> weak_ptr_factory_{this};
@@ -208,7 +280,7 @@
   mgr_->AddAccountAndAuthenticate(kPrincipal, kManaged, kPassword,
                                   kRememberPassword, kConfig,
                                   kDontAllowExisting, GetResultCallback());
-  WaitAndVerifyResult(kerberos::ERROR_NONE);
+  WaitAndVerifyResult({kerberos::ERROR_NONE}, kOneNotification, kOneAccount);
 
   Account account = GetAccount();
   EXPECT_EQ(kNormalizedPrincipal, account.principal_name());
@@ -222,11 +294,16 @@
        AddAccountAndAuthenticateFailsForBadPrincipal) {
   const kerberos::ErrorType expected_error =
       kerberos::ERROR_PARSE_PRINCIPAL_FAILED;
-  AddAccountAndAuthenticate(kBadPrincipal1, expected_error);
-  AddAccountAndAuthenticate(kBadPrincipal2, expected_error);
-  AddAccountAndAuthenticate(kBadPrincipal3, expected_error);
-  AddAccountAndAuthenticate(kBadPrincipal4, expected_error);
-  AddAccountAndAuthenticate(kBadPrincipal5, expected_error);
+  AddAccountAndAuthenticate(kBadPrincipal1, expected_error, kNoNotification,
+                            kNoAccount);
+  AddAccountAndAuthenticate(kBadPrincipal2, expected_error, kNoNotification,
+                            kNoAccount);
+  AddAccountAndAuthenticate(kBadPrincipal3, expected_error, kNoNotification,
+                            kNoAccount);
+  AddAccountAndAuthenticate(kBadPrincipal4, expected_error, kNoNotification,
+                            kNoAccount);
+  AddAccountAndAuthenticate(kBadPrincipal5, expected_error, kNoNotification,
+                            kNoAccount);
 }
 
 // AddAccountAndAuthenticate calls KerberosClient methods in a certain order.
@@ -237,7 +314,7 @@
   mgr_->AddAccountAndAuthenticate(kPrincipal, kUnmanaged, kPassword,
                                   kDontRememberPassword, kConfig,
                                   kAllowExisting, GetResultCallback());
-  WaitAndVerifyResult(kerberos::ERROR_NONE);
+  WaitAndVerifyResult({kerberos::ERROR_NONE}, kOneNotification, kOneAccount);
   std::string calls =
       client_test_interface()->StopRecordingAndGetRecordedFunctionCalls();
   EXPECT_EQ(calls, "AddAccount,SetConfig,AcquireKerberosTgt,GetKerberosFiles");
@@ -248,7 +325,7 @@
   mgr_->AddAccountAndAuthenticate(kPrincipal, kManaged, kNoPassword,
                                   kDontRememberPassword, kConfig,
                                   kAllowExisting, GetResultCallback());
-  WaitAndVerifyResult(kerberos::ERROR_NONE);
+  WaitAndVerifyResult({kerberos::ERROR_NONE}, kOneNotification, kOneAccount);
   calls = client_test_interface()->StopRecordingAndGetRecordedFunctionCalls();
   EXPECT_EQ(calls, "AddAccount,SetConfig,GetKerberosFiles");
 }
@@ -257,24 +334,27 @@
 // ERROR_DUPLICATE_PRINCIPAL_NAME if |kDontAllowExisting| is passed in.
 TEST_F(KerberosCredentialsManagerTest,
        AddAccountAndAuthenticateRejectExistingAccount) {
-  AddAccountAndAuthenticate(kPrincipal, kerberos::ERROR_NONE);
+  AddAccountAndAuthenticate(kPrincipal, kerberos::ERROR_NONE, kOneNotification,
+                            kOneAccount);
   mgr_->AddAccountAndAuthenticate(kPrincipal, kUnmanaged, kPassword,
                                   kRememberPassword, kConfig,
                                   kDontAllowExisting, GetResultCallback());
-  WaitAndVerifyResult(kerberos::ERROR_DUPLICATE_PRINCIPAL_NAME);
+  WaitAndVerifyResult({kerberos::ERROR_DUPLICATE_PRINCIPAL_NAME},
+                      kOneNotification, kOneAccount);
 }
 
 // AddAccountAndAuthenticate succeeds and overwrites existing accounts if
 // |kAllowExisting| is passed in.
 TEST_F(KerberosCredentialsManagerTest,
        AddAccountAndAuthenticateAllowExistingAccount) {
-  AddAccountAndAuthenticate(kPrincipal, kerberos::ERROR_NONE);
+  AddAccountAndAuthenticate(kPrincipal, kerberos::ERROR_NONE, kOneNotification,
+                            kOneAccount);
   EXPECT_FALSE(GetAccount().password_was_remembered());
 
   mgr_->AddAccountAndAuthenticate(kPrincipal, kUnmanaged, kPassword,
                                   kRememberPassword, kConfig, kAllowExisting,
                                   GetResultCallback());
-  WaitAndVerifyResult(kerberos::ERROR_NONE);
+  WaitAndVerifyResult({kerberos::ERROR_NONE}, kOneNotification, kOneAccount);
 
   // Check change in password_was_remembered() to validate that the account was
   // overwritten.
@@ -290,7 +370,8 @@
   mgr_->AddAccountAndAuthenticate(kPrincipal, kUnmanaged, kPassword,
                                   kRememberPassword, kInvalidConfig,
                                   kAllowExisting, GetResultCallback());
-  WaitAndVerifyResult(kerberos::ERROR_BAD_CONFIG);
+  WaitAndVerifyResult({kerberos::ERROR_BAD_CONFIG}, kOneNotification,
+                      kNoAccount);
   std::string calls =
       client_test_interface()->StopRecordingAndGetRecordedFunctionCalls();
   EXPECT_EQ(calls, "AddAccount,SetConfig,RemoveAccount");
@@ -302,7 +383,8 @@
   mgr_->AddAccountAndAuthenticate(kPrincipal, kUnmanaged, kInvalidPassword,
                                   kRememberPassword, kConfig, kAllowExisting,
                                   GetResultCallback());
-  WaitAndVerifyResult(kerberos::ERROR_BAD_PASSWORD);
+  WaitAndVerifyResult({kerberos::ERROR_BAD_PASSWORD}, kOneNotification,
+                      kNoAccount);
   calls = client_test_interface()->StopRecordingAndGetRecordedFunctionCalls();
   EXPECT_EQ(calls, "AddAccount,SetConfig,AcquireKerberosTgt,RemoveAccount");
   EXPECT_EQ(0u, ListAccounts().size());
@@ -311,13 +393,15 @@
 // AddAccountAndAuthenticate does not remove accounts that already existed.
 TEST_F(KerberosCredentialsManagerTest,
        AddAccountAndAuthenticateDoesNotRemoveExistingAccountOnFailure) {
-  AddAccountAndAuthenticate(kPrincipal, kerberos::ERROR_NONE);
+  AddAccountAndAuthenticate(kPrincipal, kerberos::ERROR_NONE, kOneNotification,
+                            kOneAccount);
 
   client_test_interface()->StartRecordingFunctionCalls();
   mgr_->AddAccountAndAuthenticate(kPrincipal, kUnmanaged, kPassword,
                                   kRememberPassword, kInvalidConfig,
                                   kAllowExisting, GetResultCallback());
-  WaitAndVerifyResult(kerberos::ERROR_BAD_CONFIG);
+  WaitAndVerifyResult({kerberos::ERROR_BAD_CONFIG}, kOneNotification,
+                      kOneAccount);
   std::string calls =
       client_test_interface()->StopRecordingAndGetRecordedFunctionCalls();
   EXPECT_EQ(calls, "AddAccount,SetConfig");
@@ -333,7 +417,8 @@
   mgr_->AddAccountAndAuthenticate(kPrincipal, kManaged, kPassword,
                                   kRememberPassword, kInvalidConfig,
                                   kAllowExisting, GetResultCallback());
-  WaitAndVerifyResult(kerberos::ERROR_BAD_CONFIG);
+  WaitAndVerifyResult({kerberos::ERROR_BAD_CONFIG}, kOneNotification,
+                      kOneAccount);
   std::string calls =
       client_test_interface()->StopRecordingAndGetRecordedFunctionCalls();
   EXPECT_EQ(calls, "AddAccount,SetConfig");
@@ -349,14 +434,14 @@
   mgr_->AddAccountAndAuthenticate(kPrincipal, kUnmanaged, kPassword,
                                   kDontRememberPassword, kConfig,
                                   kAllowExisting, GetResultCallback());
-  WaitAndVerifyResult(kerberos::ERROR_NONE);
+  WaitAndVerifyResult({kerberos::ERROR_NONE}, kOneNotification, kOneAccount);
   EXPECT_EQ(kNormalizedPrincipal, mgr_->GetActiveAccount());
 
   // Adding another unmanaged account DOES change the active account.
   mgr_->AddAccountAndAuthenticate(kOtherPrincipal, kUnmanaged, kPassword,
                                   kDontRememberPassword, kConfig,
                                   kAllowExisting, GetResultCallback());
-  WaitAndVerifyResult(kerberos::ERROR_NONE);
+  WaitAndVerifyResult({kerberos::ERROR_NONE}, kOneNotification, kTwoAccounts);
   EXPECT_EQ(kNormalizedOtherPrincipal, mgr_->GetActiveAccount());
 }
 
@@ -369,23 +454,82 @@
   mgr_->AddAccountAndAuthenticate(kPrincipal, kManaged, kPassword,
                                   kDontRememberPassword, kConfig,
                                   kAllowExisting, GetResultCallback());
-  WaitAndVerifyResult(kerberos::ERROR_NONE);
+  WaitAndVerifyResult({kerberos::ERROR_NONE}, kOneNotification, kOneAccount);
   EXPECT_EQ(kNormalizedPrincipal, mgr_->GetActiveAccount());
 
   // Adding another managed account DOES NOT change the active account.
   mgr_->AddAccountAndAuthenticate(kOtherPrincipal, kManaged, kPassword,
                                   kDontRememberPassword, kConfig,
                                   kAllowExisting, GetResultCallback());
-  WaitAndVerifyResult(kerberos::ERROR_NONE);
+  WaitAndVerifyResult({kerberos::ERROR_NONE}, kOneNotification, kTwoAccounts);
   EXPECT_EQ(kNormalizedPrincipal, mgr_->GetActiveAccount());
 }
 
+// AddAccountAndAuthenticate attempts to add multiple accounts, all of them
+// fail, and observers are notified once.
+TEST_F(KerberosCredentialsManagerTest,
+       AddAccountAndAuthenticateAddsMultipleAccountsAllFail) {
+  EXPECT_TRUE(mgr_->GetActiveAccount().empty());
+  mgr_->AddAccountAndAuthenticate(kPrincipal, kManaged, kInvalidPassword,
+                                  kDontRememberPassword, kConfig,
+                                  kAllowExisting, GetResultCallback());
+  mgr_->AddAccountAndAuthenticate(kOtherPrincipal, kManaged, kInvalidPassword,
+                                  kDontRememberPassword, kConfig,
+                                  kAllowExisting, GetResultCallback());
+  mgr_->AddAccountAndAuthenticate(kYetAnotherPrincipal, kManaged,
+                                  kInvalidPassword, kDontRememberPassword,
+                                  kConfig, kAllowExisting, GetResultCallback());
+
+  WaitAndVerifyResult(
+      {kerberos::ERROR_BAD_PASSWORD, kerberos::ERROR_BAD_PASSWORD,
+       kerberos::ERROR_BAD_PASSWORD},
+      kOneNotification, kThreeAccounts);
+  EXPECT_TRUE(mgr_->GetActiveAccount().empty());
+}
+
+// AddAccountAndAuthenticate attempts to add multiple accounts, all of them
+// succeed, and observers are notified once.
+TEST_F(KerberosCredentialsManagerTest,
+       AddAccountAndAuthenticateAddsMultipleAccountsAllSucceed) {
+  EXPECT_TRUE(mgr_->GetActiveAccount().empty());
+  mgr_->AddAccountAndAuthenticate(kPrincipal, kManaged, kPassword,
+                                  kDontRememberPassword, kConfig,
+                                  kAllowExisting, GetResultCallback());
+  mgr_->AddAccountAndAuthenticate(kOtherPrincipal, kManaged, kPassword,
+                                  kDontRememberPassword, kConfig,
+                                  kAllowExisting, GetResultCallback());
+  mgr_->AddAccountAndAuthenticate(kYetAnotherPrincipal, kManaged, kPassword,
+                                  kDontRememberPassword, kConfig,
+                                  kAllowExisting, GetResultCallback());
+
+  WaitAndVerifyResult(
+      {kerberos::ERROR_NONE, kerberos::ERROR_NONE, kerberos::ERROR_NONE},
+      kOneNotification, kThreeAccounts);
+  EXPECT_EQ(kNormalizedPrincipal, mgr_->GetActiveAccount());
+}
+
+// AddAccountAndAuthenticate attempts to add multiple accounts, only one
+// succeed, and observers are notified once.
+TEST_F(KerberosCredentialsManagerTest,
+       AddAccountAndAuthenticateAddsMultipleAccountsSingleSuccess) {
+  EXPECT_TRUE(mgr_->GetActiveAccount().empty());
+  mgr_->AddAccountAndAuthenticate(kPrincipal, kManaged, kInvalidPassword,
+                                  kDontRememberPassword, kConfig,
+                                  kAllowExisting, GetResultCallback());
+  mgr_->AddAccountAndAuthenticate(kOtherPrincipal, kManaged, kPassword,
+                                  kDontRememberPassword, kConfig,
+                                  kAllowExisting, GetResultCallback());
+  mgr_->AddAccountAndAuthenticate(kYetAnotherPrincipal, kManaged,
+                                  kInvalidPassword, kDontRememberPassword,
+                                  kConfig, kAllowExisting, GetResultCallback());
+
+  WaitAndVerifyResult({kerberos::ERROR_BAD_PASSWORD, kerberos::ERROR_NONE,
+                       kerberos::ERROR_BAD_PASSWORD},
+                      kOneNotification, kThreeAccounts);
+  EXPECT_EQ(kNormalizedOtherPrincipal, mgr_->GetActiveAccount());
+}
+
 // TODO(https://crbug.com/952251): Add more tests
-// - AddAccountAndAuthenticate
-//     + On success, calls OnAccountsChanged on observers (in case multiple
-//       accounts are added by the KerberosAccounts policy, only by the last
-//       account, EVEN IF IT FAILS!)
-//     + On success and if it was the active principal, calls GetKerberosFiles.
 // - RemoveAccount
 //     + Normalization like in AddAccountAndAuthenticate
 //     + Calls the RemoveAccount KerberosClient method
diff --git a/chrome/browser/chromeos/login/ui/oobe_ui_dialog_delegate.cc b/chrome/browser/chromeos/login/ui/oobe_ui_dialog_delegate.cc
index 818b703..17fcf20 100644
--- a/chrome/browser/chromeos/login/ui/oobe_ui_dialog_delegate.cc
+++ b/chrome/browser/chromeos/login/ui/oobe_ui_dialog_delegate.cc
@@ -42,6 +42,7 @@
 
 constexpr char kGaiaURL[] = "chrome://oobe/gaia-signin";
 constexpr char kAppLaunchBailout[] = "app_launch_bailout";
+constexpr char kAppLaunchNetworkConfig[] = "app_launch_network_config";
 constexpr char kCancel[] = "cancel";
 
 CoreOobeView::DialogPaddingMode ConvertDialogPaddingMode(
@@ -306,6 +307,9 @@
 
   accel_map_[ui::Accelerator(
       ui::VKEY_S, ui::EF_CONTROL_DOWN | ui::EF_ALT_DOWN)] = kAppLaunchBailout;
+  accel_map_[ui::Accelerator(ui::VKEY_N,
+                             ui::EF_CONTROL_DOWN | ui::EF_ALT_DOWN)] =
+      kAppLaunchNetworkConfig;
   accel_map_[ui::Accelerator(ui::VKEY_ESCAPE, 0)] = kCancel;
 
   DCHECK(!dialog_view_ && !widget_);
diff --git a/chrome/browser/chromeos/login/users/chrome_user_manager_impl.cc b/chrome/browser/chromeos/login/users/chrome_user_manager_impl.cc
index 7a8f238f..8d465bb 100644
--- a/chrome/browser/chromeos/login/users/chrome_user_manager_impl.cc
+++ b/chrome/browser/chromeos/login/users/chrome_user_manager_impl.cc
@@ -642,8 +642,7 @@
     const content::NotificationDetails& details) {
   DCHECK_EQ(type, chrome::NOTIFICATION_LOGIN_USER_PROFILE_PREPARED);
   Profile* profile = content::Details<Profile>(details).ptr();
-  if (IsUserLoggedIn() && !IsLoggedInAsGuest() && !IsLoggedInAsKioskApp() &&
-      !IsLoggedInAsArcKioskApp()) {
+  if (IsUserLoggedIn() && !IsLoggedInAsGuest() && !IsLoggedInAsAnyKioskApp()) {
     if (!profile->IsOffTheRecord()) {
       if (AuthSyncObserver::ShouldObserve(profile)) {
         AuthSyncObserver* sync_observer =
@@ -1042,6 +1041,7 @@
       user_manager::User::USER_IMAGE_INVALID, false);
 
   base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
+  command_line->AppendSwitch(::switches::kForceWebAppMode);
   command_line->AppendSwitch(
       ::switches::kSilentLaunch);  // To open no extra windows.
 
diff --git a/chrome/browser/chromeos/login/users/fake_chrome_user_manager.cc b/chrome/browser/chromeos/login/users/fake_chrome_user_manager.cc
index 887f126..8cffacc 100644
--- a/chrome/browser/chromeos/login/users/fake_chrome_user_manager.cc
+++ b/chrome/browser/chromeos/login/users/fake_chrome_user_manager.cc
@@ -558,6 +558,11 @@
              : false;
 }
 
+bool FakeChromeUserManager::IsLoggedInAsAnyKioskApp() const {
+  const user_manager::User* active_user = GetActiveUser();
+  return active_user && active_user->IsKioskType();
+}
+
 bool FakeChromeUserManager::IsLoggedInAsStub() const {
   return false;
 }
diff --git a/chrome/browser/chromeos/login/users/fake_chrome_user_manager.h b/chrome/browser/chromeos/login/users/fake_chrome_user_manager.h
index f9f4d82..f517d8e 100644
--- a/chrome/browser/chromeos/login/users/fake_chrome_user_manager.h
+++ b/chrome/browser/chromeos/login/users/fake_chrome_user_manager.h
@@ -109,6 +109,7 @@
   bool IsLoggedInAsSupervisedUser() const override;
   bool IsLoggedInAsKioskApp() const override;
   bool IsLoggedInAsArcKioskApp() const override;
+  bool IsLoggedInAsAnyKioskApp() const override;
   bool IsLoggedInAsStub() const override;
   bool IsUserNonCryptohomeDataEphemeral(
       const AccountId& account_id) const override;
diff --git a/chrome/browser/chromeos/login/users/mock_user_manager.cc b/chrome/browser/chromeos/login/users/mock_user_manager.cc
index 596b55c..b49099c5 100644
--- a/chrome/browser/chromeos/login/users/mock_user_manager.cc
+++ b/chrome/browser/chromeos/login/users/mock_user_manager.cc
@@ -66,6 +66,10 @@
   return GetActiveUser();
 }
 
+bool MockUserManager::IsLoggedInAsAnyKioskApp() const {
+  return IsLoggedInAsKioskApp() || IsLoggedInAsArcKioskApp();
+}
+
 MultiProfileUserController* MockUserManager::GetMultiProfileUserController() {
   return nullptr;
 }
diff --git a/chrome/browser/chromeos/login/users/mock_user_manager.h b/chrome/browser/chromeos/login/users/mock_user_manager.h
index 66c1756..671ec43 100644
--- a/chrome/browser/chromeos/login/users/mock_user_manager.h
+++ b/chrome/browser/chromeos/login/users/mock_user_manager.h
@@ -131,6 +131,9 @@
   user_manager::User* GetActiveUser() override;
   const user_manager::User* GetPrimaryUser() const override;
 
+  // We can't mock it as easily.
+  bool IsLoggedInAsAnyKioskApp() const override;
+
   // ChromeUserManager overrides:
   MultiProfileUserController* GetMultiProfileUserController() override;
   UserImageManager* GetUserImageManager(const AccountId& account_id) override;
diff --git a/chrome/browser/chromeos/login/web_kiosk_controller.cc b/chrome/browser/chromeos/login/web_kiosk_controller.cc
index 16633bad..ff4c940b 100644
--- a/chrome/browser/chromeos/login/web_kiosk_controller.cc
+++ b/chrome/browser/chromeos/login/web_kiosk_controller.cc
@@ -61,7 +61,7 @@
 void WebKioskController::OnTimerFire() {
   // Start launching now.
   if (app_prepared_) {
-    // TODO(crbug.com/1006230): Implement LaunchApp();
+    app_launcher_->LaunchApp();
   } else {
     launch_on_install_ = true;
   }
@@ -152,8 +152,16 @@
   // Reset virtual keyboard to use IME engines in app profile early.
   ChromeKeyboardControllerClient::Get()->RebuildKeyboardIfEnabled();
 
-  // TODO(crbug.com/1006230): Implement app installation/initalization start
-  // here.
+  app_launcher_.reset(new WebKioskAppLauncher(profile, this));
+  app_launcher_->Initialize(account_id_);
+}
+
+void WebKioskController::InitializeNetwork() {
+  if (!web_kiosk_splash_screen_view_)
+    return;
+
+  // TODO(crbug.com/1006230): Implement network dialog flow.
+  app_launcher_->ContinueWithNetworkReady();
 }
 
 void WebKioskController::OnAppStartedInstalling() {
@@ -167,14 +175,14 @@
 
 void WebKioskController::OnAppPrepared() {
   app_prepared_ = true;
-  if (web_kiosk_splash_screen_view_)
+  if (!web_kiosk_splash_screen_view_)
     return;
   web_kiosk_splash_screen_view_->UpdateAppLaunchState(
       AppLaunchSplashScreenView::AppLaunchState::
           APP_LAUNCH_STATE_WAITING_APP_WINDOW);
   web_kiosk_splash_screen_view_->Show();
   if (launch_on_install_) {
-    // TODO(crbug.com/1006230): Launch app here.
+    app_launcher_->LaunchApp();
   }
 }
 
@@ -183,6 +191,7 @@
   // more seconds to give the user ability to exit Web kiosk.
   SYSLOG(INFO) << "Kiosk launch succeeded, wait for app window.";
   session_manager::SessionManager::Get()->SessionStarted();
+  CloseSplashScreen();
 }
 
 void WebKioskController::OnAppLaunchFailed() {
diff --git a/chrome/browser/chromeos/login/web_kiosk_controller.h b/chrome/browser/chromeos/login/web_kiosk_controller.h
index f3853e7..4cfea7f 100644
--- a/chrome/browser/chromeos/login/web_kiosk_controller.h
+++ b/chrome/browser/chromeos/login/web_kiosk_controller.h
@@ -8,6 +8,7 @@
 #include "base/macros.h"
 #include "base/memory/weak_ptr.h"
 #include "chrome/browser/chromeos/app_mode/kiosk_app_manager_base.h"
+#include "chrome/browser/chromeos/app_mode/web_app/web_kiosk_app_launcher.h"
 #include "chrome/browser/chromeos/login/session/user_session_manager.h"
 #include "chrome/browser/ui/webui/chromeos/login/app_launch_splash_screen_handler.h"
 #include "chromeos/login/auth/login_performer.h"
@@ -29,19 +30,14 @@
 // kiosk profile, and updating the splash screen UI.
 class WebKioskController : public LoginPerformer::Delegate,
                            public UserSessionManagerDelegate,
-                           public AppLaunchSplashScreenView::Delegate {
+                           public AppLaunchSplashScreenView::Delegate,
+                           public WebKioskAppLauncher::Delegate {
  public:
   WebKioskController(LoginDisplayHost* host, OobeUI* oobe_ui);
   ~WebKioskController() override;
 
   void StartWebKiosk(const AccountId& account_id);
 
-  // Callbacks to Web Kiosk Launcher
-  void OnAppStartedInstalling();
-  void OnAppPrepared();
-  void OnAppLaunched();
-  void OnAppLaunchFailed();
-
  private:
   // LoginPerformer::Delegate:
   void OnAuthFailure(const AuthFailure& error) override;
@@ -63,6 +59,13 @@
   void OnDeletingSplashScreenView() override;
   KioskAppManagerBase::App GetAppData() override;
 
+  // WebKioskAppLauncher:
+  void InitializeNetwork() override;
+  void OnAppStartedInstalling() override;
+  void OnAppPrepared() override;
+  void OnAppLaunched() override;
+  void OnAppLaunchFailed() override;
+
   void CleanUp();
   void OnTimerFire();
   void CloseSplashScreen();
@@ -75,6 +78,9 @@
   bool app_prepared_ = false;
   bool launch_on_install_ = false;
 
+  // Used to prepare and launch the actual web kiosk app, is created after
+  // profile initialization.
+  std::unique_ptr<WebKioskAppLauncher> app_launcher_;
   // Used to execute login operations.
   std::unique_ptr<LoginPerformer> login_performer_;
 
diff --git a/chrome/browser/chromeos/net/client_cert_filter_chromeos.cc b/chrome/browser/chromeos/net/client_cert_filter_chromeos.cc
index 9d55315b..e05f240 100644
--- a/chrome/browser/chromeos/net/client_cert_filter_chromeos.cc
+++ b/chrome/browser/chromeos/net/client_cert_filter_chromeos.cc
@@ -7,80 +7,142 @@
 #include <utility>
 
 #include "base/bind.h"
+#include "base/task/post_task.h"
+#include "content/public/browser/browser_task_traits.h"
 #include "crypto/nss_util_internal.h"
+#include "crypto/scoped_nss_types.h"
+#include "net/cert/nss_profile_filter_chromeos.h"
 
 namespace chromeos {
 
+class ClientCertFilterChromeOS::CertFilterIO {
+ public:
+  CertFilterIO(bool use_system_slot, const std::string& username_hash)
+      : use_system_slot_(use_system_slot), username_hash_(username_hash) {}
+
+  // Must be called on the IO thread. Calls |callback| on the UI thread.
+  void Init(base::OnceClosure callback) {
+    DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
+    DCHECK(!init_called_);
+    init_called_ = true;
+
+    waiting_for_private_slot_ = true;
+
+    if (use_system_slot_) {
+      system_slot_ = crypto::GetSystemNSSKeySlot(base::BindOnce(
+          &CertFilterIO::GotSystemSlot, weak_ptr_factory_.GetWeakPtr()));
+    }
+
+    private_slot_ = crypto::GetPrivateSlotForChromeOSUser(
+        username_hash_, base::BindOnce(&CertFilterIO::GotPrivateSlot,
+                                       weak_ptr_factory_.GetWeakPtr()));
+
+    // If the returned slot is null, GotPrivateSlot will be called back
+    // eventually. If it is not null, the private slot was available
+    // synchronously and the callback will not be called.
+    if (private_slot_)
+      waiting_for_private_slot_ = false;
+
+    init_callback_ = std::move(callback);
+    InitIfSlotsAvailable();
+  }
+
+  // May be called on any thread, after Init()'s callback has run.
+  bool IsCertAllowed(CERTCertificate* cert) {
+    return nss_profile_filter_.IsCertAllowed(cert);
+  }
+
+ private:
+  // Called back if the system slot was retrieved asynchronously. Continues the
+  // initialization.
+  void GotSystemSlot(crypto::ScopedPK11Slot system_slot) {
+    DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
+    system_slot_ = std::move(system_slot);
+    InitIfSlotsAvailable();
+  }
+
+  // Called back if the private slot was retrieved asynchronously. Continues the
+  // initialization.
+  void GotPrivateSlot(crypto::ScopedPK11Slot private_slot) {
+    DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
+    waiting_for_private_slot_ = false;
+    private_slot_ = std::move(private_slot);
+    InitIfSlotsAvailable();
+  }
+
+  // If the required slots (|private_slot_| and conditionally |system_slot_|)
+  // are available, initializes |nss_profile_filter_| and posts |init_callback_|
+  // to the UI thread.
+  void InitIfSlotsAvailable() {
+    DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
+    if ((use_system_slot_ && !system_slot_) || waiting_for_private_slot_)
+      return;
+    nss_profile_filter_.Init(
+        crypto::GetPublicSlotForChromeOSUser(username_hash_),
+        std::move(private_slot_), std::move(system_slot_));
+    if (!init_callback_.is_null()) {
+      base::PostTask(FROM_HERE, {content::BrowserThread::UI},
+                     std::move(init_callback_));
+    }
+  }
+
+  // True once Init() was called.
+  bool init_called_ = false;
+
+  // The callback provided to Init(). Called on the UI thread.
+  base::OnceClosure init_callback_;
+
+  bool use_system_slot_;
+  std::string username_hash_;
+
+  // Used to store the system slot, if required, for initialization. Will be
+  // null after the filter is initialized.
+  crypto::ScopedPK11Slot system_slot_;
+
+  // Used to store the private slot for initialization. Will be null after the
+  // filter is initialized.
+  crypto::ScopedPK11Slot private_slot_;
+
+  // If a private slot is requested but the slot, maybe null, is not obtained
+  // yet, this is equal true. As long as this is true, the NSSProfileFilter will
+  // not be initialized.
+  bool waiting_for_private_slot_ = false;
+
+  net::NSSProfileFilterChromeOS nss_profile_filter_;
+  base::WeakPtrFactory<CertFilterIO> weak_ptr_factory_{this};
+};
+
 ClientCertFilterChromeOS::ClientCertFilterChromeOS(
     bool use_system_slot,
     const std::string& username_hash)
-    : init_called_(false),
-      use_system_slot_(use_system_slot),
-      username_hash_(username_hash),
-      waiting_for_private_slot_(false) {}
-
-ClientCertFilterChromeOS::~ClientCertFilterChromeOS() {
+    : cert_filter_io_(new CertFilterIO(use_system_slot, username_hash)) {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
 }
 
-bool ClientCertFilterChromeOS::Init(const base::Closure& callback) {
-  DCHECK(!init_called_);
-  init_called_ = true;
+ClientCertFilterChromeOS::~ClientCertFilterChromeOS() {}
 
-  waiting_for_private_slot_ = true;
+bool ClientCertFilterChromeOS::Init(base::OnceClosure callback) {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
 
-  if (use_system_slot_) {
-    system_slot_ = crypto::GetSystemNSSKeySlot(
-        base::Bind(&ClientCertFilterChromeOS::GotSystemSlot,
-                   weak_ptr_factory_.GetWeakPtr()));
-  }
-
-  private_slot_ = crypto::GetPrivateSlotForChromeOSUser(
-      username_hash_, base::Bind(&ClientCertFilterChromeOS::GotPrivateSlot,
-                                 weak_ptr_factory_.GetWeakPtr()));
-
-  // If the returned slot is null, GotPrivateSlot will be called back
-  // eventually. If it is not null, the private slot was available synchronously
-  // and the callback will not be called.
-  if (private_slot_)
-    waiting_for_private_slot_ = false;
-
-  // Do not call back if we initialized synchronously.
-  if (InitIfSlotsAvailable())
-    return true;
-
-  init_callback_ = callback;
+  // base::Unretained() is safe here because |cert_filter_io_| is destroyed on
+  // a post to the IO thread.
+  base::PostTask(
+      FROM_HERE, {content::BrowserThread::IO},
+      base::BindOnce(
+          &CertFilterIO::Init, base::Unretained(cert_filter_io_.get()),
+          // Wrap |callback| in OnInitComplete so it is cancelled if the
+          // ClientCertFilterChromeOS is destroyed earlier.
+          base::BindOnce(&ClientCertFilterChromeOS::OnInitComplete,
+                         weak_ptr_factory_.GetWeakPtr(), std::move(callback))));
   return false;
 }
 
 bool ClientCertFilterChromeOS::IsCertAllowed(CERTCertificate* cert) const {
-  return nss_profile_filter_.IsCertAllowed(cert);
+  return cert_filter_io_->IsCertAllowed(cert);
 }
 
-void ClientCertFilterChromeOS::GotSystemSlot(
-    crypto::ScopedPK11Slot system_slot) {
-  system_slot_ = std::move(system_slot);
-  if (InitIfSlotsAvailable() && !init_callback_.is_null()) {
-    init_callback_.Run();
-    init_callback_.Reset();
-  }
-}
-
-void ClientCertFilterChromeOS::GotPrivateSlot(
-    crypto::ScopedPK11Slot private_slot) {
-  waiting_for_private_slot_ = false;
-  private_slot_ = std::move(private_slot);
-  if (InitIfSlotsAvailable() && !init_callback_.is_null()) {
-    init_callback_.Run();
-    init_callback_.Reset();
-  }
-}
-
-bool ClientCertFilterChromeOS::InitIfSlotsAvailable() {
-  if ((use_system_slot_ && !system_slot_) || waiting_for_private_slot_)
-    return false;
-  nss_profile_filter_.Init(crypto::GetPublicSlotForChromeOSUser(username_hash_),
-                           std::move(private_slot_), std::move(system_slot_));
-  return true;
+void ClientCertFilterChromeOS::OnInitComplete(base::OnceClosure callback) {
+  std::move(callback).Run();
 }
 
 }  // namespace chromeos
diff --git a/chrome/browser/chromeos/net/client_cert_filter_chromeos.h b/chrome/browser/chromeos/net/client_cert_filter_chromeos.h
index 1f9cf038..1ec989b0 100644
--- a/chrome/browser/chromeos/net/client_cert_filter_chromeos.h
+++ b/chrome/browser/chromeos/net/client_cert_filter_chromeos.h
@@ -10,8 +10,9 @@
 
 #include "base/callback.h"
 #include "base/memory/weak_ptr.h"
-#include "crypto/scoped_nss_types.h"
-#include "net/cert/nss_profile_filter_chromeos.h"
+#include "content/public/browser/browser_thread.h"
+
+typedef struct CERTCertificateStr CERTCertificate;
 
 namespace chromeos {
 
@@ -33,7 +34,7 @@
   // otherwise returns false and calls |callback| once the initialization is
   // completed.
   // Must be called at most once.
-  bool Init(const base::Closure& callback);
+  bool Init(base::OnceClosure callback);
 
   // Returns true if |cert| is allowed to be used as a client certificate (e.g.
   // for a certain browser context or user). This is only called once
@@ -41,43 +42,14 @@
   bool IsCertAllowed(CERTCertificate* cert) const;
 
  private:
-  // Called back if the system slot was retrieved asynchronously. Continues the
-  // initialization.
-  void GotSystemSlot(crypto::ScopedPK11Slot system_slot);
+  class CertFilterIO;
 
-  // Called back if the private slot was retrieved asynchronously. Continues the
-  // initialization.
-  void GotPrivateSlot(crypto::ScopedPK11Slot private_slot);
+  void OnInitComplete(base::OnceClosure callback);
 
-  // If the required slots (|private_slot_| and conditionally |system_slot_|)
-  // are available, initializes |nss_profile_filter_| and returns true.
-  // Otherwise returns false.
-  bool InitIfSlotsAvailable();
-
-  // True once Init() was called.
-  bool init_called_;
-
-  // The callback provided to Init(), which may be null. Reset after the filter
-  // is initialized.
-  base::Closure init_callback_;
-
-  bool use_system_slot_;
-  std::string username_hash_;
-
-  // Used to store the system slot, if required, for initialization. Will be
-  // null after the filter is initialized.
-  crypto::ScopedPK11Slot system_slot_;
-
-  // Used to store the private slot for initialization. Will be null after the
-  // filter is initialized.
-  crypto::ScopedPK11Slot private_slot_;
-
-  // If a private slot is requested but the slot, maybe null, is not obtained
-  // yet, this is equal true. As long as this is true, the NSSProfileFilter will
-  // not be initialized.
-  bool waiting_for_private_slot_;
-
-  net::NSSProfileFilterChromeOS nss_profile_filter_;
+  // This class lives on the UI thread but the NSS ChromeOS user integration
+  // must be called from the IO thread.
+  std::unique_ptr<CertFilterIO, content::BrowserThread::DeleteOnIOThread>
+      cert_filter_io_;
   base::WeakPtrFactory<ClientCertFilterChromeOS> weak_ptr_factory_{this};
 };
 
diff --git a/chrome/browser/chromeos/net/client_cert_store_chromeos_unittest.cc b/chrome/browser/chromeos/net/client_cert_store_chromeos_unittest.cc
index 73143593..9b77f20 100644
--- a/chrome/browser/chromeos/net/client_cert_store_chromeos_unittest.cc
+++ b/chrome/browser/chromeos/net/client_cert_store_chromeos_unittest.cc
@@ -15,10 +15,8 @@
 #include "base/memory/ptr_util.h"
 #include "base/memory/ref_counted.h"
 #include "base/run_loop.h"
-#include "base/single_thread_task_runner.h"
-#include "base/test/task_environment.h"
-#include "base/threading/thread_task_runner_handle.h"
 #include "chrome/browser/chromeos/certificate_provider/certificate_provider.h"
+#include "content/public/test/browser_task_environment.h"
 #include "crypto/nss_util_internal.h"
 #include "crypto/scoped_nss_types.h"
 #include "crypto/scoped_test_nss_chromeos_user.h"
@@ -72,8 +70,7 @@
   crypto::ScopedTestNSSChromeOSUser user1_{"user1"};
   crypto::ScopedTestNSSChromeOSUser user2_{"user2"};
   crypto::ScopedTestSystemNSSKeySlot system_slot_;
-  base::test::TaskEnvironment task_environment_{
-      base::test::TaskEnvironment::MainThreadType::IO};
+  content::BrowserTaskEnvironment task_environment_;
 };
 
 // Ensure that cert requests, that are started before the filter is initialized,
diff --git a/chrome/browser/chromeos/platform_keys/platform_keys_nss.cc b/chrome/browser/chromeos/platform_keys/platform_keys_nss.cc
index b24d364..c55c886 100644
--- a/chrome/browser/chromeos/platform_keys/platform_keys_nss.cc
+++ b/chrome/browser/chromeos/platform_keys/platform_keys_nss.cc
@@ -222,8 +222,6 @@
 class SelectCertificatesState : public NSSOperationState {
  public:
   explicit SelectCertificatesState(
-      const std::string& username_hash,
-      const bool use_system_key_slot,
       const scoped_refptr<net::SSLCertRequestInfo>& request,
       const subtle::SelectCertificatesCallback& callback);
   ~SelectCertificatesState() override {}
@@ -241,8 +239,6 @@
         from, base::Bind(callback_, base::Passed(&matches), error_message));
   }
 
-  const std::string username_hash_;
-  const bool use_system_key_slot_;
   scoped_refptr<net::SSLCertRequestInfo> cert_request_info_;
   std::unique_ptr<net::ClientCertStore> cert_store_;
 
@@ -393,15 +389,9 @@
       callback_(callback) {}
 
 SelectCertificatesState::SelectCertificatesState(
-    const std::string& username_hash,
-    const bool use_system_key_slot,
     const scoped_refptr<net::SSLCertRequestInfo>& cert_request_info,
     const subtle::SelectCertificatesCallback& callback)
-    : username_hash_(username_hash),
-      use_system_key_slot_(use_system_key_slot),
-      cert_request_info_(cert_request_info),
-      callback_(callback) {
-}
+    : cert_request_info_(cert_request_info), callback_(callback) {}
 
 GetCertificatesState::GetCertificatesState(
     const GetCertificatesCallback& callback)
@@ -572,12 +562,10 @@
 }
 
 // Called when ClientCertStoreChromeOS::GetClientCerts is done. Builds the list
-// of net::CertificateList and calls back. Used by
-// SelectCertificatesOnIOThread().
-void DidSelectCertificatesOnIOThread(
-    std::unique_ptr<SelectCertificatesState> state,
-    net::ClientCertIdentityList identities) {
-  DCHECK_CURRENTLY_ON(BrowserThread::IO);
+// of net::CertificateList and calls back. Used by SelectCertificates().
+void DidSelectCertificates(std::unique_ptr<SelectCertificatesState> state,
+                           net::ClientCertIdentityList identities) {
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   // Convert the ClientCertIdentityList to a CertificateList since returning
   // ClientCertIdentities would require changing the platformKeys extension
   // api. This assumes that the necessary keys can be found later with
@@ -586,23 +574,12 @@
       std::make_unique<net::CertificateList>();
   for (const std::unique_ptr<net::ClientCertIdentity>& identity : identities)
     certs->push_back(identity->certificate());
-  state->CallBack(FROM_HERE, std::move(certs), std::string() /* no error */);
-}
-
-// Continues selecting certificates on the IO thread. Used by
-// SelectClientCertificates().
-void SelectCertificatesOnIOThread(
-    std::unique_ptr<SelectCertificatesState> state) {
-  DCHECK_CURRENTLY_ON(BrowserThread::IO);
-  state->cert_store_.reset(new ClientCertStoreChromeOS(
-      nullptr,  // no additional provider
-      state->use_system_key_slot_, state->username_hash_,
-      ClientCertStoreChromeOS::PasswordDelegateFactory()));
-
-  SelectCertificatesState* state_ptr = state.get();
-  state_ptr->cert_store_->GetClientCerts(
-      *state_ptr->cert_request_info_,
-      base::BindOnce(&DidSelectCertificatesOnIOThread, std::move(state)));
+  // DidSelectCertificates() may be called synchronously, so run the callback on
+  // a separate event loop iteration to avoid potential reentrancy bugs.
+  base::PostTask(FROM_HERE, {BrowserThread::UI},
+                 base::BindOnce(&SelectCertificatesState::CallBack,
+                                std::move(state), FROM_HERE, std::move(certs),
+                                std::string() /* no error */));
 }
 
 // Filters the obtained certificates on a worker thread. Used by
@@ -886,12 +863,19 @@
   // device.
   const bool use_system_key_slot = user->IsAffiliated();
 
-  std::unique_ptr<SelectCertificatesState> state(new SelectCertificatesState(
-      user->username_hash(), use_system_key_slot, cert_request_info, callback));
+  auto state =
+      std::make_unique<SelectCertificatesState>(cert_request_info, callback);
 
-  base::PostTask(
-      FROM_HERE, {BrowserThread::IO},
-      base::BindOnce(&SelectCertificatesOnIOThread, std::move(state)));
+  state->cert_store_ = std::make_unique<ClientCertStoreChromeOS>(
+      nullptr,  // no additional provider
+      use_system_key_slot, user->username_hash(),
+      ClientCertStoreChromeOS::PasswordDelegateFactory());
+
+  // Note DidSelectCertificates() may be called synchronously.
+  SelectCertificatesState* state_ptr = state.get();
+  state_ptr->cert_store_->GetClientCerts(
+      *state_ptr->cert_request_info_,
+      base::BindOnce(&DidSelectCertificates, std::move(state)));
 }
 
 }  // namespace subtle
diff --git a/chrome/browser/chromeos/plugin_vm/plugin_vm_image_manager.cc b/chrome/browser/chromeos/plugin_vm/plugin_vm_image_manager.cc
index 0525990..1dc0ebb 100644
--- a/chrome/browser/chromeos/plugin_vm/plugin_vm_image_manager.cc
+++ b/chrome/browser/chromeos/plugin_vm/plugin_vm_image_manager.cc
@@ -149,6 +149,7 @@
   chromeos::DBusThreadManager::Get()
       ->GetDebugDaemonClient()
       ->StartPluginVmDispatcher(
+          chromeos::ProfileHelper::GetUserIdHashFromProfile(profile_),
           base::BindOnce(&PluginVmImageManager::OnPluginVmDispatcherStarted,
                          weak_ptr_factory_.GetWeakPtr()));
 }
diff --git a/chrome/browser/chromeos/plugin_vm/plugin_vm_manager.cc b/chrome/browser/chromeos/plugin_vm/plugin_vm_manager.cc
index 58afe21..4ed6a125 100644
--- a/chrome/browser/chromeos/plugin_vm/plugin_vm_manager.cc
+++ b/chrome/browser/chromeos/plugin_vm/plugin_vm_manager.cc
@@ -140,8 +140,8 @@
   chromeos::DBusThreadManager::Get()
       ->GetDebugDaemonClient()
       ->StartPluginVmDispatcher(
-          base::BindOnce(&PluginVmManager::OnStartPluginVmDispatcher,
-                         weak_ptr_factory_.GetWeakPtr()));
+          owner_id_, base::BindOnce(&PluginVmManager::OnStartPluginVmDispatcher,
+                                    weak_ptr_factory_.GetWeakPtr()));
 }
 
 void PluginVmManager::StopPluginVm() {
diff --git a/chrome/browser/chromeos/system/automatic_reboot_manager.cc b/chrome/browser/chromeos/system/automatic_reboot_manager.cc
index 5145dc3a..45ae2a6 100644
--- a/chrome/browser/chromeos/system/automatic_reboot_manager.cc
+++ b/chrome/browser/chromeos/system/automatic_reboot_manager.cc
@@ -396,8 +396,7 @@
 void AutomaticRebootManager::Reboot() {
   // If a non-kiosk-app session is in progress, do not reboot.
   if (user_manager::UserManager::Get()->IsUserLoggedIn() &&
-      !user_manager::UserManager::Get()->IsLoggedInAsKioskApp() &&
-      !user_manager::UserManager::Get()->IsLoggedInAsArcKioskApp()) {
+      !user_manager::UserManager::Get()->IsLoggedInAsAnyKioskApp()) {
     VLOG(1) << "Skipping reboot because non-kiosk session is active";
     return;
   }
diff --git a/chrome/browser/client_hints/client_hints.cc b/chrome/browser/client_hints/client_hints.cc
index b6c7f81..63ec9fc1 100644
--- a/chrome/browser/client_hints/client_hints.cc
+++ b/chrome/browser/client_hints/client_hints.cc
@@ -8,6 +8,7 @@
 
 #include "chrome/browser/client_hints/client_hints.h"
 
+#include "base/metrics/histogram_macros.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/chrome_content_browser_client.h"
 #include "chrome/browser/content_settings/host_content_settings_map_factory.h"
@@ -17,12 +18,31 @@
 #include "components/content_settings/core/common/content_settings_utils.h"
 #include "components/language/core/browser/pref_names.h"
 #include "components/prefs/pref_service.h"
+#include "content/public/browser/browser_thread.h"
+#include "content/public/browser/render_process_host.h"
+#include "content/public/common/origin_util.h"
 
 namespace client_hints {
 
 ClientHints::ClientHints(content::BrowserContext* context)
     : context_(context) {}
 
+ClientHints::ClientHints(content::WebContents* tab) {
+  binding_ = std::make_unique<
+      content::WebContentsFrameBindingSet<client_hints::mojom::ClientHints>>(
+      tab, this);
+}
+
+content::BrowserContext* ClientHints::GetContext() {
+  if (!context_) {
+    content::RenderProcessHost* rph =
+        binding_->GetCurrentTargetFrame()->GetProcess();
+    DCHECK(rph);
+    context_ = rph->GetBrowserContext();
+  }
+  return context_;
+}
+
 ClientHints::~ClientHints() = default;
 
 network::NetworkQualityTracker* ClientHints::GetNetworkQualityTracker() {
@@ -34,7 +54,7 @@
     const GURL& url,
     blink::WebEnabledClientHints* client_hints) {
   ContentSettingsForOneType client_hints_rules;
-  Profile* profile = Profile::FromBrowserContext(context_);
+  Profile* profile = Profile::FromBrowserContext(GetContext());
   if (!profile)
     return;
 
@@ -45,7 +65,7 @@
 }
 
 bool ClientHints::IsJavaScriptAllowed(const GURL& url) {
-  Profile* profile = Profile::FromBrowserContext(context_);
+  Profile* profile = Profile::FromBrowserContext(GetContext());
   if (!profile)
     return false;
 
@@ -55,7 +75,7 @@
 }
 
 std::string ClientHints::GetAcceptLanguageString() {
-  Profile* profile = Profile::FromBrowserContext(context_);
+  Profile* profile = Profile::FromBrowserContext(GetContext());
   if (!profile)
     return std::string();
   DCHECK(profile->GetPrefs());
@@ -66,4 +86,68 @@
   return ::GetUserAgentMetadata();
 }
 
+void ClientHints::PersistClientHints(
+    const url::Origin& primary_origin,
+    const std::vector<blink::mojom::WebClientHintsType>& client_hints,
+    base::TimeDelta expiration_duration) {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+
+  const GURL primary_url = primary_origin.GetURL();
+
+  // TODO(tbansal): crbug.com/735518. Consider killing the renderer that sent
+  // the malformed IPC.
+  if (!primary_url.is_valid() || !content::IsOriginSecure(primary_url))
+    return;
+
+  DCHECK(!client_hints.empty());
+  DCHECK_LE(
+      client_hints.size(),
+      static_cast<size_t>(blink::mojom::WebClientHintsType::kMaxValue) + 1);
+
+  if (client_hints.empty() ||
+      client_hints.size() >
+          (static_cast<size_t>(blink::mojom::WebClientHintsType::kMaxValue) +
+           1)) {
+    // Return early if the list does not have the right number of values.
+    // Persisting wrong number of values to the disk may cause errors when
+    // reading them back in the future.
+    return;
+  }
+
+  if (expiration_duration <= base::TimeDelta::FromSeconds(0))
+    return;
+
+  Profile* profile = Profile::FromBrowserContext(GetContext());
+
+  HostContentSettingsMap* map =
+      HostContentSettingsMapFactory::GetForProfile(profile);
+
+  std::unique_ptr<base::ListValue> expiration_times_list =
+      std::make_unique<base::ListValue>();
+  expiration_times_list->Reserve(client_hints.size());
+
+  // Use wall clock since the expiration time would be persisted across embedder
+  // restarts.
+  double expiration_time =
+      (base::Time::Now() + expiration_duration).ToDoubleT();
+
+  for (const auto& entry : client_hints)
+    expiration_times_list->AppendInteger(static_cast<int>(entry));
+
+  auto expiration_times_dictionary = std::make_unique<base::DictionaryValue>();
+  expiration_times_dictionary->SetList("client_hints",
+                                       std::move(expiration_times_list));
+  expiration_times_dictionary->SetDouble("expiration_time", expiration_time);
+
+  // TODO(tbansal): crbug.com/735518. Disable updates to client hints settings
+  // when cookies are disabled for |primary_origin|.
+  map->SetWebsiteSettingDefaultScope(
+      primary_url, GURL(), ContentSettingsType::CLIENT_HINTS, std::string(),
+      std::move(expiration_times_dictionary));
+
+  UMA_HISTOGRAM_EXACT_LINEAR("ClientHints.UpdateEventCount", 1, 2);
+}
+
+WEB_CONTENTS_USER_DATA_KEY_IMPL(ClientHints)
+
 }  // namespace client_hints
diff --git a/chrome/browser/client_hints/client_hints.h b/chrome/browser/client_hints/client_hints.h
index 5711beb..0e6a125 100644
--- a/chrome/browser/client_hints/client_hints.h
+++ b/chrome/browser/client_hints/client_hints.h
@@ -12,15 +12,19 @@
 #include "base/optional.h"
 #include "components/keyed_service/core/keyed_service.h"
 #include "content/public/browser/client_hints_controller_delegate.h"
+#include "content/public/browser/web_contents_binding_set.h"
+#include "content/public/browser/web_contents_user_data.h"
 
 class GURL;
 
 namespace client_hints {
 
 class ClientHints : public KeyedService,
-                    public content::ClientHintsControllerDelegate {
+                    public content::ClientHintsControllerDelegate,
+                    public content::WebContentsUserData<ClientHints> {
  public:
   explicit ClientHints(content::BrowserContext* context);
+  explicit ClientHints(content::WebContents* tab);
   ~ClientHints() override;
 
   // content::ClientHintsControllerDelegate:
@@ -36,8 +40,21 @@
 
   blink::UserAgentMetadata GetUserAgentMetadata() override;
 
+  void PersistClientHints(
+      const url::Origin& primary_origin,
+      const std::vector<blink::mojom::WebClientHintsType>& client_hints,
+      base::TimeDelta expiration_duration) override;
+
  private:
-  content::BrowserContext* context_;
+  content::BrowserContext* GetContext();
+
+  friend class content::WebContentsUserData<ClientHints>;
+  content::BrowserContext* context_ = nullptr;
+  std::unique_ptr<
+      content::WebContentsFrameBindingSet<client_hints::mojom::ClientHints>>
+      binding_;
+
+  WEB_CONTENTS_USER_DATA_KEY_DECL();
 
   DISALLOW_COPY_AND_ASSIGN(ClientHints);
 };
diff --git a/chrome/browser/client_hints/client_hints_observer.cc b/chrome/browser/client_hints/client_hints_observer.cc
deleted file mode 100644
index 4a8b7d7..0000000
--- a/chrome/browser/client_hints/client_hints_observer.cc
+++ /dev/null
@@ -1,95 +0,0 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chrome/browser/client_hints/client_hints_observer.h"
-
-#include <memory>
-
-#include "base/metrics/histogram_macros.h"
-#include "base/values.h"
-#include "chrome/browser/content_settings/host_content_settings_map_factory.h"
-#include "chrome/browser/profiles/profile.h"
-#include "components/content_settings/core/browser/host_content_settings_map.h"
-#include "components/content_settings/core/common/content_settings_pattern.h"
-#include "components/content_settings/core/common/content_settings_types.h"
-#include "content/public/browser/browser_context.h"
-#include "content/public/browser/browser_thread.h"
-#include "content/public/browser/render_frame_host.h"
-#include "content/public/browser/render_process_host.h"
-#include "content/public/browser/web_contents.h"
-#include "content/public/common/origin_util.h"
-#include "url/gurl.h"
-#include "url/origin.h"
-
-ClientHintsObserver::ClientHintsObserver(content::WebContents* tab)
-    : binding_(tab, this) {}
-
-ClientHintsObserver::~ClientHintsObserver() {}
-
-void ClientHintsObserver::PersistClientHints(
-    const url::Origin& primary_origin,
-    const std::vector<::blink::mojom::WebClientHintsType>& client_hints,
-    base::TimeDelta expiration_duration) {
-  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
-
-  const GURL primary_url = primary_origin.GetURL();
-
-  // TODO(tbansal): crbug.com/735518. Consider killing the renderer that sent
-  // the malformed IPC.
-  if (!primary_url.is_valid() || !content::IsOriginSecure(primary_url))
-    return;
-
-  DCHECK(!client_hints.empty());
-  DCHECK_LE(
-      client_hints.size(),
-      static_cast<size_t>(blink::mojom::WebClientHintsType::kMaxValue) + 1);
-
-  if (client_hints.empty() ||
-      client_hints.size() >
-          (static_cast<size_t>(blink::mojom::WebClientHintsType::kMaxValue) +
-           1)) {
-    // Return early if the list does not have the right number of values.
-    // Persisting wrong number of values to the disk may cause errors when
-    // reading them back in the future.
-    return;
-  }
-
-  if (expiration_duration <= base::TimeDelta::FromSeconds(0))
-    return;
-
-  content::RenderProcessHost* rph =
-      binding_.GetCurrentTargetFrame()->GetProcess();
-  content::BrowserContext* browser_context = rph->GetBrowserContext();
-  Profile* profile = Profile::FromBrowserContext(browser_context);
-
-  HostContentSettingsMap* map =
-      HostContentSettingsMapFactory::GetForProfile(profile);
-
-  std::unique_ptr<base::ListValue> expiration_times_list =
-      std::make_unique<base::ListValue>();
-  expiration_times_list->Reserve(client_hints.size());
-
-  // Use wall clock since the expiration time would be persisted across embedder
-  // restarts.
-  double expiration_time =
-      (base::Time::Now() + expiration_duration).ToDoubleT();
-
-  for (const auto& entry : client_hints)
-    expiration_times_list->AppendInteger(static_cast<int>(entry));
-
-  auto expiration_times_dictionary = std::make_unique<base::DictionaryValue>();
-  expiration_times_dictionary->SetList("client_hints",
-                                       std::move(expiration_times_list));
-  expiration_times_dictionary->SetDouble("expiration_time", expiration_time);
-
-  // TODO(tbansal): crbug.com/735518. Disable updates to client hints settings
-  // when cookies are disabled for |primary_origin|.
-  map->SetWebsiteSettingDefaultScope(
-      primary_url, GURL(), ContentSettingsType::CLIENT_HINTS, std::string(),
-      std::move(expiration_times_dictionary));
-
-  UMA_HISTOGRAM_EXACT_LINEAR("ClientHints.UpdateEventCount", 1, 2);
-}
-
-WEB_CONTENTS_USER_DATA_KEY_IMPL(ClientHintsObserver)
diff --git a/chrome/browser/client_hints/client_hints_observer.h b/chrome/browser/client_hints/client_hints_observer.h
deleted file mode 100644
index 413b618..0000000
--- a/chrome/browser/client_hints/client_hints_observer.h
+++ /dev/null
@@ -1,44 +0,0 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CHROME_BROWSER_CLIENT_HINTS_CLIENT_HINTS_OBSERVER_H_
-#define CHROME_BROWSER_CLIENT_HINTS_CLIENT_HINTS_OBSERVER_H_
-
-#include "base/macros.h"
-#include "base/time/time.h"
-#include "chrome/common/client_hints.mojom.h"
-#include "components/content_settings/core/common/content_settings.h"
-#include "content/public/browser/web_contents.h"
-#include "content/public/browser/web_contents_binding_set.h"
-#include "content/public/browser/web_contents_user_data.h"
-#include "url/origin.h"
-
-// ClientHintsObserver updates the browser of the lifetime of
-// client hints for different origins based on IPC messages received from
-// renderer::ContentSettingsObserver.
-class ClientHintsObserver
-    : public client_hints::mojom::ClientHints,
-      public content::WebContentsUserData<ClientHintsObserver> {
- public:
-  explicit ClientHintsObserver(content::WebContents* tab);
-  ~ClientHintsObserver() override;
-
- private:
-  friend class content::WebContentsUserData<ClientHintsObserver>;
-
-  // mojom::ContentSettings implementation.
-  void PersistClientHints(
-      const url::Origin& primary_origin,
-      const std::vector<::blink::mojom::WebClientHintsType>& client_hints,
-      base::TimeDelta expiration_duration) override;
-
-  content::WebContentsFrameBindingSet<client_hints::mojom::ClientHints>
-      binding_;
-
-  WEB_CONTENTS_USER_DATA_KEY_DECL();
-
-  DISALLOW_COPY_AND_ASSIGN(ClientHintsObserver);
-};
-
-#endif  // CHROME_BROWSER_CLIENT_HINTS_CLIENT_HINTS_OBSERVER_H_
diff --git a/chrome/browser/custom_handlers/protocol_handler_registry.cc b/chrome/browser/custom_handlers/protocol_handler_registry.cc
index d9250066..efd0efa 100644
--- a/chrome/browser/custom_handlers/protocol_handler_registry.cc
+++ b/chrome/browser/custom_handlers/protocol_handler_registry.cc
@@ -95,23 +95,24 @@
 }
 
 void ProtocolHandlerRegistry::Delegate::RegisterWithOSAsDefaultClient(
-    const std::string& protocol, ProtocolHandlerRegistry* registry) {
+    const std::string& protocol,
+    shell_integration::DefaultWebClientWorkerCallback callback) {
   // The worker pointer is reference counted. While it is running, the
   // sequence it runs on will hold references it will be automatically freed
   // once all its tasks have finished.
   base::MakeRefCounted<shell_integration::DefaultProtocolClientWorker>(
-      registry->GetDefaultWebClientCallback(protocol), protocol)
+      std::move(callback), protocol)
       ->StartSetAsDefault();
 }
 
 void ProtocolHandlerRegistry::Delegate::CheckDefaultClientWithOS(
     const std::string& protocol,
-    ProtocolHandlerRegistry* registry) {
+    shell_integration::DefaultWebClientWorkerCallback callback) {
   // The worker pointer is reference counted. While it is running, the
   // sequence it runs on will hold references it will be automatically freed
   // once all its tasks have finished.
   base::MakeRefCounted<shell_integration::DefaultProtocolClientWorker>(
-      registry->GetDefaultWebClientCallback(protocol), protocol)
+      std::move(callback), protocol)
       ->StartCheckIsDefault();
 }
 
@@ -259,7 +260,9 @@
   if (ShouldRemoveHandlersNotInOS()) {
     for (ProtocolHandlerMap::const_iterator p = default_handlers_.begin();
          p != default_handlers_.end(); ++p) {
-      delegate_->CheckDefaultClientWithOS(p->second.protocol(), this);
+      const std::string& protocol = p->second.protocol();
+      delegate_->CheckDefaultClientWithOS(
+          protocol, GetDefaultWebClientCallback(protocol));
     }
   }
 }
@@ -591,14 +594,17 @@
 
 void ProtocolHandlerRegistry::SetDefault(const ProtocolHandler& handler) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
-  ProtocolHandlerMap::const_iterator p = default_handlers_.find(
-      handler.protocol());
+
+  const std::string& protocol = handler.protocol();
+  ProtocolHandlerMap::const_iterator p = default_handlers_.find(protocol);
   // If we're not loading, and we are setting a default for a new protocol,
   // register with the OS.
   if (!is_loading_ && p == default_handlers_.end())
-      delegate_->RegisterWithOSAsDefaultClient(handler.protocol(), this);
-  default_handlers_.erase(handler.protocol());
-  default_handlers_.insert(std::make_pair(handler.protocol(), handler));
+    delegate_->RegisterWithOSAsDefaultClient(
+        protocol, GetDefaultWebClientCallback(protocol));
+  default_handlers_.erase(protocol);
+  default_handlers_.insert(std::make_pair(protocol, handler));
+
   PromoteHandler(handler);
 }
 
diff --git a/chrome/browser/custom_handlers/protocol_handler_registry.h b/chrome/browser/custom_handlers/protocol_handler_registry.h
index f7536d97..ab449ca 100644
--- a/chrome/browser/custom_handlers/protocol_handler_registry.h
+++ b/chrome/browser/custom_handlers/protocol_handler_registry.h
@@ -52,9 +52,10 @@
     virtual bool IsExternalHandlerRegistered(const std::string& protocol);
     virtual void RegisterWithOSAsDefaultClient(
         const std::string& protocol,
-        ProtocolHandlerRegistry* registry);
-    virtual void CheckDefaultClientWithOS(const std::string& protocol,
-                                          ProtocolHandlerRegistry* registry);
+        shell_integration::DefaultWebClientWorkerCallback callback);
+    virtual void CheckDefaultClientWithOS(
+        const std::string& protocol,
+        shell_integration::DefaultWebClientWorkerCallback callback);
   };
 
   class Observer : public base::CheckedObserver {
@@ -194,11 +195,6 @@
   // load command was issued, otherwise the command will be ignored.
   void AddPredefinedHandler(const ProtocolHandler& handler);
 
-  // Gets the callback for DefaultProtocolClientWorker. Allows the Delegate to
-  // create the worker on behalf of ProtocolHandlerRegistry.
-  shell_integration::DefaultWebClientWorkerCallback GetDefaultWebClientCallback(
-      const std::string& protocol);
-
  private:
   friend class base::DeleteHelper<ProtocolHandlerRegistry>;
   friend struct content::BrowserThread::DeleteOnThread<
@@ -294,6 +290,10 @@
       const std::string& protocol,
       shell_integration::DefaultWebClientState state);
 
+  // Gets the callback for DefaultProtocolClientWorker.
+  shell_integration::DefaultWebClientWorkerCallback GetDefaultWebClientCallback(
+      const std::string& protocol);
+
   // Map from protocols (strings) to protocol handlers.
   ProtocolHandlerMultiMap protocol_handlers_;
 
diff --git a/chrome/browser/custom_handlers/protocol_handler_registry_unittest.cc b/chrome/browser/custom_handlers/protocol_handler_registry_unittest.cc
index 92903843..f51f42c 100644
--- a/chrome/browser/custom_handlers/protocol_handler_registry_unittest.cc
+++ b/chrome/browser/custom_handlers/protocol_handler_registry_unittest.cc
@@ -8,6 +8,7 @@
 
 #include <memory>
 #include <set>
+#include <utility>
 
 #include "base/bind.h"
 #include "base/run_loop.h"
@@ -66,14 +67,14 @@
 
   void RegisterWithOSAsDefaultClient(
       const std::string& protocol,
-      ProtocolHandlerRegistry* registry) override {
+      shell_integration::DefaultWebClientWorkerCallback callback) override {
     // Do as-if the registration has to run on another sequence and post back
     // the result with a task to the current thread.
     base::ThreadTaskRunnerHandle::Get()->PostTask(
         FROM_HERE,
-        base::BindOnce(registry->GetDefaultWebClientCallback(protocol),
-                       force_os_failure_ ? shell_integration::NOT_DEFAULT
-                                         : shell_integration::IS_DEFAULT));
+        base::BindOnce(callback, force_os_failure_
+                                     ? shell_integration::NOT_DEFAULT
+                                     : shell_integration::IS_DEFAULT));
 
     if (!force_os_failure_)
       os_registered_protocols_.insert(protocol);
diff --git a/chrome/browser/extensions/background_xhr_browsertest.cc b/chrome/browser/extensions/background_xhr_browsertest.cc
index e1bbdb6..0b00c4e 100644
--- a/chrome/browser/extensions/background_xhr_browsertest.cc
+++ b/chrome/browser/extensions/background_xhr_browsertest.cc
@@ -6,16 +6,15 @@
 
 #include "base/bind.h"
 #include "base/json/json_reader.h"
-#include "base/run_loop.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/string_util.h"
 #include "base/strings/stringprintf.h"
-#include "base/task/post_task.h"
 #include "chrome/browser/extensions/extension_apitest.h"
 #include "chrome/browser/extensions/extension_browsertest.h"
 #include "chrome/browser/extensions/extension_with_management_policy_apitest.h"
+#include "chrome/browser/net/profile_network_context_service.h"
+#include "chrome/browser/net/profile_network_context_service_factory.h"
 #include "chrome/browser/profiles/profile.h"
-#include "chrome/browser/profiles/profile_io_data.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/common/chrome_switches.h"
 #include "chrome/test/base/ui_test_utils.h"
@@ -47,13 +46,6 @@
   return nullptr;
 }
 
-void InstallNullCertStoreFactoryOnIOThread(
-    content::ResourceContext* resource_context) {
-  ProfileIOData::FromResourceContext(resource_context)
-      ->set_client_cert_store_factory_for_testing(
-          base::Bind(&CreateNullCertStore));
-}
-
 }  // namespace
 
 class BackgroundXhrTest : public ExtensionBrowserTest {
@@ -87,13 +79,9 @@
 IN_PROC_BROWSER_TEST_F(BackgroundXhrTest, TlsClientAuth) {
   // Install a null ClientCertStore so the client auth prompt isn't bypassed due
   // to the system certificate store returning no certificates.
-  base::RunLoop loop;
-  base::PostTaskAndReply(
-      FROM_HERE, {content::BrowserThread::IO},
-      base::BindOnce(&InstallNullCertStoreFactoryOnIOThread,
-                     browser()->profile()->GetResourceContext()),
-      loop.QuitClosure());
-  loop.Run();
+  ProfileNetworkContextServiceFactory::GetForContext(browser()->profile())
+      ->set_client_cert_store_factory_for_testing(
+          base::BindRepeating(&CreateNullCertStore));
 
   // Launch HTTPS server.
   net::EmbeddedTestServer https_server(net::EmbeddedTestServer::TYPE_HTTPS);
diff --git a/chrome/browser/flag-metadata.json b/chrome/browser/flag-metadata.json
index 766daf8..21ac126 100644
--- a/chrome/browser/flag-metadata.json
+++ b/chrome/browser/flag-metadata.json
@@ -3262,7 +3262,7 @@
   {
     "name": "smooth-scrolling",
     "owners": [ "bokan", "input-dev" ],
-    "expiry_milestone": 80
+    "expiry_milestone": 85
   },
   {
     "name": "split-settings",
@@ -3540,11 +3540,6 @@
     "expiry_milestone": 76
   },
   {
-    "name": "use-pdf-compositor-service-for-print",
-    "owners": [ "//printing/OWNERS" ],
-    "expiry_milestone": 79
-  },
-  {
     "name": "use-search-click-for-right-click",
     "owners": [ "zentaro" ],
     "expiry_milestone": 80
diff --git a/chrome/browser/net/profile_network_context_service.cc b/chrome/browser/net/profile_network_context_service.cc
index b9f1551f3..2ce64c8 100644
--- a/chrome/browser/net/profile_network_context_service.cc
+++ b/chrome/browser/net/profile_network_context_service.cc
@@ -50,11 +50,16 @@
 #include "net/base/features.h"
 #include "net/http/http_auth_preferences.h"
 #include "net/http/http_util.h"
+#include "net/ssl/client_cert_store.h"
 #include "services/network/public/cpp/cors/origin_access_list.h"
 #include "services/network/public/cpp/features.h"
 #include "services/network/public/mojom/network_service.mojom.h"
 
 #if defined(OS_CHROMEOS)
+#include "chrome/browser/chromeos/certificate_provider/certificate_provider.h"
+#include "chrome/browser/chromeos/certificate_provider/certificate_provider_service.h"
+#include "chrome/browser/chromeos/certificate_provider/certificate_provider_service_factory.h"
+#include "chrome/browser/chromeos/net/client_cert_store_chromeos.h"
 #include "chrome/browser/chromeos/policy/policy_cert_service.h"
 #include "chrome/browser/chromeos/policy/policy_cert_service_factory.h"
 #include "chrome/browser/chromeos/profiles/profile_helper.h"
@@ -64,6 +69,19 @@
 #include "components/user_manager/user_manager.h"
 #endif
 
+#if defined(USE_NSS_CERTS)
+#include "chrome/browser/ui/crypto_module_delegate_nss.h"
+#include "net/ssl/client_cert_store_nss.h"
+#endif  // defined(USE_NSS_CERTS)
+
+#if defined(OS_WIN)
+#include "net/ssl/client_cert_store_win.h"
+#endif  // defined(OS_WIN)
+
+#if defined(OS_MACOSX)
+#include "net/ssl/client_cert_store_mac.h"
+#endif  // defined(OS_MACOSX)
+
 #if BUILDFLAG(TRIAL_COMPARISON_CERT_VERIFIER_SUPPORTED)
 #include "chrome/browser/net/trial_comparison_cert_verifier_controller.h"
 #endif
@@ -187,7 +205,8 @@
   using_builtin_cert_verifier_ = ShouldUseBuiltinCertVerifier(profile_);
   VLOG(0) << "Using " << (using_builtin_cert_verifier_ ? "built-in" : "legacy")
           << " cert verifier.";
-#endif
+#endif  // OS_CHROMEOS
+
   // When any of the following CT preferences change, we schedule an update
   // to aggregate the actual update using a |ct_policy_update_timer_|.
   pref_change_registrar_.Add(
@@ -466,6 +485,71 @@
   g_discard_domain_reliability_uploads_for_testing = new bool(value);
 }
 
+std::unique_ptr<net::ClientCertStore>
+ProfileNetworkContextService::CreateClientCertStore() {
+  if (!client_cert_store_factory_.is_null())
+    return client_cert_store_factory_.Run();
+
+#if defined(OS_CHROMEOS)
+  bool use_system_key_slot = false;
+  // Enable client certificates for the Chrome OS sign-in frame, if this feature
+  // is not disabled by a flag.
+  // Note that while this applies to the whole sign-in profile, client
+  // certificates will only be selected for the StoragePartition currently used
+  // in the sign-in frame (see SigninPartitionManager).
+  if (chromeos::switches::IsSigninFrameClientCertsEnabled() &&
+      chromeos::ProfileHelper::IsSigninProfile(profile_)) {
+    use_system_key_slot = true;
+  }
+
+  std::string username_hash;
+  const user_manager::User* user =
+      chromeos::ProfileHelper::Get()->GetUserByProfile(profile_);
+  if (user && !user->username_hash().empty()) {
+    username_hash = user->username_hash();
+
+    // Use the device-wide system key slot only if the user is affiliated on
+    // the device.
+    if (user->IsAffiliated()) {
+      use_system_key_slot = true;
+    }
+  }
+
+  chromeos::CertificateProviderService* cert_provider_service =
+      chromeos::CertificateProviderServiceFactory::GetForBrowserContext(
+          profile_);
+  std::unique_ptr<chromeos::CertificateProvider> certificate_provider;
+  if (cert_provider_service) {
+    certificate_provider = cert_provider_service->CreateCertificateProvider();
+  }
+
+  // ClientCertStoreChromeOS internally depends on NSS initialization that
+  // happens when the ResourceContext is created. Call GetResourceContext() so
+  // the dependency is explicit. See https://crbug.com/1018972.
+  profile_->GetResourceContext();
+
+  return std::make_unique<chromeos::ClientCertStoreChromeOS>(
+      std::move(certificate_provider), use_system_key_slot, username_hash,
+      base::Bind(&CreateCryptoModuleBlockingPasswordDelegate,
+                 kCryptoModulePasswordClientAuth));
+#elif defined(USE_NSS_CERTS)
+  return std::make_unique<net::ClientCertStoreNSS>(
+      base::Bind(&CreateCryptoModuleBlockingPasswordDelegate,
+                 kCryptoModulePasswordClientAuth));
+#elif defined(OS_WIN)
+  return std::make_unique<net::ClientCertStoreWin>();
+#elif defined(OS_MACOSX)
+  return std::make_unique<net::ClientCertStoreMac>();
+#elif defined(OS_ANDROID)
+  // Android does not use the ClientCertStore infrastructure. On Android client
+  // cert matching is done by the OS as part of the call to show the cert
+  // selection dialog.
+  return nullptr;
+#else
+#error Unknown platform.
+#endif
+}
+
 network::mojom::NetworkContextParamsPtr
 ProfileNetworkContextService::CreateNetworkContextParams(
     bool in_memory,
diff --git a/chrome/browser/net/profile_network_context_service.h b/chrome/browser/net/profile_network_context_service.h
index 05c5d4f..96a96b1 100644
--- a/chrome/browser/net/profile_network_context_service.h
+++ b/chrome/browser/net/profile_network_context_service.h
@@ -6,8 +6,10 @@
 #define CHROME_BROWSER_NET_PROFILE_NETWORK_CONTEXT_SERVICE_H_
 
 #include <memory>
+#include <string>
 #include <utility>
 
+#include "base/callback_forward.h"
 #include "base/files/file_path.h"
 #include "base/gtest_prod_util.h"
 #include "base/macros.h"
@@ -29,6 +31,10 @@
 class Profile;
 class TrialComparisonCertVerifierController;
 
+namespace net {
+class ClientCertStore;
+}
+
 namespace user_prefs {
 class PrefRegistrySyncable;
 }
@@ -70,6 +76,15 @@
 
   static void SetDiscardDomainReliabilityUploadsForTesting(bool value);
 
+  void set_client_cert_store_factory_for_testing(
+      base::RepeatingCallback<std::unique_ptr<net::ClientCertStore>()>
+          factory) {
+    client_cert_store_factory_ = std::move(factory);
+  }
+
+  // Get platform ClientCertStore. May return nullptr.
+  std::unique_ptr<net::ClientCertStore> CreateClientCertStore();
+
  private:
   FRIEND_TEST_ALL_PREFIXES(ProfileNetworkContextServiceBrowsertest,
                            DefaultCacheSize);
@@ -155,6 +170,10 @@
       trial_comparison_cert_verifier_controller_;
 #endif
 
+  // Used for testing.
+  base::RepeatingCallback<std::unique_ptr<net::ClientCertStore>()>
+      client_cert_store_factory_;
+
 #if defined(OS_CHROMEOS)
   bool using_builtin_cert_verifier_;
 #endif
diff --git a/chrome/browser/net/profile_network_context_service_factory.cc b/chrome/browser/net/profile_network_context_service_factory.cc
index 3ab33703..51e9341a 100644
--- a/chrome/browser/net/profile_network_context_service_factory.cc
+++ b/chrome/browser/net/profile_network_context_service_factory.cc
@@ -4,10 +4,15 @@
 
 #include "chrome/browser/net/profile_network_context_service_factory.h"
 
+#include "build/build_config.h"
 #include "chrome/browser/net/profile_network_context_service.h"
 #include "chrome/browser/profiles/profile.h"
 #include "components/keyed_service/content/browser_context_dependency_manager.h"
 
+#if defined(OS_CHROMEOS)
+#include "chrome/browser/chromeos/certificate_provider/certificate_provider_service_factory.h"
+#endif
+
 ProfileNetworkContextService*
 ProfileNetworkContextServiceFactory::GetForContext(
     content::BrowserContext* browser_context) {
@@ -23,7 +28,11 @@
 ProfileNetworkContextServiceFactory::ProfileNetworkContextServiceFactory()
     : BrowserContextKeyedServiceFactory(
           "ProfileNetworkContextService",
-          BrowserContextDependencyManager::GetInstance()) {}
+          BrowserContextDependencyManager::GetInstance()) {
+#if defined(OS_CHROMEOS)
+  DependsOn(chromeos::CertificateProviderServiceFactory::GetInstance());
+#endif
+}
 
 ProfileNetworkContextServiceFactory::~ProfileNetworkContextServiceFactory() {}
 
diff --git a/chrome/browser/prerender/prerender_browsertest.cc b/chrome/browser/prerender/prerender_browsertest.cc
index b9ff1905..d6c9c7e8 100644
--- a/chrome/browser/prerender/prerender_browsertest.cc
+++ b/chrome/browser/prerender/prerender_browsertest.cc
@@ -41,6 +41,8 @@
 #include "chrome/browser/extensions/extension_apitest.h"
 #include "chrome/browser/external_protocol/external_protocol_handler.h"
 #include "chrome/browser/net/prediction_options.h"
+#include "chrome/browser/net/profile_network_context_service.h"
+#include "chrome/browser/net/profile_network_context_service_factory.h"
 #include "chrome/browser/password_manager/password_store_factory.h"
 #include "chrome/browser/predictors/autocomplete_action_predictor.h"
 #include "chrome/browser/predictors/autocomplete_action_predictor_factory.h"
@@ -54,7 +56,6 @@
 #include "chrome/browser/prerender/prerender_tab_helper.h"
 #include "chrome/browser/prerender/prerender_test_utils.h"
 #include "chrome/browser/profiles/profile.h"
-#include "chrome/browser/profiles/profile_io_data.h"
 #include "chrome/browser/task_manager/mock_web_contents_task_manager.h"
 #include "chrome/browser/task_manager/providers/web_contents/web_contents_tags_manager.h"
 #include "chrome/browser/task_manager/task_manager_browsertest_util.h"
@@ -1599,9 +1600,9 @@
 // certificate will never be seen since it's an https top-level resource.
 IN_PROC_BROWSER_TEST_F(PrerenderBrowserTest,
                        PrerenderSSLClientCertTopLevel) {
-  ProfileIOData::FromResourceContext(
-      current_browser()->profile()->GetResourceContext())
-      ->set_client_cert_store_factory_for_testing(base::Bind(
+  ProfileNetworkContextServiceFactory::GetForContext(
+      current_browser()->profile())
+      ->set_client_cert_store_factory_for_testing(base::BindRepeating(
           &CreateCertStore, net::ImportCertFromFile(
                                 net::GetTestCertsDirectory(), "ok_cert.pem")));
   net::EmbeddedTestServer https_server(net::EmbeddedTestServer::TYPE_HTTPS);
@@ -1619,9 +1620,9 @@
 // subresource will cancel the prerendered page.
 IN_PROC_BROWSER_TEST_F(PrerenderBrowserTest,
                        PrerenderSSLClientCertSubresource) {
-  ProfileIOData::FromResourceContext(
-      current_browser()->profile()->GetResourceContext())
-      ->set_client_cert_store_factory_for_testing(base::Bind(
+  ProfileNetworkContextServiceFactory::GetForContext(
+      current_browser()->profile())
+      ->set_client_cert_store_factory_for_testing(base::BindRepeating(
           &CreateCertStore, net::ImportCertFromFile(
                                 net::GetTestCertsDirectory(), "ok_cert.pem")));
   net::EmbeddedTestServer https_server(net::EmbeddedTestServer::TYPE_HTTPS);
@@ -1645,9 +1646,9 @@
 // Checks that an SSL Client Certificate request that originates from an
 // iframe will cancel the prerendered page.
 IN_PROC_BROWSER_TEST_F(PrerenderBrowserTest, PrerenderSSLClientCertIframe) {
-  ProfileIOData::FromResourceContext(
-      current_browser()->profile()->GetResourceContext())
-      ->set_client_cert_store_factory_for_testing(base::Bind(
+  ProfileNetworkContextServiceFactory::GetForContext(
+      current_browser()->profile())
+      ->set_client_cert_store_factory_for_testing(base::BindRepeating(
           &CreateCertStore, net::ImportCertFromFile(
                                 net::GetTestCertsDirectory(), "ok_cert.pem")));
   net::EmbeddedTestServer https_server(net::EmbeddedTestServer::TYPE_HTTPS);
diff --git a/chrome/browser/profiles/profile_attributes_entry.cc b/chrome/browser/profiles/profile_attributes_entry.cc
index 1aaac9a..d4504ba 100644
--- a/chrome/browser/profiles/profile_attributes_entry.cc
+++ b/chrome/browser/profiles/profile_attributes_entry.cc
@@ -131,51 +131,43 @@
 
 bool ProfileAttributesEntry::ShouldShowProfileLocalName(
     const base::string16& gaia_name_to_display) const {
-  bool is_using_default_name = IsUsingDefaultName();
-
-  // Customized profile name that is not equal to Gaia name.
-  if (!is_using_default_name &&
-      !base::EqualsCaseInsensitiveASCII(gaia_name_to_display,
-                                        GetLocalProfileName())) {
-    return true;
+  // Never show the profile name if it is equal to GAIA given name,
+  // e.g. Matt (Matt), in that case we should only show the GAIA name.
+  if (base::EqualsCaseInsensitiveASCII(gaia_name_to_display,
+                                       GetLocalProfileName())) {
+    return false;
   }
 
+  // Customized profile name that is not equal to Gaia name, e.g. Matt (Work).
+  if (!IsUsingDefaultName())
+    return true;
+
+  // The profile local name is a default profile name : Person n.
   std::vector<ProfileAttributesEntry*> entries =
       profile_info_cache_->GetAllProfilesAttributes();
 
   for (ProfileAttributesEntry* entry : entries) {
-    if (entry == this) {
+    if (entry == this)
       continue;
-    }
 
     base::string16 other_gaia_name_to_display = entry->GetGAIANameToDisplay();
     if (other_gaia_name_to_display.empty() ||
         other_gaia_name_to_display != gaia_name_to_display)
       continue;
 
-    bool other_is_using_default_name = entry->IsUsingDefaultName();
-    if (is_using_default_name) {
-      // Both profiles have a default profile name.
-      if (other_is_using_default_name) {
-        return true;
-      }
-      // The other profile name will be shown, no need to show |Person %n|.
-      continue;
-    }
-
-    // Current profile has a custom profile name that is equal to GAIA name.
-    if (other_is_using_default_name) {
-      // The other profile has a default profile name (Person %n) that will not
-      // be shown. Show the profile name for this profile to clear ambiguity.
+    // Another profile with the same GAIA name.
+    bool other_profile_name_equal_GAIA_name = base::EqualsCaseInsensitiveASCII(
+        other_gaia_name_to_display, entry->GetLocalProfileName());
+    // If for the other profile, the profile name is equal to GAIA name then it
+    // will not be shown. For disambiguation, show for the current profile the
+    // profile name even if it is Person n.
+    if (other_profile_name_equal_GAIA_name)
       return true;
-    }
 
-    // The other profile has a custom name. If for both profiles, the profile
-    // name is equal to Gaia name, then the profile name must be shown for both
-    // of them.
-    base::string16 other_local_profile_name = entry->GetLocalProfileName();
-    if (base::EqualsCaseInsensitiveASCII(other_gaia_name_to_display,
-                                         other_local_profile_name)) {
+    bool other_is_using_default_name = entry->IsUsingDefaultName();
+    // Both profiles have a default profile name,
+    // e.g. Matt (Person 1), Matt (Person 2).
+    if (other_is_using_default_name) {
       return true;
     }
   }
diff --git a/chrome/browser/profiles/profile_info_cache_unittest.cc b/chrome/browser/profiles/profile_info_cache_unittest.cc
index 3ed1b4654..cb5fbf8c 100644
--- a/chrome/browser/profiles/profile_info_cache_unittest.cc
+++ b/chrome/browser/profiles/profile_info_cache_unittest.cc
@@ -314,6 +314,7 @@
   GetCache()->SetLocalProfileNameOfProfileAtIndex(index1, ASCIIToUTF16("patt"));
   EXPECT_EQ(ASCIIToUTF16("Patt"),
             GetCache()->GetNameToDisplayOfProfileAtIndex(index1));
+
   // Multiple profiles.
   // Add another profile with the same GAIA name and a default profile name.
   GetCache()->AddProfileToCache(
@@ -383,27 +384,19 @@
   EXPECT_EQ(ASCIIToUTF16("Patt (Personal)"),
             GetCache()->GetNameToDisplayOfProfileAtIndex(index3));
 
-  // Set one of the profile names to be equal to GAIA name, we should still show
-  // the profile name to clear ambiguity.
+  // Set one of the profile names to be equal to GAIA name, we should show
+  // the profile name even if it is Person n to clear ambiguity.
   GetCache()->SetLocalProfileNameOfProfileAtIndex(index3, ASCIIToUTF16("patt"));
-  EXPECT_EQ(ASCIIToUTF16("Patt"),
-            GetCache()->GetNameToDisplayOfProfileAtIndex(index1));
-  EXPECT_EQ(ASCIIToUTF16("Patt (patt)"),
-            GetCache()->GetNameToDisplayOfProfileAtIndex(index3));
-
-  // One profile with a custom name and another profile with a custom name equal
-  // to GAIA name.
-  GetCache()->SetProfileIsUsingDefaultNameAtIndex(index1, false);
-  GetCache()->SetLocalProfileNameOfProfileAtIndex(index1, ASCIIToUTF16("Work"));
-  EXPECT_EQ(ASCIIToUTF16("Patt (Work)"),
+  EXPECT_EQ(ASCIIToUTF16("Patt (Person 1)"),
             GetCache()->GetNameToDisplayOfProfileAtIndex(index1));
   EXPECT_EQ(ASCIIToUTF16("Patt"),
             GetCache()->GetNameToDisplayOfProfileAtIndex(index3));
 
+  // Never show the profile name if it is equal GAIA name.
   GetCache()->SetLocalProfileNameOfProfileAtIndex(index1, ASCIIToUTF16("Patt"));
-  EXPECT_EQ(ASCIIToUTF16("Patt (Patt)"),
+  EXPECT_EQ(ASCIIToUTF16("Patt"),
             GetCache()->GetNameToDisplayOfProfileAtIndex(index1));
-  EXPECT_EQ(ASCIIToUTF16("Patt (patt)"),
+  EXPECT_EQ(ASCIIToUTF16("Patt"),
             GetCache()->GetNameToDisplayOfProfileAtIndex(index3));
   EXPECT_EQ(ASCIIToUTF16("Olly"),
             GetCache()->GetNameToDisplayOfProfileAtIndex(index2));
diff --git a/chrome/browser/profiles/profile_io_data.cc b/chrome/browser/profiles/profile_io_data.cc
index efd8ce2..09bf51b 100644
--- a/chrome/browser/profiles/profile_io_data.cc
+++ b/chrome/browser/profiles/profile_io_data.cc
@@ -78,15 +78,10 @@
 #endif  // BUILDFLAG(ENABLE_EXTENSIONS)
 
 #if defined(OS_CHROMEOS)
-#include "chrome/browser/chromeos/certificate_provider/certificate_provider.h"
-#include "chrome/browser/chromeos/certificate_provider/certificate_provider_service.h"
-#include "chrome/browser/chromeos/certificate_provider/certificate_provider_service_factory.h"
 #include "chrome/browser/chromeos/login/startup_utils.h"
-#include "chrome/browser/chromeos/net/client_cert_store_chromeos.h"
 #include "chrome/browser/chromeos/profiles/profile_helper.h"
 #include "chrome/browser/chromeos/settings/cros_settings.h"
 #include "chrome/browser/net/nss_context.h"
-#include "chromeos/constants/chromeos_switches.h"
 #include "chromeos/dbus/cryptohome/cryptohome_client.h"
 #include "chromeos/dbus/dbus_thread_manager.h"
 #include "chromeos/settings/cros_settings_names.h"
@@ -99,19 +94,6 @@
 #include "services/network/cert_verify_proc_chromeos.h"
 #endif  // defined(OS_CHROMEOS)
 
-#if defined(USE_NSS_CERTS)
-#include "chrome/browser/ui/crypto_module_delegate_nss.h"
-#include "net/ssl/client_cert_store_nss.h"
-#endif  // defined(USE_NSS_CERTS)
-
-#if defined(OS_WIN)
-#include "net/ssl/client_cert_store_win.h"
-#endif  // defined(OS_WIN)
-
-#if defined(OS_MACOSX)
-#include "net/ssl/client_cert_store_mac.h"
-#endif  // defined(OS_MACOSX)
-
 using content::BrowserContext;
 using content::BrowserThread;
 using content::ResourceContext;
@@ -255,18 +237,6 @@
   DCHECK(protocol_handler_registry);
 
 #if defined(OS_CHROMEOS)
-  // Enable client certificates for the Chrome OS sign-in frame, if this feature
-  // is not disabled by a flag.
-  // Note that while this applies to the whole sign-in profile, client
-  // certificates will only be selected for the StoragePartition currently used
-  // in the sign-in frame (see SigninPartitionManager).
-  if (chromeos::switches::IsSigninFrameClientCertsEnabled() &&
-      chromeos::ProfileHelper::IsSigninProfile(profile)) {
-    // We only need the system slot for client certificates, not in NSS context
-    // (the sign-in profile's NSS context is not initialized).
-    params->system_key_slot_use_type = SystemKeySlotUseType::kUseForClientAuth;
-  }
-
   const user_manager::User* user =
       chromeos::ProfileHelper::Get()->GetUserByProfile(profile);
   // No need to initialize NSS for users with empty username hash:
@@ -280,21 +250,10 @@
                    base::BindOnce(&StartNSSInitOnIOThread, user->GetAccountId(),
                                   user->username_hash(), profile->GetPath()));
 
-    // Use the device-wide system key slot only if the user is affiliated on
-    // the device.
     if (user->IsAffiliated()) {
-      params->system_key_slot_use_type =
-          SystemKeySlotUseType::kUseForClientAuthAndCertManagement;
+      params->user_is_affiliated = true;
     }
   }
-
-  chromeos::CertificateProviderService* cert_provider_service =
-      chromeos::CertificateProviderServiceFactory::GetForBrowserContext(
-          profile);
-  if (cert_provider_service) {
-    params->certificate_provider =
-        cert_provider_service->CreateCertificateProvider();
-  }
 #endif
 
   params->profile = profile;
@@ -318,9 +277,6 @@
 
 ProfileIOData::ProfileIOData()
     : initialized_(false),
-#if defined(OS_CHROMEOS)
-      system_key_slot_use_type_(SystemKeySlotUseType::kNone),
-#endif
       resource_context_(new ResourceContext(this)) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
 }
@@ -412,38 +368,6 @@
   return host_content_settings_map_.get();
 }
 
-std::unique_ptr<net::ClientCertStore> ProfileIOData::CreateClientCertStore() {
-  if (!client_cert_store_factory_.is_null())
-    return client_cert_store_factory_.Run();
-#if defined(OS_CHROMEOS)
-  bool use_system_key_slot =
-      system_key_slot_use_type_ == SystemKeySlotUseType::kUseForClientAuth ||
-      system_key_slot_use_type_ ==
-          SystemKeySlotUseType::kUseForClientAuthAndCertManagement;
-  return std::unique_ptr<net::ClientCertStore>(
-      new chromeos::ClientCertStoreChromeOS(
-          certificate_provider_ ? certificate_provider_->Copy() : nullptr,
-          use_system_key_slot, username_hash_,
-          base::Bind(&CreateCryptoModuleBlockingPasswordDelegate,
-                     kCryptoModulePasswordClientAuth)));
-#elif defined(USE_NSS_CERTS)
-  return std::unique_ptr<net::ClientCertStore>(new net::ClientCertStoreNSS(
-      base::Bind(&CreateCryptoModuleBlockingPasswordDelegate,
-                 kCryptoModulePasswordClientAuth)));
-#elif defined(OS_WIN)
-  return std::unique_ptr<net::ClientCertStore>(new net::ClientCertStoreWin());
-#elif defined(OS_MACOSX)
-  return std::unique_ptr<net::ClientCertStore>(new net::ClientCertStoreMac());
-#elif defined(OS_ANDROID)
-  // Android does not use the ClientCertStore infrastructure. On Android client
-  // cert matching is done by the OS as part of the call to show the cert
-  // selection dialog.
-  return nullptr;
-#else
-#error Unknown platform.
-#endif
-}
-
 ProfileIOData::ResourceContext::ResourceContext(ProfileIOData* io_data)
     : io_data_(io_data) {
   DCHECK(io_data);
@@ -468,18 +392,14 @@
 
 #if defined(OS_CHROMEOS)
   username_hash_ = profile_params_->username_hash;
-  system_key_slot_use_type_ = profile_params_->system_key_slot_use_type;
   // If we're using the system slot for certificate management, we also must
   // have access to the user's slots.
-  DCHECK(!(username_hash_.empty() &&
-           system_key_slot_use_type_ ==
-               SystemKeySlotUseType::kUseForClientAuthAndCertManagement));
-  if (system_key_slot_use_type_ ==
-      SystemKeySlotUseType::kUseForClientAuthAndCertManagement) {
+  DCHECK(!(username_hash_.empty() && profile_params_->user_is_affiliated));
+  // Use the device-wide system key slot only if the user is affiliated on
+  // the device.
+  if (profile_params_->user_is_affiliated) {
     EnableNSSSystemKeySlotForResourceContext(resource_context_.get());
   }
-
-  certificate_provider_ = std::move(profile_params_->certificate_provider);
 #endif
 
   profile_params_.reset();
diff --git a/chrome/browser/profiles/profile_io_data.h b/chrome/browser/profiles/profile_io_data.h
index eb77043..456e729 100644
--- a/chrome/browser/profiles/profile_io_data.h
+++ b/chrome/browser/profiles/profile_io_data.h
@@ -33,10 +33,6 @@
 
 class HostContentSettingsMap;
 
-namespace chromeos {
-class CertificateProvider;
-}
-
 namespace content_settings {
 class CookieSettings;
 }
@@ -45,10 +41,6 @@
 class InfoMap;
 }
 
-namespace net {
-class ClientCertStore;
-}  // namespace net
-
 // Conceptually speaking, the ProfileIOData represents data that lives on the IO
 // thread that is owned by a Profile.  Profile owns ProfileIOData, but will make
 // sure to delete it on the IO thread (except possibly in unit tests where there
@@ -90,29 +82,7 @@
   }
 #endif
 
-  void set_client_cert_store_factory_for_testing(
-      const base::Callback<std::unique_ptr<net::ClientCertStore>()>& factory) {
-    client_cert_store_factory_ = factory;
-  }
-
-  // Get platform ClientCertStore. May return nullptr.
-  std::unique_ptr<net::ClientCertStore> CreateClientCertStore();
-
  protected:
-#if defined(OS_CHROMEOS)
-  // Defines possible ways in which a profile may use the Chrome OS system
-  // token.
-  enum class SystemKeySlotUseType {
-    // This profile does not use the system key slot.
-    kNone,
-    // This profile only uses the system key slot for client certiticates.
-    kUseForClientAuth,
-    // This profile uses the system key slot for client certificates and for
-    // certificate management.
-    kUseForClientAuthAndCertManagement
-  };
-#endif
-
   // Created on the UI thread, read on the IO thread during ProfileIOData lazy
   // initialization.
   struct ProfileParams {
@@ -129,8 +99,7 @@
 
 #if defined(OS_CHROMEOS)
     std::string username_hash;
-    SystemKeySlotUseType system_key_slot_use_type = SystemKeySlotUseType::kNone;
-    std::unique_ptr<chromeos::CertificateProvider> certificate_provider;
+    bool user_is_affiliated = false;
 #endif
 
     // The profile this struct was populated from. It's passed as a void* to
@@ -192,10 +161,6 @@
   // Deleted after lazy initialization.
   mutable std::unique_ptr<ProfileParams> profile_params_;
 
-  // Used for testing.
-  mutable base::Callback<std::unique_ptr<net::ClientCertStore>()>
-      client_cert_store_factory_;
-
   // Member variables which are pointed to by the various context objects.
   mutable BooleanPrefMember safe_browsing_enabled_;
 
@@ -206,8 +171,6 @@
 
 #if defined(OS_CHROMEOS)
   mutable std::string username_hash_;
-  mutable SystemKeySlotUseType system_key_slot_use_type_;
-  mutable std::unique_ptr<chromeos::CertificateProvider> certificate_provider_;
 #endif
 
   mutable std::unique_ptr<ResourceContext> resource_context_;
diff --git a/chrome/browser/resource_coordinator/local_site_characteristics_data_unittest_utils.cc b/chrome/browser/resource_coordinator/local_site_characteristics_data_unittest_utils.cc
index f6b0b38..c77172e 100644
--- a/chrome/browser/resource_coordinator/local_site_characteristics_data_unittest_utils.cc
+++ b/chrome/browser/resource_coordinator/local_site_characteristics_data_unittest_utils.cc
@@ -12,7 +12,6 @@
 #include "chrome/browser/resource_coordinator/local_site_characteristics_webcontents_observer.h"
 #include "chrome/browser/resource_coordinator/tab_helper.h"
 #include "chrome/browser/resource_coordinator/tab_manager_features.h"
-#include "components/performance_manager/performance_manager_impl.h"
 #include "content/public/browser/browser_task_traits.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/system_connector.h"
@@ -94,11 +93,6 @@
   std::move(callback).Run(base::nullopt, base::nullopt);
 }
 
-ChromeTestHarnessWithLocalDB::ChromeTestHarnessWithLocalDB() {
-  scoped_feature_list_.InitAndEnableFeature(
-      features::kSiteCharacteristicsDatabase);
-}
-
 ChromeTestHarnessWithLocalDB::~ChromeTestHarnessWithLocalDB() = default;
 
 void ChromeTestHarnessWithLocalDB::SetUp() {
@@ -125,5 +119,10 @@
   ChromeRenderViewHostTestHarness::TearDown();
 }
 
+void ChromeTestHarnessWithLocalDB::EnableFeatures() {
+  scoped_feature_list_.InitAndEnableFeature(
+      features::kSiteCharacteristicsDatabase);
+}
+
 }  // namespace testing
 }  // namespace resource_coordinator
diff --git a/chrome/browser/resource_coordinator/local_site_characteristics_data_unittest_utils.h b/chrome/browser/resource_coordinator/local_site_characteristics_data_unittest_utils.h
index f94faf3..f0c77607 100644
--- a/chrome/browser/resource_coordinator/local_site_characteristics_data_unittest_utils.h
+++ b/chrome/browser/resource_coordinator/local_site_characteristics_data_unittest_utils.h
@@ -14,6 +14,7 @@
 #include "chrome/browser/resource_coordinator/local_site_characteristics_data_store_factory.h"
 #include "chrome/browser/resource_coordinator/local_site_characteristics_database.h"
 #include "chrome/test/base/chrome_render_view_host_test_harness.h"
+#include "components/performance_manager/performance_manager_impl.h"
 #include "testing/gmock/include/gmock/gmock.h"
 
 namespace content {
@@ -90,7 +91,15 @@
 // Site Characteristics Database is initialized.
 class ChromeTestHarnessWithLocalDB : public ChromeRenderViewHostTestHarness {
  public:
-  ChromeTestHarnessWithLocalDB();
+  // Construct a ChromeTestHarnessWithLocalDB with zero or more arguments
+  // passed to ChromeRenderViewHostTestHarness.
+  template <typename... TaskEnvironmentTraits>
+  explicit ChromeTestHarnessWithLocalDB(TaskEnvironmentTraits&&... traits)
+      : ChromeRenderViewHostTestHarness(
+            std::forward<TaskEnvironmentTraits>(traits)...) {
+    EnableFeatures();
+  }
+
   ~ChromeTestHarnessWithLocalDB() override;
 
  protected:
@@ -98,6 +107,9 @@
   void TearDown() override;
 
  private:
+  // Configures |scoped_feature_list_|.
+  void EnableFeatures();
+
   base::test::ScopedFeatureList scoped_feature_list_;
   std::unique_ptr<performance_manager::PerformanceManagerImpl>
       performance_manager_;
diff --git a/chrome/browser/resource_coordinator/tab_lifecycle_unit.cc b/chrome/browser/resource_coordinator/tab_lifecycle_unit.cc
index e1eedc3..c3a7202 100644
--- a/chrome/browser/resource_coordinator/tab_lifecycle_unit.cc
+++ b/chrome/browser/resource_coordinator/tab_lifecycle_unit.cc
@@ -378,6 +378,15 @@
     recently_audible_time_ = NowTicks();
 }
 
+void TabLifecycleUnitSource::TabLifecycleUnit::SetInitialStateFromPageNodeData(
+    performance_manager::mojom::InterventionPolicy origin_trial_policy,
+    bool is_holding_weblock,
+    bool is_holding_indexeddb_lock) {
+  origin_trial_freeze_policy_ = origin_trial_policy;
+  is_holding_weblock_ = is_holding_weblock;
+  is_holding_indexeddb_lock_ = is_holding_indexeddb_lock;
+}
+
 void TabLifecycleUnitSource::TabLifecycleUnit::UpdateLifecycleState(
     performance_manager::mojom::LifecycleState state) {
   switch (state) {
diff --git a/chrome/browser/resource_coordinator/tab_lifecycle_unit.h b/chrome/browser/resource_coordinator/tab_lifecycle_unit.h
index 54450977..a209472 100644
--- a/chrome/browser/resource_coordinator/tab_lifecycle_unit.h
+++ b/chrome/browser/resource_coordinator/tab_lifecycle_unit.h
@@ -86,8 +86,15 @@
   // "recently audible" state of the tab changes.
   void SetRecentlyAudible(bool recently_audible);
 
-  // Updates the tab's lifecycle state when changed outside the tab lifecycle
-  // unit.
+  // Set the initial state of this lifecycle unit with some data coming from the
+  // performance_manager Graph.
+  void SetInitialStateFromPageNodeData(
+      performance_manager::mojom::InterventionPolicy origin_trial_policy,
+      bool is_holding_weblock,
+      bool is_holding_indexeddb_lock);
+
+  // Updates the tab's lifecycle state when changed outside the tab
+  // lifecycle unit.
   void UpdateLifecycleState(performance_manager::mojom::LifecycleState state);
 
   // Updates the tab's origin trial freeze policy.
@@ -123,6 +130,8 @@
   bool IsAutoDiscardable() const;
   void SetAutoDiscardable(bool auto_discardable);
 
+  bool IsHoldingWebLockForTesting() { return is_holding_weblock_; }
+
  protected:
   friend class TabManagerTest;
 
diff --git a/chrome/browser/resource_coordinator/tab_lifecycle_unit_source.cc b/chrome/browser/resource_coordinator/tab_lifecycle_unit_source.cc
index a4acd03c..ed23bf0f 100644
--- a/chrome/browser/resource_coordinator/tab_lifecycle_unit_source.cc
+++ b/chrome/browser/resource_coordinator/tab_lifecycle_unit_source.cc
@@ -8,6 +8,7 @@
 #include "base/logging.h"
 #include "base/stl_util.h"
 #include "base/task/post_task.h"
+#include "base/threading/thread_task_runner_handle.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/resource_coordinator/discard_metrics_lifecycle_unit_observer.h"
 #include "chrome/browser/resource_coordinator/lifecycle_unit_source_observer.h"
@@ -23,7 +24,6 @@
 #include "chrome/common/pref_names.h"
 #include "components/performance_manager/performance_manager_impl.h"
 #include "components/performance_manager/public/graph/page_node.h"
-#include "components/performance_manager/public/web_contents_proxy.h"
 #include "components/prefs/pref_change_registrar.h"
 #include "components/prefs/pref_service.h"
 #include "content/public/browser/browser_task_traits.h"
@@ -318,6 +318,31 @@
     lifecycle_unit->AddObserver(new DiscardMetricsLifecycleUnitObserver());
     lifecycle_unit->AddObserver(new TracingLifecycleUnitObserver());
 
+    auto page_node =
+        performance_manager::PerformanceManager::GetPageNodeForWebContents(
+            contents);
+
+    auto task_runner =
+        base::CreateSingleThreadTaskRunner({base::CurrentThread()});
+    performance_manager::PerformanceManager::CallOnGraph(
+        FROM_HERE,
+        base::BindOnce(
+            [](base::WeakPtr<performance_manager::PageNode> page_node,
+               scoped_refptr<base::SingleThreadTaskRunner> runner,
+               performance_manager::Graph* graph) {
+              if (!page_node)
+                return;
+              runner->PostTask(
+                  FROM_HERE,
+                  base::BindOnce(
+                      &TabLifecycleUnitSource::SetInitialStateFromPageNodeData,
+                      page_node->GetContentsProxy(),
+                      page_node->GetOriginTrialFreezePolicy(),
+                      page_node->IsHoldingWebLock(),
+                      page_node->IsHoldingIndexedDBLock()));
+            },
+            std::move(page_node), task_runner));
+
     NotifyLifecycleUnitCreated(lifecycle_unit);
   }
 }
@@ -415,6 +440,20 @@
 }
 
 // static
+void TabLifecycleUnitSource::SetInitialStateFromPageNodeData(
+    const performance_manager::WebContentsProxy& contents_proxy,
+    performance_manager::mojom::InterventionPolicy origin_trial_policy,
+    bool is_holding_weblock,
+    bool is_holding_indexeddb_lock) {
+  if (!contents_proxy.Get())
+    return;
+  TabLifecycleUnit* lifecycle_unit = GetTabLifecycleUnit(contents_proxy.Get());
+  DCHECK(lifecycle_unit);
+  lifecycle_unit->SetInitialStateFromPageNodeData(
+      origin_trial_policy, is_holding_weblock, is_holding_indexeddb_lock);
+}
+
+// static
 void TabLifecycleUnitSource::OnOriginTrialFreezePolicyChanged(
     content::WebContents* web_contents,
     performance_manager::mojom::InterventionPolicy policy) {
diff --git a/chrome/browser/resource_coordinator/tab_lifecycle_unit_source.h b/chrome/browser/resource_coordinator/tab_lifecycle_unit_source.h
index 160961f..a60d2dd 100644
--- a/chrome/browser/resource_coordinator/tab_lifecycle_unit_source.h
+++ b/chrome/browser/resource_coordinator/tab_lifecycle_unit_source.h
@@ -16,6 +16,7 @@
 #include "chrome/browser/ui/tabs/tab_strip_model_observer.h"
 #include "components/performance_manager/public/mojom/coordination_unit.mojom.h"
 #include "components/performance_manager/public/mojom/lifecycle.mojom.h"
+#include "components/performance_manager/public/web_contents_proxy.h"
 
 class PrefChangeRegistrar;
 class PrefService;
@@ -148,6 +149,14 @@
   void OnBrowserSetLastActive(Browser* browser) override;
   void OnBrowserNoLongerActive(Browser* browser) override;
 
+  // Called when a TabLifecycleUnit is created to set some properties from
+  // the corresponding PageNode.
+  static void SetInitialStateFromPageNodeData(
+      const performance_manager::WebContentsProxy& contents_proxy,
+      performance_manager::mojom::InterventionPolicy origin_trial_policy,
+      bool is_holding_weblock,
+      bool is_holding_indexeddb_lock);
+
   // This is called indirectly from the corresponding event on a PageNode in the
   // performance_manager Graph.
   static void OnLifecycleStateChanged(
diff --git a/chrome/browser/resource_coordinator/tab_lifecycle_unit_source_unittest.cc b/chrome/browser/resource_coordinator/tab_lifecycle_unit_source_unittest.cc
index ddeac21..81bd1d8 100644
--- a/chrome/browser/resource_coordinator/tab_lifecycle_unit_source_unittest.cc
+++ b/chrome/browser/resource_coordinator/tab_lifecycle_unit_source_unittest.cc
@@ -10,9 +10,8 @@
 
 #include "base/bind.h"
 #include "base/macros.h"
-#include "base/message_loop/message_loop_current.h"
-#include "base/test/simple_test_tick_clock.h"
-#include "base/test/test_mock_time_task_runner.h"
+#include "base/test/bind_test_util.h"
+#include "base/test/task_environment.h"
 #include "build/build_config.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/resource_coordinator/lifecycle_unit.h"
@@ -32,6 +31,9 @@
 #include "chrome/browser/ui/tabs/test_tab_strip_model_delegate.h"
 #include "chrome/common/pref_names.h"
 #include "chrome/test/base/testing_profile.h"
+#include "components/performance_manager/performance_manager_tab_helper.h"
+#include "components/performance_manager/public/performance_manager.h"
+#include "components/performance_manager/test_support/graph_impl.h"
 #include "components/prefs/pref_registry_simple.h"
 #include "components/prefs/testing_pref_service.h"
 #include "content/public/browser/navigation_controller.h"
@@ -98,11 +100,9 @@
     : public testing::ChromeTestHarnessWithLocalDB {
  protected:
   TabLifecycleUnitSourceTest()
-      : scoped_context_(
-            std::make_unique<base::TestMockTimeTaskRunner::ScopedContext>(
-                task_runner_)),
-        scoped_set_tick_clock_for_testing_(task_runner_->GetMockTickClock()) {
-    base::MessageLoopCurrent::Get()->SetTaskRunner(task_runner_);
+      : testing::ChromeTestHarnessWithLocalDB(
+            base::test::SingleThreadTaskEnvironment::TimeSource::MOCK_TIME) {
+    task_runner_ = task_environment()->GetMainThreadTaskRunner();
   }
 
   void SetUp() override {
@@ -124,8 +124,7 @@
     tab_strip_model_->CloseAllTabs();
     tab_strip_model_.reset();
 
-    task_runner_->RunUntilIdle();
-    scoped_context_.reset();
+    task_environment()->RunUntilIdle();
     ChromeTestHarnessWithLocalDB::TearDown();
   }
 
@@ -140,7 +139,7 @@
       source_->SetFocusedTabStripModelForTesting(tab_strip_model_.get());
 
     // Add a foreground tab to the tab strip.
-    task_runner_->FastForwardBy(kShortDelay);
+    task_environment()->FastForwardBy(kShortDelay);
     auto time_before_first_tab = NowTicks();
     EXPECT_CALL(source_observer_, OnLifecycleUnitCreated(::testing::_))
         .WillOnce(::testing::Invoke([&](LifecycleUnit* lifecycle_unit) {
@@ -160,13 +159,14 @@
     ::testing::Mock::VerifyAndClear(&source_observer_);
     EXPECT_TRUE(source_->GetTabLifecycleUnitExternal(raw_first_web_contents));
     base::RepeatingClosure run_loop_cb = base::BindRepeating(
-        &base::TestMockTimeTaskRunner::RunUntilIdle, task_runner_);
+        &base::test::SingleThreadTaskEnvironment::RunUntilIdle,
+        base::Unretained(task_environment()));
     testing::WaitForLocalDBEntryToBeInitialized(raw_first_web_contents,
                                                 run_loop_cb);
     testing::ExpireLocalDBObservationWindows(raw_first_web_contents);
 
     // Add another foreground tab to the focused tab strip.
-    task_runner_->FastForwardBy(kShortDelay);
+    task_environment()->FastForwardBy(kShortDelay);
     auto time_before_second_tab = NowTicks();
     EXPECT_CALL(source_observer_, OnLifecycleUnitCreated(::testing::_))
         .WillOnce(::testing::Invoke([&](LifecycleUnit* lifecycle_unit) {
@@ -209,7 +209,7 @@
         second_lifecycle_unit->GetLastFocusedTime();
 
     // Add a background tab to the focused tab strip.
-    task_runner_->FastForwardBy(kShortDelay);
+    task_environment()->FastForwardBy(kShortDelay);
     LifecycleUnit* third_lifecycle_unit = nullptr;
     EXPECT_CALL(source_observer_, OnLifecycleUnitCreated(::testing::_))
         .WillOnce(::testing::Invoke([&](LifecycleUnit* lifecycle_unit) {
@@ -262,7 +262,7 @@
     if (reason == LifecycleUnitDiscardReason::PROACTIVE) {
       EXPECT_EQ(LifecycleUnitState::PENDING_DISCARD,
                 lifecycle_unit->GetState());
-      task_runner_->FastForwardBy(kProactiveDiscardFreezeTimeout);
+      task_environment()->FastForwardBy(kProactiveDiscardFreezeTimeout);
     }
     EXPECT_EQ(LifecycleUnitState::DISCARDED, lifecycle_unit->GetState());
   }
@@ -276,7 +276,7 @@
                   &second_lifecycle_unit);
 
     // Advance time so tabs are urgent discardable.
-    task_runner_->AdvanceMockTickClock(kBackgroundUrgentProtectionTime);
+    task_environment()->AdvanceClock(kBackgroundUrgentProtectionTime);
 
     // Detach the non-active tab. Verify that it can no longer be discarded.
     ExpectCanDiscardTrueAllReasons(first_lifecycle_unit);
@@ -328,7 +328,7 @@
         ->SetLastActiveTime(kDummyLastActiveTime);
 
     // Advance time so tabs are urgent discardable.
-    task_runner_->AdvanceMockTickClock(kBackgroundUrgentProtectionTime);
+    task_environment()->AdvanceClock(kBackgroundUrgentProtectionTime);
 
     // Discard the tab.
     EXPECT_EQ(LifecycleUnitState::ACTIVE,
@@ -361,7 +361,7 @@
         tab_strip_model_->GetWebContentsAt(0);
 
     // Advance time so tabs are urgent discardable.
-    task_runner_->AdvanceMockTickClock(kBackgroundUrgentProtectionTime);
+    task_environment()->AdvanceClock(kBackgroundUrgentProtectionTime);
 
     // Discard the tab.
     EXPECT_EQ(LifecycleUnitState::ACTIVE,
@@ -400,7 +400,7 @@
         tab_strip_model_->GetWebContentsAt(0);
 
     // Advance time so tabs are urgent discardable.
-    task_runner_->AdvanceMockTickClock(kBackgroundUrgentProtectionTime);
+    task_environment()->AdvanceClock(kBackgroundUrgentProtectionTime);
 
     // Discard the tab.
     EXPECT_EQ(LifecycleUnitState::ACTIVE,
@@ -435,11 +435,8 @@
   ::testing::StrictMock<MockLifecycleUnitSourceObserver> source_observer_;
   ::testing::StrictMock<MockTabLifecycleObserver> tab_observer_;
   std::unique_ptr<TabStripModel> tab_strip_model_;
-  scoped_refptr<base::TestMockTimeTaskRunner> task_runner_ =
-      base::MakeRefCounted<base::TestMockTimeTaskRunner>();
-  std::unique_ptr<base::TestMockTimeTaskRunner::ScopedContext> scoped_context_;
+  scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
 
- private:
   std::unique_ptr<content::WebContents> CreateAndNavigateWebContents() {
     std::unique_ptr<content::WebContents> web_contents =
         CreateTestWebContents();
@@ -452,8 +449,8 @@
     return web_contents;
   }
 
+ private:
   TestTabStripModelDelegate tab_strip_model_delegate_;
-  ScopedSetTickClockForTesting scoped_set_tick_clock_for_testing_;
 
   DISALLOW_COPY_AND_ASSIGN(TabLifecycleUnitSourceTest);
 };
@@ -475,7 +472,7 @@
                 &second_lifecycle_unit);
 
   // Activate the first tab.
-  task_runner_->FastForwardBy(kShortDelay);
+  task_environment()->FastForwardBy(kShortDelay);
   auto time_before_activate = NowTicks();
   tab_strip_model_->ActivateTabAt(0, {TabStripModel::GestureType::kOther});
   EXPECT_TRUE(IsFocused(first_lifecycle_unit));
@@ -493,7 +490,7 @@
                 &second_lifecycle_unit);
 
   // Close the second tab. The first tab should be focused.
-  task_runner_->FastForwardBy(kShortDelay);
+  task_environment()->FastForwardBy(kShortDelay);
   ::testing::StrictMock<MockLifecycleUnitObserver> second_observer;
   second_lifecycle_unit->AddObserver(&second_observer);
   EXPECT_CALL(second_observer, OnLifecycleUnitDestroyed(second_lifecycle_unit));
@@ -727,10 +724,10 @@
                 &foreground_lifecycle_unit);
   content::WebContents* initial_web_contents =
       tab_strip_model_->GetWebContentsAt(0);
-  task_runner_->FastForwardBy(kShortDelay);
+  task_environment()->FastForwardBy(kShortDelay);
 
   // Advance time so tabs are urgent discardable.
-  task_runner_->AdvanceMockTickClock(kBackgroundUrgentProtectionTime);
+  task_environment()->AdvanceClock(kBackgroundUrgentProtectionTime);
 
   // Discard the tab.
   EXPECT_EQ(LifecycleUnitState::ACTIVE, background_lifecycle_unit->GetState());
@@ -825,6 +822,49 @@
   ::testing::Mock::VerifyAndClear(&tab_observer_);
 }
 
+TEST_F(TabLifecycleUnitSourceTest, AsyncInitialization) {
+  std::unique_ptr<content::WebContents> web_contents =
+      CreateAndNavigateWebContents();
+  content::WebContents* raw_web_contents = web_contents.get();
+  performance_manager::PerformanceManagerTabHelper::CreateForWebContents(
+      raw_web_contents);
+
+  auto page_node =
+      performance_manager::PerformanceManager::GetPageNodeForWebContents(
+          raw_web_contents);
+
+  // Set the |IsHoldingWebLock| property for the PageNode associated with
+  // |web_contents|.
+  base::RunLoop run_loop;
+  performance_manager::PerformanceManager::CallOnGraph(
+      FROM_HERE,
+      base::BindLambdaForTesting([&](performance_manager::Graph* unused) {
+        EXPECT_TRUE(page_node);
+        EXPECT_FALSE(page_node->IsHoldingWebLock());
+        auto* node_impl =
+            performance_manager::PageNodeImpl::FromNode(page_node.get());
+        node_impl->SetIsHoldingWebLockForTesting(true);
+        run_loop.Quit();
+      }));
+  run_loop.Run();
+
+  // Append the WebContents to the tab strip, this will cause the
+  // TabLifeCycleUnit to be created.
+  LifecycleUnit* unit = nullptr;
+  EXPECT_CALL(source_observer_, OnLifecycleUnitCreated(::testing::_))
+      .WillOnce(::testing::Invoke(
+          [&](LifecycleUnit* lifecycle_unit) { unit = lifecycle_unit; }));
+  tab_strip_model_->AppendWebContents(std::move(web_contents), true);
+  ::testing::Mock::VerifyAndClear(&source_observer_);
+  EXPECT_TRUE(unit);
+
+  // Wait for the |IsHoldingWebLock| to be set in the TabLifeCycleUnit.
+  while (!static_cast<TabLifecycleUnitSource::TabLifecycleUnit*>(unit)
+              ->IsHoldingWebLockForTesting()) {
+    task_environment()->RunUntilIdle();
+  }
+}
+
 namespace {
 
 class MockOnPrefChanged {
diff --git a/chrome/browser/resources/chromeos/login/BUILD.gn b/chrome/browser/resources/chromeos/login/BUILD.gn
index a37b11a..3b24ce1b 100644
--- a/chrome/browser/resources/chromeos/login/BUILD.gn
+++ b/chrome/browser/resources/chromeos/login/BUILD.gn
@@ -146,6 +146,9 @@
 }
 
 js_library("active_directory_password_change") {
+  deps = [
+    "//ui/webui/resources/js:i18n_behavior",
+  ]
 }
 
 js_library("app_downloading") {
@@ -157,7 +160,9 @@
 
 js_library("arc_terms_of_service") {
   deps = [
+    ":html-echo",
     ":oobe_dialog_host_behavior",
+    "//ui/webui/resources/js:i18n_behavior",
   ]
 }
 
@@ -214,6 +219,9 @@
 }
 
 js_library("gaia_password_changed") {
+  deps = [
+    "//ui/webui/resources/js:i18n_behavior",
+  ]
 }
 
 js_library("hd-iron-icon") {
diff --git a/chrome/browser/resources/chromeos/login/active_directory_password_change.html b/chrome/browser/resources/chromeos/login/active_directory_password_change.html
index e3b30c2..335d8f1f 100644
--- a/chrome/browser/resources/chromeos/login/active_directory_password_change.html
+++ b/chrome/browser/resources/chromeos/login/active_directory_password_change.html
@@ -39,7 +39,7 @@
         <gaia-card id="gaiaCard" class="fit">
           <div slot="header" class="flex vertical layout end-justified start">
             <h1 id="welcomeMessage" class="welcome-message">
-              [[computeWelcomeMessage_(username)]]
+              [[i18nDynamic(locale, 'adPassChangeMessage', username)]]
             </h1>
           </div>
           <div slot="footer" class="flex vertical layout justified">
@@ -47,25 +47,29 @@
                 i18n-values="button-text:offlineLoginNextBtn">
               <gaia-input slot="inputs" id="oldPassword" type="password"
                   required>
-                <div slot="label" i18n-content="adPassChangeOldPasswordHint">
+                <div slot="label">
+                  [[i18nDynamic(locale, 'adPassChangeOldPasswordHint')]]
                 </div>
-                <div slot="error" i18n-content="adPassChangeOldPasswordError">
+                <div slot="error">
+                  [[i18nDynamic(locale, 'adPassChangeOldPasswordError')]]
                 </div>
               </gaia-input>
               <gaia-input slot="inputs" id="newPassword1" type="password"
                   required>
-                <div slot="label" i18n-content="adPassChangeNewPasswordHint">
+                <div slot="label">
+                  [[i18nDynamic(locale, 'adPassChangeNewPasswordHint')]]
                 </div>
-                <div slot="error"
-                    i18n-content="adPassChangeNewPasswordRejected">
+                <div slot="error">
+                  [[i18nDynamic(locale, 'adPassChangeNewPasswordRejected')]]
                 </div>
               </gaia-input>
               <gaia-input slot="inputs" id="newPassword2" type="password"
                   required>
-                <div slot="label"
-                    i18n-content="adPassChangeRepeatNewPasswordHint">
+                <div slot="label">
+                  [[i18nDynamic(locale, 'adPassChangeRepeatNewPasswordHint')]]
                 </div>
-                <div slot="error" i18n-content="adPassChangePasswordsMismatch">
+                <div slot="error">
+                  [[i18nDynamic(locale, 'adPassChangePasswordsMismatch')]]
                 </div>
               </gaia-input>
             </gaia-input-form>
diff --git a/chrome/browser/resources/chromeos/login/active_directory_password_change.js b/chrome/browser/resources/chromeos/login/active_directory_password_change.js
index 1dcc039..f1a31746 100644
--- a/chrome/browser/resources/chromeos/login/active_directory_password_change.js
+++ b/chrome/browser/resources/chromeos/login/active_directory_password_change.js
@@ -19,6 +19,8 @@
 Polymer({
   is: 'active-directory-password-change',
 
+  behaviors: [I18nBehavior],
+
   properties: {
     /**
      * The user principal name.
@@ -52,15 +54,6 @@
     }
   },
 
-  /**
-   * @param {string} username
-   * @return {string}
-   * @private
-   */
-  computeWelcomeMessage_: function(username) {
-    return loadTimeData.getStringF('adPassChangeMessage', username);
-  },
-
   /** @private */
   onSubmit_: function() {
     if (!this.$.oldPassword.checkValidity() ||
diff --git a/chrome/browser/resources/chromeos/login/enterprise_enrollment.html b/chrome/browser/resources/chromeos/login/enterprise_enrollment.html
index fa12548..74642bc3 100644
--- a/chrome/browser/resources/chromeos/login/enterprise_enrollment.html
+++ b/chrome/browser/resources/chromeos/login/enterprise_enrollment.html
@@ -34,13 +34,15 @@
             <hd-iron-icon slot="oobe-icon"
                 icon1x="oobe-32:briefcase" icon2x="oobe-64:briefcase">
             </hd-iron-icon>
-            <h1 slot="title" i18n-content="oauthEnrollScreenTitle"></h1>
+            <h1 slot="title">
+              [[i18nDynamic(locale, 'oauthEnrollScreenTitle')]]
+            </h1>
             <paper-progress slot="progress" indeterminate>
             </paper-progress>
 
             <div slot="footer" class="flex layout vertical" role="alert">
               <div class="oauth-enroll-step-message">
-                <span i18n-content="oauthEnrollWorking"></span>
+                [[i18nDynamic(locale, 'oauthEnrollWorking')]]
               </div>
             </div>
           </oobe-dialog>
@@ -72,15 +74,17 @@
           <hd-iron-icon slot="oobe-icon"
               icon1x="oobe-32:briefcase" icon2x="oobe-64:briefcase">
           </hd-iron-icon>
-          <h1 slot="title" i18n-content="oauthEnrollSuccessTitle"></h1>
+          <h1 slot="title">
+            [[i18nDynamic(locale, 'oauthEnrollSuccessTitle')]]
+          </h1>
           <div slot="subtitle">
             <div hidden="[[!isEmpty_(enrolledDomain_)]]">
               [[i18nDynamic(locale,'oauthEnrollSuccessTitle')]]
             </div>
             <div hidden="[[isEmpty_(enrolledDomain_)]]">
-              <div inner-h-t-m-l=
+              <html-echo content=
                   "[[successText_(locale, deviceName_, enrolledDomain_)]]">
-              </div>
+              </html-echo>
               <div>[[i18nDynamic(locale, 'oauthEnrollAbeSuccessSupport')]]</div>
             </div>
           </div>
@@ -94,7 +98,7 @@
             <oobe-text-button inverse id="success-done-button"
                 on-tap="onEnrollmentFinished_"
                 class="focus-on-show">
-              <div i18n-content="oauthEnrollDone"></div>
+              <div>[[i18nDynamic(locale, 'oauthEnrollDone')]]</div>
             </oobe-text-button>
           </div>
         </oobe-dialog>
@@ -105,31 +109,39 @@
           <hd-iron-icon slot="oobe-icon"
               icon1x="oobe-32:briefcase" icon2x="oobe-64:briefcase">
           </hd-iron-icon>
-          <h1 slot="title" i18n-content="oauthEnrollScreenTitle"></h1>
-          <div slot="subtitle" i18n-content="oauthEnrollDeviceInformation">
+          <h1 slot="title">
+            [[i18nDynamic(locale, 'oauthEnrollScreenTitle')]]
+          </h1>
+          <div slot="subtitle">
+            [[i18nDynamic(locale, 'oauthEnrollDeviceInformation')]]
           </div>
           <div slot="footer" class="layout vertical start">
             <div class="oauth-enroll-step-message">
-                      <span id="oauth-enroll-attribute-prompt-message"
-                            i18n-content="oauthEnrollAttributeExplanation">
-                      </span>
+              <span id="oauth-enroll-attribute-prompt-message">
+                [[i18nDynamic(locale, 'oauthEnrollAttributeExplanation')]]
+              </span>
               <a href="#" id="oauth-enroll-learn-more-link"
-                 class="oauth-enroll-link"
-                 i18n-content="oauthEnrollExplainAttributeLink"></a>
+                 class="oauth-enroll-link">
+                [[i18nDynamic(locale, 'oauthEnrollExplainAttributeLink')]]
+              </a>
             </div>
             <gaia-input id="oauth-enroll-asset-id" type="text"
                         class="focus-on-show">
-              <div slot="label" i18n-content="oauthEnrollAssetIdLabel">
+              <div slot="label">
+                [[i18nDynamic(locale, 'oauthEnrollAssetIdLabel')]]
               </div>
             </gaia-input>
             <gaia-input id="oauth-enroll-location" type="text">
-              <div slot="label" i18n-content="oauthEnrollLocationLabel">
+              <div slot="label">
+                [[i18nDynamic(locale, 'oauthEnrollLocationLabel')]]
               </div>
             </gaia-input>
           </div>
           <div slot="bottom-buttons" class="layout horizontal end-justified">
             <oobe-text-button id="attributes-skip" on-tap="skipAttributes_">
-              <div i18n-content="oauthEnrollSkip"></div>
+              <div>
+                [[i18nDynamic(locale, 'oauthEnrollSkip')]]
+              </div>
             </oobe-text-button>
             <div class="flex"></div>
             <oobe-next-button id="attributes-submit"
diff --git a/chrome/browser/resources/chromeos/login/gaia_password_changed.html b/chrome/browser/resources/chromeos/login/gaia_password_changed.html
index 9d7d6f6..6367cde 100644
--- a/chrome/browser/resources/chromeos/login/gaia_password_changed.html
+++ b/chrome/browser/resources/chromeos/login/gaia_password_changed.html
@@ -55,20 +55,24 @@
           </gaia-header>
           <div slot="footer" class="gaia-body-text"
               class="horizontal layout center">
-            <p i18n-content="passwordChangedTitle">
-            </p>
+            <p>[[i18nDynamic(locale, 'passwordChangedTitle')]]</p>
           </div>
           <gaia-input-form slot="footer" id="oldPasswordInputForm"
               disabled="[[disabled]]" on-submit="onPasswordSubmitted_"
               i18n-values="button-text:nextButtonText">
             <gaia-input slot="inputs" id="oldPasswordInput" type="password"
                 required>
-              <div slot="label" i18n-content="oldPasswordHint"></div>
-              <div slot="error" i18n-content="oldPasswordIncorrect"></div>
+              <div slot="label">
+                [[i18nDynamic(locale,'oldPasswordHint')]]
+              </div>
+              <div slot="error">
+                [[i18nDynamic(locale,'oldPasswordIncorrect')]]
+              </div>
             </gaia-input>
             <gaia-button id="forgot-password-link"
-                on-click="onForgotPasswordClicked_"
-                i18n-content="forgotOldPasswordButtonText" link></gaia-button>
+                on-click="onForgotPasswordClicked_" link>
+              [[i18nDynamic(locale,'forgotOldPasswordButtonText')]]
+            </gaia-button>
           </gaia-input-form>
         </gaia-card>
       </neon-animatable>
@@ -79,14 +83,18 @@
           <div slot="footer">
             <div class="gaia-body-text horizontal layout center">
               <iron-icon icon="cr:warning"></iron-icon>
-              <p i18n-content="passwordChangedProceedAnywayTitle" class="flex">
+              <p class="flex">
+                [[i18nDynamic(locale,'passwordChangedProceedAnywayTitle')]]
               </p>
             </div>
             <div class="horizontal layout justified center">
-              <gaia-button id="try-again-link" on-click="onTryAgainClicked_"
-                  i18n-content="passwordChangedTryAgain" link></gaia-button>
-              <gaia-button id="proceedAnywayBtn" on-click="onProceedClicked_"
-                  i18n-content="proceedAnywayButton"></gaia-button>
+              <gaia-button id="try-again-link"
+                  on-click="onTryAgainClicked_" link>
+                [[i18nDynamic(locale,'passwordChangedTryAgain')]]
+              </gaia-button>
+              <gaia-button id="proceedAnywayBtn" on-click="onProceedClicked_">
+                [[i18nDynamic(locale,'proceedAnywayButton')]]
+              </gaia-button>
             </div>
           </div>
         </gaia-card>
diff --git a/chrome/browser/resources/chromeos/login/gaia_password_changed.js b/chrome/browser/resources/chromeos/login/gaia_password_changed.js
index 24763e7..3cade497 100644
--- a/chrome/browser/resources/chromeos/login/gaia_password_changed.js
+++ b/chrome/browser/resources/chromeos/login/gaia_password_changed.js
@@ -5,6 +5,8 @@
 Polymer({
   is: 'gaia-password-changed',
 
+  behaviors: [I18nBehavior],
+
   properties: {
     email: String,
 
diff --git a/chrome/browser/resources/chromeos/login/multidevice_setup_first_run.html b/chrome/browser/resources/chromeos/login/multidevice_setup_first_run.html
index cc350ec..1b3c39b 100644
--- a/chrome/browser/resources/chromeos/login/multidevice_setup_first_run.html
+++ b/chrome/browser/resources/chromeos/login/multidevice_setup_first_run.html
@@ -114,7 +114,7 @@
             class="multidevice-help-overlay-close-top
                 multidevice-help-overlay-close-button"
             on-click="hideWebviewOverlay_"
-            title="[[getOverlayCloseTopTitle_()]]">
+            title="[[i18nDynamic(locale, 'arcOverlayClose')]]">
         </div>
         <div id="multidevice-help-overlay-webview-container">
           <div class="multidevice-help-overlay-webview-loading
@@ -132,7 +132,7 @@
               on-click="hideWebviewOverlay_">
             <!-- TODO(crbug.com/894537): Use string that is specific to
                     MultiDevice. -->
-            <div i18n-content="arcOverlayClose"></div>
+            <div>[[i18nDynamic(locale, 'arcOverlayClose')]]</div>
           </oobe-text-button>
         </div>
       </div>
diff --git a/chrome/browser/resources/chromeos/login/multidevice_setup_first_run.js b/chrome/browser/resources/chromeos/login/multidevice_setup_first_run.js
index 2db9191..be9f928ea 100644
--- a/chrome/browser/resources/chromeos/login/multidevice_setup_first_run.js
+++ b/chrome/browser/resources/chromeos/login/multidevice_setup_first_run.js
@@ -161,11 +161,6 @@
       this.webviewSrc_ = event.detail;
       this.webviewOverlayHidden_ = false;
     },
-
-    /** @private */
-    getOverlayCloseTopTitle_: function() {
-      return this.i18n('arcOverlayClose');
-    },
   });
 
   return {
diff --git a/chrome/browser/resources/chromeos/login/offline_gaia.html b/chrome/browser/resources/chromeos/login/offline_gaia.html
index f7937a7d..f722823 100644
--- a/chrome/browser/resources/chromeos/login/offline_gaia.html
+++ b/chrome/browser/resources/chromeos/login/offline_gaia.html
@@ -108,11 +108,13 @@
     </oobe-dialog>
     <cr-dialog id="forgotPasswordDlg"
         on-close="onDialogOverlayClosed_">
-      <div slot="body"
-          i18n-content="offlineLoginForgotPasswordDlg"></div>
+      <div slot="body">
+        [[i18nDynamic(locale, 'offlineLoginForgotPasswordDlg')]]
+      </div>
       <div slot="button-container">
         <cr-button autofocus on-tap="onForgotPasswordCloseTap_"
-            i18n-content="offlineLoginCloseBtn" class="action-button">
+            class="action-button">
+          [[i18nDynamic(locale, 'offlineLoginCloseBtn')]]
         </cr-button>
       </div>
     </cr-dialog>
diff --git a/chrome/browser/resources/chromeos/login/oobe_buttons.html b/chrome/browser/resources/chromeos/login/oobe_buttons.html
index 373a888..03da8fd1 100644
--- a/chrome/browser/resources/chromeos/login/oobe_buttons.html
+++ b/chrome/browser/resources/chromeos/login/oobe_buttons.html
@@ -138,7 +138,7 @@
 
   Example:
     <oobe-welcome-secondary-button icon="close">
-      <div i18n-content="oobeCloseButtonText"></div>
+      <div>[[i18nDynamic(locale, 'offlineLoginCloseBtn')]]</div>
     </oobe-welcome-secondary-button>
 
   Attributes:
diff --git a/chrome/browser/resources/chromeos/login/screen_active_directory_password_change.js b/chrome/browser/resources/chromeos/login/screen_active_directory_password_change.js
index 0bcd53fd..cdb3eba 100644
--- a/chrome/browser/resources/chromeos/login/screen_active_directory_password_change.js
+++ b/chrome/browser/resources/chromeos/login/screen_active_directory_password_change.js
@@ -74,5 +74,14 @@
               $('ad-password-change'), cr.ui.Bubble.Attachment.BOTTOM, error,
               BUBBLE_HORIZONTAL_PADDING, BUBBLE_VERTICAL_PADDING);
         },
+
+        /**
+         * Updates localized content of the screen that is not updated via
+         * template.
+         */
+        updateLocalizedContent: function() {
+          $('active-directory-password-change').i18nUpdateLocale();
+        },
+
       };
     });
diff --git a/chrome/browser/resources/chromeos/login/screen_password_changed.js b/chrome/browser/resources/chromeos/login/screen_password_changed.js
index a32c225..5dc7eca 100644
--- a/chrome/browser/resources/chromeos/login/screen_password_changed.js
+++ b/chrome/browser/resources/chromeos/login/screen_password_changed.js
@@ -56,6 +56,14 @@
       // We'll get here after the successful online authentication.
       Oobe.showScreen({id: SCREEN_PASSWORD_CHANGED});
       Oobe.getInstance().setSigninUIState(SIGNIN_UI_STATE.PASSWORD_CHANGED);
-    }
+    },
+
+    /**
+     * Updates localized content of the screen that is not updated via template.
+     */
+    updateLocalizedContent: function() {
+      this.gaiaPasswordChanged_.i18nUpdateLocale();
+    },
+
   };
 });
diff --git a/chrome/browser/resources/chromeos/zip_archiver/manifest.json b/chrome/browser/resources/chromeos/zip_archiver/manifest.json
index bf14acab..45b0772 100644
--- a/chrome/browser/resources/chromeos/zip_archiver/manifest.json
+++ b/chrome/browser/resources/chromeos/zip_archiver/manifest.json
@@ -68,6 +68,7 @@
         "js/volume.js"
       ]
     },
-    "content_security_policy": "default-src 'none'; script-src 'self' chrome://resources; style-src 'unsafe-inline' chrome://resources;"
+    // connect-src for Polymer: crbug.com/1022018
+    "content_security_policy": "default-src 'none'; script-src 'self' chrome://resources; style-src 'unsafe-inline' chrome://resources; connect-src chrome://resources"
   }
 }
diff --git a/chrome/browser/resources/settings/bluetooth_page/bluetooth_subpage.html b/chrome/browser/resources/settings/bluetooth_page/bluetooth_subpage.html
index c09baadf..9213e4c 100644
--- a/chrome/browser/resources/settings/bluetooth_page/bluetooth_subpage.html
+++ b/chrome/browser/resources/settings/bluetooth_page/bluetooth_subpage.html
@@ -48,7 +48,8 @@
       <cr-toggle id="enableBluetooth"
           checked="{{bluetoothToggleState}}"
           disabled$="[[!isToggleEnabled_(adapterState, stateChangeInProgress)]]"
-          aria-label="$i18n{bluetoothToggleA11yLabel}">
+          aria-label="$i18n{bluetoothToggleA11yLabel}"
+          aria-live="polite" aria-relevant="all">
       </cr-toggle>
     </div>
 
diff --git a/chrome/browser/resources/settings/site_settings/site_settings_prefs_browser_proxy.js b/chrome/browser/resources/settings/site_settings/site_settings_prefs_browser_proxy.js
index 2b826c3..8f0b623 100644
--- a/chrome/browser/resources/settings/site_settings/site_settings_prefs_browser_proxy.js
+++ b/chrome/browser/resources/settings/site_settings/site_settings_prefs_browser_proxy.js
@@ -41,7 +41,8 @@
  *            engagement: number,
  *            usage: number,
               numCookies: number,
-              hasPermissionSettings: boolean}}
+              hasPermissionSettings: boolean,
+              isInstalled: boolean}}
  */
 let OriginInfo;
 
@@ -52,7 +53,8 @@
  * "example.com".
  * @typedef {{etldPlus1: string,
  *            numCookies: number,
- *            origins: Array<OriginInfo>}}
+ *            origins: Array<OriginInfo>,
+ *            hasInstalledPWA: boolean}}
  */
 let SiteGroup;
 
diff --git a/chrome/browser/safe_browsing/chrome_cleaner/BUILD.gn b/chrome/browser/safe_browsing/chrome_cleaner/BUILD.gn
index 630f878..668bbdd 100644
--- a/chrome/browser/safe_browsing/chrome_cleaner/BUILD.gn
+++ b/chrome/browser/safe_browsing/chrome_cleaner/BUILD.gn
@@ -22,7 +22,6 @@
   deps = [
     "//base",
     "//components/chrome_cleaner/public/constants",
-    "//components/chrome_cleaner/public/interfaces",
     "//components/variations",
     "//url",
   ]
@@ -63,7 +62,6 @@
     "//chrome/common",
     "//chrome/installer/util:with_no_strings",
     "//components/chrome_cleaner/public/constants",
-    "//components/chrome_cleaner/public/interfaces",
     "//components/chrome_cleaner/public/proto",
     "//components/component_updater",
     "//components/crx_file",
diff --git a/chrome/browser/safe_browsing/chrome_cleaner/chrome_cleaner_controller_impl_win.cc b/chrome/browser/safe_browsing/chrome_cleaner/chrome_cleaner_controller_impl_win.cc
index 5a61c890..dd3c09b7 100644
--- a/chrome/browser/safe_browsing/chrome_cleaner/chrome_cleaner_controller_impl_win.cc
+++ b/chrome/browser/safe_browsing/chrome_cleaner/chrome_cleaner_controller_impl_win.cc
@@ -38,6 +38,7 @@
 #include "chrome/browser/ui/browser.h"
 #include "chrome/installer/util/scoped_token_privilege.h"
 #include "components/chrome_cleaner/public/constants/constants.h"
+#include "components/chrome_cleaner/public/proto/chrome_prompt.pb.h"
 #include "components/component_updater/component_updater_service.h"
 #include "components/component_updater/pref_names.h"
 #include "components/prefs/pref_service.h"
@@ -53,7 +54,7 @@
 namespace {
 
 using ::content::BrowserThread;
-using PromptAcceptance = ChromePromptActions::PromptAcceptance;
+using PromptUserResponse = chrome_cleaner::PromptUserResponse;
 
 // The global singleton instance. Exposed outside of GetInstance() so that it
 // can be reset by tests.
@@ -470,11 +471,11 @@
 
   DCHECK(prompt_user_reply_callback_);
 
-  PromptAcceptance acceptance = PromptAcceptance::DENIED;
+  PromptUserResponse::PromptAcceptance acceptance = PromptUserResponse::DENIED;
   State new_state = State::kIdle;
   switch (user_response) {
     case UserResponse::kAcceptedWithLogs:
-      acceptance = PromptAcceptance::ACCEPTED_WITH_LOGS;
+      acceptance = PromptUserResponse::ACCEPTED_WITH_LOGS;
       SetLogsEnabled(profile, true);
       RecordCleanerLogsAcceptanceHistogram(true);
       new_state = State::kCleaning;
@@ -483,7 +484,7 @@
       extension_registry_ = extensions::ExtensionRegistry::Get(profile);
       break;
     case UserResponse::kAcceptedWithoutLogs:
-      acceptance = PromptAcceptance::ACCEPTED_WITHOUT_LOGS;
+      acceptance = PromptUserResponse::ACCEPTED_WITHOUT_LOGS;
       SetLogsEnabled(profile, false);
       RecordCleanerLogsAcceptanceHistogram(false);
       new_state = State::kCleaning;
@@ -493,7 +494,7 @@
       break;
     case UserResponse::kDenied:  // Fallthrough
     case UserResponse::kDismissed:
-      acceptance = PromptAcceptance::DENIED;
+      acceptance = PromptUserResponse::DENIED;
       idle_reason_ = IdleReason::kUserDeclinedCleanup;
       new_state = State::kIdle;
       break;
@@ -631,9 +632,9 @@
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
 
   // If the weak pointer has been invalidated, the controller is no longer able
-  // to receive callbacks, so respond with PromptAcceptance::Denied immediately.
+  // to receive callbacks, so respond with DENIED immediately.
   if (!controller) {
-    std::move(reply_callback).Run(PromptAcceptance::DENIED);
+    std::move(reply_callback).Run(PromptUserResponse::DENIED);
     return;
   }
 
@@ -656,7 +657,7 @@
                                base::Time::Now() - time_scanning_started_);
 
   if (scanner_results.files_to_delete().empty()) {
-    std::move(reply_callback).Run(PromptAcceptance::DENIED);
+    std::move(reply_callback).Run(PromptUserResponse::DENIED);
     idle_reason_ = IdleReason::kScanningFoundNothing;
     SetStateAndNotifyObservers(State::kIdle);
     RecordPromptNotShownWithReasonHistogram(NO_PROMPT_REASON_NOTHING_FOUND);
diff --git a/chrome/browser/safe_browsing/chrome_cleaner/chrome_cleaner_controller_impl_win_unittest.cc b/chrome/browser/safe_browsing/chrome_cleaner/chrome_cleaner_controller_impl_win_unittest.cc
index 59c3417..b13d186 100644
--- a/chrome/browser/safe_browsing/chrome_cleaner/chrome_cleaner_controller_impl_win_unittest.cc
+++ b/chrome/browser/safe_browsing/chrome_cleaner/chrome_cleaner_controller_impl_win_unittest.cc
@@ -30,6 +30,7 @@
 #include "chrome/test/base/testing_profile.h"
 #include "chrome/test/base/testing_profile_manager.h"
 #include "components/chrome_cleaner/public/constants/constants.h"
+#include "components/chrome_cleaner/public/proto/chrome_prompt.pb.h"
 #include "components/chrome_cleaner/test/test_name_helper.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/test/browser_task_environment.h"
@@ -54,11 +55,9 @@
 using CrashPoint = MockChromeCleanerProcess::CrashPoint;
 using ExtensionCleaningFeatureStatus =
     MockChromeCleanerProcess::ExtensionCleaningFeatureStatus;
-using ProtobufIPCFeatureStatus =
-    MockChromeCleanerProcess::ProtobufIPCFeatureStatus;
 using IdleReason = ChromeCleanerController::IdleReason;
 using ItemsReporting = MockChromeCleanerProcess::ItemsReporting;
-using PromptAcceptance = ChromePromptActions::PromptAcceptance;
+using PromptUserResponse = chrome_cleaner::PromptUserResponse;
 using State = ChromeCleanerController::State;
 using UserResponse = ChromeCleanerController::UserResponse;
 using UwsFoundStatus = MockChromeCleanerProcess::UwsFoundStatus;
@@ -66,19 +65,20 @@
 // Returns the PromptAcceptance value that ChromeCleanerController is supposed
 // to send to the Chrome Cleaner process when ReplyWithUserResponse() is
 // called with |user_response|.
-PromptAcceptance UserResponseToPromptAcceptance(UserResponse user_response) {
+PromptUserResponse::PromptAcceptance UserResponseToPromptAcceptance(
+    UserResponse user_response) {
   switch (user_response) {
     case UserResponse::kAcceptedWithLogs:
-      return PromptAcceptance::ACCEPTED_WITH_LOGS;
+      return PromptUserResponse::ACCEPTED_WITH_LOGS;
     case UserResponse::kAcceptedWithoutLogs:
-      return PromptAcceptance::ACCEPTED_WITHOUT_LOGS;
+      return PromptUserResponse::ACCEPTED_WITHOUT_LOGS;
     case UserResponse::kDenied:  // Fallthrough
     case UserResponse::kDismissed:
-      return PromptAcceptance::DENIED;
+      return PromptUserResponse::DENIED;
   }
 
   NOTREACHED();
-  return PromptAcceptance::UNSPECIFIED;
+  return PromptUserResponse::UNSPECIFIED;
 }
 
 class MockChromeCleanerControllerObserver
@@ -232,7 +232,6 @@
                    CrashPoint,
                    UwsFoundStatus,
                    ExtensionCleaningFeatureStatus,
-                   ProtobufIPCFeatureStatus,
                    ItemsReporting,
                    ItemsReporting,
                    UserResponse>
@@ -251,9 +250,8 @@
 
   void SetUp() override {
     std::tie(process_status_, crash_point_, uws_found_status_,
-             extension_cleaning_feature_status_, protobuf_ipc_feature_status_,
-             registry_keys_reporting_, extensions_reporting_, user_response_) =
-        GetParam();
+             extension_cleaning_feature_status_, registry_keys_reporting_,
+             extensions_reporting_, user_response_) = GetParam();
 
     std::vector<base::Feature> enabled_features;
     std::vector<base::Feature> disabled_features;
@@ -263,11 +261,6 @@
     } else {
       disabled_features.push_back(kChromeCleanupExtensionsFeature);
     }
-    if (protobuf_ipc_feature_status_ == ProtobufIPCFeatureStatus::kEnabled) {
-      enabled_features.push_back(kChromeCleanupProtobufIPCFeature);
-    } else {
-      disabled_features.push_back(kChromeCleanupProtobufIPCFeature);
-    }
     features_.InitWithFeatures(enabled_features, disabled_features);
 
     InitializeEmptyExtensionService();
@@ -280,7 +273,7 @@
     cleaner_process_options_.set_crash_point(crash_point_);
     cleaner_process_options_.set_expected_user_response(
         uws_found_status_ == UwsFoundStatus::kNoUwsFound
-            ? PromptAcceptance::DENIED
+            ? PromptUserResponse::DENIED
             : UserResponseToPromptAcceptance(user_response_));
 
     ChromeCleanerControllerImpl::ResetInstanceForTesting();
@@ -471,7 +464,6 @@
   MockChromeCleanerProcess::CrashPoint crash_point_;
   UwsFoundStatus uws_found_status_;
   ExtensionCleaningFeatureStatus extension_cleaning_feature_status_;
-  ProtobufIPCFeatureStatus protobuf_ipc_feature_status_;
   ItemsReporting registry_keys_reporting_;
   ItemsReporting extensions_reporting_;
   ChromeCleanerController::UserResponse user_response_;
@@ -650,8 +642,6 @@
                    UwsFoundStatus::kUwsFoundNoRebootRequired),
             Values(ExtensionCleaningFeatureStatus::kEnabled,
                    ExtensionCleaningFeatureStatus::kDisabled),
-            Values(ProtobufIPCFeatureStatus::kEnabled,
-                   ProtobufIPCFeatureStatus::kDisabled),
             Values(ItemsReporting::kUnsupported,
                    ItemsReporting::kNotReported,
                    ItemsReporting::kReported),
@@ -671,8 +661,6 @@
             ValuesIn(kCrashPointsAfterStartup),
             Values(UwsFoundStatus::kUwsFoundRebootRequired),
             Values(ExtensionCleaningFeatureStatus::kEnabled),
-            Values(ProtobufIPCFeatureStatus::kEnabled,
-                   ProtobufIPCFeatureStatus::kDisabled),
             Values(ItemsReporting::kReported),
             Values(ItemsReporting::kReported),
             Values(UserResponse::kDenied, UserResponse::kDismissed)),
@@ -689,8 +677,6 @@
             ValuesIn(kCrashPointsAfterStartup),
             Values(UwsFoundStatus::kNoUwsFound),
             Values(ExtensionCleaningFeatureStatus::kDisabled),
-            Values(ProtobufIPCFeatureStatus::kEnabled,
-                   ProtobufIPCFeatureStatus::kDisabled),
             Values(ItemsReporting::kNotReported),
             Values(ItemsReporting::kNotReported),
             Values(UserResponse::kDismissed)),
@@ -706,7 +692,6 @@
             Values(CrashPoint::kNone),
             Values(UwsFoundStatus::kNoUwsFound),
             Values(ExtensionCleaningFeatureStatus::kDisabled),
-            Values(ProtobufIPCFeatureStatus::kDisabled),
             Values(ItemsReporting::kUnsupported),
             Values(ItemsReporting::kUnsupported),
             Values(UserResponse::kAcceptedWithLogs)),
@@ -726,8 +711,6 @@
             Values(CrashPoint::kOnStartup),
             Values(UwsFoundStatus::kNoUwsFound),
             Values(ExtensionCleaningFeatureStatus::kDisabled),
-            Values(ProtobufIPCFeatureStatus::kEnabled,
-                   ProtobufIPCFeatureStatus::kDisabled),
             Values(ItemsReporting::kUnsupported),
             Values(ItemsReporting::kUnsupported),
             Values(UserResponse::kAcceptedWithLogs)),
diff --git a/chrome/browser/safe_browsing/chrome_cleaner/chrome_cleaner_runner_win.cc b/chrome/browser/safe_browsing/chrome_cleaner/chrome_cleaner_runner_win.cc
index cb73f32..d10e56b 100644
--- a/chrome/browser/safe_browsing/chrome_cleaner/chrome_cleaner_runner_win.cc
+++ b/chrome/browser/safe_browsing/chrome_cleaner/chrome_cleaner_runner_win.cc
@@ -29,8 +29,6 @@
 #include "chrome/installer/util/install_util.h"
 #include "components/chrome_cleaner/public/constants/constants.h"
 #include "components/version_info/version_info.h"
-#include "content/public/browser/browser_task_traits.h"
-#include "content/public/browser/browser_thread.h"
 
 namespace safe_browsing {
 
@@ -166,30 +164,16 @@
       base::BindOnce(&ChromeCleanerRunner::OnPromptUser,
                      base::RetainedRef(this)));
 
+  // The channel will make blocking calls to ::WriteFile.
+  scoped_refptr<base::SequencedTaskRunner> channel_task_runner =
+      base::CreateSequencedTaskRunner({base::ThreadPool(), base::MayBlock()});
+
   // ChromePromptChannel method calls will be posted to this sequence using
   // WeakPtr's, so the channel must be deleted on the same sequence.
-  scoped_refptr<base::SequencedTaskRunner> channel_task_runner;
-  using ChromePromptChannelPtr =
-      std::unique_ptr<ChromePromptChannel, base::OnTaskRunnerDeleter>;
-  ChromePromptChannelPtr channel(nullptr, base::OnTaskRunnerDeleter(nullptr));
-  if (base::FeatureList::IsEnabled(kChromeCleanupProtobufIPCFeature)) {
-    // The channel will make blocking calls to ::WriteFile.
-    channel_task_runner =
-        base::CreateSequencedTaskRunner({base::ThreadPool(), base::MayBlock()});
-    channel =
-        ChromePromptChannelPtr(new ChromePromptChannelProtobuf(
-                                   std::move(on_connection_closed),
-                                   std::move(actions), channel_task_runner),
-                               base::OnTaskRunnerDeleter(channel_task_runner));
-  } else {
-    // Mojo uses the IO thread.
-    channel_task_runner =
-        base::CreateSingleThreadTaskRunner({content::BrowserThread::IO});
-    channel = ChromePromptChannelPtr(
-        new ChromePromptChannelMojo(std::move(on_connection_closed),
-                                    std::move(actions), channel_task_runner),
-        base::OnTaskRunnerDeleter(channel_task_runner));
-  }
+  std::unique_ptr<ChromePromptChannel, base::OnTaskRunnerDeleter> channel(
+      new ChromePromptChannel(std::move(on_connection_closed),
+                              std::move(actions), channel_task_runner),
+      base::OnTaskRunnerDeleter(channel_task_runner));
 
   base::LaunchOptions launch_options;
   if (!channel->PrepareForCleaner(&cleaner_command_line_,
diff --git a/chrome/browser/safe_browsing/chrome_cleaner/chrome_cleaner_runner_win.h b/chrome/browser/safe_browsing/chrome_cleaner/chrome_cleaner_runner_win.h
index 0cd566e..71e96f7 100644
--- a/chrome/browser/safe_browsing/chrome_cleaner/chrome_cleaner_runner_win.h
+++ b/chrome/browser/safe_browsing/chrome_cleaner/chrome_cleaner_runner_win.h
@@ -98,20 +98,15 @@
   // This function will pass command line flags to the Chrome Cleaner
   // executable as appropriate based on the flags in |reporter_invocation| and
   // the |metrics_status| parameters. The Cleaner process will communicate with
-  // Chrome via a Mojo or protobuf IPC interface and any IPC requests or
-  // notifications are passed to the caller via the |on_prompt_user| and
-  // |on_connection_closed| callbacks. Finally, when the Chrome Cleaner process
-  // terminates, a ProcessStatus is passed along to |on_process_done|.
+  // Chrome via an IPC interface and any IPC requests or notifications are
+  // passed to the caller via the |on_prompt_user| and |on_connection_closed|
+  // callbacks. Finally, when the Chrome Cleaner process terminates, a
+  // ProcessStatus is passed along to |on_process_done|.
   //
   // This IPC interface needs the |extension_service| in order to disable
   // extensions that the Cleaner process wants to disable.
   //
-  // The details of the Mojo interface are documented in
-  // "components/chrome_cleaner/public/interfaces/chrome_prompt.mojom.h".
-  //
-  // TODO(crbug.com/969139): Add a reference to the protobuf interface. Once
-  // the experiment is over, update this comment to only reference the
-  // interface that's actually used.
+  // See ChromePromptChannel for more details of the IPC interface.
   static void RunChromeCleanerAndReplyWithExitCode(
       extensions::ExtensionService* extension_service,
       extensions::ExtensionRegistry* extension_registry,
diff --git a/chrome/browser/safe_browsing/chrome_cleaner/chrome_cleaner_runner_win_unittest.cc b/chrome/browser/safe_browsing/chrome_cleaner/chrome_cleaner_runner_win_unittest.cc
index c932a94..452258f 100644
--- a/chrome/browser/safe_browsing/chrome_cleaner/chrome_cleaner_runner_win_unittest.cc
+++ b/chrome/browser/safe_browsing/chrome_cleaner/chrome_cleaner_runner_win_unittest.cc
@@ -41,9 +41,7 @@
 using ChromeMetricsStatus = ChromeCleanerRunner::ChromeMetricsStatus;
 using ExtensionCleaningFeatureStatus =
     MockChromeCleanerProcess::ExtensionCleaningFeatureStatus;
-using ProtobufIPCFeatureStatus =
-    MockChromeCleanerProcess::ProtobufIPCFeatureStatus;
-using PromptAcceptance = ChromePromptActions::PromptAcceptance;
+using PromptUserResponse = chrome_cleaner::PromptUserResponse;
 using UwsFoundStatus = MockChromeCleanerProcess::UwsFoundStatus;
 
 enum class ReporterEngine {
@@ -214,11 +212,10 @@
 
 typedef std::tuple<UwsFoundStatus,
                    ExtensionCleaningFeatureStatus,
-                   ProtobufIPCFeatureStatus,
                    MockChromeCleanerProcess::ItemsReporting,
                    MockChromeCleanerProcess::ItemsReporting,
                    MockChromeCleanerProcess::CrashPoint,
-                   PromptAcceptance>
+                   PromptUserResponse::PromptAcceptance>
     ChromeCleanerRunnerTestParams;
 
 // Test fixture for testing ChromeCleanerRunner with a mock Chrome Cleaner
@@ -243,12 +240,11 @@
     MockChromeCleanerProcess::ItemsReporting extensions_reporting;
     MockChromeCleanerProcess::CrashPoint crash_point;
     std::tie(uws_found_state, extension_cleaning_feature_status_,
-             protobuf_ipc_feature_status_, registry_keys_reporting,
-             extensions_reporting, crash_point, prompt_acceptance_to_send_) =
-        GetParam();
+             registry_keys_reporting, extensions_reporting, crash_point,
+             prompt_acceptance_to_send_) = GetParam();
 
     ASSERT_FALSE(uws_found_state == UwsFoundStatus::kNoUwsFound &&
-                 prompt_acceptance_to_send_ != PromptAcceptance::DENIED);
+                 prompt_acceptance_to_send_ != PromptUserResponse::DENIED);
 
     std::vector<base::Feature> enabled_features;
     std::vector<base::Feature> disabled_features;
@@ -258,11 +254,6 @@
     } else {
       disabled_features.push_back(kChromeCleanupExtensionsFeature);
     }
-    if (protobuf_ipc_feature_status_ == ProtobufIPCFeatureStatus::kEnabled) {
-      enabled_features.push_back(kChromeCleanupProtobufIPCFeature);
-    } else {
-      disabled_features.push_back(kChromeCleanupProtobufIPCFeature);
-    }
     features_.InitWithFeatures(enabled_features, disabled_features);
 
     cleaner_process_options_.SetReportedResults(
@@ -353,9 +344,9 @@
   base::RunLoop run_loop_;
 
   MockChromeCleanerProcess::Options cleaner_process_options_;
-  PromptAcceptance prompt_acceptance_to_send_ = PromptAcceptance::UNSPECIFIED;
+  PromptUserResponse::PromptAcceptance prompt_acceptance_to_send_ =
+      PromptUserResponse::UNSPECIFIED;
   ExtensionCleaningFeatureStatus extension_cleaning_feature_status_;
-  ProtobufIPCFeatureStatus protobuf_ipc_feature_status_;
 
   // Set by OnProcessDone().
   ChromeCleanerRunner::ProcessStatus process_status_;
@@ -450,8 +441,6 @@
     Combine(Values(UwsFoundStatus::kNoUwsFound),
             // When no UwS is found we don't care about extension removel.
             Values(ExtensionCleaningFeatureStatus::kDisabled),
-            // When no UwS is found there is no prompt.
-            Values(ProtobufIPCFeatureStatus::kDisabled),
             Values(MockChromeCleanerProcess::ItemsReporting::kUnsupported,
                    MockChromeCleanerProcess::ItemsReporting::kNotReported,
                    MockChromeCleanerProcess::ItemsReporting::kReported),
@@ -459,7 +448,7 @@
                    MockChromeCleanerProcess::ItemsReporting::kNotReported,
                    MockChromeCleanerProcess::ItemsReporting::kReported),
             Values(MockChromeCleanerProcess::CrashPoint::kNone),
-            Values(PromptAcceptance::DENIED)),
+            Values(PromptUserResponse::DENIED)),
     chrome_cleaner::GetParamNameForTest());
 
 INSTANTIATE_TEST_SUITE_P(
@@ -469,15 +458,13 @@
         Values(UwsFoundStatus::kNoUwsFound),
         // When no UwS is found we don't care about extension removel.
         Values(ExtensionCleaningFeatureStatus::kDisabled),
-        // When no UwS is found there is no prompt.
-        Values(ProtobufIPCFeatureStatus::kDisabled),
         Values(MockChromeCleanerProcess::ItemsReporting::kReported),
         Values(MockChromeCleanerProcess::ItemsReporting::kReported),
         Values(MockChromeCleanerProcess::CrashPoint::kOnStartup,
                MockChromeCleanerProcess::CrashPoint::kAfterConnection,
                MockChromeCleanerProcess::CrashPoint::kAfterRequestSent,
                MockChromeCleanerProcess::CrashPoint::kAfterResponseReceived),
-        Values(PromptAcceptance::DENIED)),
+        Values(PromptUserResponse::DENIED)),
     chrome_cleaner::GetParamNameForTest());
 
 INSTANTIATE_TEST_SUITE_P(
@@ -487,8 +474,6 @@
                    UwsFoundStatus::kUwsFoundNoRebootRequired),
             Values(ExtensionCleaningFeatureStatus::kEnabled,
                    ExtensionCleaningFeatureStatus::kDisabled),
-            Values(ProtobufIPCFeatureStatus::kEnabled,
-                   ProtobufIPCFeatureStatus::kDisabled),
             Values(MockChromeCleanerProcess::ItemsReporting::kUnsupported,
                    MockChromeCleanerProcess::ItemsReporting::kNotReported,
                    MockChromeCleanerProcess::ItemsReporting::kReported),
@@ -496,9 +481,9 @@
                    MockChromeCleanerProcess::ItemsReporting::kNotReported,
                    MockChromeCleanerProcess::ItemsReporting::kReported),
             Values(MockChromeCleanerProcess::CrashPoint::kNone),
-            Values(PromptAcceptance::DENIED,
-                   PromptAcceptance::ACCEPTED_WITH_LOGS,
-                   PromptAcceptance::ACCEPTED_WITHOUT_LOGS)),
+            Values(PromptUserResponse::DENIED,
+                   PromptUserResponse::ACCEPTED_WITH_LOGS,
+                   PromptUserResponse::ACCEPTED_WITHOUT_LOGS)),
     chrome_cleaner::GetParamNameForTest());
 
 INSTANTIATE_TEST_SUITE_P(
@@ -507,15 +492,13 @@
     Combine(
         Values(UwsFoundStatus::kUwsFoundRebootRequired),
         Values(ExtensionCleaningFeatureStatus::kDisabled),
-        Values(ProtobufIPCFeatureStatus::kEnabled,
-               ProtobufIPCFeatureStatus::kDisabled),
         Values(MockChromeCleanerProcess::ItemsReporting::kReported),
         Values(MockChromeCleanerProcess::ItemsReporting::kReported),
         Values(MockChromeCleanerProcess::CrashPoint::kOnStartup,
                MockChromeCleanerProcess::CrashPoint::kAfterConnection,
                MockChromeCleanerProcess::CrashPoint::kAfterRequestSent,
                MockChromeCleanerProcess::CrashPoint::kAfterResponseReceived),
-        Values(PromptAcceptance::ACCEPTED_WITH_LOGS)),
+        Values(PromptUserResponse::ACCEPTED_WITH_LOGS)),
     chrome_cleaner::GetParamNameForTest());
 
 }  // namespace
diff --git a/chrome/browser/safe_browsing/chrome_cleaner/chrome_prompt_actions_win.cc b/chrome/browser/safe_browsing/chrome_cleaner/chrome_prompt_actions_win.cc
index efd8d03..f66f9050 100644
--- a/chrome/browser/safe_browsing/chrome_cleaner/chrome_prompt_actions_win.cc
+++ b/chrome/browser/safe_browsing/chrome_cleaner/chrome_prompt_actions_win.cc
@@ -101,12 +101,4 @@
   return result;
 }
 
-// Keep the printable name short since it's used in tests with very long
-// parameter lists.
-std::ostream& operator<<(
-    std::ostream& out,
-    ChromePromptActions::PromptAcceptance prompt_acceptance) {
-  return out << "Accept" << static_cast<int>(prompt_acceptance);
-}
-
 }  // namespace safe_browsing
diff --git a/chrome/browser/safe_browsing/chrome_cleaner/chrome_prompt_actions_win.h b/chrome/browser/safe_browsing/chrome_cleaner/chrome_prompt_actions_win.h
index 0a70cc8..9e04c657 100644
--- a/chrome/browser/safe_browsing/chrome_cleaner/chrome_prompt_actions_win.h
+++ b/chrome/browser/safe_browsing/chrome_cleaner/chrome_prompt_actions_win.h
@@ -13,6 +13,7 @@
 #include "base/files/file_path.h"
 #include "base/optional.h"
 #include "base/strings/string16.h"
+#include "components/chrome_cleaner/public/proto/chrome_prompt.pb.h"
 
 namespace extensions {
 class ExtensionRegistry;
@@ -30,24 +31,9 @@
 // message received.
 class ChromePromptActions {
  public:
-  // TODO(crbug.com/969139): This mirrors the PromptAcceptance enums from
-  // chrome_prompt.mojom and chrome_prompt.proto. Once the
-  // ChromeCleanupProtobufIPC experiment is over remove this and use the proto
-  // version directly.
-  enum class PromptAcceptance {
-    UNSPECIFIED,
-    // The user explicitly accepted the cleanup operation and cleaner logs
-    // upload is allowed.
-    ACCEPTED_WITH_LOGS,
-    // The user explicitly accepted the cleanup operation and cleaner logs
-    // upload is not allowed.
-    ACCEPTED_WITHOUT_LOGS,
-    // The user explicitly denied the Chrome prompt.
-    DENIED,
-  };
-
   // A callback to be called after showing the prompt, with the user's choice.
-  using PromptUserReplyCallback = base::OnceCallback<void(PromptAcceptance)>;
+  using PromptUserReplyCallback = base::OnceCallback<void(
+      chrome_cleaner::PromptUserResponse::PromptAcceptance)>;
 
   // A callback to show the prompt. The ChromeCleanerScannerResults contains
   // the items that were detected by the scanner, for display in the prompt.
@@ -77,9 +63,6 @@
   // Deletes the given |extension_ids|. Returns false on an error, including if
   // not all |extension_ids| were displayed to the user in the last PromptUser
   // call.
-  //
-  // TODO(crbug.com/969139): Now that we're updating the IPC interface, rename
-  // this DeleteExtensions to match the implementation.
   bool DisableExtensions(const std::vector<base::string16>& extension_ids);
 
  private:
@@ -102,11 +85,6 @@
   std::vector<base::string16> extension_ids_;
 };
 
-//  Format for debug output in tests.
-std::ostream& operator<<(
-    std::ostream& out,
-    ChromePromptActions::PromptAcceptance prompt_acceptance);
-
 }  // namespace safe_browsing
 
 #endif  // CHROME_BROWSER_SAFE_BROWSING_CHROME_CLEANER_CHROME_PROMPT_ACTIONS_WIN_H_
diff --git a/chrome/browser/safe_browsing/chrome_cleaner/chrome_prompt_channel_win.cc b/chrome/browser/safe_browsing/chrome_cleaner/chrome_prompt_channel_win.cc
index 383fec4..a9dbb78 100644
--- a/chrome/browser/safe_browsing/chrome_cleaner/chrome_prompt_channel_win.cc
+++ b/chrome/browser/safe_browsing/chrome_cleaner/chrome_prompt_channel_win.cc
@@ -12,6 +12,7 @@
 #include <vector>
 
 #include "base/bind.h"
+#include "base/bind_helpers.h"
 #include "base/callback.h"
 #include "base/callback_helpers.h"
 #include "base/command_line.h"
@@ -19,7 +20,6 @@
 #include "base/logging.h"
 #include "base/metrics/sparse_histogram.h"
 #include "base/optional.h"
-#include "base/rand_util.h"
 #include "base/strings/strcat.h"
 #include "base/strings/string16.h"
 #include "base/strings/string_number_conversions.h"
@@ -31,21 +31,17 @@
 #include "base/win/windows_types.h"
 #include "components/chrome_cleaner/public/constants/constants.h"
 #include "components/chrome_cleaner/public/constants/result_codes.h"
-#include "content/public/browser/browser_task_traits.h"
-#include "content/public/browser/browser_thread.h"
 #include "extensions/browser/extension_system.h"
-#include "mojo/public/cpp/bindings/binding.h"
 
 namespace safe_browsing {
 
 using base::win::ScopedHandle;
 using chrome_cleaner::ChromePromptRequest;
-using content::BrowserThread;
 using CleanerProcessDelegate = ChromePromptChannel::CleanerProcessDelegate;
-using ErrorCategory = ChromePromptChannelProtobuf::ErrorCategory;
-using CustomErrors = ChromePromptChannelProtobuf::CustomErrors;
+using ErrorCategory = ChromePromptChannel::ErrorCategory;
+using CustomErrors = ChromePromptChannel::CustomErrors;
 
-constexpr char ChromePromptChannelProtobuf::kErrorHistogramName[] =
+constexpr char ChromePromptChannel::kErrorHistogramName[] =
     "SoftwareReporter.Cleaner.ChromePromptChannelError";
 
 namespace {
@@ -54,10 +50,9 @@
 void WriteStatusErrorCodeToHistogram(ErrorCategory category,
                                      ErrorType error_type) {
   base::HistogramBase* histogram = base::SparseHistogram::FactoryGet(
-      ChromePromptChannelProtobuf::kErrorHistogramName,
+      ChromePromptChannel::kErrorHistogramName,
       base::HistogramBase::kUmaTargetedHistogramFlag);
-  histogram->Add(
-      ChromePromptChannelProtobuf::GetErrorCodeInt(category, error_type));
+  histogram->Add(ChromePromptChannel::GetErrorCodeInt(category, error_type));
 }
 
 class CleanerProcessWrapper : public CleanerProcessDelegate {
@@ -70,7 +65,6 @@
   base::ProcessHandle Handle() const override { return process_.Handle(); }
 
   void TerminateOnError() const override {
-    // TODO(crbug.com/969139): Assign a new exit code?
     process_.Terminate(
         chrome_cleaner::RESULT_CODE_CHROME_PROMPT_IPC_DISCONNECTED_TOO_SOON,
         /*wait=*/false);
@@ -217,12 +211,11 @@
 // When done, calls |on_connection_closed|. On error also slays
 // |cleaner_process|, which is the other end of the pipe.
 void ServiceChromePromptRequests(
-    base::WeakPtr<ChromePromptChannelProtobuf> channel,
+    base::WeakPtr<ChromePromptChannel> channel,
     scoped_refptr<base::SequencedTaskRunner> task_runner,
     HANDLE request_read_handle,
     std::unique_ptr<CleanerProcessDelegate> cleaner_process,
     base::OnceClosure on_connection_closed) {
-
   // Always call OnConnectionClosed when finished whether it's with an error or
   // because a CloseConnectionRequest was received.
   base::ScopedClosureRunner call_connection_closed(
@@ -270,7 +263,7 @@
     }
 
     if (request_length < 1 ||
-        request_length > ChromePromptChannelProtobuf::kMaxMessageLength) {
+        request_length > ChromePromptChannel::kMaxMessageLength) {
       WriteStatusErrorCodeToHistogram(ErrorCategory::kCustomError,
                                       CustomErrors::kRequestInvalidSize);
       LOG(ERROR) << "Bad request length: " << request_length;
@@ -296,23 +289,20 @@
       case ChromePromptRequest::kQueryCapability:
         task_runner->PostTask(
             FROM_HERE,
-            base::BindOnce(
-                &ChromePromptChannelProtobuf::HandleQueryCapabilityRequest,
-                channel, chrome_prompt_request.query_capability()));
+            base::BindOnce(&ChromePromptChannel::HandleQueryCapabilityRequest,
+                           channel, chrome_prompt_request.query_capability()));
         break;
       case ChromePromptRequest::kPromptUser:
         task_runner->PostTask(
             FROM_HERE,
-            base::BindOnce(
-                &ChromePromptChannelProtobuf::HandlePromptUserRequest, channel,
-                chrome_prompt_request.prompt_user()));
+            base::BindOnce(&ChromePromptChannel::HandlePromptUserRequest,
+                           channel, chrome_prompt_request.prompt_user()));
         break;
       case ChromePromptRequest::kRemoveExtensions:
         task_runner->PostTask(
             FROM_HERE,
-            base::BindOnce(
-                &ChromePromptChannelProtobuf::HandleRemoveExtensionsRequest,
-                channel, chrome_prompt_request.remove_extensions()));
+            base::BindOnce(&ChromePromptChannel::HandleRemoveExtensionsRequest,
+                           channel, chrome_prompt_request.remove_extensions()));
         break;
       case ChromePromptRequest::kCloseConnection: {
         // Normal exit: do not kill the cleaner. OnConnectionClosed will still
@@ -323,8 +313,7 @@
         // no longer needed.
         task_runner->PostTask(
             FROM_HERE,
-            base::BindOnce(&ChromePromptChannelProtobuf::CloseHandles,
-                           channel));
+            base::BindOnce(&ChromePromptChannel::CloseHandles, channel));
         return;
       }
       default:
@@ -339,62 +328,11 @@
 
 }  // namespace
 
-namespace internal {
-
-// Implementation of the ChromePrompt Mojo interface. Must be constructed and
-// destructed on the IO thread. Calls ChromePromptActions to do the
-// work for each message received.
-class ChromePromptImpl : public chrome_cleaner::mojom::ChromePrompt {
- public:
-  ChromePromptImpl(chrome_cleaner::mojom::ChromePromptRequest request,
-                   base::OnceClosure on_connection_closed,
-                   std::unique_ptr<ChromePromptActions> actions)
-      : binding_(this, std::move(request)), actions_(std::move(actions)) {
-    DCHECK_CURRENTLY_ON(BrowserThread::IO);
-    binding_.set_connection_error_handler(std::move(on_connection_closed));
-  }
-
-  ~ChromePromptImpl() override { DCHECK_CURRENTLY_ON(BrowserThread::IO); }
-
-  void PromptUser(
-      const std::vector<base::FilePath>& files_to_delete,
-      const base::Optional<std::vector<base::string16>>& registry_keys,
-      const base::Optional<std::vector<base::string16>>& extension_ids,
-      PromptUserCallback callback) override {
-    // Wrap |callback| in a ChromePromptActions::PromptUserReplyCallback that
-    // converts |prompt_acceptance| to a Mojo enum and invokes |callback| on
-    // the IO thread.
-    auto callback_wrapper =
-        [](PromptUserCallback mojo_callback,
-           ChromePromptActions::PromptAcceptance acceptance) {
-          auto mojo_acceptance =
-              static_cast<chrome_cleaner::mojom::PromptAcceptance>(acceptance);
-          base::CreateSingleThreadTaskRunner({BrowserThread::IO})
-              ->PostTask(FROM_HERE, base::BindOnce(std::move(mojo_callback),
-                                                   mojo_acceptance));
-        };
-    actions_->PromptUser(files_to_delete, registry_keys, extension_ids,
-                         base::BindOnce(callback_wrapper, std::move(callback)));
-  }
-
-  void DisableExtensions(
-      const std::vector<base::string16>& extension_ids,
-      chrome_cleaner::mojom::ChromePrompt::DisableExtensionsCallback callback)
-      override {
-    std::move(callback).Run(actions_->DisableExtensions(extension_ids));
-  }
-
- private:
-  ChromePromptImpl(const ChromePromptImpl& other) = delete;
-  ChromePromptImpl& operator=(ChromePromptImpl& other) = delete;
-
-  mojo::Binding<chrome_cleaner::mojom::ChromePrompt> binding_;
-  std::unique_ptr<ChromePromptActions> actions_;
-};
-
-}  // namespace internal
-
-// ChromePromptChannel
+// static
+std::unique_ptr<CleanerProcessDelegate>
+ChromePromptChannel::CreateDelegateForProcess(const base::Process& process) {
+  return std::make_unique<CleanerProcessWrapper>(process);
+}
 
 ChromePromptChannel::ChromePromptChannel(
     base::OnceClosure on_connection_closed,
@@ -402,93 +340,20 @@
     scoped_refptr<base::SequencedTaskRunner> task_runner)
     : on_connection_closed_(std::move(on_connection_closed)),
       actions_(std::move(actions)),
-      task_runner_(std::move(task_runner)) {}
-
-ChromePromptChannel::~ChromePromptChannel() = default;
-
-// static
-std::unique_ptr<CleanerProcessDelegate>
-ChromePromptChannel::CreateDelegateForProcess(const base::Process& process) {
-  return std::make_unique<CleanerProcessWrapper>(process);
-}
-
-// ChromePromptChannelMojo
-
-ChromePromptChannelMojo::ChromePromptChannelMojo(
-    base::OnceClosure on_connection_closed,
-    std::unique_ptr<ChromePromptActions> actions,
-    scoped_refptr<base::SequencedTaskRunner> task_runner)
-    : ChromePromptChannel(std::move(on_connection_closed),
-                          std::move(actions),
-                          std::move(task_runner)) {}
-
-ChromePromptChannelMojo::~ChromePromptChannelMojo() = default;
-
-bool ChromePromptChannelMojo::PrepareForCleaner(
-    base::CommandLine* command_line,
-    base::HandlesToInheritVector* handles_to_inherit) {
-  std::string pipe_name = base::NumberToString(base::RandUint64());
-  request_pipe_ = invitation_.AttachMessagePipe(pipe_name);
-  command_line->AppendSwitchASCII(chrome_cleaner::kChromeMojoPipeTokenSwitch,
-                                  pipe_name);
-  mojo_channel_.PrepareToPassRemoteEndpoint(handles_to_inherit, command_line);
-  return true;
-}
-
-void ChromePromptChannelMojo::CleanupAfterCleanerLaunchFailed() {
-  // Mojo requires RemoteProcessLaunchAttempted to be called after the launch
-  // whether it succeeded or failed.
-  mojo_channel_.RemoteProcessLaunchAttempted();
-}
-
-void ChromePromptChannelMojo::ConnectToCleaner(
-    std::unique_ptr<CleanerProcessDelegate> cleaner_process) {
-  mojo_channel_.RemoteProcessLaunchAttempted();
-  task_runner_->PostTask(
-      FROM_HERE,
-      base::BindOnce(&ChromePromptChannelMojo::CreateChromePromptImpl,
-                     weak_factory_.GetWeakPtr(),
-                     chrome_cleaner::mojom::ChromePromptRequest(
-                         std::move(request_pipe_))));
-  mojo::OutgoingInvitation::Send(std::move(invitation_),
-                                 cleaner_process->Handle(),
-                                 mojo_channel_.TakeLocalEndpoint());
-}
-
-void ChromePromptChannelMojo::CreateChromePromptImpl(
-    chrome_cleaner::mojom::ChromePromptRequest chrome_prompt_request) {
-  DCHECK_CURRENTLY_ON(BrowserThread::IO);
-  DCHECK(!chrome_prompt_impl_);
-
-  chrome_prompt_impl_ = std::make_unique<internal::ChromePromptImpl>(
-      std::move(chrome_prompt_request),
-      // Pass ownership of on_connection_closed_ and actions_ to the
-      // ChromePromptImpl.
-      std::move(on_connection_closed_), std::move(actions_));
-}
-
-// ChromePromptChannelProtobuf
-
-ChromePromptChannelProtobuf::ChromePromptChannelProtobuf(
-    base::OnceClosure on_connection_closed,
-    std::unique_ptr<ChromePromptActions> actions,
-    scoped_refptr<base::SequencedTaskRunner> task_runner)
-    : ChromePromptChannel(std::move(on_connection_closed),
-                          std::move(actions),
-                          std::move(task_runner)) {
+      task_runner_(std::move(task_runner)) {
   // The sequence checker validates that all handler methods and the destructor
   // are called from the same sequence, which is not the same sequence as the
   // constructor.
   DETACH_FROM_SEQUENCE(sequence_checker_);
 }
 
-ChromePromptChannelProtobuf::~ChromePromptChannelProtobuf() {
+ChromePromptChannel::~ChromePromptChannel() {
   // To avoid race conditions accessing WeakPtr's this must be deleted on the
   // same sequence as the request handler methods are called.
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 }
 
-bool ChromePromptChannelProtobuf::PrepareForCleaner(
+bool ChromePromptChannel::PrepareForCleaner(
     base::CommandLine* command_line,
     base::HandlesToInheritVector* handles_to_inherit) {
   // Requests flow from client to server.
@@ -519,14 +384,14 @@
   return true;
 }
 
-void ChromePromptChannelProtobuf::CleanupAfterCleanerLaunchFailed() {
+void ChromePromptChannel::CleanupAfterCleanerLaunchFailed() {
   request_read_handle_.Close();
   request_write_handle_.Close();
   response_read_handle_.Close();
   response_write_handle_.Close();
 }
 
-void ChromePromptChannelProtobuf::ConnectToCleaner(
+void ChromePromptChannel::ConnectToCleaner(
     std::unique_ptr<CleanerProcessDelegate> cleaner_process) {
   // The handles that were passed to the cleaner are no longer needed in this
   // process.
@@ -552,11 +417,11 @@
                      std::move(on_connection_closed_)));
 }
 
-void ChromePromptChannelProtobuf::WriteResponseMessage(
+void ChromePromptChannel::WriteResponseMessage(
     const google::protobuf::MessageLite& message) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   base::ScopedClosureRunner error_handler(base::BindOnce(
-      &ChromePromptChannelProtobuf::CloseHandles, base::Unretained(this)));
+      &ChromePromptChannel::CloseHandles, base::Unretained(this)));
 
   std::string response_string;
   if (!message.SerializeToString(&response_string)) {
@@ -587,7 +452,7 @@
   error_handler.ReplaceClosure(base::DoNothing());
 }
 
-void ChromePromptChannelProtobuf::CloseHandles() {
+void ChromePromptChannel::CloseHandles() {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   // This will cause the next ::ReadFile call in ServiceChromePromptRequests to
   // fail, triggering the error handler that kills the cleaner process.
@@ -597,18 +462,18 @@
   response_write_handle_.Close();
 }
 
-void ChromePromptChannelProtobuf::HandleQueryCapabilityRequest(
+void ChromePromptChannel::HandleQueryCapabilityRequest(
     const chrome_cleaner::QueryCapabilityRequest&) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   // No optional capabilities are supported. Send back an empty response.
   WriteResponseMessage(chrome_cleaner::QueryCapabilityResponse());
 }
 
-void ChromePromptChannelProtobuf::HandlePromptUserRequest(
+void ChromePromptChannel::HandlePromptUserRequest(
     const chrome_cleaner::PromptUserRequest& request) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   base::ScopedClosureRunner error_handler(base::BindOnce(
-      &ChromePromptChannelProtobuf::CloseHandles, base::Unretained(this)));
+      &ChromePromptChannel::CloseHandles, base::Unretained(this)));
 
   // If there are any fields we don't know how to display, do not prompt. (Not
   // an error, could just be a more recent cleaner version.)
@@ -658,8 +523,6 @@
     extension_ids.reserve(request.extension_ids_size());
     for (const std::string& extension_id : request.extension_ids()) {
       base::string16 extension_id_utf16;
-      // TODO(crbug.com/969139): change ChromePromptActions to use strings for
-      // extension_id and skip this conversion.
       if (!base::UTF8ToUTF16(extension_id.c_str(), extension_id.size(),
                              &extension_id_utf16)) {
         LOG(ERROR) << "Undisplayable extension id in PromptUserRequest.";
@@ -678,11 +541,11 @@
   // Ensure SendPromptUserResponse runs on this sequence.
   auto response_callback = base::BindOnce(
       [](scoped_refptr<base::SequencedTaskRunner> task_runner,
-         base::WeakPtr<ChromePromptChannelProtobuf> channel,
-         ChromePromptActions::PromptAcceptance acceptance) {
+         base::WeakPtr<ChromePromptChannel> channel,
+         chrome_cleaner::PromptUserResponse::PromptAcceptance acceptance) {
         task_runner->PostTask(
             FROM_HERE,
-            base::BindOnce(&ChromePromptChannelProtobuf::SendPromptUserResponse,
+            base::BindOnce(&ChromePromptChannel::SendPromptUserResponse,
                            channel, acceptance));
       },
       task_runner_, weak_factory_.GetWeakPtr());
@@ -690,11 +553,11 @@
                        optional_extension_ids, std::move(response_callback));
 }
 
-void ChromePromptChannelProtobuf::HandleRemoveExtensionsRequest(
+void ChromePromptChannel::HandleRemoveExtensionsRequest(
     const chrome_cleaner::RemoveExtensionsRequest& request) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   base::ScopedClosureRunner error_handler(base::BindOnce(
-      &ChromePromptChannelProtobuf::CloseHandles, base::Unretained(this)));
+      &ChromePromptChannel::CloseHandles, base::Unretained(this)));
 
   // extension_ids are mandatory.
   if (!request.extension_ids_size()) {
@@ -706,8 +569,6 @@
   extension_ids.reserve(request.extension_ids_size());
   for (const std::string& extension_id : request.extension_ids()) {
     base::string16 extension_id_utf16;
-    // TODO(crbug.com/969139): change ChromePromptActions to use strings for
-    // extension_id and skip this conversion.
     if (!base::UTF8ToUTF16(extension_id.c_str(), extension_id.size(),
                            &extension_id_utf16)) {
       LOG(ERROR) << "Unusable extension id in RemoveExtensionsReqest.";
@@ -724,13 +585,11 @@
   WriteResponseMessage(response);
 }
 
-void ChromePromptChannelProtobuf::SendPromptUserResponse(
-    ChromePromptActions::PromptAcceptance acceptance) {
+void ChromePromptChannel::SendPromptUserResponse(
+    chrome_cleaner::PromptUserResponse::PromptAcceptance acceptance) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   chrome_cleaner::PromptUserResponse response;
-  response.set_prompt_acceptance(
-      static_cast<chrome_cleaner::PromptUserResponse::PromptAcceptance>(
-          acceptance));
+  response.set_prompt_acceptance(acceptance);
   WriteResponseMessage(response);
 }
 
diff --git a/chrome/browser/safe_browsing/chrome_cleaner/chrome_prompt_channel_win.h b/chrome/browser/safe_browsing/chrome_cleaner/chrome_prompt_channel_win.h
index 2f1d8fb..5069a59 100644
--- a/chrome/browser/safe_browsing/chrome_cleaner/chrome_prompt_channel_win.h
+++ b/chrome/browser/safe_browsing/chrome_cleaner/chrome_prompt_channel_win.h
@@ -16,11 +16,7 @@
 #include "base/sequenced_task_runner.h"
 #include "base/win/scoped_handle.h"
 #include "chrome/browser/safe_browsing/chrome_cleaner/chrome_prompt_actions_win.h"
-#include "components/chrome_cleaner/public/interfaces/chrome_prompt.mojom.h"
 #include "components/chrome_cleaner/public/proto/chrome_prompt.pb.h"
-#include "mojo/public/cpp/platform/platform_channel.h"
-#include "mojo/public/cpp/system/invitation.h"
-#include "mojo/public/cpp/system/message_pipe.h"
 #include "third_party/protobuf/src/google/protobuf/message_lite.h"
 
 namespace base {
@@ -29,15 +25,18 @@
 
 namespace safe_browsing {
 
-namespace internal {
-class ChromePromptImpl;
-}  // namespace internal
-
 class ChromePromptActions;
 
 // Handles IPC to the Chrome Cleaner process. The Chrome Cleaner process will
 // send requests and ChromePromptChannel will handle the request, often by
 // using ChromePromptActions, and write the response.
+//
+// This implementation serializes protobufs over a pipe, instead of using Mojo,
+// because the Chrome Cleaner process might be built with a different version
+// of Mojo that isn't wire-compatible.
+//
+// The interface specification is in
+// "components/chrome_cleaner/public/proto/chrome_prompt.proto".
 class ChromePromptChannel {
  public:
   // Gives access to the Chrome Cleaner process that the channel communicates
@@ -51,91 +50,6 @@
     virtual void TerminateOnError() const = 0;
   };
 
-  // Returns a CleanerProcessDelegate that wraps |process|.
-  static std::unique_ptr<CleanerProcessDelegate> CreateDelegateForProcess(
-      const base::Process& process);
-
-  // Creates a ChromePromptChannel that calls |on_connection_closed| when the
-  // IPC channel closes (either normally or on error) and uses |actions| to
-  // fulfill requests. |task_runner| can be used to run any tasks that must be
-  // seqeuenced with destruction of the ChromePromptChannel.
-  ChromePromptChannel(base::OnceClosure on_connection_closed,
-                      std::unique_ptr<ChromePromptActions> actions,
-                      scoped_refptr<base::SequencedTaskRunner> task_runner);
-
-  virtual ~ChromePromptChannel();
-
-  // Prepares an IPC channel to be used by the cleaner process that is about to
-  // be launched. Adds all handles used by the channel to |handles_to_inherit|
-  // so that the cleaner process can access them, and adds switches to
-  // |command_line| that the cleaner process can use to connect to the channel.
-  virtual bool PrepareForCleaner(
-      base::CommandLine* command_line,
-      base::HandlesToInheritVector* handles_to_inherit) = 0;
-
-  // Does any cleanup required if the cleaner process fails to launch after
-  // PrepareForCleaner was called.
-  virtual void CleanupAfterCleanerLaunchFailed() = 0;
-
-  // Kicks off communication between the IPC channel prepared by
-  // PrepareForCleaner and the process in |cleaner_process|. If the connection
-  // fails, |connection_closed_callback_| should be called.
-  virtual void ConnectToCleaner(
-      std::unique_ptr<CleanerProcessDelegate> cleaner_process) = 0;
-
-  scoped_refptr<base::SequencedTaskRunner> task_runner() const {
-    return task_runner_;
-  }
-
- protected:
-  base::OnceClosure on_connection_closed_;
-  std::unique_ptr<ChromePromptActions> actions_;
-  scoped_refptr<base::SequencedTaskRunner> task_runner_;
-
- private:
-  ChromePromptChannel(const ChromePromptChannel& other) = delete;
-  ChromePromptChannel& operator=(const ChromePromptChannel& other) = delete;
-};
-
-// Handles IPC to the Chrome Cleaner process using Mojo.
-class ChromePromptChannelMojo : public ChromePromptChannel {
- public:
-  ChromePromptChannelMojo(base::OnceClosure on_connection_closed,
-                          std::unique_ptr<ChromePromptActions> actions,
-                          scoped_refptr<base::SequencedTaskRunner> task_runner);
-
-  ~ChromePromptChannelMojo() override;
-
-  bool PrepareForCleaner(
-      base::CommandLine* command_line,
-      base::HandlesToInheritVector* handles_to_inherit) override;
-
-  void CleanupAfterCleanerLaunchFailed() override;
-
-  void ConnectToCleaner(
-      std::unique_ptr<CleanerProcessDelegate> cleaner_process) override;
-
- private:
-  ChromePromptChannelMojo(const ChromePromptChannelMojo& other) = delete;
-  ChromePromptChannelMojo& operator=(const ChromePromptChannelMojo& other) =
-      delete;
-
-  void CreateChromePromptImpl(
-      chrome_cleaner::mojom::ChromePromptRequest chrome_prompt_request);
-
-  mojo::OutgoingInvitation invitation_;
-  mojo::PlatformChannel mojo_channel_;
-  mojo::ScopedMessagePipeHandle request_pipe_;
-
-  std::unique_ptr<internal::ChromePromptImpl> chrome_prompt_impl_;
-
-  base::WeakPtrFactory<ChromePromptChannelMojo> weak_factory_{this};
-};
-
-// Handles IPC to the Chrome Cleaner process by serializing protobufs over
-// a pipe.
-class ChromePromptChannelProtobuf : public ChromePromptChannel {
- public:
   static const char kErrorHistogramName[];
   static constexpr uint32_t kMaxMessageLength = 1 * 1024 * 1024;  // 1M bytes
 
@@ -193,21 +107,40 @@
     return category_and_code;
   }
 
-  ChromePromptChannelProtobuf(
-      base::OnceClosure on_connection_closed,
-      std::unique_ptr<ChromePromptActions> actions,
-      scoped_refptr<base::SequencedTaskRunner> task_runner);
+  // Returns a CleanerProcessDelegate that wraps |process|.
+  static std::unique_ptr<CleanerProcessDelegate> CreateDelegateForProcess(
+      const base::Process& process);
 
-  ~ChromePromptChannelProtobuf() override;
+  // Creates a ChromePromptChannel that calls |on_connection_closed| when the
+  // IPC channel closes (either normally or on error) and uses |actions| to
+  // fulfill requests. |task_runner| can be used to run any tasks that must be
+  // seqeuenced with destruction of the ChromePromptChannel.
+  ChromePromptChannel(base::OnceClosure on_connection_closed,
+                      std::unique_ptr<ChromePromptActions> actions,
+                      scoped_refptr<base::SequencedTaskRunner> task_runner);
 
-  bool PrepareForCleaner(
-      base::CommandLine* command_line,
-      base::HandlesToInheritVector* handles_to_inherit) override;
+  ~ChromePromptChannel();
 
-  void CleanupAfterCleanerLaunchFailed() override;
+  // Prepares an IPC channel to be used by the cleaner process that is about to
+  // be launched. Adds all handles used by the channel to |handles_to_inherit|
+  // so that the cleaner process can access them, and adds switches to
+  // |command_line| that the cleaner process can use to connect to the channel.
+  bool PrepareForCleaner(base::CommandLine* command_line,
+                         base::HandlesToInheritVector* handles_to_inherit);
 
+  // Does any cleanup required if the cleaner process fails to launch after
+  // PrepareForCleaner was called.
+  void CleanupAfterCleanerLaunchFailed();
+
+  // Kicks off communication between the IPC channel prepared by
+  // PrepareForCleaner and the process in |cleaner_process|. If the connection
+  // fails, |connection_closed_callback_| should be called.
   void ConnectToCleaner(
-      std::unique_ptr<CleanerProcessDelegate> cleaner_process) override;
+      std::unique_ptr<CleanerProcessDelegate> cleaner_process);
+
+  scoped_refptr<base::SequencedTaskRunner> task_runner() const {
+    return task_runner_;
+  }
 
   // Handles |request| and sends a QueryCapabilityResponse in reply.
   void HandleQueryCapabilityRequest(
@@ -226,20 +159,23 @@
   void CloseHandles();
 
  private:
-  ChromePromptChannelProtobuf(const ChromePromptChannelProtobuf& other) =
-      delete;
-  ChromePromptChannelProtobuf& operator=(
-      const ChromePromptChannelProtobuf& other) = delete;
+  ChromePromptChannel(const ChromePromptChannel& other) = delete;
+  ChromePromptChannel& operator=(const ChromePromptChannel& other) = delete;
 
   // Serializes |message| to response_write_handle_. Calls CloseHandles on
   // error.
   void WriteResponseMessage(const google::protobuf::MessageLite& message);
 
   // Sends a PromptUserResponse with the given |acceptance| value.
-  void SendPromptUserResponse(ChromePromptActions::PromptAcceptance acceptance);
+  void SendPromptUserResponse(
+      chrome_cleaner::PromptUserResponse::PromptAcceptance acceptance);
 
   SEQUENCE_CHECKER(sequence_checker_);
 
+  base::OnceClosure on_connection_closed_;
+  std::unique_ptr<ChromePromptActions> actions_;
+  scoped_refptr<base::SequencedTaskRunner> task_runner_;
+
   // Requests always flow from the Chrome Cleanup tool to Chrome.
   // This class owns request_read_handle_ but request_write_handle_ will be
   // closed once it is passed to the child process.
@@ -252,7 +188,7 @@
   base::win::ScopedHandle response_read_handle_;
   base::win::ScopedHandle response_write_handle_;
 
-  base::WeakPtrFactory<ChromePromptChannelProtobuf> weak_factory_{this};
+  base::WeakPtrFactory<ChromePromptChannel> weak_factory_{this};
 };
 
 }  // namespace safe_browsing
diff --git a/chrome/browser/safe_browsing/chrome_cleaner/chrome_prompt_channel_win_unittest.cc b/chrome/browser/safe_browsing/chrome_cleaner/chrome_prompt_channel_win_unittest.cc
index ac1a90c0..e64ebc64 100644
--- a/chrome/browser/safe_browsing/chrome_cleaner/chrome_prompt_channel_win_unittest.cc
+++ b/chrome/browser/safe_browsing/chrome_cleaner/chrome_prompt_channel_win_unittest.cc
@@ -40,8 +40,8 @@
 using ::testing::Key;
 using ::testing::Not;
 using ::testing::StrictMock;
-using ErrorCategory = ChromePromptChannelProtobuf::ErrorCategory;
-using CustomErrors = ChromePromptChannelProtobuf::CustomErrors;
+using ErrorCategory = ChromePromptChannel::ErrorCategory;
+using CustomErrors = ChromePromptChannel::CustomErrors;
 using ErrorExpectationMap = std::map<ErrorCategory, uint32_t>;
 
 static constexpr uint8_t kVersion = 1U;
@@ -61,20 +61,20 @@
   MOCK_CONST_METHOD0(TerminateOnError, void());
 };
 
-class ChromePromptChannelProtobufTest : public ::testing::Test {
+class ChromePromptChannelTest : public ::testing::Test {
  public:
   using ChromePromptChannelPtr =
-      std::unique_ptr<ChromePromptChannelProtobuf, base::OnTaskRunnerDeleter>;
+      std::unique_ptr<ChromePromptChannel, base::OnTaskRunnerDeleter>;
 
-  ChromePromptChannelProtobufTest() = default;
+  ChromePromptChannelTest() = default;
 
-  ~ChromePromptChannelProtobufTest() override = default;
+  ~ChromePromptChannelTest() override = default;
 
   void SetUp() override {
     auto task_runner =
         base::CreateSequencedTaskRunner({base::ThreadPool(), base::MayBlock()});
     channel_ = ChromePromptChannelPtr(
-        new ChromePromptChannelProtobuf(
+        new ChromePromptChannel(
             /*on_connection_closed=*/run_loop_.QuitClosure(),
             std::make_unique<ChromePromptActions>(
                 /*extension_service=*/nullptr,
@@ -90,7 +90,7 @@
 
     // Instead of spawning a cleaner process, extract the prompt handles from
     // the command-line. Duplicate them so that we retain ownership if
-    // ChromePromptChannelProtobuf closes them.
+    // ChromePromptChannel closes them.
     response_read_handle_ = DuplicateHandleFromCommandLine(
         command_line, chrome_cleaner::kChromeReadHandleSwitch);
     ASSERT_TRUE(response_read_handle_.IsValid());
@@ -115,35 +115,35 @@
     // start failing.
     EXPECT_CALL(*mock_cleaner_process_, TerminateOnError)
         .WillOnce(InvokeWithoutArgs(
-            this, &ChromePromptChannelProtobufTest::CloseCleanerHandles));
+            this, &ChromePromptChannelTest::CloseCleanerHandles));
   }
 
   // Expect the histograms contains at least the specified sample.
   template <typename T>
   void ExpectSample(ErrorCategory category, T error, int count = 1) {
     histogram_tester_.ExpectBucketCount(
-        ChromePromptChannelProtobuf::kErrorHistogramName,
-        ChromePromptChannelProtobuf::GetErrorCodeInt(category, error), count);
+        ChromePromptChannel::kErrorHistogramName,
+        ChromePromptChannel::GetErrorCodeInt(category, error), count);
   }
 
   // Expect that the histogram contains only the specified sample.
   template <typename T>
   void ExpectUniqueSample(ErrorCategory category, T error, int count = 1) {
     histogram_tester_.ExpectUniqueSample(
-        ChromePromptChannelProtobuf::kErrorHistogramName,
-        ChromePromptChannelProtobuf::GetErrorCodeInt(category, error), count);
+        ChromePromptChannel::kErrorHistogramName,
+        ChromePromptChannel::GetErrorCodeInt(category, error), count);
   }
 
   void ExpectHistogramSize(uint32_t size) {
-    histogram_tester_.ExpectTotalCount(
-        ChromePromptChannelProtobuf::kErrorHistogramName, size);
+    histogram_tester_.ExpectTotalCount(ChromePromptChannel::kErrorHistogramName,
+                                       size);
   }
 
   // This is used when we want to validate that certain operations failed a
   // precise number of times without needing to know the specific error code.
   void ExpectCategoryErrorCount(const ErrorExpectationMap& expected_counts) {
     const std::vector<base::Bucket> buckets = histogram_tester_.GetAllSamples(
-        ChromePromptChannelProtobuf::kErrorHistogramName);
+        ChromePromptChannel::kErrorHistogramName);
 
     ErrorExpectationMap actual_counts;
     for (const base::Bucket& bucket : buckets) {
@@ -168,9 +168,8 @@
 
   void PostCloseCleanerHandles() {
     channel_->task_runner()->PostTask(
-        FROM_HERE,
-        base::BindOnce(&ChromePromptChannelProtobufTest::CloseCleanerHandles,
-                       base::Unretained(this)));
+        FROM_HERE, base::BindOnce(&ChromePromptChannelTest::CloseCleanerHandles,
+                                  base::Unretained(this)));
   }
 
   template <typename T>
@@ -186,9 +185,8 @@
   template <typename T>
   void PostWriteByValue(T value) {
     channel_->task_runner()->PostTask(
-        FROM_HERE,
-        base::BindOnce(&ChromePromptChannelProtobufTest::WriteByValue<T>,
-                       base::Unretained(this), value));
+        FROM_HERE, base::BindOnce(&ChromePromptChannelTest::WriteByValue<T>,
+                                  base::Unretained(this), value));
   }
 
   // Writes bytes taken by pointer to the pipe without blocking the main test
@@ -212,7 +210,7 @@
   void PostWriteByPointer(const T* ptr, uint32_t size, bool should_succeed) {
     channel_->task_runner()->PostTask(
         FROM_HERE,
-        base::BindOnce(&ChromePromptChannelProtobufTest::WriteByPointer<T>,
+        base::BindOnce(&ChromePromptChannelTest::WriteByPointer<T>,
                        base::Unretained(this), ptr, size, should_succeed));
   }
 
@@ -286,7 +284,7 @@
   base::HistogramTester histogram_tester_;
 };
 
-TEST_F(ChromePromptChannelProtobufTest, PipeInfo) {
+TEST_F(ChromePromptChannelTest, PipeInfo) {
   DWORD read_pipe_flags = 0;
   DWORD read_pipe_max_instances = 0;
   ASSERT_TRUE(::GetNamedPipeInfo(response_read_handle_.Get(), &read_pipe_flags,
@@ -302,7 +300,7 @@
   EXPECT_EQ(write_pipe_max_instances, 1UL);
 }
 
-TEST_F(ChromePromptChannelProtobufTest, ImmediateExit) {
+TEST_F(ChromePromptChannelTest, ImmediateExit) {
   EXPECT_CALL(*mock_cleaner_process_, TerminateOnError).Times(1);
   channel_->ConnectToCleaner(std::move(mock_cleaner_process_));
 
@@ -313,7 +311,7 @@
   ExpectReadFails();
 }
 
-TEST_F(ChromePromptChannelProtobufTest, VersionIsTooLarge) {
+TEST_F(ChromePromptChannelTest, VersionIsTooLarge) {
   SetupCommunicationFailure();
   channel_->ConnectToCleaner(std::move(mock_cleaner_process_));
 
@@ -329,7 +327,7 @@
   ExpectReadFails();
 }
 
-TEST_F(ChromePromptChannelProtobufTest, VersionIsZero) {
+TEST_F(ChromePromptChannelTest, VersionIsZero) {
   SetupCommunicationFailure();
   channel_->ConnectToCleaner(std::move(mock_cleaner_process_));
 
@@ -344,7 +342,7 @@
   ExpectReadFails();
 }
 
-TEST_F(ChromePromptChannelProtobufTest, ExitAfterVersion) {
+TEST_F(ChromePromptChannelTest, ExitAfterVersion) {
   SetupCommunicationFailure();
   channel_->ConnectToCleaner(std::move(mock_cleaner_process_));
 
@@ -360,7 +358,7 @@
   ExpectReadFails();
 }
 
-TEST_F(ChromePromptChannelProtobufTest, PostSizeOfZero) {
+TEST_F(ChromePromptChannelTest, PostSizeOfZero) {
   SetupCommunicationFailure();
   channel_->ConnectToCleaner(std::move(mock_cleaner_process_));
 
@@ -376,7 +374,7 @@
   ExpectReadFails();
 }
 
-TEST_F(ChromePromptChannelProtobufTest, PostSizeMoreThanMax) {
+TEST_F(ChromePromptChannelTest, PostSizeMoreThanMax) {
   SetupCommunicationFailure();
   channel_->ConnectToCleaner(std::move(mock_cleaner_process_));
 
@@ -384,7 +382,7 @@
   PostWriteByValue(kVersion);
 
   // Send invalid size
-  PostWriteByValue(ChromePromptChannelProtobuf::kMaxMessageLength + 1);
+  PostWriteByValue(ChromePromptChannel::kMaxMessageLength + 1);
   WaitForDisconnect();
 
   ExpectUniqueSample(ErrorCategory::kCustomError,
@@ -392,7 +390,7 @@
   ExpectReadFails();
 }
 
-TEST_F(ChromePromptChannelProtobufTest, PostExtraData) {
+TEST_F(ChromePromptChannelTest, PostExtraData) {
   SetupCommunicationFailure();
   channel_->ConnectToCleaner(std::move(mock_cleaner_process_));
 
@@ -416,7 +414,7 @@
 }
 
 // The pipes are valid before ConnectToCleaner just as much as after.
-TEST_F(ChromePromptChannelProtobufTest, VersionSentBeforeConnection) {
+TEST_F(ChromePromptChannelTest, VersionSentBeforeConnection) {
   SetupCommunicationFailure();
 
   // Valid version but BEFORE connection
@@ -438,7 +436,7 @@
   ExpectReadFails();
 }
 
-TEST_F(ChromePromptChannelProtobufTest, LengthShortWrite) {
+TEST_F(ChromePromptChannelTest, LengthShortWrite) {
   SetupCommunicationFailure();
   channel_->ConnectToCleaner(std::move(mock_cleaner_process_));
 
@@ -460,7 +458,7 @@
   ExpectReadFails();
 }
 
-TEST_F(ChromePromptChannelProtobufTest, RequestShortWrite) {
+TEST_F(ChromePromptChannelTest, RequestShortWrite) {
   SetupCommunicationFailure();
   channel_->ConnectToCleaner(std::move(mock_cleaner_process_));
 
@@ -484,7 +482,7 @@
   ExpectReadFails();
 }
 
-TEST_F(ChromePromptChannelProtobufTest, ExitBeforeVersion) {
+TEST_F(ChromePromptChannelTest, ExitBeforeVersion) {
   SetupCommunicationFailure();
   channel_->ConnectToCleaner(std::move(mock_cleaner_process_));
 
@@ -496,7 +494,7 @@
   ExpectReadFails();
 }
 
-TEST_F(ChromePromptChannelProtobufTest, PostEmptyData) {
+TEST_F(ChromePromptChannelTest, PostEmptyData) {
   SetupCommunicationFailure();
   channel_->ConnectToCleaner(std::move(mock_cleaner_process_));
 
@@ -519,7 +517,7 @@
   ExpectReadFails();
 }
 
-TEST_F(ChromePromptChannelProtobufTest, PostExtraPromptRequestField) {
+TEST_F(ChromePromptChannelTest, PostExtraPromptRequestField) {
   SetupCommunicationFailure();
   channel_->ConnectToCleaner(std::move(mock_cleaner_process_));
 
@@ -545,7 +543,7 @@
   ExpectReadFails();
 }
 
-TEST_F(ChromePromptChannelProtobufTest, PostQueryCapabilities) {
+TEST_F(ChromePromptChannelTest, PostQueryCapabilities) {
   SetupCommunicationFailure();
   channel_->ConnectToCleaner(std::move(mock_cleaner_process_));
 
@@ -576,7 +574,7 @@
   ExpectReadFails();
 }
 
-TEST_F(ChromePromptChannelProtobufTest, PostInvalidRequest) {
+TEST_F(ChromePromptChannelTest, PostInvalidRequest) {
   SetupCommunicationFailure();
   channel_->ConnectToCleaner(std::move(mock_cleaner_process_));
 
diff --git a/chrome/browser/safe_browsing/chrome_cleaner/mock_chrome_cleaner_process_win.cc b/chrome/browser/safe_browsing/chrome_cleaner/mock_chrome_cleaner_process_win.cc
index c9a4545..01106e7 100644
--- a/chrome/browser/safe_browsing/chrome_cleaner/mock_chrome_cleaner_process_win.cc
+++ b/chrome/browser/safe_browsing/chrome_cleaner/mock_chrome_cleaner_process_win.cc
@@ -4,6 +4,8 @@
 
 #include "chrome/browser/safe_browsing/chrome_cleaner/mock_chrome_cleaner_process_win.h"
 
+#include <windows.h>
+
 #include <stdlib.h>
 
 #include <memory>
@@ -32,15 +34,10 @@
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/grit/generated_resources.h"
 #include "components/chrome_cleaner/public/constants/constants.h"
-#include "components/chrome_cleaner/public/interfaces/chrome_prompt.mojom.h"
 #include "components/chrome_cleaner/public/proto/chrome_prompt.pb.h"
 #include "extensions/browser/extension_registry.h"
 #include "extensions/common/extension.h"
 #include "extensions/common/manifest.h"
-#include "mojo/core/embedder/embedder.h"
-#include "mojo/core/embedder/scoped_ipc_support.h"
-#include "mojo/public/cpp/platform/platform_channel.h"
-#include "mojo/public/cpp/system/invitation.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "ui/base/l10n/l10n_util.h"
@@ -49,12 +46,9 @@
 
 namespace {
 
-using ::chrome_cleaner::mojom::ChromePromptPtr;
-using ::chrome_cleaner::mojom::ChromePromptPtrInfo;
-using mojo::core::ScopedIPCSupport;
 using CrashPoint = MockChromeCleanerProcess::CrashPoint;
 using ItemsReporting = MockChromeCleanerProcess::ItemsReporting;
-using PromptAcceptance = ChromePromptActions::PromptAcceptance;
+using PromptUserResponse = chrome_cleaner::PromptUserResponse;
 using UwsFoundStatus = MockChromeCleanerProcess::UwsFoundStatus;
 
 constexpr char kCrashPointSwitch[] = "mock-crash-point";
@@ -68,118 +62,9 @@
 
 class MockCleanerResults {
  public:
-  explicit MockCleanerResults(const MockChromeCleanerProcess::Options& options)
-      : options_(options) {}
-
-  virtual ~MockCleanerResults() = default;
-
-  virtual void SendScanResults(base::OnceClosure done_closure) = 0;
-
-  PromptAcceptance received_prompt_acceptance() const {
-    return received_prompt_acceptance_;
-  }
-
-  void ReceivePromptAcceptance(base::OnceClosure done_closure,
-                               PromptAcceptance acceptance) {
-    received_prompt_acceptance_ = acceptance;
-    if (options_.crash_point() == CrashPoint::kAfterResponseReceived)
-      ::exit(MockChromeCleanerProcess::kDeliberateCrashExitCode);
-    std::move(done_closure).Run();
-  }
-
- protected:
-  MockChromeCleanerProcess::Options options_;
-  PromptAcceptance received_prompt_acceptance_ = PromptAcceptance::UNSPECIFIED;
-
- private:
-  MockCleanerResults(const MockCleanerResults& other) = delete;
-  MockCleanerResults& operator=(const MockCleanerResults& other) = delete;
-};
-
-// MockCleanerResultsMojo
-
-class MockCleanerResultsMojo : public MockCleanerResults {
- public:
-  // Sets up Mojo IPC support using |io_task_runner| to process messages.
-  // Connects to the IPC pipe given on |command_line|.
-  MockCleanerResultsMojo(
-      const MockChromeCleanerProcess::Options& options,
-      scoped_refptr<base::SequencedTaskRunner> io_task_runner,
-      const base::CommandLine& command_line)
-      : MockCleanerResults(options), io_task_runner_(io_task_runner) {
-    mojo::core::Init();
-    scoped_ipc_support_ = std::make_unique<ScopedIPCSupport>(
-        io_task_runner, ScopedIPCSupport::ShutdownPolicy::CLEAN);
-
-    auto invitation = mojo::IncomingInvitation::Accept(
-        mojo::PlatformChannel::RecoverPassedEndpointFromCommandLine(
-            command_line));
-    const std::string pipe_token = command_line.GetSwitchValueASCII(
-        chrome_cleaner::kChromeMojoPipeTokenSwitch);
-    ChromePromptPtrInfo prompt_ptr_info(
-        invitation.ExtractMessagePipe(pipe_token), 0);
-
-    // Mojo requires that the ChromePromptPtr is bound on the IO sequence.
-    io_task_runner->PostTask(
-        FROM_HERE, base::BindOnce(&ChromePromptPtr::Bind,
-                                  base::Unretained(chrome_prompt_ptr_.get()),
-                                  std::move(prompt_ptr_info), nullptr));
-  }
-
-  ~MockCleanerResultsMojo() override {
-    // Mojo requires that the ChromePromptPtr is deleted on the IO sequence.
-    // Do not shut down Mojo until after ChromePromptPtr is deleted.
-    io_task_runner_->PostTask(
-        FROM_HERE,
-        base::BindOnce(
-            [](std::unique_ptr<ChromePromptPtr> chrome_prompt_ptr,
-               std::unique_ptr<ScopedIPCSupport> scoped_ipc_support) {
-              chrome_prompt_ptr.release();
-              scoped_ipc_support.release();
-            },
-            std::move(chrome_prompt_ptr_), std::move(scoped_ipc_support_)));
-  }
-
-  void SendScanResults(base::OnceClosure done_closure) override {
-    if (options_.crash_point() == CrashPoint::kAfterRequestSent) {
-      // This task is posted to the IPC thread so that it will happen after the
-      // request is sent to the parent process and before the response gets
-      // handled on the IPC thread.
-      base::SequencedTaskRunnerHandle::Get()->PostTask(
-          FROM_HERE, base::BindOnce([]() {
-            ::exit(MockChromeCleanerProcess::kDeliberateCrashExitCode);
-          }));
-    }
-
-    (*chrome_prompt_ptr_)
-        ->PromptUser(
-            options_.files_to_delete(), options_.registry_keys(),
-            options_.extension_ids(),
-            base::BindOnce(&MockCleanerResultsMojo::ReceivePromptUserResponse,
-                           base::Unretained(this), std::move(done_closure)));
-  }
-
-  void ReceivePromptUserResponse(
-      base::OnceClosure done_closure,
-      chrome_cleaner::mojom::PromptAcceptance acceptance) {
-    ReceivePromptAcceptance(std::move(done_closure),
-                            static_cast<PromptAcceptance>(acceptance));
-  }
-
- private:
-  scoped_refptr<base::SequencedTaskRunner> io_task_runner_;
-  std::unique_ptr<ChromePromptPtr> chrome_prompt_ptr_ =
-      std::make_unique<ChromePromptPtr>();
-  std::unique_ptr<ScopedIPCSupport> scoped_ipc_support_;
-};
-
-// MockCleanerResultsProtobuf
-
-class MockCleanerResultsProtobuf : public MockCleanerResults {
- public:
-  MockCleanerResultsProtobuf(const MockChromeCleanerProcess::Options& options,
-                             const base::CommandLine& command_line)
-      : MockCleanerResults(options) {
+  MockCleanerResults(const MockChromeCleanerProcess::Options& options,
+                     const base::CommandLine& command_line)
+      : options_(options) {
     uint32_t handle_value;
     if (base::StringToUint(command_line.GetSwitchValueNative(
                                chrome_cleaner::kChromeReadHandleSwitch),
@@ -193,16 +78,14 @@
     }
   }
 
-  ~MockCleanerResultsProtobuf() override = default;
+  ~MockCleanerResults() = default;
 
-  void SendScanResults(base::OnceClosure done_closure) override {
+  void SendScanResults(base::OnceClosure done_closure) {
     base::ScopedClosureRunner call_done_closure(std::move(done_closure));
     if (!read_handle_.IsValid() || !write_handle_.IsValid()) {
       LOG(ERROR) << "IPC pipes were not connected correctly";
       return;
     }
-    // TODO(crbug.com/969139): Populate a request proto based on |options_| and
-    // send it.
 
     // Send the protocol version number.
     DWORD bytes_written = 0;
@@ -247,9 +130,9 @@
       return;
     }
     ReceivePromptAcceptance(
-        base::BindOnce(&MockCleanerResultsProtobuf::SendCloseConnectionRequest,
+        base::BindOnce(&MockCleanerResults::SendCloseConnectionRequest,
                        base::Unretained(this), call_done_closure.Release()),
-        static_cast<PromptAcceptance>(response.prompt_acceptance()));
+        response.prompt_acceptance());
   }
 
   void SendCloseConnectionRequest(base::OnceClosure done_closure) {
@@ -260,7 +143,23 @@
     std::move(done_closure).Run();
   }
 
+  PromptUserResponse::PromptAcceptance received_prompt_acceptance() const {
+    return received_prompt_acceptance_;
+  }
+
+  void ReceivePromptAcceptance(
+      base::OnceClosure done_closure,
+      PromptUserResponse::PromptAcceptance acceptance) {
+    received_prompt_acceptance_ = acceptance;
+    if (options_.crash_point() == CrashPoint::kAfterResponseReceived)
+      ::exit(MockChromeCleanerProcess::kDeliberateCrashExitCode);
+    std::move(done_closure).Run();
+  }
+
  private:
+  MockCleanerResults(const MockCleanerResults& other) = delete;
+  MockCleanerResults& operator=(const MockCleanerResults& other) = delete;
+
   bool WriteMessage(const std::string& message) {
     uint32_t message_length = message.size();
     DWORD bytes_written = 0;
@@ -296,6 +195,10 @@
     return response_message;
   }
 
+  MockChromeCleanerProcess::Options options_;
+  PromptUserResponse::PromptAcceptance received_prompt_acceptance_ =
+      PromptUserResponse::UNSPECIFIED;
+
   base::win::ScopedHandle read_handle_;
   base::win::ScopedHandle write_handle_;
 };
@@ -395,12 +298,13 @@
   }
 
   if (command_line.HasSwitch(kExpectedUserResponseSwitch)) {
-    static const std::vector<PromptAcceptance> kValidPromptAcceptanceList{
-        PromptAcceptance::UNSPECIFIED,
-        PromptAcceptance::ACCEPTED_WITH_LOGS,
-        PromptAcceptance::ACCEPTED_WITHOUT_LOGS,
-        PromptAcceptance::DENIED,
-    };
+    static const std::vector<PromptUserResponse::PromptAcceptance>
+        kValidPromptAcceptanceList{
+            PromptUserResponse::UNSPECIFIED,
+            PromptUserResponse::ACCEPTED_WITH_LOGS,
+            PromptUserResponse::ACCEPTED_WITHOUT_LOGS,
+            PromptUserResponse::DENIED,
+        };
 
     int expected_response_int = 0;
     if (!base::StringToInt(
@@ -409,8 +313,9 @@
       return false;
     }
 
-    const PromptAcceptance expected_response =
-        static_cast<PromptAcceptance>(expected_response_int);
+    const PromptUserResponse::PromptAcceptance expected_response =
+        static_cast<PromptUserResponse::PromptAcceptance>(
+            expected_response_int);
     if (!base::Contains(kValidPromptAcceptanceList, expected_response)) {
       return false;
     }
@@ -469,7 +374,7 @@
       kExtensionsReportingSwitch,
       base::NumberToString(static_cast<int>(extensions_reporting())));
 
-  if (expected_user_response() != PromptAcceptance::UNSPECIFIED) {
+  if (expected_user_response() != PromptUserResponse::UNSPECIFIED) {
     command_line->AppendSwitchASCII(
         kExpectedUserResponseSwitch,
         base::NumberToString(static_cast<int>(expected_user_response())));
@@ -563,15 +468,15 @@
 }
 
 int MockChromeCleanerProcess::Options::ExpectedExitCode(
-    PromptAcceptance received_prompt_acceptance) const {
+    PromptUserResponse::PromptAcceptance received_prompt_acceptance) const {
   if (crash_point() != CrashPoint::kNone)
     return kDeliberateCrashExitCode;
 
   if (files_to_delete_.empty())
     return kNothingFoundExitCode;
 
-  if (received_prompt_acceptance == PromptAcceptance::ACCEPTED_WITH_LOGS ||
-      received_prompt_acceptance == PromptAcceptance::ACCEPTED_WITHOUT_LOGS) {
+  if (received_prompt_acceptance == PromptUserResponse::ACCEPTED_WITH_LOGS ||
+      received_prompt_acceptance == PromptUserResponse::ACCEPTED_WITHOUT_LOGS) {
     return reboot_required() ? kRebootRequiredExitCode
                              : kRebootNotRequiredExitCode;
   }
@@ -606,15 +511,7 @@
   if (::testing::Test::HasFailure())
     return kInternalTestFailureExitCode;
 
-  std::unique_ptr<MockCleanerResults> mock_results;
-
-  if (command_line_->HasSwitch(chrome_cleaner::kChromeMojoPipeTokenSwitch)) {
-    mock_results = std::make_unique<MockCleanerResultsMojo>(
-        options_, io_thread.task_runner(), *command_line_);
-  } else {
-    mock_results =
-        std::make_unique<MockCleanerResultsProtobuf>(options_, *command_line_);
-  }
+  MockCleanerResults mock_results(options_, *command_line_);
 
   if (options_.crash_point() == CrashPoint::kAfterConnection)
     exit(kDeliberateCrashExitCode);
@@ -633,18 +530,18 @@
 
   io_thread.task_runner()->PostTask(
       FROM_HERE, base::BindOnce(&MockCleanerResults::SendScanResults,
-                                base::Unretained(mock_results.get()),
+                                base::Unretained(&mock_results),
                                 base::Passed(&quit_closure)));
 
   run_loop.Run();
 
-  EXPECT_NE(mock_results->received_prompt_acceptance(),
-            PromptAcceptance::UNSPECIFIED);
-  EXPECT_EQ(mock_results->received_prompt_acceptance(),
+  EXPECT_NE(mock_results.received_prompt_acceptance(),
+            PromptUserResponse::UNSPECIFIED);
+  EXPECT_EQ(mock_results.received_prompt_acceptance(),
             options_.expected_user_response());
   if (::testing::Test::HasFailure())
     return kInternalTestFailureExitCode;
-  return options_.ExpectedExitCode(mock_results->received_prompt_acceptance());
+  return options_.ExpectedExitCode(mock_results.received_prompt_acceptance());
 }
 
 // Keep the printable names of these enums short since they're used in tests
@@ -664,12 +561,6 @@
   return out << "Ext" << static_cast<int>(status);
 }
 
-std::ostream& operator<<(
-    std::ostream& out,
-    MockChromeCleanerProcess::ProtobufIPCFeatureStatus status) {
-  return out << "Ipc" << static_cast<int>(status);
-}
-
 std::ostream& operator<<(std::ostream& out, ItemsReporting items_reporting) {
   return out << "Items" << static_cast<int>(items_reporting);
 }
diff --git a/chrome/browser/safe_browsing/chrome_cleaner/mock_chrome_cleaner_process_win.h b/chrome/browser/safe_browsing/chrome_cleaner/mock_chrome_cleaner_process_win.h
index b70e643..3a4c28d 100644
--- a/chrome/browser/safe_browsing/chrome_cleaner/mock_chrome_cleaner_process_win.h
+++ b/chrome/browser/safe_browsing/chrome_cleaner/mock_chrome_cleaner_process_win.h
@@ -70,11 +70,6 @@
     kDisabled,
   };
 
-  enum class ProtobufIPCFeatureStatus {
-    kEnabled,
-    kDisabled,
-  };
-
   static constexpr int kInternalTestFailureExitCode = 100001;
   static constexpr int kDeliberateCrashExitCode = 100002;
   static constexpr int kNothingFoundExitCode = 2;
@@ -130,11 +125,13 @@
     CrashPoint crash_point() const { return crash_point_; }
 
     void set_expected_user_response(
-        ChromePromptActions::PromptAcceptance expected_user_response) {
+        chrome_cleaner::PromptUserResponse::PromptAcceptance
+            expected_user_response) {
       expected_user_response_ = expected_user_response;
     }
 
-    ChromePromptActions::PromptAcceptance expected_user_response() const {
+    chrome_cleaner::PromptUserResponse::PromptAcceptance
+    expected_user_response() const {
       return expected_user_response_;
     }
 
@@ -146,8 +143,8 @@
       return extensions_reporting_;
     }
 
-    int ExpectedExitCode(
-        ChromePromptActions::PromptAcceptance received_prompt_acceptance) const;
+    int ExpectedExitCode(chrome_cleaner::PromptUserResponse::PromptAcceptance
+                             received_prompt_acceptance) const;
 
    private:
     std::vector<base::FilePath> files_to_delete_;
@@ -158,8 +155,9 @@
     CrashPoint crash_point_ = CrashPoint::kNone;
     ItemsReporting registry_keys_reporting_ = ItemsReporting::kUnsupported;
     ItemsReporting extensions_reporting_ = ItemsReporting::kUnsupported;
-    ChromePromptActions::PromptAcceptance expected_user_response_ =
-        ChromePromptActions::PromptAcceptance::UNSPECIFIED;
+    chrome_cleaner::PromptUserResponse::PromptAcceptance
+        expected_user_response_ =
+            chrome_cleaner::PromptUserResponse::UNSPECIFIED;
   };
 
   MockChromeCleanerProcess();
@@ -196,10 +194,6 @@
 
 std::ostream& operator<<(
     std::ostream& out,
-    MockChromeCleanerProcess::ProtobufIPCFeatureStatus status);
-
-std::ostream& operator<<(
-    std::ostream& out,
     MockChromeCleanerProcess::ItemsReporting items_reporting);
 
 }  // namespace safe_browsing
diff --git a/chrome/browser/safe_browsing/chrome_cleaner/srt_field_trial_win.cc b/chrome/browser/safe_browsing/chrome_cleaner/srt_field_trial_win.cc
index 49ea9fa..38b09646 100644
--- a/chrome/browser/safe_browsing/chrome_cleaner/srt_field_trial_win.cc
+++ b/chrome/browser/safe_browsing/chrome_cleaner/srt_field_trial_win.cc
@@ -42,9 +42,6 @@
 const base::Feature kChromeCleanupExtensionsFeature{
     "ChromeCleanupExtensions", base::FEATURE_DISABLED_BY_DEFAULT};
 
-const base::Feature kChromeCleanupProtobufIPCFeature{
-    "ChromeCleanupProtobufIPC", base::FEATURE_ENABLED_BY_DEFAULT};
-
 bool IsSRTPromptFeatureEnabled() {
   return base::FeatureList::IsEnabled(kChromeCleanupInBrowserPromptFeature);
 }
diff --git a/chrome/browser/safe_browsing/chrome_cleaner/srt_field_trial_win.h b/chrome/browser/safe_browsing/chrome_cleaner/srt_field_trial_win.h
index b462b70..0c168d4d 100644
--- a/chrome/browser/safe_browsing/chrome_cleaner/srt_field_trial_win.h
+++ b/chrome/browser/safe_browsing/chrome_cleaner/srt_field_trial_win.h
@@ -64,11 +64,6 @@
 // for, and cleanup, bad extensions.
 extern const base::Feature kChromeCleanupExtensionsFeature;
 
-// Protobuf IPC feature. When enabled, Chrome Cleaner will communicate by
-// serializing protobufs over a custom IPC pipe that isn't tied to the Mojo
-// version.
-extern const base::Feature kChromeCleanupProtobufIPCFeature;
-
 // Returns true if this Chrome is in a field trial group which shows the SRT
 // prompt.
 bool IsSRTPromptFeatureEnabled();
diff --git a/chrome/browser/safe_browsing/cloud_content_scanning/deep_scanning_dialog_delegate.cc b/chrome/browser/safe_browsing/cloud_content_scanning/deep_scanning_dialog_delegate.cc
index 0f714b2..75c4972 100644
--- a/chrome/browser/safe_browsing/cloud_content_scanning/deep_scanning_dialog_delegate.cc
+++ b/chrome/browser/safe_browsing/cloud_content_scanning/deep_scanning_dialog_delegate.cc
@@ -36,12 +36,12 @@
 
 namespace safe_browsing {
 
-const base::Feature kDeepScanningOfUploads{"DeepScanningOfUploads",
+const base::Feature kDeepScanningOfUploads{"SafeBrowsingDeepScanningOfUploads",
                                            base::FEATURE_DISABLED_BY_DEFAULT};
 
 // TODO(rogerta): keeping this disabled by default until UX is finalized.
-const base::Feature kDeepScanningOfUploadsUI{"DeepScanningOfUploadsUI",
-                                             base::FEATURE_DISABLED_BY_DEFAULT};
+const base::Feature kDeepScanningOfUploadsUI{
+    "SafeBrowsingDeepScanningOfUploadsUI", base::FEATURE_DISABLED_BY_DEFAULT};
 
 namespace {
 
diff --git a/chrome/browser/sessions/session_restore.cc b/chrome/browser/sessions/session_restore.cc
index 161e86f..78823fc4 100644
--- a/chrome/browser/sessions/session_restore.cc
+++ b/chrome/browser/sessions/session_restore.cc
@@ -22,6 +22,7 @@
 #include "base/macros.h"
 #include "base/metrics/field_trial.h"
 #include "base/metrics/histogram_macros.h"
+#include "base/numerics/ranges.h"
 #include "base/run_loop.h"
 #include "base/single_thread_task_runner.h"
 #include "base/task/cancelable_task_tracker.h"
@@ -178,11 +179,8 @@
 
       // Restore and show the browser.
       const int initial_tab_count = 0;
-      int selected_tab_index =
-          std::max(0, std::min((*i)->selected_tab_index,
-                               static_cast<int>((*i)->tabs.size()) - 1));
       RestoreTabsToBrowser(*(*i), browser, initial_tab_count,
-                           selected_tab_index, &created_contents);
+                           &created_contents);
       NotifySessionServiceOfRestoredTabs(browser, initial_tab_count);
     }
 
@@ -437,18 +435,12 @@
           browser == browser_ && !(*i)->tabs.empty();
       if (close_active_tab)
         --initial_tab_count;
-      int selected_tab_index =
-          initial_tab_count > 0
-              ? browser->tab_strip_model()->active_index()
-              : std::max(0, std::min((*i)->selected_tab_index,
-                                     static_cast<int>((*i)->tabs.size()) - 1));
       if ((*i)->window_id == active_window_id)
         browser_to_activate = browser;
 
       // 5. Restore tabs in |browser|. This will also call Show() on |browser|
       //    if its initial show state is not mimimized.
-      RestoreTabsToBrowser(*(*i), browser, initial_tab_count,
-                           selected_tab_index, created_contents);
+      RestoreTabsToBrowser(*(*i), browser, initial_tab_count, created_contents);
       DCHECK(browser->window()->IsVisible() ||
              browser->window()->IsMinimized());
 
@@ -531,12 +523,11 @@
 
   // Adds the tabs from |window| to |browser|. Normal tabs go after the existing
   // tabs but pinned tabs will be pushed in front.
-  // If there are no existing tabs, the tab at |selected_tab_index| will be
-  // selected. Otherwise, the tab selection will remain untouched.
+  // If there are no existing tabs, the tab at |window.selected_tab_index| will
+  // be selected. Otherwise, the tab selection will remain untouched.
   void RestoreTabsToBrowser(const sessions::SessionWindow& window,
                             Browser* browser,
                             int initial_tab_count,
-                            int selected_tab_index,
                             std::vector<RestoredTab>* created_contents) {
     DVLOG(1) << "RestoreTabsToBrowser " << window.tabs.size();
     DCHECK(!window.tabs.empty());
@@ -562,6 +553,9 @@
     // yet due the ordering of TabStripModelObserver notifications in an edge
     // case.
 
+    const int selected_tab_index = base::ClampToRange(
+        window.selected_tab_index, 0, static_cast<int>(window.tabs.size() - 1));
+
     for (int i = 0; i < static_cast<int>(window.tabs.size()); ++i) {
       const sessions::SessionTab& tab = *(window.tabs[i]);
 
diff --git a/chrome/browser/signin/e2e_tests/demo_signin_e2e_test.cc b/chrome/browser/signin/e2e_tests/live_sign_in_test.cc
similarity index 96%
rename from chrome/browser/signin/e2e_tests/demo_signin_e2e_test.cc
rename to chrome/browser/signin/e2e_tests/live_sign_in_test.cc
index 1ae6f84..e4263f3c 100644
--- a/chrome/browser/signin/e2e_tests/demo_signin_e2e_test.cc
+++ b/chrome/browser/signin/e2e_tests/live_sign_in_test.cc
@@ -74,10 +74,10 @@
 };
 
 // Live tests for SignIn.
-class DemoSignInTest : public signin::test::LiveTest {
+class LiveSignInTest : public signin::test::LiveTest {
  public:
-  DemoSignInTest() = default;
-  ~DemoSignInTest() override = default;
+  LiveSignInTest() = default;
+  ~LiveSignInTest() override = default;
 
   void SetUp() override {
     LiveTest::SetUp();
@@ -152,7 +152,7 @@
 // Sings in an account through the settings page and checks that the account is
 // added to Chrome. Sync should be disabled because the test doesn't pass
 // through the Sync confirmation dialog.
-IN_PROC_BROWSER_TEST_F(DemoSignInTest, SimpleSignInFlow) {
+IN_PROC_BROWSER_TEST_F(LiveSignInTest, SimpleSignInFlow) {
   TestAccount ta;
   CHECK(GetTestAccountsUtil()->GetAccount("TEST_ACCOUNT_1", ta));
   SignInFromSettings(ta);
@@ -173,7 +173,7 @@
 // Sync is enabled.
 // Then, signs out on the web and checks that the account is removed from
 // cookies and Sync paused error is displayed.
-IN_PROC_BROWSER_TEST_F(DemoSignInTest, WebSignOut) {
+IN_PROC_BROWSER_TEST_F(LiveSignInTest, WebSignOut) {
   TestAccount test_account;
   CHECK(GetTestAccountsUtil()->GetAccount("TEST_ACCOUNT_1", test_account));
   TurnOnSync(test_account);
@@ -207,7 +207,7 @@
 // Sings in two accounts on the web and checks that cookies and refresh tokens
 // are added to Chrome. Sync should be disabled.
 // Then, signs out on the web and checks that accounts are removed from Chrome.
-IN_PROC_BROWSER_TEST_F(DemoSignInTest, WebSignInAndSignOut) {
+IN_PROC_BROWSER_TEST_F(LiveSignInTest, WebSignInAndSignOut) {
   TestAccount test_account_1;
   CHECK(GetTestAccountsUtil()->GetAccount("TEST_ACCOUNT_1", test_account_1));
   SignInFromWeb(test_account_1);
@@ -253,7 +253,7 @@
 // Sync is enabled. Signs in a second account on the web.
 // Then, turns Sync off from the settings page and checks that both accounts are
 // removed from Chrome and from cookies.
-IN_PROC_BROWSER_TEST_F(DemoSignInTest, TurnOffSync) {
+IN_PROC_BROWSER_TEST_F(LiveSignInTest, TurnOffSync) {
   TestAccount test_account_1;
   CHECK(GetTestAccountsUtil()->GetAccount("TEST_ACCOUNT_1", test_account_1));
   TurnOnSync(test_account_1);
diff --git a/chrome/browser/signin/signin_ui_util_unittest.cc b/chrome/browser/signin/signin_ui_util_unittest.cc
index 616f87d..297c794 100644
--- a/chrome/browser/signin/signin_ui_util_unittest.cc
+++ b/chrome/browser/signin/signin_ui_util_unittest.cc
@@ -14,7 +14,6 @@
 #include "chrome/browser/profiles/profile_attributes_storage.h"
 #include "chrome/browser/signin/identity_manager_factory.h"
 #include "chrome/browser/signin/identity_test_environment_profile_adaptor.h"
-#include "chrome/browser/signin/scoped_account_consistency.h"
 #include "chrome/browser/signin/signin_promo.h"
 #include "chrome/browser/ui/ui_features.h"
 #include "chrome/test/base/browser_with_test_window_test.h"
@@ -264,7 +263,6 @@
     }
   }
 
-  const ScopedAccountConsistencyDice scoped_account_consistency_;
   signin_metrics::AccessPoint access_point_ =
       signin_metrics::AccessPoint::ACCESS_POINT_BOOKMARK_BUBBLE;
 
diff --git a/chrome/browser/ssl/ssl_browsertest.cc b/chrome/browser/ssl/ssl_browsertest.cc
index 6104815..2c346b2 100644
--- a/chrome/browser/ssl/ssl_browsertest.cc
+++ b/chrome/browser/ssl/ssl_browsertest.cc
@@ -44,9 +44,10 @@
 #include "chrome/browser/content_settings/tab_specific_content_settings.h"
 #include "chrome/browser/extensions/browsertest_util.h"
 #include "chrome/browser/interstitials/security_interstitial_page_test_utils.h"
+#include "chrome/browser/net/profile_network_context_service.h"
+#include "chrome/browser/net/profile_network_context_service_factory.h"
 #include "chrome/browser/net/system_network_context_manager.h"
 #include "chrome/browser/profiles/profile.h"
-#include "chrome/browser/profiles/profile_io_data.h"
 #include "chrome/browser/profiles/profile_manager.h"
 #include "chrome/browser/ssl/bad_clock_blocking_page.h"
 #include "chrome/browser/ssl/captive_portal_blocking_page.h"
@@ -2247,8 +2248,9 @@
 
 IN_PROC_BROWSER_TEST_F(SSLUITest, TestBrowserUseClientCertStore) {
   // Make the browser use the ClientCertStoreStub instead of the regular one.
-  ProfileIOData::FromResourceContext(browser()->profile()->GetResourceContext())
-      ->set_client_cert_store_factory_for_testing(base::Bind(&CreateCertStore));
+  ProfileNetworkContextServiceFactory::GetForContext(browser()->profile())
+      ->set_client_cert_store_factory_for_testing(
+          base::BindRepeating(&CreateCertStore));
 
   net::EmbeddedTestServer https_server(net::EmbeddedTestServer::TYPE_HTTPS);
   net::SSLServerConfig ssl_config;
@@ -2282,7 +2284,7 @@
 
 IN_PROC_BROWSER_TEST_F(SSLUITest, TestClientAuthSigningFails) {
   // Make the browser use the ClientCertStoreStub instead of the regular one.
-  ProfileIOData::FromResourceContext(browser()->profile()->GetResourceContext())
+  ProfileNetworkContextServiceFactory::GetForContext(browser()->profile())
       ->set_client_cert_store_factory_for_testing(
           base::BindRepeating(&CreateFailSigningCertStore));
 
@@ -2319,7 +2321,7 @@
 
 IN_PROC_BROWSER_TEST_F(SSLUITest, TestClientAuthContinueWithoutCert) {
   // Make the browser use a ClientCertStoreStub that returns no certs.
-  ProfileIOData::FromResourceContext(browser()->profile()->GetResourceContext())
+  ProfileNetworkContextServiceFactory::GetForContext(browser()->profile())
       ->set_client_cert_store_factory_for_testing(
           base::BindRepeating(&CreateEmptyCertStore));
 
@@ -2345,7 +2347,7 @@
 
 IN_PROC_BROWSER_TEST_F(SSLUITest, TestCertDBChangedFlushesClientAuthCache) {
   // Make the browser use the ClientCertStoreStub instead of the regular one.
-  ProfileIOData::FromResourceContext(browser()->profile()->GetResourceContext())
+  ProfileNetworkContextServiceFactory::GetForContext(browser()->profile())
       ->set_client_cert_store_factory_for_testing(
           base::BindRepeating(&CreateCertStore));
 
@@ -2383,7 +2385,7 @@
   EXPECT_EQ("", tab->GetLastCommittedURL().ref());
 
   // Now use a ClientCertStoreStub that always returns an empty list.
-  ProfileIOData::FromResourceContext(browser()->profile()->GetResourceContext())
+  ProfileNetworkContextServiceFactory::GetForContext(browser()->profile())
       ->set_client_cert_store_factory_for_testing(
           base::BindRepeating(&CreateEmptyCertStore));
 
diff --git a/chrome/browser/sync/test/integration/enable_disable_test.cc b/chrome/browser/sync/test/integration/enable_disable_test.cc
index 4352fd6..33e572910 100644
--- a/chrome/browser/sync/test/integration/enable_disable_test.cc
+++ b/chrome/browser/sync/test/integration/enable_disable_test.cc
@@ -11,6 +11,7 @@
 #include "base/test/metrics/histogram_tester.h"
 #include "base/values.h"
 #include "chrome/browser/sync/test/integration/bookmarks_helper.h"
+#include "chrome/browser/sync/test/integration/feature_toggler.h"
 #include "chrome/browser/sync/test/integration/profile_sync_service_harness.h"
 #include "chrome/browser/sync/test/integration/sync_test.h"
 #include "chrome/browser/sync/test/integration/updated_progress_marker_checker.h"
@@ -74,9 +75,11 @@
 
 // This test enables and disables types and verifies the type is sufficiently
 // affected by checking for existence of a root node.
-class EnableDisableSingleClientTest : public SyncTest {
+class EnableDisableSingleClientTest : public FeatureToggler, public SyncTest {
  public:
-  EnableDisableSingleClientTest() : SyncTest(SINGLE_CLIENT) {}
+  EnableDisableSingleClientTest()
+      : FeatureToggler(switches::kProfileSyncServiceUsesThreadPool),
+        SyncTest(SINGLE_CLIENT) {}
   ~EnableDisableSingleClientTest() override {}
 
   // Don't use self-notifications as they can trigger additional sync cycles.
@@ -167,7 +170,7 @@
   DISALLOW_COPY_AND_ASSIGN(EnableDisableSingleClientTest);
 };
 
-IN_PROC_BROWSER_TEST_F(EnableDisableSingleClientTest, EnableOneAtATime) {
+IN_PROC_BROWSER_TEST_P(EnableDisableSingleClientTest, EnableOneAtATime) {
   // Setup sync with no enabled types.
   SetupTest(/*all_types_enabled=*/false);
 
@@ -202,7 +205,7 @@
   }
 }
 
-IN_PROC_BROWSER_TEST_F(EnableDisableSingleClientTest, DisableOneAtATime) {
+IN_PROC_BROWSER_TEST_P(EnableDisableSingleClientTest, DisableOneAtATime) {
   // Setup sync with no disabled types.
   SetupTest(/*all_types_enabled=*/true);
 
@@ -238,7 +241,7 @@
   }
 }
 
-IN_PROC_BROWSER_TEST_F(EnableDisableSingleClientTest,
+IN_PROC_BROWSER_TEST_P(EnableDisableSingleClientTest,
                        FastEnableDisableOneAtATime) {
   // Setup sync with no enabled types.
   SetupTest(/*all_types_enabled=*/false);
@@ -270,7 +273,7 @@
   }
 }
 
-IN_PROC_BROWSER_TEST_F(EnableDisableSingleClientTest,
+IN_PROC_BROWSER_TEST_P(EnableDisableSingleClientTest,
                        FastDisableEnableOneAtATime) {
   // Setup sync with no disabled types.
   SetupTest(/*all_types_enabled=*/true);
@@ -294,7 +297,7 @@
   }
 }
 
-IN_PROC_BROWSER_TEST_F(EnableDisableSingleClientTest,
+IN_PROC_BROWSER_TEST_P(EnableDisableSingleClientTest,
                        FastEnableDisableEnableOneAtATime) {
   // Setup sync with no enabled types.
   SetupTest(/*all_types_enabled=*/false);
@@ -320,7 +323,7 @@
   }
 }
 
-IN_PROC_BROWSER_TEST_F(EnableDisableSingleClientTest, EnableDisable) {
+IN_PROC_BROWSER_TEST_P(EnableDisableSingleClientTest, EnableDisable) {
   SetupTest(/*all_types_enabled=*/false);
 
   // Enable all, and then disable immediately afterwards, before datatypes
@@ -337,11 +340,11 @@
   }
 }
 
-IN_PROC_BROWSER_TEST_F(EnableDisableSingleClientTest, PRE_EnableAndRestart) {
+IN_PROC_BROWSER_TEST_P(EnableDisableSingleClientTest, PRE_EnableAndRestart) {
   SetupTest(/*all_types_enabled=*/true);
 }
 
-IN_PROC_BROWSER_TEST_F(EnableDisableSingleClientTest, EnableAndRestart) {
+IN_PROC_BROWSER_TEST_P(EnableDisableSingleClientTest, EnableAndRestart) {
   ASSERT_TRUE(SetupClients());
 
   EXPECT_TRUE(GetClient(0)->AwaitEngineInitialization());
@@ -354,7 +357,7 @@
   }
 }
 
-IN_PROC_BROWSER_TEST_F(EnableDisableSingleClientTest, FastEnableDisableEnable) {
+IN_PROC_BROWSER_TEST_P(EnableDisableSingleClientTest, FastEnableDisableEnable) {
   SetupTest(/*all_types_enabled=*/false);
 
   // Enable all, and then disable+reenable immediately afterwards, before
@@ -376,7 +379,7 @@
 // redownloaded when Sync is started again. This does not actually verify that
 // the data is gone from disk (which seems infeasible); it's mostly here as a
 // baseline for the following tests.
-IN_PROC_BROWSER_TEST_F(EnableDisableSingleClientTest,
+IN_PROC_BROWSER_TEST_P(EnableDisableSingleClientTest,
                        RedownloadsAfterClearData) {
   ASSERT_TRUE(SetupClients());
   ASSERT_FALSE(bookmarks_helper::GetBookmarkModel(0)->IsBookmarked(
@@ -406,7 +409,7 @@
   EXPECT_EQ(GetNumUpdatesDownloadedInLastCycle(), initial_updates_downloaded);
 }
 
-IN_PROC_BROWSER_TEST_F(EnableDisableSingleClientTest,
+IN_PROC_BROWSER_TEST_P(EnableDisableSingleClientTest,
                        DoesNotRedownloadAfterKeepData) {
   ASSERT_TRUE(SetupClients());
   ASSERT_FALSE(bookmarks_helper::GetBookmarkModel(0)->IsBookmarked(
@@ -448,7 +451,7 @@
                                          /*REMOTE_INITIAL_UPDATE=*/5));
 }
 
-IN_PROC_BROWSER_TEST_F(EnableDisableSingleClientTest, ClearsPrefsIfClearData) {
+IN_PROC_BROWSER_TEST_P(EnableDisableSingleClientTest, ClearsPrefsIfClearData) {
   SetupTest(/*all_types_enabled=*/true);
 
   SyncPrefs prefs(GetProfile(0)->GetPrefs());
@@ -458,7 +461,7 @@
   EXPECT_EQ("", prefs.GetCacheGuid());
 }
 
-IN_PROC_BROWSER_TEST_F(EnableDisableSingleClientTest,
+IN_PROC_BROWSER_TEST_P(EnableDisableSingleClientTest,
                        DoesNotClearPrefsWithKeepData) {
   SetupTest(/*all_types_enabled=*/true);
 
@@ -486,7 +489,7 @@
   }
 };
 
-IN_PROC_BROWSER_TEST_F(EnableDisableSingleClientSelfNotifyTest,
+IN_PROC_BROWSER_TEST_P(EnableDisableSingleClientSelfNotifyTest,
                        PRE_ResendsBagOfChips) {
   sync_pb::ChipBag bag_of_chips;
   bag_of_chips.set_server_chips(kTestServerChips);
@@ -503,7 +506,7 @@
   EXPECT_EQ(kTestServerChips, message.bag_of_chips().server_chips());
 }
 
-IN_PROC_BROWSER_TEST_F(EnableDisableSingleClientSelfNotifyTest,
+IN_PROC_BROWSER_TEST_P(EnableDisableSingleClientSelfNotifyTest,
                        ResendsBagOfChips) {
   ASSERT_TRUE(SetupClients());
   SyncPrefs prefs(GetProfile(0)->GetPrefs());
@@ -515,4 +518,8 @@
   EXPECT_EQ(kTestServerChips, message.bag_of_chips().server_chips());
 }
 
+INSTANTIATE_TEST_SUITE_P(,
+                         EnableDisableSingleClientSelfNotifyTest,
+                         ::testing::Values(false, true));
+
 }  // namespace
diff --git a/chrome/browser/sync/test/integration/single_client_bookmarks_sync_test.cc b/chrome/browser/sync/test/integration/single_client_bookmarks_sync_test.cc
index 12cab7b97..94a41ee 100644
--- a/chrome/browser/sync/test/integration/single_client_bookmarks_sync_test.cc
+++ b/chrome/browser/sync/test/integration/single_client_bookmarks_sync_test.cc
@@ -10,6 +10,7 @@
 #include "base/test/metrics/histogram_tester.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/sync/test/integration/bookmarks_helper.h"
+#include "chrome/browser/sync/test/integration/feature_toggler.h"
 #include "chrome/browser/sync/test/integration/profile_sync_service_harness.h"
 #include "chrome/browser/sync/test/integration/single_client_status_change_checker.h"
 #include "chrome/browser/sync/test/integration/sync_test.h"
@@ -62,9 +63,11 @@
 // fake server across PRE_MyTest and MyTest.
 const char kBookmarkGuid[] = "e397ed62-9532-4dbf-ae55-200236eba15c";
 
-class SingleClientBookmarksSyncTest : public SyncTest {
+class SingleClientBookmarksSyncTest : public FeatureToggler, public SyncTest {
  public:
-  SingleClientBookmarksSyncTest() : SyncTest(SINGLE_CLIENT) {}
+  SingleClientBookmarksSyncTest()
+      : FeatureToggler(switches::kProfileSyncServiceUsesThreadPool),
+        SyncTest(SINGLE_CLIENT) {}
   ~SingleClientBookmarksSyncTest() override {}
 
   // Verify that the local bookmark model (for the Profile corresponding to
@@ -93,7 +96,7 @@
   }
 }
 
-IN_PROC_BROWSER_TEST_F(SingleClientBookmarksSyncTest, Sanity) {
+IN_PROC_BROWSER_TEST_P(SingleClientBookmarksSyncTest, Sanity) {
   ASSERT_TRUE(SetupClients()) << "SetupClients() failed.";
 
   // Starting state:
@@ -227,7 +230,7 @@
     VerifyBookmarkModelMatchesFakeServer(kSingleProfileIndex);
 }
 
-IN_PROC_BROWSER_TEST_F(SingleClientBookmarksSyncTest, CommitLocalCreations) {
+IN_PROC_BROWSER_TEST_P(SingleClientBookmarksSyncTest, CommitLocalCreations) {
   ASSERT_TRUE(SetupClients()) << "SetupClients() failed.";
 
   // Starting state:
@@ -268,7 +271,7 @@
   EXPECT_TRUE(ModelMatchesVerifier(kSingleProfileIndex));
 }
 
-IN_PROC_BROWSER_TEST_F(SingleClientBookmarksSyncTest, InjectedBookmark) {
+IN_PROC_BROWSER_TEST_P(SingleClientBookmarksSyncTest, InjectedBookmark) {
   std::string title = "Montreal Canadiens";
   fake_server::EntityBuilderFactory entity_builder_factory;
   fake_server::BookmarkEntityBuilder bookmark_builder =
@@ -286,7 +289,7 @@
 // Test that a client doesn't mutate the favicon data in the process
 // of storing the favicon data from sync to the database or in the process
 // of requesting data from the database for sync.
-IN_PROC_BROWSER_TEST_F(SingleClientBookmarksSyncTest,
+IN_PROC_BROWSER_TEST_P(SingleClientBookmarksSyncTest,
                        SetFaviconHiDPIDifferentCodec) {
   // Set the supported scale factors to 1x and 2x such that
   // BookmarkModel::GetFavicon() requests both 1x and 2x.
@@ -331,7 +334,7 @@
 
 // Test that a client deletes favicons from sync when they have been removed
 // from the local database.
-IN_PROC_BROWSER_TEST_F(SingleClientBookmarksSyncTest, DeleteFaviconFromSync) {
+IN_PROC_BROWSER_TEST_P(SingleClientBookmarksSyncTest, DeleteFaviconFromSync) {
   ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
   ASSERT_TRUE(ModelMatchesVerifier(kSingleProfileIndex));
 
@@ -357,7 +360,7 @@
       GetBookmarkModel(kSingleProfileIndex)->GetFavicon(bookmark).IsEmpty());
 }
 
-IN_PROC_BROWSER_TEST_F(SingleClientBookmarksSyncTest,
+IN_PROC_BROWSER_TEST_P(SingleClientBookmarksSyncTest,
                        BookmarkAllNodesRemovedEvent) {
   ASSERT_TRUE(SetupClients()) << "SetupClients() failed.";
   // Starting state:
@@ -418,7 +421,7 @@
   ASSERT_TRUE(ModelMatchesVerifier(kSingleProfileIndex));
 }
 
-IN_PROC_BROWSER_TEST_F(SingleClientBookmarksSyncTest, DownloadDeletedBookmark) {
+IN_PROC_BROWSER_TEST_P(SingleClientBookmarksSyncTest, DownloadDeletedBookmark) {
   std::string title = "Patrick Star";
   fake_server::EntityBuilderFactory entity_builder_factory;
   fake_server::BookmarkEntityBuilder bookmark_builder =
@@ -445,7 +448,7 @@
                   .Wait());
 }
 
-IN_PROC_BROWSER_TEST_F(SingleClientBookmarksSyncTest,
+IN_PROC_BROWSER_TEST_P(SingleClientBookmarksSyncTest,
                        DownloadModifiedBookmark) {
   std::string title = "Syrup";
   GURL original_url = GURL("https://en.wikipedia.org/?title=Maple_syrup");
@@ -481,7 +484,7 @@
   ASSERT_EQ(1u, CountBookmarksWithTitlesMatching(kSingleProfileIndex, title));
 }
 
-IN_PROC_BROWSER_TEST_F(SingleClientBookmarksSyncTest, DownloadBookmarkFolder) {
+IN_PROC_BROWSER_TEST_P(SingleClientBookmarksSyncTest, DownloadBookmarkFolder) {
   const std::string title = "Seattle Sounders FC";
   fake_server::EntityBuilderFactory entity_builder_factory;
   fake_server::BookmarkEntityBuilder bookmark_builder =
@@ -497,7 +500,7 @@
   ASSERT_EQ(1u, CountFoldersWithTitlesMatching(kSingleProfileIndex, title));
 }
 
-IN_PROC_BROWSER_TEST_F(SingleClientBookmarksSyncTest,
+IN_PROC_BROWSER_TEST_P(SingleClientBookmarksSyncTest,
                        DownloadLegacyBookmarkFolder) {
   const std::string title = "Seattle Sounders FC";
   fake_server::EntityBuilderFactory entity_builder_factory;
@@ -518,7 +521,7 @@
 // before committing them because historically they were illegal server titles.
 // This test makes sure that this functionality is implemented for backward
 // compatibility with legacy clients.
-IN_PROC_BROWSER_TEST_F(SingleClientBookmarksSyncTest,
+IN_PROC_BROWSER_TEST_P(SingleClientBookmarksSyncTest,
                        ShouldCommitBookmarksWithIllegalServerNames) {
   ASSERT_TRUE(SetupClients()) << "SetupClients() failed.";
   ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
@@ -553,7 +556,7 @@
 // commit because historically they were considered illegal server titles. This
 // test makes sure that this functionality is implemented for backward
 // compatibility with legacy clients.
-IN_PROC_BROWSER_TEST_F(SingleClientBookmarksSyncTest,
+IN_PROC_BROWSER_TEST_P(SingleClientBookmarksSyncTest,
                        ShouldCreateLocalBookmarksWithIllegalServerNames) {
   const std::vector<std::string> illegal_titles = {"", ".", ".."};
 
@@ -581,7 +584,7 @@
 
 // Legacy bookmark clients append a blank space to empty titles. This tests that
 // this is respected when merging local and remote hierarchies.
-IN_PROC_BROWSER_TEST_F(SingleClientBookmarksSyncTest,
+IN_PROC_BROWSER_TEST_P(SingleClientBookmarksSyncTest,
                        ShouldTruncateBlanksWhenMatchingTitles) {
   const std::string remote_blank_title = " ";
   const std::string local_empty_title = "";
@@ -616,7 +619,7 @@
 
 // Legacy bookmark clients truncate long titles up to 255 bytes. This tests that
 // this is respected when merging local and remote hierarchies.
-IN_PROC_BROWSER_TEST_F(SingleClientBookmarksSyncTest,
+IN_PROC_BROWSER_TEST_P(SingleClientBookmarksSyncTest,
                        ShouldTruncateLongTitles) {
   const std::string remote_truncated_title =
       "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrst"
@@ -656,7 +659,7 @@
                                                    remote_truncated_title));
 }
 
-IN_PROC_BROWSER_TEST_F(SingleClientBookmarksSyncTest,
+IN_PROC_BROWSER_TEST_P(SingleClientBookmarksSyncTest,
                        DownloadBookmarkFoldersWithPositions) {
   const std::string title0 = "Folder left";
   const std::string title1 = "Folder middle";
@@ -696,11 +699,11 @@
   EXPECT_EQ(base::ASCIIToUTF16(title2), bar->children()[2]->GetTitle());
 }
 
-IN_PROC_BROWSER_TEST_F(SingleClientBookmarksSyncTest, E2E_ONLY(SanitySetup)) {
+IN_PROC_BROWSER_TEST_P(SingleClientBookmarksSyncTest, E2E_ONLY(SanitySetup)) {
   ASSERT_TRUE(SetupSync()) <<  "SetupSync() failed.";
 }
 
-IN_PROC_BROWSER_TEST_F(
+IN_PROC_BROWSER_TEST_P(
     SingleClientBookmarksSyncTest,
     RemoveRightAfterAddShouldNotSendCommitRequestsOrTombstones) {
   base::HistogramTester histogram_tester;
@@ -731,7 +734,7 @@
                                          /*LOCAL_DELETION=*/0));
 }
 
-IN_PROC_BROWSER_TEST_F(SingleClientBookmarksSyncTest,
+IN_PROC_BROWSER_TEST_P(SingleClientBookmarksSyncTest,
                        PRE_PersistProgressMarkerOnRestart) {
   const std::string title = "Seattle Sounders FC";
   fake_server::EntityBuilderFactory entity_builder_factory;
@@ -750,7 +753,7 @@
                                          /*REMOTE_INITIAL_UPDATE=*/5));
 }
 
-IN_PROC_BROWSER_TEST_F(SingleClientBookmarksSyncTest,
+IN_PROC_BROWSER_TEST_P(SingleClientBookmarksSyncTest,
                        PersistProgressMarkerOnRestart) {
   const std::string title = "Seattle Sounders FC";
   fake_server::EntityBuilderFactory entity_builder_factory;
@@ -782,7 +785,7 @@
                                          /*REMOTE_INITIAL_UPDATE=*/5));
 }
 
-IN_PROC_BROWSER_TEST_F(SingleClientBookmarksSyncTest,
+IN_PROC_BROWSER_TEST_P(SingleClientBookmarksSyncTest,
                        ApplyRemoteCreationWithValidGUID) {
   // Start syncing.
   DisableVerifier();
@@ -810,7 +813,7 @@
       GetBookmarkBarNode(kSingleProfileIndex)->children()[0].get()->guid());
 }
 
-IN_PROC_BROWSER_TEST_F(SingleClientBookmarksSyncTest,
+IN_PROC_BROWSER_TEST_P(SingleClientBookmarksSyncTest,
                        ApplyRemoteCreationWithoutValidGUID) {
   // Start syncing.
   DisableVerifier();
@@ -844,7 +847,7 @@
       GetBookmarkBarNode(kSingleProfileIndex)->children()[0].get()->guid());
 }
 
-IN_PROC_BROWSER_TEST_F(SingleClientBookmarksSyncTest,
+IN_PROC_BROWSER_TEST_P(SingleClientBookmarksSyncTest,
                        ApplyRemoteCreationWithoutValidGUIDOrOCII) {
   // Start syncing.
   DisableVerifier();
@@ -875,7 +878,7 @@
                                             originator_client_item_id));
 }
 
-IN_PROC_BROWSER_TEST_F(SingleClientBookmarksSyncTest,
+IN_PROC_BROWSER_TEST_P(SingleClientBookmarksSyncTest,
                        MergeRemoteCreationWithValidGUID) {
   const GURL url = GURL("http://www.foo.com");
   fake_server::EntityBuilderFactory entity_builder_factory;
@@ -901,7 +904,7 @@
   EXPECT_TRUE(ContainsBookmarkNodeWithGUID(kSingleProfileIndex, guid));
 }
 
-IN_PROC_BROWSER_TEST_F(SingleClientBookmarksSyncTest,
+IN_PROC_BROWSER_TEST_P(SingleClientBookmarksSyncTest,
                        MergeRemoteCreationWithoutValidGUID) {
   const GURL url = GURL("http://www.foo.com");
   const std::string originator_client_item_id = base::GenerateGUID();
@@ -931,7 +934,7 @@
                                            originator_client_item_id));
 }
 
-IN_PROC_BROWSER_TEST_F(SingleClientBookmarksSyncTest,
+IN_PROC_BROWSER_TEST_P(SingleClientBookmarksSyncTest,
                        MergeRemoteCreationWithoutValidGUIDOrOCII) {
   const GURL url = GURL("http://www.foo.com");
   const std::string originator_client_item_id = "INVALID OCII";
@@ -962,7 +965,7 @@
                                             originator_client_item_id));
 }
 
-IN_PROC_BROWSER_TEST_F(SingleClientBookmarksSyncTest,
+IN_PROC_BROWSER_TEST_P(SingleClientBookmarksSyncTest,
                        ApplyRemoteUpdateWithValidGUID) {
   // Create a bookmark.
   const GURL url = GURL("https://foo.com");
@@ -1008,7 +1011,7 @@
       GetBookmarkBarNode(kSingleProfileIndex)->children()[0].get()->guid());
 }
 
-IN_PROC_BROWSER_TEST_F(SingleClientBookmarksSyncTest,
+IN_PROC_BROWSER_TEST_P(SingleClientBookmarksSyncTest,
                        MergeRemoteUpdateWithValidGUID) {
   DisableVerifier();
   ASSERT_TRUE(SetupClients());
@@ -1041,4 +1044,7 @@
   EXPECT_EQ(1u, GetBookmarkBarNode(kSingleProfileIndex)->children().size());
 }
 
+INSTANTIATE_TEST_SUITE_P(,
+                         SingleClientBookmarksSyncTest,
+                         ::testing::Values(false, true));
 }  // namespace
diff --git a/chrome/browser/sync/test/integration/two_client_bookmarks_sync_test.cc b/chrome/browser/sync/test/integration/two_client_bookmarks_sync_test.cc
index 8f05f481..f44ffc9 100644
--- a/chrome/browser/sync/test/integration/two_client_bookmarks_sync_test.cc
+++ b/chrome/browser/sync/test/integration/two_client_bookmarks_sync_test.cc
@@ -16,6 +16,7 @@
 #include "chrome/browser/policy/profile_policy_connector_builder.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/sync/test/integration/bookmarks_helper.h"
+#include "chrome/browser/sync/test/integration/feature_toggler.h"
 #include "chrome/browser/sync/test/integration/profile_sync_service_harness.h"
 #include "chrome/browser/sync/test/integration/sync_integration_test_util.h"
 #include "chrome/browser/sync/test/integration/sync_test.h"
@@ -81,9 +82,11 @@
 const char kGenericSubfolderName[] = "Subfolder Name";
 const char kValidPassphrase[] = "passphrase!";
 
-class TwoClientBookmarksSyncTest : public SyncTest {
+class TwoClientBookmarksSyncTest : public FeatureToggler, public SyncTest {
  public:
-  TwoClientBookmarksSyncTest() : SyncTest(TWO_CLIENT) {}
+  TwoClientBookmarksSyncTest()
+      : FeatureToggler(switches::kProfileSyncServiceUsesThreadPool),
+        SyncTest(TWO_CLIENT) {}
   ~TwoClientBookmarksSyncTest() override {}
 
   void TearDownInProcessBrowserTestFixture() override {
@@ -99,7 +102,7 @@
   DISALLOW_COPY_AND_ASSIGN(TwoClientBookmarksSyncTest);
 };
 
-IN_PROC_BROWSER_TEST_F(TwoClientBookmarksSyncTest, Sanity) {
+IN_PROC_BROWSER_TEST_P(TwoClientBookmarksSyncTest, Sanity) {
   ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
   ASSERT_TRUE(AllModelsMatchVerifier());
 
@@ -129,7 +132,7 @@
   ASSERT_TRUE(BookmarksMatchChecker().Wait());
 }
 
-IN_PROC_BROWSER_TEST_F(TwoClientBookmarksSyncTest, SimultaneousURLChanges) {
+IN_PROC_BROWSER_TEST_P(TwoClientBookmarksSyncTest, SimultaneousURLChanges) {
   ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
   ASSERT_TRUE(AllModelsMatchVerifier());
 
@@ -150,7 +153,7 @@
   ASSERT_TRUE(BookmarksMatchChecker().Wait());
 }
 
-IN_PROC_BROWSER_TEST_F(TwoClientBookmarksSyncTest, SC_AddFirstFolder) {
+IN_PROC_BROWSER_TEST_P(TwoClientBookmarksSyncTest, SC_AddFirstFolder) {
   ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
   ASSERT_TRUE(AllModelsMatchVerifier());
 
@@ -158,7 +161,7 @@
   ASSERT_TRUE(BookmarksMatchVerifierChecker().Wait());
 }
 
-IN_PROC_BROWSER_TEST_F(TwoClientBookmarksSyncTest,
+IN_PROC_BROWSER_TEST_P(TwoClientBookmarksSyncTest,
                        SC_Add3FoldersInShuffledOrder) {
   ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
   ASSERT_TRUE(AllModelsMatchVerifier());
@@ -169,7 +172,7 @@
   ASSERT_TRUE(BookmarksMatchVerifierChecker().Wait());
 }
 
-IN_PROC_BROWSER_TEST_F(TwoClientBookmarksSyncTest,
+IN_PROC_BROWSER_TEST_P(TwoClientBookmarksSyncTest,
                        SC_AddFirstBMWithoutFavicon) {
   ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
   ASSERT_TRUE(AllModelsMatchVerifier());
@@ -178,7 +181,7 @@
   ASSERT_TRUE(BookmarksMatchVerifierChecker().Wait());
 }
 
-IN_PROC_BROWSER_TEST_F(TwoClientBookmarksSyncTest, SC_AddFirstBMWithFavicon) {
+IN_PROC_BROWSER_TEST_P(TwoClientBookmarksSyncTest, SC_AddFirstBMWithFavicon) {
   ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
   ASSERT_TRUE(AllModelsMatchVerifier());
   const GURL page_url(kGenericURL);
@@ -197,7 +200,7 @@
 // In particular, the synced 16x16 favicon bitmap should overwrite 16x16
 // favicon bitmaps on all clients. (Though non-16x16 favicon bitmaps
 // are unchanged).
-IN_PROC_BROWSER_TEST_F(TwoClientBookmarksSyncTest, SC_SetFaviconHiDPI) {
+IN_PROC_BROWSER_TEST_P(TwoClientBookmarksSyncTest, SC_SetFaviconHiDPI) {
   // Set the supported scale factors to include 2x such that CreateFavicon()
   // creates a favicon with hidpi representations and that methods in the
   // FaviconService request hidpi favicons.
@@ -236,7 +239,7 @@
 // favicon should be redownloaded when the web when the bookmark is visited.
 // If sync prevents the "last updated time" from expiring, the favicon is
 // never redownloaded from the web. (http://crbug.com/481414)
-IN_PROC_BROWSER_TEST_F(TwoClientBookmarksSyncTest,
+IN_PROC_BROWSER_TEST_P(TwoClientBookmarksSyncTest,
                        SC_UpdatingTitleDoesNotUpdateFaviconLastUpdatedTime) {
   ASSERT_TRUE(SetupClients()) << "SetupClients() failed.";
 
@@ -282,7 +285,7 @@
 // ensures that sync has the most up to date data and prevents sync from
 // reverting the newly updated bookmark favicon back to the old favicon.
 // crbug.com/485657
-IN_PROC_BROWSER_TEST_F(TwoClientBookmarksSyncTest,
+IN_PROC_BROWSER_TEST_P(TwoClientBookmarksSyncTest,
                        SC_SetFaviconTwoBookmarksSameIconURL) {
   const GURL page_url1("http://www.google.com/a");
   const GURL page_url2("http://www.google.com/b");
@@ -315,7 +318,7 @@
   ASSERT_TRUE(BookmarksMatchVerifierChecker().Wait());
 }
 
-IN_PROC_BROWSER_TEST_F(TwoClientBookmarksSyncTest, SC_DeleteFavicon) {
+IN_PROC_BROWSER_TEST_P(TwoClientBookmarksSyncTest, SC_DeleteFavicon) {
   const GURL page_url("http://www.google.com/a");
   const GURL icon_url("http://www.google.com/favicon.ico");
 
@@ -344,7 +347,7 @@
   CheckHasNoFavicon(0, page_url);
 }
 
-IN_PROC_BROWSER_TEST_F(TwoClientBookmarksSyncTest, SC_AddNonHTTPBMs) {
+IN_PROC_BROWSER_TEST_P(TwoClientBookmarksSyncTest, SC_AddNonHTTPBMs) {
   ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
   ASSERT_TRUE(AllModelsMatchVerifier());
 
@@ -354,7 +357,7 @@
   ASSERT_TRUE(BookmarksMatchVerifierChecker().Wait());
 }
 
-IN_PROC_BROWSER_TEST_F(TwoClientBookmarksSyncTest, SC_AddFirstBMUnderFolder) {
+IN_PROC_BROWSER_TEST_P(TwoClientBookmarksSyncTest, SC_AddFirstBMUnderFolder) {
   ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
   ASSERT_TRUE(AllModelsMatchVerifier());
 
@@ -364,7 +367,7 @@
   ASSERT_TRUE(BookmarksMatchVerifierChecker().Wait());
 }
 
-IN_PROC_BROWSER_TEST_F(TwoClientBookmarksSyncTest,
+IN_PROC_BROWSER_TEST_P(TwoClientBookmarksSyncTest,
                        SC_AddSeveralBMsUnderBMBarAndOtherBM) {
   ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
   ASSERT_TRUE(AllModelsMatchVerifier());
@@ -378,7 +381,7 @@
   ASSERT_TRUE(BookmarksMatchVerifierChecker().Wait());
 }
 
-IN_PROC_BROWSER_TEST_F(TwoClientBookmarksSyncTest, SC_AddSeveralBMsAndFolders) {
+IN_PROC_BROWSER_TEST_P(TwoClientBookmarksSyncTest, SC_AddSeveralBMsAndFolders) {
   ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
   ASSERT_TRUE(AllModelsMatchVerifier());
 
@@ -408,7 +411,7 @@
   ASSERT_TRUE(BookmarksMatchVerifierChecker().Wait());
 }
 
-IN_PROC_BROWSER_TEST_F(TwoClientBookmarksSyncTest,
+IN_PROC_BROWSER_TEST_P(TwoClientBookmarksSyncTest,
                        SC_DuplicateBMWithDifferentURLSameName) {
   ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
   ASSERT_TRUE(AllModelsMatchVerifier());
@@ -421,7 +424,7 @@
 }
 
 // Add bookmarks with different name and same URL.
-IN_PROC_BROWSER_TEST_F(TwoClientBookmarksSyncTest,
+IN_PROC_BROWSER_TEST_P(TwoClientBookmarksSyncTest,
                        SC_DuplicateBookmarksWithSameURL) {
   ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
   ASSERT_TRUE(AllModelsMatchVerifier());
@@ -433,7 +436,7 @@
   ASSERT_TRUE(BookmarksMatchVerifierChecker().Wait());
 }
 
-IN_PROC_BROWSER_TEST_F(TwoClientBookmarksSyncTest, SC_RenameBMName) {
+IN_PROC_BROWSER_TEST_P(TwoClientBookmarksSyncTest, SC_RenameBMName) {
   ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
   ASSERT_TRUE(AllModelsMatchVerifier());
 
@@ -447,7 +450,7 @@
   ASSERT_TRUE(BookmarksMatchVerifierChecker().Wait());
 }
 
-IN_PROC_BROWSER_TEST_F(TwoClientBookmarksSyncTest, SC_RenameBMURL) {
+IN_PROC_BROWSER_TEST_P(TwoClientBookmarksSyncTest, SC_RenameBMURL) {
   ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
   ASSERT_TRUE(AllModelsMatchVerifier());
 
@@ -462,7 +465,7 @@
 }
 
 // Renaming the same bookmark name twice.
-IN_PROC_BROWSER_TEST_F(TwoClientBookmarksSyncTest,
+IN_PROC_BROWSER_TEST_P(TwoClientBookmarksSyncTest,
                        SC_TwiceRenamingBookmarkName) {
   ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
   ASSERT_TRUE(AllModelsMatchVerifier());
@@ -481,7 +484,7 @@
 }
 
 // Renaming the same bookmark URL twice.
-IN_PROC_BROWSER_TEST_F(TwoClientBookmarksSyncTest,
+IN_PROC_BROWSER_TEST_P(TwoClientBookmarksSyncTest,
                        SC_TwiceRenamingBookmarkURL) {
   ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
   ASSERT_TRUE(AllModelsMatchVerifier());
@@ -499,7 +502,7 @@
   ASSERT_TRUE(BookmarksMatchVerifierChecker().Wait());
 }
 
-IN_PROC_BROWSER_TEST_F(TwoClientBookmarksSyncTest, SC_RenameBMFolder) {
+IN_PROC_BROWSER_TEST_P(TwoClientBookmarksSyncTest, SC_RenameBMFolder) {
   ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
   ASSERT_TRUE(AllModelsMatchVerifier());
 
@@ -513,7 +516,7 @@
   ASSERT_TRUE(BookmarksMatchVerifierChecker().Wait());
 }
 
-IN_PROC_BROWSER_TEST_F(TwoClientBookmarksSyncTest, SC_RenameEmptyBMFolder) {
+IN_PROC_BROWSER_TEST_P(TwoClientBookmarksSyncTest, SC_RenameEmptyBMFolder) {
   ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
   ASSERT_TRUE(AllModelsMatchVerifier());
 
@@ -526,7 +529,7 @@
   ASSERT_TRUE(BookmarksMatchVerifierChecker().Wait());
 }
 
-IN_PROC_BROWSER_TEST_F(TwoClientBookmarksSyncTest,
+IN_PROC_BROWSER_TEST_P(TwoClientBookmarksSyncTest,
                        SC_RenameBMFolderWithLongHierarchy) {
   ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
   ASSERT_TRUE(AllModelsMatchVerifier());
@@ -551,7 +554,7 @@
   ASSERT_TRUE(BookmarksMatchVerifierChecker().Wait());
 }
 
-IN_PROC_BROWSER_TEST_F(TwoClientBookmarksSyncTest,
+IN_PROC_BROWSER_TEST_P(TwoClientBookmarksSyncTest,
                        SC_RenameBMFolderThatHasParentAndChildren) {
   ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
   ASSERT_TRUE(AllModelsMatchVerifier());
@@ -582,7 +585,7 @@
   ASSERT_TRUE(BookmarksMatchVerifierChecker().Wait());
 }
 
-IN_PROC_BROWSER_TEST_F(TwoClientBookmarksSyncTest, SC_RenameBMNameAndURL) {
+IN_PROC_BROWSER_TEST_P(TwoClientBookmarksSyncTest, SC_RenameBMNameAndURL) {
   ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
   ASSERT_TRUE(AllModelsMatchVerifier());
 
@@ -600,7 +603,7 @@
   ASSERT_TRUE(BookmarksMatchVerifierChecker().Wait());
 }
 
-IN_PROC_BROWSER_TEST_F(TwoClientBookmarksSyncTest,
+IN_PROC_BROWSER_TEST_P(TwoClientBookmarksSyncTest,
                        SC_DeleteBMEmptyAccountAfterwards) {
   ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
   ASSERT_TRUE(AllModelsMatchVerifier());
@@ -612,7 +615,7 @@
   ASSERT_TRUE(BookmarksMatchVerifierChecker().Wait());
 }
 
-IN_PROC_BROWSER_TEST_F(TwoClientBookmarksSyncTest,
+IN_PROC_BROWSER_TEST_P(TwoClientBookmarksSyncTest,
                        SC_DelBMNonEmptyAccountAfterwards) {
   ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
   ASSERT_TRUE(AllModelsMatchVerifier());
@@ -628,7 +631,7 @@
   ASSERT_TRUE(BookmarksMatchVerifierChecker().Wait());
 }
 
-IN_PROC_BROWSER_TEST_F(TwoClientBookmarksSyncTest,
+IN_PROC_BROWSER_TEST_P(TwoClientBookmarksSyncTest,
                        SC_DelFirstBMUnderBMFoldNonEmptyFoldAfterwards) {
   ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
   ASSERT_TRUE(AllModelsMatchVerifier());
@@ -646,7 +649,7 @@
   ASSERT_TRUE(BookmarksMatchVerifierChecker().Wait());
 }
 
-IN_PROC_BROWSER_TEST_F(TwoClientBookmarksSyncTest,
+IN_PROC_BROWSER_TEST_P(TwoClientBookmarksSyncTest,
                        SC_DelLastBMUnderBMFoldNonEmptyFoldAfterwards) {
   ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
   ASSERT_TRUE(AllModelsMatchVerifier());
@@ -664,7 +667,7 @@
   ASSERT_TRUE(BookmarksMatchVerifierChecker().Wait());
 }
 
-IN_PROC_BROWSER_TEST_F(TwoClientBookmarksSyncTest,
+IN_PROC_BROWSER_TEST_P(TwoClientBookmarksSyncTest,
                        SC_DelMiddleBMUnderBMFoldNonEmptyFoldAfterwards) {
   ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
   ASSERT_TRUE(AllModelsMatchVerifier());
@@ -682,7 +685,7 @@
   ASSERT_TRUE(BookmarksMatchVerifierChecker().Wait());
 }
 
-IN_PROC_BROWSER_TEST_F(TwoClientBookmarksSyncTest,
+IN_PROC_BROWSER_TEST_P(TwoClientBookmarksSyncTest,
                        SC_DelBMsUnderBMFoldEmptyFolderAfterwards) {
   ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
   ASSERT_TRUE(AllModelsMatchVerifier());
@@ -701,7 +704,7 @@
   ASSERT_TRUE(BookmarksMatchVerifierChecker().Wait());
 }
 
-IN_PROC_BROWSER_TEST_F(TwoClientBookmarksSyncTest,
+IN_PROC_BROWSER_TEST_P(TwoClientBookmarksSyncTest,
                        SC_DelEmptyBMFoldEmptyAccountAfterwards) {
   ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
   ASSERT_TRUE(AllModelsMatchVerifier());
@@ -713,7 +716,7 @@
   ASSERT_TRUE(BookmarksMatchVerifierChecker().Wait());
 }
 
-IN_PROC_BROWSER_TEST_F(TwoClientBookmarksSyncTest,
+IN_PROC_BROWSER_TEST_P(TwoClientBookmarksSyncTest,
                        SC_DelEmptyBMFoldNonEmptyAccountAfterwards) {
   ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
   ASSERT_TRUE(AllModelsMatchVerifier());
@@ -735,7 +738,7 @@
   ASSERT_TRUE(BookmarksMatchVerifierChecker().Wait());
 }
 
-IN_PROC_BROWSER_TEST_F(TwoClientBookmarksSyncTest,
+IN_PROC_BROWSER_TEST_P(TwoClientBookmarksSyncTest,
                        SC_DelBMFoldWithBMsNonEmptyAccountAfterwards) {
   ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
   ASSERT_TRUE(AllModelsMatchVerifier());
@@ -764,7 +767,7 @@
   ASSERT_TRUE(BookmarksMatchVerifierChecker().Wait());
 }
 
-IN_PROC_BROWSER_TEST_F(TwoClientBookmarksSyncTest,
+IN_PROC_BROWSER_TEST_P(TwoClientBookmarksSyncTest,
                        SC_DelBMFoldWithBMsAndBMFoldsNonEmptyACAfterwards) {
   ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
   ASSERT_TRUE(AllModelsMatchVerifier());
@@ -812,7 +815,7 @@
   ASSERT_TRUE(BookmarksMatchVerifierChecker().Wait());
 }
 
-IN_PROC_BROWSER_TEST_F(TwoClientBookmarksSyncTest,
+IN_PROC_BROWSER_TEST_P(TwoClientBookmarksSyncTest,
                        SC_DelBMFoldWithParentAndChildrenBMsAndBMFolds) {
   ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
   ASSERT_TRUE(AllModelsMatchVerifier());
@@ -843,7 +846,7 @@
   ASSERT_TRUE(BookmarksMatchVerifierChecker().Wait());
 }
 
-IN_PROC_BROWSER_TEST_F(TwoClientBookmarksSyncTest, SC_ReverseTheOrderOfTwoBMs) {
+IN_PROC_BROWSER_TEST_P(TwoClientBookmarksSyncTest, SC_ReverseTheOrderOfTwoBMs) {
   ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
   ASSERT_TRUE(AllModelsMatchVerifier());
 
@@ -861,7 +864,7 @@
   ASSERT_TRUE(BookmarksMatchVerifierChecker().Wait());
 }
 
-IN_PROC_BROWSER_TEST_F(TwoClientBookmarksSyncTest, SC_ReverseTheOrderOf10BMs) {
+IN_PROC_BROWSER_TEST_P(TwoClientBookmarksSyncTest, SC_ReverseTheOrderOf10BMs) {
   ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
   ASSERT_TRUE(AllModelsMatchVerifier());
 
@@ -876,7 +879,7 @@
   ASSERT_TRUE(BookmarksMatchVerifierChecker().Wait());
 }
 
-IN_PROC_BROWSER_TEST_F(TwoClientBookmarksSyncTest,
+IN_PROC_BROWSER_TEST_P(TwoClientBookmarksSyncTest,
                        SC_MovingBMsFromBMBarToBMFolder) {
   ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
   ASSERT_TRUE(AllModelsMatchVerifier());
@@ -899,7 +902,7 @@
   }
 }
 
-IN_PROC_BROWSER_TEST_F(TwoClientBookmarksSyncTest,
+IN_PROC_BROWSER_TEST_P(TwoClientBookmarksSyncTest,
                        SC_MovingBMsFromBMFoldToBMBar) {
   ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
   ASSERT_TRUE(AllModelsMatchVerifier());
@@ -922,7 +925,7 @@
   }
 }
 
-IN_PROC_BROWSER_TEST_F(TwoClientBookmarksSyncTest,
+IN_PROC_BROWSER_TEST_P(TwoClientBookmarksSyncTest,
                        SC_MovingBMsFromParentBMFoldToChildBMFold) {
   ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
   ASSERT_TRUE(AllModelsMatchVerifier());
@@ -951,7 +954,7 @@
   ASSERT_TRUE(BookmarksMatchVerifierChecker().Wait());
 }
 
-IN_PROC_BROWSER_TEST_F(TwoClientBookmarksSyncTest,
+IN_PROC_BROWSER_TEST_P(TwoClientBookmarksSyncTest,
                        SC_MovingBMsFromChildBMFoldToParentBMFold) {
   ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
   ASSERT_TRUE(AllModelsMatchVerifier());
@@ -980,7 +983,7 @@
   ASSERT_TRUE(BookmarksMatchVerifierChecker().Wait());
 }
 
-IN_PROC_BROWSER_TEST_F(TwoClientBookmarksSyncTest, SC_HoistBMs10LevelUp) {
+IN_PROC_BROWSER_TEST_P(TwoClientBookmarksSyncTest, SC_HoistBMs10LevelUp) {
   ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
   ASSERT_TRUE(AllModelsMatchVerifier());
 
@@ -1018,7 +1021,7 @@
 }
 
 // Flaky. http://crbug.com/107744.
-IN_PROC_BROWSER_TEST_F(TwoClientBookmarksSyncTest, SC_SinkBMs10LevelDown) {
+IN_PROC_BROWSER_TEST_P(TwoClientBookmarksSyncTest, SC_SinkBMs10LevelDown) {
   ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
   ASSERT_TRUE(AllModelsMatchVerifier());
 
@@ -1055,7 +1058,7 @@
   ASSERT_TRUE(BookmarksMatchVerifierChecker().Wait());
 }
 
-IN_PROC_BROWSER_TEST_F(TwoClientBookmarksSyncTest,
+IN_PROC_BROWSER_TEST_P(TwoClientBookmarksSyncTest,
                        SC_SinkEmptyBMFold5LevelsDown) {
   ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
   ASSERT_TRUE(AllModelsMatchVerifier());
@@ -1083,7 +1086,7 @@
   ASSERT_TRUE(BookmarksMatchVerifierChecker().Wait());
 }
 
-IN_PROC_BROWSER_TEST_F(TwoClientBookmarksSyncTest,
+IN_PROC_BROWSER_TEST_P(TwoClientBookmarksSyncTest,
                        SC_SinkNonEmptyBMFold5LevelsDown) {
   ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
   ASSERT_TRUE(AllModelsMatchVerifier());
@@ -1116,7 +1119,7 @@
   ASSERT_TRUE(BookmarksMatchVerifierChecker().Wait());
 }
 
-IN_PROC_BROWSER_TEST_F(TwoClientBookmarksSyncTest, SC_HoistFolder5LevelsUp) {
+IN_PROC_BROWSER_TEST_P(TwoClientBookmarksSyncTest, SC_HoistFolder5LevelsUp) {
   ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
   ASSERT_TRUE(AllModelsMatchVerifier());
 
@@ -1149,7 +1152,7 @@
   ASSERT_TRUE(BookmarksMatchVerifierChecker().Wait());
 }
 
-IN_PROC_BROWSER_TEST_F(TwoClientBookmarksSyncTest,
+IN_PROC_BROWSER_TEST_P(TwoClientBookmarksSyncTest,
                        SC_ReverseTheOrderOfTwoBMFolders) {
   ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
   ASSERT_TRUE(AllModelsMatchVerifier());
@@ -1170,7 +1173,7 @@
   ASSERT_TRUE(BookmarksMatchVerifierChecker().Wait());
 }
 
-IN_PROC_BROWSER_TEST_F(TwoClientBookmarksSyncTest,
+IN_PROC_BROWSER_TEST_P(TwoClientBookmarksSyncTest,
                        SC_ReverseTheOrderOfTenBMFolders) {
   ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
   ASSERT_TRUE(AllModelsMatchVerifier());
@@ -1191,7 +1194,7 @@
   ASSERT_TRUE(BookmarksMatchVerifierChecker().Wait());
 }
 
-IN_PROC_BROWSER_TEST_F(TwoClientBookmarksSyncTest,
+IN_PROC_BROWSER_TEST_P(TwoClientBookmarksSyncTest,
                        MC_BiDirectionalPushAddingBM) {
   ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
   ASSERT_TRUE(AllModelsMatchVerifier());
@@ -1209,7 +1212,7 @@
   ASSERT_FALSE(ContainsDuplicateBookmarks(0));
 }
 
-IN_PROC_BROWSER_TEST_F(TwoClientBookmarksSyncTest,
+IN_PROC_BROWSER_TEST_P(TwoClientBookmarksSyncTest,
                        MC_BiDirectionalPush_AddingSameBMs) {
   ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
   ASSERT_TRUE(AllModelsMatchVerifier());
@@ -1226,14 +1229,14 @@
   ASSERT_TRUE(BookmarksMatchChecker().Wait());
 }
 
-IN_PROC_BROWSER_TEST_F(TwoClientBookmarksSyncTest,
+IN_PROC_BROWSER_TEST_P(TwoClientBookmarksSyncTest,
                        MC_BootStrapEmptyStateEverywhere) {
   ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
   ASSERT_TRUE(AwaitQuiescence());
   ASSERT_TRUE(AllModelsMatchVerifier());
 }
 
-IN_PROC_BROWSER_TEST_F(TwoClientBookmarksSyncTest,
+IN_PROC_BROWSER_TEST_P(TwoClientBookmarksSyncTest,
                        MC_Merge_CaseInsensitivity_InNames) {
   ASSERT_TRUE(SetupClients()) << "SetupClients() failed.";
   DisableVerifier();
@@ -1256,7 +1259,7 @@
   ASSERT_FALSE(ContainsDuplicateBookmarks(0));
 }
 
-IN_PROC_BROWSER_TEST_F(TwoClientBookmarksSyncTest,
+IN_PROC_BROWSER_TEST_P(TwoClientBookmarksSyncTest,
                        MC_SimpleMergeOfDifferentBMModels) {
   ASSERT_TRUE(SetupClients()) << "SetupClients() failed.";
   DisableVerifier();
@@ -1283,7 +1286,7 @@
   ASSERT_FALSE(ContainsDuplicateBookmarks(0));
 }
 
-IN_PROC_BROWSER_TEST_F(TwoClientBookmarksSyncTest,
+IN_PROC_BROWSER_TEST_P(TwoClientBookmarksSyncTest,
                        MC_MergeSimpleBMHierarchyUnderBMBar) {
   ASSERT_TRUE(SetupClients()) << "SetupClients() failed.";
   DisableVerifier();
@@ -1307,7 +1310,7 @@
   ASSERT_FALSE(ContainsDuplicateBookmarks(0));
 }
 
-IN_PROC_BROWSER_TEST_F(TwoClientBookmarksSyncTest,
+IN_PROC_BROWSER_TEST_P(TwoClientBookmarksSyncTest,
                        MC_MergeSimpleBMHierarchyEqualSetsUnderBMBar) {
   ASSERT_TRUE(SetupClients()) << "SetupClients() failed.";
   DisableVerifier();
@@ -1327,7 +1330,7 @@
 }
 
 // Merge bookmark folders with different bookmarks.
-IN_PROC_BROWSER_TEST_F(TwoClientBookmarksSyncTest,
+IN_PROC_BROWSER_TEST_P(TwoClientBookmarksSyncTest,
                        MC_MergeBMFoldersWithDifferentBMs) {
   ASSERT_TRUE(SetupClients()) << "SetupClients() failed.";
   DisableVerifier();
@@ -1351,7 +1354,7 @@
 }
 
 // Merge moderately complex bookmark models.
-IN_PROC_BROWSER_TEST_F(TwoClientBookmarksSyncTest,
+IN_PROC_BROWSER_TEST_P(TwoClientBookmarksSyncTest,
                        MC_MergeDifferentBMModelsModeratelyComplex) {
   ASSERT_TRUE(SetupClients()) << "SetupClients() failed.";
   DisableVerifier();
@@ -1394,7 +1397,7 @@
 }
 
 // Merge simple bookmark subset under bookmark folder.
-IN_PROC_BROWSER_TEST_F(TwoClientBookmarksSyncTest,
+IN_PROC_BROWSER_TEST_P(TwoClientBookmarksSyncTest,
                        MC_MergeSimpleBMHierarchySubsetUnderBMFolder) {
   ASSERT_TRUE(SetupClients()) << "SetupClients() failed.";
   DisableVerifier();
@@ -1420,7 +1423,7 @@
 }
 
 // Merge subsets of bookmark under bookmark bar.
-IN_PROC_BROWSER_TEST_F(TwoClientBookmarksSyncTest,
+IN_PROC_BROWSER_TEST_P(TwoClientBookmarksSyncTest,
                        MC_MergeSimpleBMHierarchySubsetUnderBookmarkBar) {
   ASSERT_TRUE(SetupClients()) << "SetupClients() failed.";
   DisableVerifier();
@@ -1445,7 +1448,7 @@
 }
 
 // Merge simple bookmark hierarchy under bookmark folder.
-IN_PROC_BROWSER_TEST_F(TwoClientBookmarksSyncTest,
+IN_PROC_BROWSER_TEST_P(TwoClientBookmarksSyncTest,
                        MC_Merge_SimpleBMHierarchy_Under_BMFolder) {
   ASSERT_TRUE(SetupClients()) << "SetupClients() failed.";
   DisableVerifier();
@@ -1477,7 +1480,7 @@
 
 // Merge disjoint sets of bookmark hierarchy under bookmark
 // folder.
-IN_PROC_BROWSER_TEST_F(TwoClientBookmarksSyncTest,
+IN_PROC_BROWSER_TEST_P(TwoClientBookmarksSyncTest,
                        MC_Merge_SimpleBMHierarchy_DisjointSets_Under_BMFolder) {
   ASSERT_TRUE(SetupClients()) << "SetupClients() failed.";
   DisableVerifier();
@@ -1507,7 +1510,7 @@
 }
 
 // Merge disjoint sets of bookmark hierarchy under bookmark bar.
-IN_PROC_BROWSER_TEST_F(
+IN_PROC_BROWSER_TEST_P(
     TwoClientBookmarksSyncTest,
     MC_Merge_SimpleBMHierarchy_DisjointSets_Under_BookmarkBar) {
   ASSERT_TRUE(SetupClients()) << "SetupClients() failed.";
@@ -1531,7 +1534,7 @@
 }
 
 // Merge sets of duplicate bookmarks under bookmark bar.
-IN_PROC_BROWSER_TEST_F(TwoClientBookmarksSyncTest,
+IN_PROC_BROWSER_TEST_P(TwoClientBookmarksSyncTest,
                        MC_Merge_SimpleBMHierarchy_DuplicateBMs_Under_BMBar) {
   ASSERT_TRUE(SetupClients()) << "SetupClients() failed.";
   DisableVerifier();
@@ -1562,7 +1565,7 @@
   }
 }
 
-IN_PROC_BROWSER_TEST_F(TwoClientBookmarksSyncTest, DisableBookmarks) {
+IN_PROC_BROWSER_TEST_P(TwoClientBookmarksSyncTest, DisableBookmarks) {
   ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
   ASSERT_TRUE(AllModelsMatchVerifier());
 
@@ -1577,7 +1580,7 @@
   ASSERT_TRUE(BookmarksMatchChecker().Wait());
 }
 
-IN_PROC_BROWSER_TEST_F(TwoClientBookmarksSyncTest, DisableSync) {
+IN_PROC_BROWSER_TEST_P(TwoClientBookmarksSyncTest, DisableSync) {
   ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
   ASSERT_TRUE(AllModelsMatchVerifier());
 
@@ -1594,7 +1597,7 @@
 }
 
 // Test adding duplicate folder - Both with different BMs underneath.
-IN_PROC_BROWSER_TEST_F(TwoClientBookmarksSyncTest, MC_DuplicateFolders) {
+IN_PROC_BROWSER_TEST_P(TwoClientBookmarksSyncTest, MC_DuplicateFolders) {
   ASSERT_TRUE(SetupClients()) << "SetupClients() failed.";
   DisableVerifier();
 
@@ -1616,7 +1619,7 @@
   ASSERT_FALSE(ContainsDuplicateBookmarks(0));
 }
 
-IN_PROC_BROWSER_TEST_F(TwoClientBookmarksSyncTest, MC_DeleteBookmark) {
+IN_PROC_BROWSER_TEST_P(TwoClientBookmarksSyncTest, MC_DeleteBookmark) {
   ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
   ASSERT_TRUE(
       GetClient(1)->DisableSyncForType(syncer::UserSelectableType::kBookmarks));
@@ -1652,7 +1655,7 @@
 
 // Test a scenario of updating the name of the same bookmark from two clients at
 // the same time.
-IN_PROC_BROWSER_TEST_F(TwoClientBookmarksSyncTest,
+IN_PROC_BROWSER_TEST_P(TwoClientBookmarksSyncTest,
                        MC_BookmarkNameChangeConflict) {
   ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
 
@@ -1677,7 +1680,7 @@
 
 // Test a scenario of updating the URL of the same bookmark from two clients at
 // the same time.
-IN_PROC_BROWSER_TEST_F(TwoClientBookmarksSyncTest,
+IN_PROC_BROWSER_TEST_P(TwoClientBookmarksSyncTest,
                        MC_BookmarkURLChangeConflict) {
   ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
 
@@ -1704,7 +1707,7 @@
 
 // Test a scenario of updating the BM Folder name from two clients at the same
 // time.
-IN_PROC_BROWSER_TEST_F(TwoClientBookmarksSyncTest,
+IN_PROC_BROWSER_TEST_P(TwoClientBookmarksSyncTest,
                        MC_FolderNameChangeConflict) {
   ASSERT_TRUE(SetupClients()) << "SetupClients() failed.";
   DisableVerifier();
@@ -1779,7 +1782,7 @@
   ASSERT_FALSE(ContainsDuplicateBookmarks(0));
 }
 
-IN_PROC_BROWSER_TEST_F(TwoClientBookmarksSyncTest,
+IN_PROC_BROWSER_TEST_P(TwoClientBookmarksSyncTest,
                        FirstClientEnablesEncryptionWithPassSecondChanges) {
   ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
   ASSERT_TRUE(AllModelsMatchVerifier());
@@ -1821,7 +1824,7 @@
 // Deliberately racy rearranging of bookmarks to test that our conflict resolver
 // code results in a consistent view across machines (no matter what the final
 // order is).
-IN_PROC_BROWSER_TEST_F(TwoClientBookmarksSyncTest, RacyPositionChanges) {
+IN_PROC_BROWSER_TEST_P(TwoClientBookmarksSyncTest, RacyPositionChanges) {
   ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
   ASSERT_TRUE(AllModelsMatchVerifier());
 
@@ -1880,7 +1883,7 @@
 // Trigger the server side creation of Synced Bookmarks. Ensure both clients
 // remain syncing afterwards. Add bookmarks to the synced bookmarks folder
 // and ensure both clients receive the bookmark.
-IN_PROC_BROWSER_TEST_F(TwoClientBookmarksSyncTest, CreateSyncedBookmarks) {
+IN_PROC_BROWSER_TEST_P(TwoClientBookmarksSyncTest, CreateSyncedBookmarks) {
   ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
   ASSERT_TRUE(AllModelsMatchVerifier());
 
@@ -1903,7 +1906,7 @@
   ASSERT_TRUE(BookmarksMatchChecker().Wait());
 }
 
-IN_PROC_BROWSER_TEST_F(TwoClientBookmarksSyncTest,
+IN_PROC_BROWSER_TEST_P(TwoClientBookmarksSyncTest,
                        BookmarkAllNodesRemovedEvent) {
   ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
   ASSERT_TRUE(AllModelsMatchVerifier());
@@ -1957,7 +1960,7 @@
 }
 
 // Verifies that managed bookmarks (installed by policy) don't get synced.
-IN_PROC_BROWSER_TEST_F(TwoClientBookmarksSyncTest, ManagedBookmarks) {
+IN_PROC_BROWSER_TEST_P(TwoClientBookmarksSyncTest, ManagedBookmarks) {
   // Make sure the first Profile has an overridden policy provider.
   EXPECT_CALL(policy_provider_, IsInitializationComplete(testing::_))
       .WillRepeatedly(testing::Return(true));
@@ -2023,12 +2026,12 @@
   ASSERT_EQ(0u, managed_node1->children().size());
 }
 
-IN_PROC_BROWSER_TEST_F(TwoClientBookmarksSyncTest, E2E_ONLY(SanitySetup)) {
+IN_PROC_BROWSER_TEST_P(TwoClientBookmarksSyncTest, E2E_ONLY(SanitySetup)) {
   ResetSyncForPrimaryAccount();
   ASSERT_TRUE(SetupSync()) <<  "SetupSync() failed.";
 }
 
-IN_PROC_BROWSER_TEST_F(TwoClientBookmarksSyncTest,
+IN_PROC_BROWSER_TEST_P(TwoClientBookmarksSyncTest,
                        E2E_ONLY(OneClientAddsBookmark)) {
   ResetSyncForPrimaryAccount();
   ASSERT_TRUE(SetupSync()) <<  "SetupSync() failed.";
@@ -2054,7 +2057,7 @@
 }
 
 // TODO(shadi): crbug.com/569213: Enable this as E2E test.
-IN_PROC_BROWSER_TEST_F(TwoClientBookmarksSyncTest,
+IN_PROC_BROWSER_TEST_P(TwoClientBookmarksSyncTest,
                        OneClientAddsFolderAndBookmark) {
   ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
   // All profiles should sync same bookmarks.
@@ -2079,7 +2082,7 @@
   }
 }
 
-IN_PROC_BROWSER_TEST_F(TwoClientBookmarksSyncTest,
+IN_PROC_BROWSER_TEST_P(TwoClientBookmarksSyncTest,
                        E2E_ONLY(TwoClientsAddBookmarks)) {
   ResetSyncForPrimaryAccount();
   ASSERT_TRUE(SetupSync()) <<  "SetupSync() failed.";
@@ -2110,7 +2113,7 @@
 
 // Verify that a bookmark added on a client with bookmark syncing disabled gets
 // synced to a second client once bookmark syncing is re-enabled.
-IN_PROC_BROWSER_TEST_F(TwoClientBookmarksSyncTest,
+IN_PROC_BROWSER_TEST_P(TwoClientBookmarksSyncTest,
                        E2E_ENABLED(AddBookmarkWhileDisabled)) {
   ResetSyncForPrimaryAccount();
   ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
@@ -2142,4 +2145,8 @@
   ASSERT_EQ(initial_count + 2, CountAllBookmarks(1));
 }
 
+INSTANTIATE_TEST_SUITE_P(,
+                         TwoClientBookmarksSyncTest,
+                         ::testing::Values(false, true));
+
 }  // namespace
diff --git a/chrome/browser/ui/content_settings/content_setting_bubble_model_unittest.cc b/chrome/browser/ui/content_settings/content_setting_bubble_model_unittest.cc
index 2fe4982..79d0bc86 100644
--- a/chrome/browser/ui/content_settings/content_setting_bubble_model_unittest.cc
+++ b/chrome/browser/ui/content_settings/content_setting_bubble_model_unittest.cc
@@ -863,7 +863,7 @@
 
   void RegisterWithOSAsDefaultClient(
       const std::string& protocol,
-      ProtocolHandlerRegistry* registry) override {
+      shell_integration::DefaultWebClientWorkerCallback callback) override {
     VLOG(1) << "Register With OS";
   }
 };
diff --git a/chrome/browser/ui/tab_helpers.cc b/chrome/browser/ui/tab_helpers.cc
index f77e03b..f688acd4 100644
--- a/chrome/browser/ui/tab_helpers.cc
+++ b/chrome/browser/ui/tab_helpers.cc
@@ -14,7 +14,7 @@
 #include "build/build_config.h"
 #include "chrome/browser/bookmarks/bookmark_model_factory.h"
 #include "chrome/browser/browser_process.h"
-#include "chrome/browser/client_hints/client_hints_observer.h"
+#include "chrome/browser/client_hints/client_hints.h"
 #include "chrome/browser/complex_tasks/task_tab_helper.h"
 #include "chrome/browser/content_settings/mixed_content_settings_tab_helper.h"
 #include "chrome/browser/content_settings/sound_content_setting_observer.h"
@@ -210,7 +210,7 @@
     ChromeSubresourceFilterClient::CreateForWebContents(web_contents);
   }
   ChromeTranslateClient::CreateForWebContents(web_contents);
-  ClientHintsObserver::CreateForWebContents(web_contents);
+  client_hints::ClientHints::CreateForWebContents(web_contents);
   ConnectionHelpTabHelper::CreateForWebContents(web_contents);
   CoreTabHelper::CreateForWebContents(web_contents);
   DataReductionProxyTabHelper::CreateForWebContents(web_contents);
diff --git a/chrome/browser/ui/views/bookmarks/bookmark_bubble_view_unittest.cc b/chrome/browser/ui/views/bookmarks/bookmark_bubble_view_unittest.cc
index f4f0d25c..a6814d9 100644
--- a/chrome/browser/ui/views/bookmarks/bookmark_bubble_view_unittest.cc
+++ b/chrome/browser/ui/views/bookmarks/bookmark_bubble_view_unittest.cc
@@ -61,17 +61,12 @@
     // Create a fake anchor view for the bubble.
     anchor_ = std::make_unique<views::View>();
 
-    std::unique_ptr<BubbleSyncPromoDelegate> delegate;
-    bubble_.reset(new BookmarkBubbleView(anchor_.get(), nullptr,
-                                         std::move(delegate), profile(),
-                                         GURL(kTestBookmarkURL), true));
+    bubble_.reset(new BookmarkBubbleView(anchor_.get(), nullptr, nullptr,
+                                         profile(), GURL(kTestBookmarkURL),
+                                         true));
     bubble_->Init();
   }
 
-  std::unique_ptr<views::View> CreateFootnoteView() {
-    return bubble_->CreateFootnoteView();
-  }
-
   std::unique_ptr<BookmarkBubbleView> bubble_;
 
  private:
@@ -85,14 +80,13 @@
   signin::MakePrimaryAccountAvailable(
       IdentityManagerFactory::GetForProfile(profile()), "fake_username");
   CreateBubbleView();
-  std::unique_ptr<views::View> footnote = CreateFootnoteView();
-  EXPECT_FALSE(footnote);
+  EXPECT_FALSE(bubble_->GetFootnoteViewForTesting());
 }
 
 // Verifies that the sync promo is displayed for a user that is not signed in.
 TEST_F(BookmarkBubbleViewTest, SyncPromoNotSignedIn) {
   CreateBubbleView();
-  std::unique_ptr<views::View> footnote = CreateFootnoteView();
+  views::View* footnote = bubble_->GetFootnoteViewForTesting();
 #if defined(OS_CHROMEOS)
   EXPECT_FALSE(footnote);
 #else  // !defined(OS_CHROMEOS)
diff --git a/chrome/browser/ui/views/omnibox/omnibox_popup_contents_view.cc b/chrome/browser/ui/views/omnibox/omnibox_popup_contents_view.cc
index eb73d91..02e0de155 100644
--- a/chrome/browser/ui/views/omnibox/omnibox_popup_contents_view.cc
+++ b/chrome/browser/ui/views/omnibox/omnibox_popup_contents_view.cc
@@ -146,13 +146,6 @@
 
   SetLayoutManager(std::make_unique<views::BoxLayout>(
       views::BoxLayout::Orientation::kVertical));
-
-  // TODO(krb): Remove this when we're sure that nothing accesses the
-  // matches between here and UpdatePopupAppearance().
-  for (size_t i = 0; i < AutocompleteResult::GetMaxMatches(); ++i) {
-    AddChildView(std::make_unique<OmniboxResultView>(this, i, theme_provider_))
-        ->SetVisible(false);
-  }
 }
 
 OmniboxPopupContentsView::~OmniboxPopupContentsView() {
@@ -248,14 +241,14 @@
   // we have enough row views.
   const size_t result_size = model_->result().size();
   for (size_t i = 0; i < result_size; ++i) {
-    // The model can send us more results than we expected when the user
-    // enables loose-limit-on-submatches and has dedicated rows. Add rows to
-    // handle what they've sent.
+    // Create child views lazily.  Since especially the first result view may be
+    // expensive to create due to loading font data, this saves time and memory
+    // during browser startup.
     if (children().size() <= i) {
       AddChildView(
-          std::make_unique<OmniboxResultView>(this, i, theme_provider_))
-          ->SetVisible(false);
+          std::make_unique<OmniboxResultView>(this, i, theme_provider_));
     }
+
     OmniboxResultView* view = result_view_at(i);
     const AutocompleteMatch& match = GetMatchAtIndex(i);
     view->SetMatch(match);
diff --git a/chrome/browser/ui/views/omnibox/omnibox_popup_contents_view_browsertest.cc b/chrome/browser/ui/views/omnibox/omnibox_popup_contents_view_browsertest.cc
index 8e27779..08e9f360 100644
--- a/chrome/browser/ui/views/omnibox/omnibox_popup_contents_view_browsertest.cc
+++ b/chrome/browser/ui/views/omnibox/omnibox_popup_contents_view_browsertest.cc
@@ -93,6 +93,8 @@
 
   // views::AXEventObserver:
   void OnViewEvent(views::View* view, ax::mojom::Event event_type) override {
+    if (!view->GetWidget())
+      return;
     ui::AXNodeData node_data;
     view->GetAccessibleNodeData(&node_data);
     if (event_type == ax::mojom::Event::kTextChanged &&
@@ -330,6 +332,12 @@
 // color.
 IN_PROC_BROWSER_TEST_F(OmniboxPopupContentsViewTest,
                        PopupMatchesLocationBarBackground) {
+  // In dark mode the omnibox focused and unfocused colors are the same, which
+  // makes this test fail; see comments below.
+  BrowserView::GetBrowserViewForBrowser(browser())
+      ->GetNativeTheme()
+      ->set_use_dark_colors(false);
+
   // Start with the Omnibox unfocused.
   omnibox_view()->GetFocusManager()->ClearFocus();
   const SkColor color_before_focus = location_bar()->background()->get_color();
@@ -420,6 +428,8 @@
   ACMatches matches;
   matches.push_back(match);
   results.AppendMatches(input, matches);
+  results.SortAndCull(input, nullptr);
+  autocomplete_controller->NotifyChanged(true);
 
   // Lets check that arrowing up and down emits the event.
   TestAXEventObserver observer;
diff --git a/chrome/browser/ui/views/omnibox/omnibox_view_views.cc b/chrome/browser/ui/views/omnibox/omnibox_view_views.cc
index 08aa7216..309d734c 100644
--- a/chrome/browser/ui/views/omnibox/omnibox_view_views.cc
+++ b/chrome/browser/ui/views/omnibox/omnibox_view_views.cc
@@ -732,6 +732,15 @@
   return false;
 }
 
+bool OmniboxViewViews::MaybeSwitchToTab(const ui::KeyEvent& event) {
+  if (model()->popup_model()->selected_line_state() !=
+      OmniboxPopupModel::BUTTON_FOCUSED)
+    return false;
+  popup_view_->OpenMatch(WindowOpenDisposition::SWITCH_TO_TAB,
+                         event.time_stamp());
+  return true;
+}
+
 void OmniboxViewViews::SetWindowTextAndCaretPos(const base::string16& text,
                                                 size_t caret_pos,
                                                 bool update_popup,
@@ -1554,11 +1563,7 @@
   const bool command = event.IsCommandDown();
   switch (event.key_code()) {
     case ui::VKEY_RETURN:
-      if (model()->popup_model()->selected_line_state() ==
-          OmniboxPopupModel::BUTTON_FOCUSED) {
-        popup_view_->OpenMatch(WindowOpenDisposition::SWITCH_TO_TAB,
-                               event.time_stamp());
-      } else {
+      if (!MaybeSwitchToTab(event)) {
         if (alt || (shift && command)) {
           model()->AcceptInput(WindowOpenDisposition::NEW_FOREGROUND_TAB,
                                event.time_stamp());
@@ -1702,15 +1707,9 @@
       break;
 
     case ui::VKEY_SPACE:
-      if (!(control || alt || shift)) {
-        if (SelectionAtEnd() &&
-            model()->popup_model()->selected_line_state() ==
-                OmniboxPopupModel::BUTTON_FOCUSED) {
-          popup_view_->OpenMatch(WindowOpenDisposition::SWITCH_TO_TAB,
-                                 event.time_stamp());
-          return true;
-        }
-      }
+      if (!control && !alt && !shift && SelectionAtEnd() &&
+          MaybeSwitchToTab(event))
+        return true;
       break;
 
     default:
diff --git a/chrome/browser/ui/views/omnibox/omnibox_view_views.h b/chrome/browser/ui/views/omnibox/omnibox_view_views.h
index 9f23ec4..8a15d3a 100644
--- a/chrome/browser/ui/views/omnibox/omnibox_view_views.h
+++ b/chrome/browser/ui/views/omnibox/omnibox_view_views.h
@@ -213,6 +213,10 @@
   bool MaybeFocusTabButton();
   bool MaybeUnfocusTabButton();
 
+  // If the tab switch button is focused, switches to the relevant tab.  Returns
+  // whether the switch was attempted.
+  bool MaybeSwitchToTab(const ui::KeyEvent& event);
+
   // OmniboxView:
   void SetCaretPos(size_t caret_pos) override;
   void UpdatePopup() override;
diff --git a/chrome/browser/ui/views/profiles/profile_menu_view_browsertest.cc b/chrome/browser/ui/views/profiles/profile_menu_view_browsertest.cc
index ece3fd9..68909b8 100644
--- a/chrome/browser/ui/views/profiles/profile_menu_view_browsertest.cc
+++ b/chrome/browser/ui/views/profiles/profile_menu_view_browsertest.cc
@@ -25,7 +25,6 @@
 #include "chrome/browser/profiles/profile_metrics.h"
 #include "chrome/browser/profiles/profiles_state.h"
 #include "chrome/browser/signin/identity_manager_factory.h"
-#include "chrome/browser/signin/scoped_account_consistency.h"
 #include "chrome/browser/sync/profile_sync_service_factory.h"
 #include "chrome/browser/sync/test/integration/profile_sync_service_harness.h"
 #include "chrome/browser/sync/test/integration/secondary_account_helper.h"
@@ -526,20 +525,9 @@
   ShowAndVerifyUi();
 }
 
-class ProfileMenuViewExtensionsParamTestWithScopedAccountConsistency
-    : public ProfileMenuViewExtensionsParamTest {
- public:
-  ProfileMenuViewExtensionsParamTestWithScopedAccountConsistency() = default;
-
- private:
-  ScopedAccountConsistencyDice scoped_dice_;
-};
-
 // Shows the |ProfileMenuView| during a Guest browsing session when the DICE
 // flag is enabled.
-IN_PROC_BROWSER_TEST_P(
-    ProfileMenuViewExtensionsParamTestWithScopedAccountConsistency,
-    InvokeUi_DiceGuest) {
+IN_PROC_BROWSER_TEST_P(ProfileMenuViewExtensionsParamTest, InvokeUi_DiceGuest) {
   ShowAndVerifyUi();
 }
 
@@ -564,20 +552,10 @@
   ShowAndVerifyUi();
 }
 
-class ProfileMenuViewExtensionsTestWithScopedAccountConsistency
-    : public ProfileMenuViewExtensionsTest {
- public:
-  ProfileMenuViewExtensionsTestWithScopedAccountConsistency() = default;
-
- private:
-  ScopedAccountConsistencyDice scoped_dice_;
-};
-
 // Open the profile chooser to increment the Dice sign-in promo show counter
 // below the threshold.
-IN_PROC_BROWSER_TEST_F(
-    ProfileMenuViewExtensionsTestWithScopedAccountConsistency,
-    IncrementDiceSigninPromoShowCounter) {
+IN_PROC_BROWSER_TEST_F(ProfileMenuViewExtensionsTest,
+                       IncrementDiceSigninPromoShowCounter) {
   browser()->profile()->GetPrefs()->SetInteger(
       prefs::kDiceSigninUserMenuPromoCount, 7);
   ASSERT_NO_FATAL_FAILURE(OpenProfileMenuView(browser()));
@@ -586,9 +564,8 @@
 
 // The DICE sync illustration is shown only the first 10 times. This test
 // ensures that the profile chooser is shown correctly above this threshold.
-IN_PROC_BROWSER_TEST_F(
-    ProfileMenuViewExtensionsTestWithScopedAccountConsistency,
-    DiceSigninPromoWithoutIllustration) {
+IN_PROC_BROWSER_TEST_F(ProfileMenuViewExtensionsTest,
+                       DiceSigninPromoWithoutIllustration) {
   browser()->profile()->GetPrefs()->SetInteger(
       prefs::kDiceSigninUserMenuPromoCount, 10);
   ASSERT_NO_FATAL_FAILURE(OpenProfileMenuView(browser()));
diff --git a/chrome/browser/ui/views/sharing/sharing_dialog_view.cc b/chrome/browser/ui/views/sharing/sharing_dialog_view.cc
index 59d5497..db7efa5 100644
--- a/chrome/browser/ui/views/sharing/sharing_dialog_view.cc
+++ b/chrome/browser/ui/views/sharing/sharing_dialog_view.cc
@@ -156,13 +156,32 @@
   return image_view;
 }
 
+std::unique_ptr<views::View> CreateFootnoteView(
+    const SharingDialogData& data,
+    content::WebContents* web_contents,
+    views::StyledLabelListener* listener) {
+  bool show_origin = ShouldShowOrigin(data, web_contents);
+  switch (data.type) {
+    case SharingDialogType::kDialogWithoutDevicesWithApp:
+      return CreateHelpText(data, listener, show_origin);
+    case SharingDialogType::kDialogWithDevicesMaybeApps:
+      return show_origin ? CreateOriginView(data) : nullptr;
+    case SharingDialogType::kErrorDialog:
+    case SharingDialogType::kEducationalDialog:
+      return nullptr;
+  }
+}
+
 }  // namespace
 
 SharingDialogView::SharingDialogView(views::View* anchor_view,
                                      content::WebContents* web_contents,
                                      SharingDialogData data)
     : LocationBarBubbleDelegateView(anchor_view, web_contents),
-      data_(std::move(data)) {}
+      data_(std::move(data)) {
+  DialogDelegate::SetFootnoteView(
+      ::CreateFootnoteView(data_, web_contents, this));
+}
 
 SharingDialogView::~SharingDialogView() = default;
 
@@ -174,19 +193,6 @@
   return ui::DIALOG_BUTTON_NONE;
 }
 
-std::unique_ptr<views::View> SharingDialogView::CreateFootnoteView() {
-  bool show_origin = ShouldShowOrigin(data_, web_contents());
-  switch (GetDialogType()) {
-    case SharingDialogType::kDialogWithoutDevicesWithApp:
-      return CreateHelpText(data_, this, show_origin);
-    case SharingDialogType::kDialogWithDevicesMaybeApps:
-      return show_origin ? CreateOriginView(data_) : nullptr;
-    case SharingDialogType::kErrorDialog:
-    case SharingDialogType::kEducationalDialog:
-      return nullptr;
-  }
-}
-
 void SharingDialogView::StyledLabelLinkClicked(views::StyledLabel* label,
                                                const gfx::Range& range,
                                                int event_flags) {
diff --git a/chrome/browser/ui/views/sharing/sharing_dialog_view.h b/chrome/browser/ui/views/sharing/sharing_dialog_view.h
index 3e4a696..0da6a385 100644
--- a/chrome/browser/ui/views/sharing/sharing_dialog_view.h
+++ b/chrome/browser/ui/views/sharing/sharing_dialog_view.h
@@ -44,7 +44,6 @@
   void WindowClosing() override;
   void WebContentsDestroyed() override;
   int GetDialogButtons() const override;
-  std::unique_ptr<views::View> CreateFootnoteView() override;
   gfx::Size CalculatePreferredSize() const override;
   void AddedToWidget() override;
   void OnThemeChanged() override;
diff --git a/chrome/browser/ui/views/toolbar/toolbar_button.cc b/chrome/browser/ui/views/toolbar/toolbar_button.cc
index 368763d..942e8a59 100644
--- a/chrome/browser/ui/views/toolbar/toolbar_button.cc
+++ b/chrome/browser/ui/views/toolbar/toolbar_button.cc
@@ -144,8 +144,9 @@
       highlight_color_animation_.GetBackgroundColor();
   if (background_color) {
     // ToolbarButtons are always the height the location bar.
-    const gfx::Insets bg_insets(
-        (height() - GetLayoutConstant(LOCATION_BAR_HEIGHT)) / 2);
+    const gfx::Insets bg_insets =
+        gfx::Insets((height() - GetLayoutConstant(LOCATION_BAR_HEIGHT)) / 2) +
+        *GetProperty(views::kInternalPaddingKey);
     SetBackground(views::CreateBackgroundFromPainter(
         views::Painter::CreateSolidRoundRectPainter(
             *background_color, highlight_radius, bg_insets)));
diff --git a/chrome/browser/ui/views/translate/translate_bubble_view.cc b/chrome/browser/ui/views/translate/translate_bubble_view.cc
index a440c05..14804eb 100644
--- a/chrome/browser/ui/views/translate/translate_bubble_view.cc
+++ b/chrome/browser/ui/views/translate/translate_bubble_view.cc
@@ -89,6 +89,39 @@
 #endif
 }
 
+std::unique_ptr<views::View> CreateWordmarkView(
+    language::TranslateUIBubbleModel bubble_model) {
+#if BUILDFLAG(GOOGLE_CHROME_BRANDING)
+  if (bubble_model != language::TranslateUIBubbleModel::TAB) {
+    return nullptr;
+  }
+
+  auto view = std::make_unique<views::View>();
+  views::GridLayout* layout =
+      view->SetLayoutManager(std::make_unique<views::GridLayout>());
+
+  // Translate icon
+  const int translate_icon_id = IDR_TRANSLATE_TAB_WORDMARK;
+  std::unique_ptr<views::ImageView> translate_icon =
+      std::make_unique<views::ImageView>();
+  gfx::ImageSkia* translate_icon_image =
+      ui::ResourceBundle::GetSharedInstance().GetImageSkiaNamed(
+          translate_icon_id);
+  translate_icon->SetImage(*translate_icon_image);
+
+  views::ColumnSet* cs = layout->AddColumnSet(0);
+  cs->AddColumn(views::GridLayout::FILL, views::GridLayout::CENTER,
+                views::GridLayout::kFixedSize, views::GridLayout::USE_PREF, 0,
+                0);
+  layout->StartRow(1, 0);
+  layout->AddView(std::move(translate_icon));
+
+  return view;
+#else
+  return nullptr;
+#endif
+}
+
 }  // namespace
 
 // static
@@ -344,38 +377,6 @@
   }
 }
 
-std::unique_ptr<views::View> TranslateBubbleView::CreateFootnoteView() {
-#if BUILDFLAG(GOOGLE_CHROME_BRANDING)
-  if (bubble_ui_model_ != language::TranslateUIBubbleModel::TAB) {
-    return nullptr;
-  }
-
-  auto view = std::make_unique<views::View>();
-  views::GridLayout* layout =
-      view->SetLayoutManager(std::make_unique<views::GridLayout>());
-
-  // Translate icon
-  const int translate_icon_id = IDR_TRANSLATE_TAB_WORDMARK;
-  std::unique_ptr<views::ImageView> translate_icon =
-      std::make_unique<views::ImageView>();
-  gfx::ImageSkia* translate_icon_image =
-      ui::ResourceBundle::GetSharedInstance().GetImageSkiaNamed(
-          translate_icon_id);
-  translate_icon->SetImage(*translate_icon_image);
-
-  views::ColumnSet* cs = layout->AddColumnSet(0);
-  cs->AddColumn(views::GridLayout::FILL, views::GridLayout::CENTER,
-                views::GridLayout::kFixedSize, views::GridLayout::USE_PREF, 0,
-                0);
-  layout->StartRow(1, 0);
-  layout->AddView(std::move(translate_icon));
-
-  return view;
-#else
-  return nullptr;
-#endif
-}
-
 views::View* TranslateBubbleView::GetInitiallyFocusedView() {
   return GetCurrentView()->GetNextFocusableView();
 }
@@ -658,6 +659,7 @@
   if (web_contents)  // web_contents can be null in unit_tests.
     mouse_handler_ =
         std::make_unique<WebContentMouseHandler>(this, web_contents);
+  DialogDelegate::SetFootnoteView(CreateWordmarkView(bubble_ui_model_));
   chrome::RecordDialogCreation(chrome::DialogIdentifier::TRANSLATE);
 }
 
diff --git a/chrome/browser/ui/views/translate/translate_bubble_view.h b/chrome/browser/ui/views/translate/translate_bubble_view.h
index 2adf74e..d25229b6 100644
--- a/chrome/browser/ui/views/translate/translate_bubble_view.h
+++ b/chrome/browser/ui/views/translate/translate_bubble_view.h
@@ -92,7 +92,6 @@
   base::string16 GetWindowTitle() const override;
   void Init() override;
   void ButtonPressed(views::Button* sender, const ui::Event& event) override;
-  std::unique_ptr<views::View> CreateFootnoteView() override;
 
   // views::WidgetDelegate methods.
   View* GetInitiallyFocusedView() override;
diff --git a/chrome/browser/ui/webui/chromeos/certificate_manager_dialog_ui.cc b/chrome/browser/ui/webui/chromeos/certificate_manager_dialog_ui.cc
index 36919052a..4fbfb465 100644
--- a/chrome/browser/ui/webui/chromeos/certificate_manager_dialog_ui.cc
+++ b/chrome/browser/ui/webui/chromeos/certificate_manager_dialog_ui.cc
@@ -51,9 +51,7 @@
       user_manager::UserManager::Get()->IsLoggedInAsGuest() ||
           user_manager::UserManager::Get()->IsLoggedInAsPublicAccount());
   source->AddBoolean(
-      "isKiosk",
-      user_manager::UserManager::Get()->IsLoggedInAsKioskApp() ||
-          user_manager::UserManager::Get()->IsLoggedInAsArcKioskApp());
+      "isKiosk", user_manager::UserManager::Get()->IsLoggedInAsAnyKioskApp());
 
   source->UseStringsJs();
   source->SetDefaultResource(IDR_CERT_MANAGER_DIALOG_HTML);
diff --git a/chrome/browser/ui/webui/conflicts/conflicts_data_fetcher.cc b/chrome/browser/ui/webui/conflicts/conflicts_data_fetcher.cc
index fdf7d29..d7da4b1 100644
--- a/chrome/browser/ui/webui/conflicts/conflicts_data_fetcher.cc
+++ b/chrome/browser/ui/webui/conflicts/conflicts_data_fetcher.cc
@@ -15,7 +15,6 @@
 #include "content/public/browser/browser_thread.h"
 
 #if defined(GOOGLE_CHROME_BUILD)
-#include "base/enterprise_util.h"
 #include "base/win/win_util.h"
 #include "chrome/browser/win/conflicts/incompatible_applications_updater.h"
 #include "chrome/browser/win/conflicts/module_blacklist_cache_updater.h"
@@ -224,12 +223,6 @@
   kNonGoogleChromeBuild,
   // The third-party features are not available on Windows 7.
   kNotAvailableWin7,
-  // The third-party features are temporarily disabled on domain-joined
-  // machines because of a known issue with third-party blocking and the
-  // IAttachmentExecute::Save() API (https://crbug.com/870998).
-  // TODO(pmonette): Move IAttachmentExecute::Save() to a utility process and
-  //                 remove this.
-  kEnterpriseManaged,
   // The ThirdPartyBlockingEnabled group policy is disabled.
   kPolicyDisabled,
   // Both the IncompatibleApplicationsWarning and the
@@ -284,9 +277,6 @@
     return kFeatureDisabled;
   }
 
-  if (base::IsMachineExternallyManaged())
-    return kEnterpriseManaged;
-
   // The above 3 cases are the only possible reasons why the manager wouldn't
   // exist.
   NOTREACHED();
@@ -306,9 +296,6 @@
              "builds.";
     case ThirdPartyFeaturesStatus::kNotAvailableWin7:
       return "The third-party features are not available on Windows 7.";
-    case ThirdPartyFeaturesStatus::kEnterpriseManaged:
-      return "The third-party features are temporarily disabled for clients on "
-             "domain-joined machines.";
     case ThirdPartyFeaturesStatus::kPolicyDisabled:
       return "The ThirdPartyBlockingEnabled group policy is disabled.";
     case ThirdPartyFeaturesStatus::kFeatureDisabled:
diff --git a/chrome/browser/ui/webui/settings/settings_ui.cc b/chrome/browser/ui/webui/settings/settings_ui.cc
index ccaedfd0..7f944de9 100644
--- a/chrome/browser/ui/webui/settings/settings_ui.cc
+++ b/chrome/browser/ui/webui/settings/settings_ui.cc
@@ -47,6 +47,9 @@
 #include "chrome/browser/ui/webui/settings/settings_security_key_handler.h"
 #include "chrome/browser/ui/webui/settings/settings_startup_pages_handler.h"
 #include "chrome/browser/ui/webui/settings/site_settings_handler.h"
+#include "chrome/browser/web_applications/components/app_registrar.h"
+#include "chrome/browser/web_applications/web_app_provider.h"
+#include "chrome/browser/web_applications/web_app_registrar.h"
 #include "chrome/common/chrome_features.h"
 #include "chrome/common/pref_names.h"
 #include "chrome/common/url_constants.h"
@@ -161,6 +164,10 @@
   registry->RegisterBooleanPref(prefs::kImportDialogSearchEngine, true);
 }
 
+web_app::AppRegistrar& GetRegistrarForProfile(Profile* profile) {
+  return web_app::WebAppProvider::Get(profile)->registrar();
+}
+
 SettingsUI::SettingsUI(content::WebUI* web_ui)
 #if defined(OS_CHROMEOS)
     : ui::MojoWebUIController(web_ui, /*enable_chrome_send =*/true),
@@ -206,7 +213,8 @@
   AddSettingsPageUIHandler(std::make_unique<ProfileInfoHandler>(profile));
   AddSettingsPageUIHandler(std::make_unique<ProtocolHandlersHandler>());
   AddSettingsPageUIHandler(std::make_unique<SearchEnginesHandler>(profile));
-  AddSettingsPageUIHandler(std::make_unique<SiteSettingsHandler>(profile));
+  AddSettingsPageUIHandler(std::make_unique<SiteSettingsHandler>(
+      profile, GetRegistrarForProfile(profile)));
   AddSettingsPageUIHandler(std::make_unique<StartupPagesHandler>(web_ui));
   AddSettingsPageUIHandler(std::make_unique<SecurityKeysPINHandler>());
   AddSettingsPageUIHandler(std::make_unique<SecurityKeysResetHandler>());
diff --git a/chrome/browser/ui/webui/settings/site_settings_handler.cc b/chrome/browser/ui/webui/settings/site_settings_handler.cc
index 8edef68d..f31d12ee 100644
--- a/chrome/browser/ui/webui/settings/site_settings_handler.cc
+++ b/chrome/browser/ui/webui/settings/site_settings_handler.cc
@@ -38,6 +38,8 @@
 #include "chrome/browser/ui/webui/site_settings_helper.h"
 #include "chrome/browser/usb/usb_chooser_context.h"
 #include "chrome/browser/usb/usb_chooser_context_factory.h"
+#include "chrome/browser/web_applications/web_app_provider.h"
+#include "chrome/browser/web_applications/web_app_registrar.h"
 #include "chrome/common/extensions/manifest_handlers/app_launch_info.h"
 #include "chrome/common/pref_names.h"
 #include "chrome/grit/generated_resources.h"
@@ -76,6 +78,8 @@
 constexpr char kOriginList[] = "origins";
 constexpr char kNumCookies[] = "numCookies";
 constexpr char kHasPermissionSettings[] = "hasPermissionSettings";
+constexpr char kHasInstalledPWA[] = "hasInstalledPWA";
+constexpr char kIsInstalled[] = "isInstalled";
 constexpr char kZoom[] = "zoom";
 // Placeholder value for ETLD+1 until a valid origin is added. If an ETLD+1
 // only has placeholder, then create an ETLD+1 origin.
@@ -141,6 +145,19 @@
   }
 }
 
+base::flat_set<web_app::AppId> GetInstalledApps(
+    Profile* profile,
+    web_app::AppRegistrar& registrar) {
+  auto apps = registrar.GetAppIds();
+  base::flat_set<std::string> installed;
+  for (auto app : apps) {
+    base::Optional<GURL> scope = registrar.GetAppScope(app);
+    if (scope.has_value())
+      installed.insert(scope.value().GetOrigin().spec());
+  }
+  return installed;
+}
+
 // Whether |pattern| applies to a single origin.
 bool PatternAppliesToSingleOrigin(const ContentSettingPatternSource& pattern) {
   const GURL url(pattern.primary_pattern.ToString());
@@ -221,9 +238,12 @@
     const std::map<std::string, std::set<std::string>>& site_group_map,
     const std::set<std::string>& origin_permission_set,
     base::Value* list_value,
-    Profile* profile) {
+    Profile* profile,
+    web_app::AppRegistrar& registrar) {
   DCHECK_EQ(base::Value::Type::LIST, list_value->type());
   DCHECK(profile);
+  base::flat_set<web_app::AppId> installed_apps =
+      GetInstalledApps(profile, registrar);
   SiteEngagementService* engagement_service =
       SiteEngagementService::Get(profile);
   for (const auto& entry : site_group_map) {
@@ -231,6 +251,7 @@
     base::Value site_group(base::Value::Type::DICTIONARY);
     site_group.SetKey(kEffectiveTopLevelDomainPlus1Name,
                       base::Value(entry.first));
+    bool has_installed_pwa = false;
     base::Value origin_list(base::Value::Type::LIST);
     for (const std::string& origin : entry.second) {
       base::Value origin_object(base::Value::Type::DICTIONARY);
@@ -248,11 +269,18 @@
           base::Value(engagement_service->GetScore(GURL(origin))));
       origin_object.SetKey("usage", base::Value(0));
       origin_object.SetKey(kNumCookies, base::Value(0));
+
+      bool is_installed = installed_apps.contains(origin);
+      if (is_installed)
+        has_installed_pwa = true;
+      origin_object.SetKey(kIsInstalled, base::Value(is_installed));
+
       origin_object.SetKey(
           kHasPermissionSettings,
           base::Value(base::Contains(origin_permission_set, origin)));
       origin_list.Append(std::move(origin_object));
     }
+    site_group.SetKey(kHasInstalledPWA, base::Value(has_installed_pwa));
     site_group.SetKey(kNumCookies, base::Value(0));
     site_group.SetKey(kOriginList, std::move(origin_list));
     list_value->Append(std::move(site_group));
@@ -308,8 +336,10 @@
 
 }  // namespace
 
-SiteSettingsHandler::SiteSettingsHandler(Profile* profile)
+SiteSettingsHandler::SiteSettingsHandler(Profile* profile,
+                                         web_app::AppRegistrar& app_registrar)
     : profile_(profile),
+      app_registrar_(app_registrar),
       pref_change_registrar_(nullptr) {}
 
 SiteSettingsHandler::~SiteSettingsHandler() {
@@ -743,7 +773,7 @@
 
   // Respond with currently available data.
   ConvertSiteGroupMapToListValue(all_sites_map_, origin_permission_set_,
-                                 &result, profile);
+                                 &result, profile, app_registrar_);
 
   LogAllSitesAction(AllSitesAction::kLoadPage);
 
@@ -760,7 +790,7 @@
   GetOriginStorage(&all_sites_map_, &origin_size_map);
   GetOriginCookies(&all_sites_map_, &origin_cookie_map);
   ConvertSiteGroupMapToListValue(all_sites_map_, origin_permission_set_,
-                                 &list_value, profile);
+                                 &list_value, profile, app_registrar_);
 
   // Merge the origin usage and cookies number into |list_value|.
   for (base::Value& site_group : list_value.GetList()) {
diff --git a/chrome/browser/ui/webui/settings/site_settings_handler.h b/chrome/browser/ui/webui/settings/site_settings_handler.h
index ed0eafa7..9e61c989 100644
--- a/chrome/browser/ui/webui/settings/site_settings_handler.h
+++ b/chrome/browser/ui/webui/settings/site_settings_handler.h
@@ -10,10 +10,13 @@
 #include <set>
 #include <string>
 
+#include "base/containers/flat_set.h"
 #include "base/scoped_observer.h"
 #include "chrome/browser/browsing_data/cookies_tree_model.h"
 #include "chrome/browser/permissions/chooser_context_base.h"
 #include "chrome/browser/ui/webui/settings/settings_page_ui_handler.h"
+#include "chrome/browser/web_applications/components/app_registrar.h"
+#include "chrome/browser/web_applications/web_app_registrar.h"
 #include "components/content_settings/core/browser/content_settings_observer.h"
 #include "components/content_settings/core/browser/host_content_settings_map.h"
 #include "content/public/browser/host_zoom_map.h"
@@ -38,7 +41,8 @@
                             public ChooserContextBase::PermissionObserver,
                             public CookiesTreeModel::Observer {
  public:
-  explicit SiteSettingsHandler(Profile* profile);
+  explicit SiteSettingsHandler(Profile* profile,
+                               web_app::AppRegistrar& web_app_registrar);
   ~SiteSettingsHandler() override;
 
   // SettingsPageUIHandler:
@@ -240,6 +244,7 @@
   void ClearAllSitesMapForTesting();
 
   Profile* profile_;
+  web_app::AppRegistrar& app_registrar_;
 
   content::NotificationRegistrar notification_registrar_;
 
diff --git a/chrome/browser/ui/webui/settings/site_settings_handler_unittest.cc b/chrome/browser/ui/webui/settings/site_settings_handler_unittest.cc
index 3744be69..74ee718 100644
--- a/chrome/browser/ui/webui/settings/site_settings_handler_unittest.cc
+++ b/chrome/browser/ui/webui/settings/site_settings_handler_unittest.cc
@@ -34,6 +34,8 @@
 #include "chrome/browser/ui/webui/site_settings_helper.h"
 #include "chrome/browser/usb/usb_chooser_context.h"
 #include "chrome/browser/usb/usb_chooser_context_factory.h"
+#include "chrome/browser/web_applications/components/web_app_helpers.h"
+#include "chrome/browser/web_applications/test/test_app_registrar.h"
 #include "chrome/common/pref_names.h"
 #include "chrome/test/base/browser_with_test_window_test.h"
 #include "chrome/test/base/testing_profile.h"
@@ -126,6 +128,10 @@
 };
 #endif
 
+std::string GenerateFakeAppId(const GURL& url) {
+  return web_app::GenerateAppIdFromURL(url);
+}
+
 }  // namespace
 
 namespace settings {
@@ -173,7 +179,7 @@
             ContentSettingsType::COOKIES)),
         kFlash(site_settings::ContentSettingsTypeToGroupName(
             ContentSettingsType::PLUGINS)),
-        handler_(&profile_) {
+        handler_(&profile_, app_registrar_) {
 #if defined(OS_CHROMEOS)
     user_manager_enabler_ = std::make_unique<user_manager::ScopedUserManager>(
         std::make_unique<chromeos::MockUserManager>());
@@ -188,6 +194,7 @@
 
   TestingProfile* profile() { return &profile_; }
   TestingProfile* incognito_profile() { return incognito_profile_; }
+  web_app::TestAppRegistrar& app_registrar() { return app_registrar_; }
   content::TestWebUI* web_ui() { return &web_ui_; }
   SiteSettingsHandler* handler() { return &handler_; }
 
@@ -472,6 +479,7 @@
   content::BrowserTaskEnvironment task_environment_;
   TestingProfile profile_;
   TestingProfile* incognito_profile_;
+  web_app::TestAppRegistrar app_registrar_;
   content::TestWebUI web_ui_;
   SiteSettingsHandler handler_;
 #if defined(OS_CHROMEOS)
@@ -789,6 +797,48 @@
   EXPECT_EQ(1, origin_info->FindKey("numCookies")->GetDouble());
 }
 
+TEST_F(SiteSettingsHandlerTest, InstalledApps) {
+  web_app::TestAppRegistrar& registrar = app_registrar();
+  const GURL url("http://abc.example.com/");
+  registrar.AddExternalApp(GenerateFakeAppId(url), {url});
+
+  SetUpCookiesTreeModel();
+
+  const base::ListValue* storage_and_cookie_list =
+      GetOnStorageFetchedSentListValue();
+  EXPECT_EQ(3U, storage_and_cookie_list->GetSize());
+
+  const base::DictionaryValue* site_group;
+  ASSERT_TRUE(storage_and_cookie_list->GetDictionary(0, &site_group));
+
+  std::string etld_plus1_string;
+  ASSERT_TRUE(site_group->GetString("etldPlus1", &etld_plus1_string));
+  ASSERT_EQ("example.com", etld_plus1_string);
+
+  ASSERT_TRUE(site_group->FindKey("hasInstalledPWA")->GetBool());
+
+  const base::ListValue* origin_list;
+  ASSERT_TRUE(site_group->GetList("origins", &origin_list));
+  const base::DictionaryValue* origin_info;
+
+  ASSERT_TRUE(origin_list->GetDictionary(0, &origin_info));
+  EXPECT_EQ("http://abc.example.com/",
+            origin_info->FindKey("origin")->GetString());
+  EXPECT_TRUE(origin_info->FindKey("isInstalled")->GetBool());
+
+  // Verify that installed booleans are false for other siteGroups/origins
+  ASSERT_TRUE(storage_and_cookie_list->GetDictionary(1, &site_group));
+
+  ASSERT_TRUE(site_group->GetString("etldPlus1", &etld_plus1_string));
+  ASSERT_EQ("google.com", etld_plus1_string);
+  ASSERT_TRUE(site_group->GetList("origins", &origin_list));
+  ASSERT_TRUE(origin_list->GetDictionary(0, &origin_info));
+  EXPECT_EQ("https://www.google.com/",
+            origin_info->FindKey("origin")->GetString());
+  EXPECT_FALSE(site_group->FindKey("hasInstalledPWA")->GetBool());
+  EXPECT_FALSE(origin_info->FindKey("isInstalled")->GetBool());
+}
+
 TEST_F(SiteSettingsHandlerTest, Origins) {
   const std::string google("https://www.google.com:443");
   const std::string uma_base("WebsiteSettings.Menu.PermissionChanged");
@@ -1234,7 +1284,7 @@
 
   void SetUp() override {
     BrowserWithTestWindowTest::SetUp();
-    handler_ = std::make_unique<SiteSettingsHandler>(profile());
+    handler_ = std::make_unique<SiteSettingsHandler>(profile(), app_registrar_);
     handler()->set_web_ui(web_ui());
     handler()->AllowJavascript();
     web_ui()->ClearTrackedCalls();
@@ -1281,6 +1331,7 @@
   const std::string kNotifications;
 
  private:
+  web_app::TestAppRegistrar app_registrar_;
   content::TestWebUI web_ui_;
   std::unique_ptr<SiteSettingsHandler> handler_;
   std::unique_ptr<BrowserWindow> window2_;
diff --git a/chrome/browser/web_bluetooth_browsertest.cc b/chrome/browser/web_bluetooth_browsertest.cc
index ca84f38..ad4da87 100644
--- a/chrome/browser/web_bluetooth_browsertest.cc
+++ b/chrome/browser/web_bluetooth_browsertest.cc
@@ -56,7 +56,7 @@
   content::WebContents* web_contents_ = nullptr;
 };
 
-IN_PROC_BROWSER_TEST_F(WebBluetoothTest, WebBluetoothAfterCrash) {
+IN_PROC_BROWSER_TEST_F(WebBluetoothTest, DISABLED_WebBluetoothAfterCrash) {
   // Make sure we can use Web Bluetooth after the tab crashes.
   // Set up adapter with one device.
   scoped_refptr<NiceMockBluetoothAdapter> adapter(
diff --git a/chrome/browser/win/conflicts/module_database.cc b/chrome/browser/win/conflicts/module_database.cc
index ed7b841d..d968e866 100644
--- a/chrome/browser/win/conflicts/module_database.cc
+++ b/chrome/browser/win/conflicts/module_database.cc
@@ -17,7 +17,6 @@
 #include "content/public/browser/browser_task_traits.h"
 
 #if defined(GOOGLE_CHROME_BUILD)
-#include "base/enterprise_util.h"
 #include "base/feature_list.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/win/conflicts/incompatible_applications_updater.h"
@@ -29,6 +28,7 @@
 #include "components/prefs/pref_change_registrar.h"
 #include "components/prefs/pref_registry_simple.h"
 #include "components/prefs/pref_service.h"
+#include "components/services/quarantine/public/cpp/quarantine_features_win.h"
 #endif
 
 namespace {
@@ -36,21 +36,6 @@
 ModuleDatabase* g_module_database = nullptr;
 
 #if defined(GOOGLE_CHROME_BUILD)
-// Returns true if either the IncompatibleApplicationsWarning or
-// ThirdPartyModulesBlocking features are enabled via the "enable-features"
-// command-line switch.
-bool AreThirdPartyFeaturesEnabledViaCommandLine() {
-  // The FeatureList API is thread-safe.
-  base::FeatureList* feature_list_instance = base::FeatureList::GetInstance();
-
-  return feature_list_instance->IsFeatureOverriddenFromCommandLine(
-             features::kIncompatibleApplicationsWarning.name,
-             base::FeatureList::OVERRIDE_ENABLE_FEATURE) ||
-         feature_list_instance->IsFeatureOverriddenFromCommandLine(
-             features::kThirdPartyModulesBlocking.name,
-             base::FeatureList::OVERRIDE_ENABLE_FEATURE);
-}
-
 // Callback for the pref change registrar. Is invoked when the
 // ThirdPartyBlockingEnabled policy is modified. Notifies the ModuleDatabase if
 // the policy was disabled.
@@ -446,23 +431,13 @@
     bool third_party_blocking_policy_enabled) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 
-  // Temporarily disable this class on domain-joined machines because enterprise
-  // clients depend on IAttachmentExecute::Save() to be invoked for downloaded
-  // files, but that API call has a known issue (https://crbug.com/870998) with
-  // third-party modules blocking. Can be Overridden by enabling the feature via
-  // the command-line.
-  // TODO(pmonette): Move IAttachmentExecute::Save() to a utility process and
-  //                 remove this.
-  if (base::IsMachineExternallyManaged() &&
-      !AreThirdPartyFeaturesEnabledViaCommandLine()) {
-    return;
-  }
-
   if (!third_party_blocking_policy_enabled)
     return;
 
   if (IncompatibleApplicationsUpdater::IsWarningEnabled() ||
       ModuleBlacklistCacheUpdater::IsBlockingEnabled()) {
+    DCHECK(base::FeatureList::IsEnabled(quarantine::kOutOfProcessQuarantine));
+
     third_party_conflicts_manager_ =
         std::make_unique<ThirdPartyConflictsManager>(this);
 
diff --git a/chrome/browser/win/conflicts/third_party_blocking_browsertest.cc b/chrome/browser/win/conflicts/third_party_blocking_browsertest.cc
index 9c4374f..f29ebc5 100644
--- a/chrome/browser/win/conflicts/third_party_blocking_browsertest.cc
+++ b/chrome/browser/win/conflicts/third_party_blocking_browsertest.cc
@@ -13,7 +13,6 @@
 #include "base/test/scoped_feature_list.h"
 #include "base/test/test_reg_util_win.h"
 #include "base/win/registry.h"
-#include "base/win/win_util.h"
 #include "base/win/windows_version.h"
 #include "chrome/browser/win/conflicts/module_blacklist_cache_updater.h"
 #include "chrome/browser/win/conflicts/module_blacklist_cache_util.h"
@@ -79,8 +78,7 @@
 
 class ThirdPartyBlockingBrowserTest : public InProcessBrowserTest {
  protected:
-  ThirdPartyBlockingBrowserTest() : scoped_domain_(false) {}
-
+  ThirdPartyBlockingBrowserTest() = default;
   ~ThirdPartyBlockingBrowserTest() override = default;
 
   // InProcessBrowserTest:
@@ -134,9 +132,6 @@
                               static_cast<int>(contents.size())));
   }
 
-  // The feature is always disabled on domain-joined machines.
-  base::win::ScopedDomainStateForTesting scoped_domain_;
-
   // Enables the ThirdPartyModulesBlocking feature.
   base::test::ScopedFeatureList scoped_feature_list_;
 
diff --git a/chrome/common/BUILD.gn b/chrome/common/BUILD.gn
index ce3a5b5..7e705c6 100644
--- a/chrome/common/BUILD.gn
+++ b/chrome/common/BUILD.gn
@@ -195,7 +195,6 @@
     ":available_offline_content_mojom",
     ":buildflags",
     ":channel_info",
-    ":client_hints_mojom",
     ":ini_parser",
     ":mojo_bindings",
     ":offline_page_auto_fetcher_mojom",
@@ -765,6 +764,7 @@
 
   public_deps = [
     "//components/content_settings/core/common:mojo_bindings",
+    "//content/public/common:client_hints_mojom",
     "//content/public/common:interfaces",
     "//mojo/public/mojom/base",
     "//third_party/blink/public/mojom:mojom_platform",
@@ -801,18 +801,6 @@
   ]
 }
 
-mojom("client_hints_mojom") {
-  sources = [
-    "client_hints.mojom",
-  ]
-
-  public_deps = [
-    "//mojo/public/mojom/base",
-    "//third_party/blink/public/mojom:web_client_hints_types_mojo_bindings",
-    "//url/mojom:url_mojom_origin",
-  ]
-}
-
 if (enable_print_preview && !is_chromeos) {
   mojom("service_process_mojom") {
     sources = [
diff --git a/chrome/common/chrome_features.cc b/chrome/common/chrome_features.cc
index af1ef85e..6fa7942a 100644
--- a/chrome/common/chrome_features.cc
+++ b/chrome/common/chrome_features.cc
@@ -877,4 +877,10 @@
     "AccessibilityInternalsPageImprovements",
     base::FEATURE_DISABLED_BY_DEFAULT};
 
+// Enables setting time limit for Chrome and PWA's on child user device.
+#if defined(OS_CHROMEOS)
+const base::Feature kWebTimeLimits{"WebTimeLimits",
+                                   base::FEATURE_DISABLED_BY_DEFAULT};
+#endif  // defined(OS_CHROMEOS)
+
 }  // namespace features
diff --git a/chrome/common/chrome_features.h b/chrome/common/chrome_features.h
index 6c401e3..29a3250 100644
--- a/chrome/common/chrome_features.h
+++ b/chrome/common/chrome_features.h
@@ -531,6 +531,11 @@
 COMPONENT_EXPORT(CHROME_FEATURES)
 extern const base::Feature kAccessibilityInternalsPageImprovements;
 
+#if defined(OS_CHROMEOS)
+COMPONENT_EXPORT(CHROME_FEATURES)
+extern const base::Feature kWebTimeLimits;
+#endif  // defined(OS_CHROMEOS)
+
 bool PrefServiceEnabled();
 
 // DON'T ADD RANDOM STUFF HERE. Put it in the main section above in
diff --git a/chrome/common/chrome_switches.cc b/chrome/common/chrome_switches.cc
index 294c2c6..f2609c6 100644
--- a/chrome/common/chrome_switches.cc
+++ b/chrome/common/chrome_switches.cc
@@ -344,6 +344,10 @@
 // Forces Chrome to use a stacked tab strip layout.
 const char kForceStackedTabStripLayout[]    = "force-stacked-tab-strip-layout";
 
+// Forces web-application mode. This hides certain system UI elements and forces
+// the app to be installed if it hasn't been already.
+const char kForceWebAppMode[]               = "force-web-app-mode";
+
 // Specifies which page will be displayed in newly-opened tabs. We need this
 // for testing purposes so that the UI tests don't depend on what comes up for
 // http://google.com.
diff --git a/chrome/common/chrome_switches.h b/chrome/common/chrome_switches.h
index d1885bb3..91ac539 100644
--- a/chrome/common/chrome_switches.h
+++ b/chrome/common/chrome_switches.h
@@ -110,6 +110,7 @@
 extern const char kForceAppMode[];
 extern const char kForceFirstRun[];
 extern const char kForceStackedTabStripLayout[];
+extern const char kForceWebAppMode[];
 extern const char kHomePage[];
 extern const char kIncognito[];
 extern const char kInstallAutogeneratedTheme[];
diff --git a/chrome/common/extensions/api/autotest_private.idl b/chrome/common/extensions/api/autotest_private.idl
index 7883213..9c311ea 100644
--- a/chrome/common/extensions/api/autotest_private.idl
+++ b/chrome/common/extensions/api/autotest_private.idl
@@ -573,6 +573,10 @@
     static void setAssistantEnabled(boolean enabled, long timeout_ms,
                                     VoidCallback callback);
 
+    // Bring up the Assistant service, and wait for the ready signal.
+    // |callback|: Called when the operation has completed.
+    static void enableAssistantAndWaitForReady(VoidCallback callback);
+
     // Sends a text query via Google Assistant.
     // |callback|: Called when response has been received.
     static void sendAssistantTextQuery(DOMString query, long timeout_ms,
diff --git a/chrome/renderer/content_settings_agent_impl.cc b/chrome/renderer/content_settings_agent_impl.cc
index 45b078c..a0accbab 100644
--- a/chrome/renderer/content_settings_agent_impl.cc
+++ b/chrome/renderer/content_settings_agent_impl.cc
@@ -12,7 +12,6 @@
 #include "base/metrics/histogram_macros.h"
 #include "base/strings/string_number_conversions.h"
 #include "chrome/common/chrome_features.h"
-#include "chrome/common/client_hints.mojom.h"
 #include "chrome/common/client_hints/client_hints.h"
 #include "chrome/common/render_messages.h"
 #include "chrome/common/ssl_insecure_content.h"
@@ -20,6 +19,7 @@
 #include "components/content_settings/core/common/content_settings.mojom.h"
 #include "components/content_settings/core/common/content_settings_pattern.h"
 #include "components/content_settings/core/common/content_settings_utils.h"
+#include "content/public/common/client_hints.mojom.h"
 #include "content/public/common/origin_util.h"
 #include "content/public/common/previews_state.h"
 #include "content/public/common/url_constants.h"
diff --git a/chrome/services/app_service/public/cpp/BUILD.gn b/chrome/services/app_service/public/cpp/BUILD.gn
index 91466b18..e9585c1 100644
--- a/chrome/services/app_service/public/cpp/BUILD.gn
+++ b/chrome/services/app_service/public/cpp/BUILD.gn
@@ -15,6 +15,24 @@
   ]
 }
 
+if (is_chromeos) {
+  source_set("instance_update") {
+    sources = [
+      "instance.cc",
+      "instance.h",
+      "instance_registry.cc",
+      "instance_registry.h",
+      "instance_update.cc",
+      "instance_update.h",
+    ]
+    deps = [
+      "//skia",
+      "//ui/aura",
+      "//ui/compositor",
+    ]
+  }
+}
+
 source_set("icon_loader") {
   sources = [
     "icon_cache.cc",
@@ -88,4 +106,13 @@
     ":icon_loader",
     "//testing/gtest",
   ]
+
+  if (is_chromeos) {
+    sources += [
+      "instance_registry_unittest.cc",
+      "instance_update_unittest.cc",
+    ]
+
+    deps += [ ":instance_update" ]
+  }
 }
diff --git a/chrome/services/app_service/public/cpp/instance.cc b/chrome/services/app_service/public/cpp/instance.cc
new file mode 100644
index 0000000..eef110a
--- /dev/null
+++ b/chrome/services/app_service/public/cpp/instance.cc
@@ -0,0 +1,33 @@
+// 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/services/app_service/public/cpp/instance.h"
+
+#include <memory>
+
+#include "ui/aura/window.h"
+
+namespace apps {
+
+Instance::Instance(const std::string& app_id, aura::Window* window)
+    : app_id_(app_id), window_(window) {
+  state_ = InstanceState::kUnknown;
+}
+
+Instance::~Instance() = default;
+
+std::unique_ptr<Instance> Instance::Clone() {
+  auto instance = std::make_unique<Instance>(this->AppId(), this->Window());
+  instance->SetLaunchId(this->LaunchId());
+  instance->UpdateState(this->State(), this->LastUpdatedTime());
+  return instance;
+}
+
+void Instance::UpdateState(InstanceState state,
+                           const base::Time& last_updated_time) {
+  state_ = state;
+  last_updated_time_ = last_updated_time;
+}
+
+}  // namespace apps
diff --git a/chrome/services/app_service/public/cpp/instance.h b/chrome/services/app_service/public/cpp/instance.h
new file mode 100644
index 0000000..264c677
--- /dev/null
+++ b/chrome/services/app_service/public/cpp/instance.h
@@ -0,0 +1,62 @@
+// 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_SERVICES_APP_SERVICE_PUBLIC_CPP_INSTANCE_H_
+#define CHROME_SERVICES_APP_SERVICE_PUBLIC_CPP_INSTANCE_H_
+
+#include <memory>
+#include <string>
+
+#include "base/time/time.h"
+
+namespace aura {
+class Window;
+}
+
+namespace apps {
+
+enum InstanceState {
+  kUnknown = 0,
+  kStarted = 0x01,
+  kRunning = 0x02,
+  kActive = 0x04,
+  kVisible = 0x08,
+};
+
+// Instance is used to represent an App Instance, or a running app.
+class Instance {
+ public:
+  Instance(const std::string& app_id, aura::Window* window);
+  ~Instance();
+
+  Instance(const Instance&) = delete;
+  Instance& operator=(const Instance&) = delete;
+
+  std::unique_ptr<Instance> Clone();
+
+  void SetLaunchId(const std::string& launch_id) { launch_id_ = launch_id; }
+  void UpdateState(InstanceState state, const base::Time& last_updated_time);
+
+  const std::string& AppId() const { return app_id_; }
+  aura::Window* Window() const { return window_; }
+  const std::string& LaunchId() const { return launch_id_; }
+  InstanceState State() const { return state_; }
+  const base::Time& LastUpdatedTime() const { return last_updated_time_; }
+
+ private:
+  std::string app_id_;
+
+  // window_ is owned by ash and will be deleted when the user closes the
+  // window. Instance itself doesn't observe the window. The window's observer
+  // is responsible to delete Instance from InstanceRegistry when the window is
+  // destroyed.
+  aura::Window* window_;
+  std::string launch_id_;
+  InstanceState state_;
+  base::Time last_updated_time_;
+};
+
+}  // namespace apps
+
+#endif  // CHROME_SERVICES_APP_SERVICE_PUBLIC_CPP_INSTANCE_H_
diff --git a/chrome/services/app_service/public/cpp/instance_registry.cc b/chrome/services/app_service/public/cpp/instance_registry.cc
new file mode 100644
index 0000000..d04754c
--- /dev/null
+++ b/chrome/services/app_service/public/cpp/instance_registry.cc
@@ -0,0 +1,118 @@
+// 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/services/app_service/public/cpp/instance_registry.h"
+
+#include <memory>
+#include <utility>
+
+#include "chrome/services/app_service/public/cpp/instance.h"
+#include "chrome/services/app_service/public/cpp/instance_update.h"
+
+namespace apps {
+
+InstanceRegistry::Observer::Observer(InstanceRegistry* instance_registry) {
+  Observe(instance_registry);
+}
+
+InstanceRegistry::Observer::Observer() = default;
+InstanceRegistry::Observer::~Observer() {
+  if (instance_registry_) {
+    instance_registry_->RemoveObserver(this);
+  }
+}
+
+void InstanceRegistry::Observer::Observe(InstanceRegistry* instance_registry) {
+  if (instance_registry == instance_registry_) {
+    return;
+  }
+
+  if (instance_registry_) {
+    instance_registry_->RemoveObserver(this);
+  }
+
+  instance_registry_ = instance_registry;
+  if (instance_registry_) {
+    instance_registry_->AddObserver(this);
+  }
+}
+
+InstanceRegistry::InstanceRegistry() = default;
+
+InstanceRegistry::~InstanceRegistry() {
+  for (auto& obs : observers_) {
+    obs.OnInstanceRegistryWillBeDestroyed(this);
+  }
+  DCHECK(!observers_.might_have_observers());
+}
+
+void InstanceRegistry::AddObserver(Observer* observer) {
+  observers_.AddObserver(observer);
+}
+
+void InstanceRegistry::RemoveObserver(Observer* observer) {
+  observers_.RemoveObserver(observer);
+}
+
+void InstanceRegistry::OnInstances(const Instances& deltas) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(my_sequence_checker_);
+
+  if (!deltas_in_progress_.empty()) {
+    for (auto& delta : deltas) {
+      deltas_pending_.push_back(delta.get()->Clone());
+    }
+    return;
+  }
+  DoOnInstances(std::move(deltas));
+  while (!deltas_pending_.empty()) {
+    Instances pending;
+    pending.swap(deltas_pending_);
+    DoOnInstances(std::move(pending));
+  }
+}
+
+void InstanceRegistry::DoOnInstances(const Instances& deltas) {
+  // Merge any deltas elements that have the same window. If an observer's
+  // OnInstanceUpdate calls back into this InstanceRegistry, we can present a
+  // single delta for any given window.
+  for (auto& delta : deltas) {
+    auto d_iter = deltas_in_progress_.find(delta->Window());
+    if (d_iter != deltas_in_progress_.end()) {
+      InstanceUpdate::Merge(d_iter->second, delta.get());
+    } else {
+      deltas_in_progress_[delta->Window()] = delta.get();
+    }
+  }
+
+  // The remaining for loops range over the deltas_in_progress_ map, not the
+  // deltas vector, so that OninstanceUpdate is called only once per unique
+  // window. Notify the observers for every de-duplicated delta.
+  for (const auto& d_iter : deltas_in_progress_) {
+    auto s_iter = states_.find(d_iter.first);
+    Instance* state =
+        (s_iter != states_.end()) ? s_iter->second.get() : nullptr;
+    Instance* delta = d_iter.second;
+
+    for (auto& obs : observers_) {
+      obs.OnInstanceUpdate(InstanceUpdate(state, delta));
+    }
+  }
+
+  // Update the states for every de-duplicated delta.
+  for (const auto& d_iter : deltas_in_progress_) {
+    auto s_iter = states_.find(d_iter.first);
+    Instance* state =
+        (s_iter != states_.end()) ? s_iter->second.get() : nullptr;
+    Instance* delta = d_iter.second;
+
+    if (state) {
+      InstanceUpdate::Merge(state, delta);
+    } else {
+      states_.insert(std::make_pair(delta->Window(), (delta->Clone())));
+    }
+  }
+  deltas_in_progress_.clear();
+}
+
+}  // namespace apps
diff --git a/chrome/services/app_service/public/cpp/instance_registry.h b/chrome/services/app_service/public/cpp/instance_registry.h
new file mode 100644
index 0000000..0a64fe42
--- /dev/null
+++ b/chrome/services/app_service/public/cpp/instance_registry.h
@@ -0,0 +1,194 @@
+// 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_SERVICES_APP_SERVICE_PUBLIC_CPP_INSTANCE_REGISTRY_H_
+#define CHROME_SERVICES_APP_SERVICE_PUBLIC_CPP_INSTANCE_REGISTRY_H_
+
+#include <map>
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "base/observer_list.h"
+#include "base/observer_list_types.h"
+#include "base/sequence_checker.h"
+#include "chrome/services/app_service/public/cpp/instance.h"
+#include "chrome/services/app_service/public/cpp/instance_update.h"
+
+namespace aura {
+class Window;
+}
+
+namespace apps {
+
+// InstanceRegistry keeps all of the Instances seen by AppServiceProxy.
+// It also keeps the "sum" of those previous deltas, so that observers of this
+// object can be updated with the InstanceUpdate structure. It can also be
+// queried synchronously.
+//
+// This class is not thread-safe.
+class InstanceRegistry {
+ public:
+  class Observer : public base::CheckedObserver {
+   public:
+    Observer(const Observer&) = delete;
+    Observer& operator=(const Observer&) = delete;
+
+    // The InstanceUpdate argument shouldn't be accessed after OnInstanceUpdate
+    // returns.
+    virtual void OnInstanceUpdate(const InstanceUpdate& update) = 0;
+
+    // Called when the InstanceRegistry object (the thing that this observer
+    // observes) will be destroyed. In response, the observer, |this|, should
+    // call "instance_registry->RemoveObserver(this)", whether directly or
+    // indirectly (e.g. via ScopedObserver::Remove or via Observe(nullptr)).
+    virtual void OnInstanceRegistryWillBeDestroyed(InstanceRegistry* cache) = 0;
+
+   protected:
+    // Use this constructor when the observer |this| is tied to a single
+    // InstanceRegistry for its entire lifetime, or until the observee (the
+    // InstanceRegistry) is destroyed, whichever comes first.
+    explicit Observer(InstanceRegistry* cache);
+
+    // Use this constructor when the observer |this| wants to observe a
+    // InstanceRegistry for part of its lifetime. It can then call Observe() to
+    // start and stop observing.
+    Observer();
+
+    ~Observer() override;
+
+    // Start observing a different InstanceRegistry. |instance_registry| may be
+    // nullptr, meaning to stop observing.
+    void Observe(InstanceRegistry* instance_registry);
+
+   private:
+    InstanceRegistry* instance_registry_ = nullptr;
+  };
+
+  InstanceRegistry();
+  ~InstanceRegistry();
+
+  InstanceRegistry(const InstanceRegistry&) = delete;
+  InstanceRegistry& operator=(const InstanceRegistry&) = delete;
+
+  void AddObserver(Observer* observer);
+  void RemoveObserver(Observer* observer);
+
+  using InstancePtr = std::unique_ptr<Instance>;
+  using Instances = std::vector<InstancePtr>;
+
+  // Notification and merging might be delayed until after OnInstances returns.
+  // For example, suppose that the initial set of states is (a0, b0, c0) for
+  // three app_id's ("a", "b", "c"). Now suppose OnInstances is called with two
+  // updates (b1, c1), and when notified of b1, an observer calls OnInstances
+  // again with (c2, d2). The c1 delta should be processed before the c2 delta,
+  // as it was sent first: c2 should be merged (with "newest wins" semantics)
+  // onto c1 and not vice versa. This means that processing c2 (scheduled by the
+  // second OnInstances call) should wait until the first OnInstances call has
+  // finished processing b1 (and then c1), which means that processing c2 is
+  // delayed until after the second OnInstances call returns.
+  //
+  // The caller presumably calls OnInstances(std::move(deltas)).
+  void OnInstances(const Instances& deltas);
+
+  // Calls f, a void-returning function whose arguments are (const
+  // apps::InstanceUpdate&), on each window in the instance_registry.
+  //
+  // f's argument is an apps::InstanceUpdate instead of an Instance* so that
+  // callers can more easily share code with Observer::OnInstanceUpdate (which
+  // also takes an apps::InstanceUpdate), and an apps::InstanceUpdate also has a
+  // StateIsNull method.
+  //
+  // The apps::InstanceUpdate argument to f shouldn't be accessed after f
+  // returns.
+  //
+  // f must be synchronous, and if it asynchronously calls ForEachInstance
+  // again, it's not guaranteed to see a consistent state.
+  template <typename FunctionType>
+  void ForEachInstance(FunctionType f) {
+    DCHECK_CALLED_ON_VALID_SEQUENCE(my_sequence_checker_);
+
+    for (const auto& s_iter : states_) {
+      apps::Instance* state = s_iter.second.get();
+
+      auto d_iter = deltas_in_progress_.find(s_iter.first);
+      apps::Instance* delta =
+          (d_iter != deltas_in_progress_.end()) ? d_iter->second : nullptr;
+
+      f(apps::InstanceUpdate(state, delta));
+    }
+
+    for (const auto& d_iter : deltas_in_progress_) {
+      apps::Instance* delta = d_iter.second;
+
+      auto s_iter = states_.find(d_iter.first);
+      if (s_iter != states_.end()) {
+        continue;
+      }
+
+      f(apps::InstanceUpdate(nullptr, delta));
+    }
+  }
+
+  // Calls f, a void-returning function whose arguments are (const
+  // apps::InstanceUpdate&), on the instance in the instance_registry with the
+  // given window. It will return true (and call f) if there is such an
+  // instance, otherwise it will return false (and not call f). The
+  // InstanceUpdate argument to f has the same semantics as for ForEachInstance,
+  // above.
+  //
+  // f must be synchronous, and if it asynchronously calls ForOneInstance again,
+  // it's not guaranteed to see a consistent state.
+  template <typename FunctionType>
+  bool ForOneInstance(const aura::Window* window, FunctionType f) {
+    DCHECK_CALLED_ON_VALID_SEQUENCE(my_sequence_checker_);
+
+    auto s_iter = states_.find(window);
+    apps::Instance* state =
+        (s_iter != states_.end()) ? s_iter->second.get() : nullptr;
+
+    auto d_iter = deltas_in_progress_.find(window);
+    apps::Instance* delta =
+        (d_iter != deltas_in_progress_.end()) ? d_iter->second : nullptr;
+
+    if (state || delta) {
+      f(apps::InstanceUpdate(state, delta));
+      return true;
+    }
+    return false;
+  }
+
+ private:
+  void DoOnInstances(const Instances& deltas);
+
+  base::ObserverList<Observer> observers_;
+
+  // Maps from window to the latest state: the "sum" of all previous deltas.
+  std::map<const aura::Window*, InstancePtr> states_;
+
+  // Track the deltas being processed or are about to be processed by
+  // OnInstances. They are separate to manage the "notification and merging
+  // might be delayed until after OnInstances returns" concern described above.
+  //
+  // OnInstances calls DoOnInstances zero or more times. If we're nested, so
+  // that there's multiple OnInstances call to this InstanceRegistry in the call
+  // stack, the deeper OnInstances call simply adds work to deltas_pending_ and
+  // returns without calling DoOnInstances. If we're not nested, OnInstances
+  // calls DoOnInstances one or more times; "more times" happens if
+  // DoOnInstances notifying observers leads to more OnInstances calls that
+  // enqueue deltas_pending_ work. The deltas_in_progress_ map (keyed by window)
+  // contains those deltas being considered by DoOnInstances.
+  //
+  // Nested OnInstances calls are expected to be rare (but still dealt with
+  // sensibly). In the typical case, OnInstances should call DoOnInstances
+  // exactly once, and deltas_pending_ will stay empty.
+  std::map<const aura::Window*, Instance*> deltas_in_progress_;
+  Instances deltas_pending_;
+
+  SEQUENCE_CHECKER(my_sequence_checker_);
+};
+
+}  // namespace apps
+
+#endif  // CHROME_SERVICES_APP_SERVICE_PUBLIC_CPP_INSTANCE_REGISTRY_H_
diff --git a/chrome/services/app_service/public/cpp/instance_registry_unittest.cc b/chrome/services/app_service/public/cpp/instance_registry_unittest.cc
new file mode 100644
index 0000000..80bade8
--- /dev/null
+++ b/chrome/services/app_service/public/cpp/instance_registry_unittest.cc
@@ -0,0 +1,416 @@
+// 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 <memory>
+#include <vector>
+
+#include "chrome/services/app_service/public/cpp/instance.h"
+#include "chrome/services/app_service/public/cpp/instance_registry.h"
+#include "chrome/services/app_service/public/cpp/instance_update.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "ui/aura/window.h"
+
+class InstanceRegistryTest : public testing::Test,
+                             public apps::InstanceRegistry::Observer {
+ protected:
+  static std::unique_ptr<apps::Instance> MakeInstance(
+      const char* app_id,
+      aura::Window* window,
+      apps::InstanceState state = apps::InstanceState::kUnknown,
+      base::Time time = base::Time()) {
+    std::unique_ptr<apps::Instance> instance =
+        std::make_unique<apps::Instance>(app_id, window);
+    instance->UpdateState(state, time);
+    return instance;
+  }
+
+  void CallForEachInstance(apps::InstanceRegistry& instance_registry) {
+    instance_registry.ForEachInstance(
+        [this](const apps::InstanceUpdate& update) {
+          OnInstanceUpdate(update);
+        });
+  }
+
+  apps::InstanceState GetState(apps::InstanceRegistry& instance_registry,
+                               const aura::Window* window) {
+    apps::InstanceState state = apps::InstanceState::kUnknown;
+    instance_registry.ForOneInstance(
+        window, [&state](const apps::InstanceUpdate& update) {
+          state = update.State();
+        });
+    return state;
+  }
+
+  // apps::InstanceRegistry::Observer overrides.
+  void OnInstanceUpdate(const apps::InstanceUpdate& update) override {
+    EXPECT_NE("", update.AppId());
+    if (update.StateChanged() &&
+        update.State() == apps::InstanceState::kRunning) {
+      num_running_apps_++;
+    }
+    updated_ids_.insert(update.AppId());
+    updated_windows_.insert(update.Window());
+  }
+
+  void OnInstanceRegistryWillBeDestroyed(
+      apps::InstanceRegistry* instance_registry) override {
+    // The test code explicitly calls both AddObserver and RemoveObserver.
+    NOTREACHED();
+  }
+
+  int num_running_apps_ = 0;
+  std::set<std::string> updated_ids_;
+  std::set<const aura::Window*> updated_windows_;
+};
+
+// In the tests below, just "recursive" means that instance_registry.OnInstances
+// calls observer.OnInstanceUpdate which calls instance_registry.ForEachInstance
+// and instance_registry.ForOneInstance. "Super-recursive" means that
+// instance_registry.OnInstances calls observer.OnInstanceUpdate calls
+// instance_registry.OnInstances which calls observer.OnInstanceUpdate.
+class InstanceRecursiveObserver : public apps::InstanceRegistry::Observer {
+ public:
+  explicit InstanceRecursiveObserver(apps::InstanceRegistry* instance_registry)
+      : instance_registry_(instance_registry) {
+    Observe(instance_registry);
+  }
+
+  ~InstanceRecursiveObserver() override = default;
+
+  void PrepareForOnInstances(int expected_num_instances,
+                             apps::InstanceState expected_status_for_p,
+                             std::vector<std::unique_ptr<apps::Instance>>*
+                                 super_recursive_instances = nullptr) {
+    expected_num_instances_ = expected_num_instances;
+    expected_status_for_p_ = expected_status_for_p;
+    num_instances_seen_on_instance_update_ = 0;
+
+    if (super_recursive_instances) {
+      super_recursive_instances_.swap(*super_recursive_instances);
+    }
+  }
+
+  int NumInstancesSeenOnInstanceUpdate() {
+    return num_instances_seen_on_instance_update_;
+  }
+
+ protected:
+  // apps::InstanceRegistry::Observer overrides.
+  void OnInstanceUpdate(const apps::InstanceUpdate& outer) override {
+    int num_instance = 0;
+    instance_registry_->ForEachInstance(
+        [this, &outer, &num_instance](const apps::InstanceUpdate& inner) {
+          if (outer.Window() == inner.Window()) {
+            ExpectEq(outer, inner);
+          }
+          if (inner.AppId() == "p") {
+            EXPECT_EQ(expected_status_for_p_, inner.State());
+          }
+          num_instance++;
+        });
+
+    EXPECT_TRUE(instance_registry_->ForOneInstance(
+        outer.Window(), [&outer](const apps::InstanceUpdate& inner) {
+          ExpectEq(outer, inner);
+        }));
+
+    if (expected_num_instances_ >= 0) {
+      EXPECT_EQ(expected_num_instances_, num_instance);
+    }
+
+    std::vector<std::unique_ptr<apps::Instance>> super_recursive;
+    while (!super_recursive_instances_.empty()) {
+      std::unique_ptr<apps::Instance> instance =
+          std::move(super_recursive_instances_.back());
+      if (instance.get() == nullptr) {
+        // This is the placeholder 'punctuation'.
+        super_recursive_instances_.pop_back();
+        break;
+      }
+      super_recursive.push_back(std::move(instance));
+      super_recursive_instances_.pop_back();
+    }
+    if (!super_recursive.empty()) {
+      instance_registry_->OnInstances(std::move(super_recursive));
+    }
+
+    num_instances_seen_on_instance_update_++;
+  }
+
+  void OnInstanceRegistryWillBeDestroyed(
+      apps::InstanceRegistry* instance_registry) override {
+    Observe(nullptr);
+  }
+
+  static void ExpectEq(const apps::InstanceUpdate& outer,
+                       const apps::InstanceUpdate& inner) {
+    EXPECT_EQ(outer.AppId(), inner.AppId());
+    EXPECT_EQ(outer.StateIsNull(), inner.StateIsNull());
+    EXPECT_EQ(outer.Window(), inner.Window());
+    EXPECT_EQ(outer.State(), inner.State());
+  }
+
+  apps::InstanceRegistry* instance_registry_;
+  apps::InstanceState expected_status_for_p_;
+  int expected_num_instances_;
+  int num_instances_seen_on_instance_update_;
+
+  // Non-empty when this.OnInstanceUpdate should trigger more
+  // instance_registry_.OnInstances calls.
+  //
+  // During OnInstanceUpdate, this vector (a stack) is popped from the back
+  // until a nullptr 'punctuation' element (a group terminator) is seen. If that
+  // group of popped elements (in LIFO order) is non-empty, that group forms the
+  // vector of App's passed to instance_registry_.OnInstances.
+  std::vector<std::unique_ptr<apps::Instance>> super_recursive_instances_;
+};
+
+TEST_F(InstanceRegistryTest, ForEachInstance) {
+  std::vector<std::unique_ptr<apps::Instance>> deltas;
+  apps::InstanceRegistry instance_registry;
+
+  updated_windows_.clear();
+  updated_ids_.clear();
+
+  CallForEachInstance(instance_registry);
+
+  EXPECT_EQ(0u, updated_windows_.size());
+  EXPECT_EQ(0u, updated_ids_.size());
+
+  deltas.clear();
+  aura::Window window1(nullptr);
+  window1.Init(ui::LAYER_NOT_DRAWN);
+  aura::Window window2(nullptr);
+  window2.Init(ui::LAYER_NOT_DRAWN);
+  aura::Window window3(nullptr);
+  window3.Init(ui::LAYER_NOT_DRAWN);
+  deltas.push_back(MakeInstance("a", &window1));
+  deltas.push_back(MakeInstance("b", &window2));
+  deltas.push_back(MakeInstance("c", &window3));
+  instance_registry.OnInstances(std::move(deltas));
+
+  updated_windows_.clear();
+  updated_ids_.clear();
+  CallForEachInstance(instance_registry);
+
+  EXPECT_EQ(3u, updated_windows_.size());
+  EXPECT_EQ(3u, updated_ids_.size());
+  EXPECT_NE(updated_windows_.end(), updated_windows_.find(&window1));
+  EXPECT_NE(updated_windows_.end(), updated_windows_.find(&window2));
+  EXPECT_NE(updated_windows_.end(), updated_windows_.find(&window3));
+  EXPECT_NE(updated_ids_.end(), updated_ids_.find("a"));
+  EXPECT_NE(updated_ids_.end(), updated_ids_.find("b"));
+  EXPECT_NE(updated_ids_.end(), updated_ids_.find("c"));
+
+  deltas.clear();
+  aura::Window window4(nullptr);
+  window4.Init(ui::LAYER_NOT_DRAWN);
+  deltas.push_back(MakeInstance("a", &window1, apps::InstanceState::kRunning));
+  deltas.push_back(MakeInstance("c", &window4));
+  instance_registry.OnInstances(std::move(deltas));
+
+  updated_windows_.clear();
+  updated_ids_.clear();
+  CallForEachInstance(instance_registry);
+
+  EXPECT_EQ(4u, updated_windows_.size());
+  EXPECT_EQ(3u, updated_ids_.size());
+  EXPECT_NE(updated_windows_.end(), updated_windows_.find(&window1));
+  EXPECT_NE(updated_windows_.end(), updated_windows_.find(&window2));
+  EXPECT_NE(updated_windows_.end(), updated_windows_.find(&window3));
+  EXPECT_NE(updated_windows_.end(), updated_windows_.find(&window4));
+  EXPECT_NE(updated_ids_.end(), updated_ids_.find("a"));
+  EXPECT_NE(updated_ids_.end(), updated_ids_.find("b"));
+  EXPECT_NE(updated_ids_.end(), updated_ids_.find("c"));
+
+  // Test that ForOneApp succeeds for window4 and fails for window5.
+
+  bool found_window4 = false;
+  EXPECT_TRUE(instance_registry.ForOneInstance(
+      &window4, [&found_window4](const apps::InstanceUpdate& update) {
+        found_window4 = true;
+        EXPECT_EQ("c", update.AppId());
+      }));
+  EXPECT_TRUE(found_window4);
+
+  bool found_window5 = false;
+  aura::Window window5(nullptr);
+  window5.Init(ui::LAYER_NOT_DRAWN);
+  EXPECT_FALSE(instance_registry.ForOneInstance(
+      &window5, [&found_window5](const apps::InstanceUpdate& update) {
+        found_window5 = true;
+      }));
+  EXPECT_FALSE(found_window5);
+}
+
+TEST_F(InstanceRegistryTest, Observer) {
+  std::vector<std::unique_ptr<apps::Instance>> deltas;
+  apps::InstanceRegistry instance_registry;
+
+  instance_registry.AddObserver(this);
+
+  num_running_apps_ = 0;
+  updated_windows_.clear();
+  updated_ids_.clear();
+  deltas.clear();
+
+  aura::Window window1(nullptr);
+  window1.Init(ui::LAYER_NOT_DRAWN);
+  aura::Window window2(nullptr);
+  window2.Init(ui::LAYER_NOT_DRAWN);
+  aura::Window window3(nullptr);
+  window3.Init(ui::LAYER_NOT_DRAWN);
+
+  deltas.push_back(MakeInstance("a", &window1));
+  deltas.push_back(MakeInstance("c", &window2));
+  deltas.push_back(MakeInstance("a", &window3));
+  instance_registry.OnInstances(std::move(deltas));
+
+  EXPECT_EQ(0, num_running_apps_);
+  EXPECT_EQ(3u, updated_windows_.size());
+  EXPECT_EQ(2u, updated_ids_.size());
+  EXPECT_NE(updated_windows_.end(), updated_windows_.find(&window1));
+  EXPECT_NE(updated_windows_.end(), updated_windows_.find(&window2));
+  EXPECT_NE(updated_windows_.end(), updated_windows_.find(&window3));
+  EXPECT_NE(updated_ids_.end(), updated_ids_.find("a"));
+  EXPECT_NE(updated_ids_.end(), updated_ids_.find("c"));
+
+  num_running_apps_ = 0;
+  updated_ids_.clear();
+  deltas.clear();
+
+  aura::Window window4(nullptr);
+  window4.Init(ui::LAYER_NOT_DRAWN);
+
+  deltas.push_back(MakeInstance("b", &window4));
+  deltas.push_back(MakeInstance("c", &window2, apps::InstanceState::kRunning));
+  instance_registry.OnInstances(std::move(deltas));
+
+  EXPECT_EQ(1, num_running_apps_);
+  EXPECT_EQ(2u, updated_ids_.size());
+  EXPECT_NE(updated_windows_.end(), updated_windows_.find(&window2));
+  EXPECT_NE(updated_windows_.end(), updated_windows_.find(&window4));
+  EXPECT_NE(updated_ids_.end(), updated_ids_.find("b"));
+  EXPECT_NE(updated_ids_.end(), updated_ids_.find("c"));
+
+  instance_registry.RemoveObserver(this);
+
+  num_running_apps_ = 0;
+  updated_windows_.clear();
+  updated_ids_.clear();
+  deltas.clear();
+
+  aura::Window window5(nullptr);
+  window5.Init(ui::LAYER_NOT_DRAWN);
+  deltas.push_back(MakeInstance("f", &window5, apps::InstanceState::kRunning));
+  instance_registry.OnInstances(std::move(deltas));
+
+  EXPECT_EQ(0, num_running_apps_);
+  EXPECT_EQ(0u, updated_windows_.size());
+  EXPECT_EQ(0u, updated_ids_.size());
+}
+
+TEST_F(InstanceRegistryTest, Recursive) {
+  std::vector<std::unique_ptr<apps::Instance>> deltas;
+  apps::InstanceRegistry instance_registry;
+  InstanceRecursiveObserver observer(&instance_registry);
+
+  observer.PrepareForOnInstances(2, apps::InstanceState::kRunning);
+  deltas.clear();
+  aura::Window window1(nullptr);
+  window1.Init(ui::LAYER_NOT_DRAWN);
+  aura::Window window2(nullptr);
+  window2.Init(ui::LAYER_NOT_DRAWN);
+  deltas.push_back(MakeInstance("o", &window1, apps::InstanceState::kRunning));
+  deltas.push_back(MakeInstance("p", &window2, apps::InstanceState::kRunning));
+  instance_registry.OnInstances(std::move(deltas));
+  EXPECT_EQ(2, observer.NumInstancesSeenOnInstanceUpdate());
+
+  observer.PrepareForOnInstances(3, apps::InstanceState::kRunning);
+  deltas.clear();
+  aura::Window window3(nullptr);
+  window3.Init(ui::LAYER_NOT_DRAWN);
+  deltas.push_back(MakeInstance("p", &window2, apps::InstanceState::kRunning));
+  deltas.push_back(MakeInstance("q", &window3, apps::InstanceState::kRunning));
+  instance_registry.OnInstances(std::move(deltas));
+  EXPECT_EQ(2, observer.NumInstancesSeenOnInstanceUpdate());
+
+  observer.PrepareForOnInstances(3, apps::InstanceState::kActive);
+  deltas.clear();
+  deltas.push_back(MakeInstance("p", &window2, apps::InstanceState::kRunning));
+  deltas.push_back(MakeInstance("p", &window2, apps::InstanceState::kRunning));
+  deltas.push_back(MakeInstance("p", &window2, apps::InstanceState::kActive));
+  instance_registry.OnInstances(std::move(deltas));
+  EXPECT_EQ(1, observer.NumInstancesSeenOnInstanceUpdate());
+}
+
+TEST_F(InstanceRegistryTest, SuperRecursive) {
+  std::vector<std::unique_ptr<apps::Instance>> deltas;
+  apps::InstanceRegistry instance_registry;
+  InstanceRecursiveObserver observer(&instance_registry);
+
+  // Set up a series of OnInstances to be called during
+  // observer.OnInstanceUpdate:
+  //  - the 1st update is {'a', &window2, kActive}.
+  //  - the 2nd update is {'b', &window3, kActive}.
+  //  - the 3rd update is {'c', &window4, kActive}.
+  //  - the 4th update is {'b', &window5, kVisible}.
+  //  - the 5th update is {'c', &window4, kVisible}.
+  //  - the 6td update is {'b', &window3, kRunning}.
+  //  - the 7th update is {'a', &window2, kRunning}.
+  //  - the 8th update is {'b', &window1, kStarted}.
+  //
+  // The vector is processed in LIFO order with nullptr punctuation to
+  // terminate each group. See the comment on the
+  // RecursiveObserver::super_recursive_apps_ field.
+  std::vector<std::unique_ptr<apps::Instance>> super_recursive_apps;
+  aura::Window window1(nullptr);
+  window1.Init(ui::LAYER_NOT_DRAWN);
+  aura::Window window2(nullptr);
+  window2.Init(ui::LAYER_NOT_DRAWN);
+  aura::Window window3(nullptr);
+  window3.Init(ui::LAYER_NOT_DRAWN);
+  aura::Window window4(nullptr);
+  window4.Init(ui::LAYER_NOT_DRAWN);
+  aura::Window window5(nullptr);
+  window5.Init(ui::LAYER_NOT_DRAWN);
+  super_recursive_apps.push_back(nullptr);
+  super_recursive_apps.push_back(
+      MakeInstance("b", &window1, apps::InstanceState::kStarted));
+  super_recursive_apps.push_back(nullptr);
+  super_recursive_apps.push_back(nullptr);
+  super_recursive_apps.push_back(MakeInstance("a", &window2));
+  super_recursive_apps.push_back(nullptr);
+  super_recursive_apps.push_back(MakeInstance("b", &window3));
+  super_recursive_apps.push_back(
+      MakeInstance("a", &window2, apps::InstanceState::kRunning));
+  super_recursive_apps.push_back(
+      MakeInstance("b", &window3, apps::InstanceState::kRunning));
+  super_recursive_apps.push_back(nullptr);
+  super_recursive_apps.push_back(nullptr);
+  super_recursive_apps.push_back(
+      MakeInstance("c", &window4, apps::InstanceState::kVisible));
+  super_recursive_apps.push_back(
+      MakeInstance("b", &window5, apps::InstanceState::kVisible));
+
+  observer.PrepareForOnInstances(-1, apps::InstanceState::kRunning,
+                                 &super_recursive_apps);
+  deltas.clear();
+  deltas.push_back(MakeInstance("a", &window2, apps::InstanceState::kActive));
+  deltas.push_back(MakeInstance("b", &window3, apps::InstanceState::kActive));
+  deltas.push_back(MakeInstance("c", &window4, apps::InstanceState::kActive));
+  instance_registry.OnInstances(std::move(deltas));
+
+  // After all of that, check that for each window, the last delta won.
+  EXPECT_EQ(apps::InstanceState::kStarted,
+            GetState(instance_registry, &window1));
+  EXPECT_EQ(apps::InstanceState::kRunning,
+            GetState(instance_registry, &window2));
+  EXPECT_EQ(apps::InstanceState::kRunning,
+            GetState(instance_registry, &window3));
+  EXPECT_EQ(apps::InstanceState::kVisible,
+            GetState(instance_registry, &window4));
+  EXPECT_EQ(apps::InstanceState::kVisible,
+            GetState(instance_registry, &window5));
+}
diff --git a/chrome/services/app_service/public/cpp/instance_update.cc b/chrome/services/app_service/public/cpp/instance_update.cc
new file mode 100644
index 0000000..544b8eda
--- /dev/null
+++ b/chrome/services/app_service/public/cpp/instance_update.cc
@@ -0,0 +1,104 @@
+// 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/services/app_service/public/cpp/instance_update.h"
+
+#include "base/strings/string_util.h"
+#include "chrome/services/app_service/public/cpp/instance.h"
+#include "ui/aura/window.h"
+
+namespace apps {
+
+// static
+void InstanceUpdate::Merge(Instance* state, const Instance* delta) {
+  DCHECK(state);
+  if (!delta) {
+    return;
+  }
+  if ((delta->AppId() != state->AppId()) ||
+      delta->Window() != state->Window()) {
+    LOG(ERROR) << "inconsistent (app_id, window): (" << delta->AppId() << ", "
+               << delta->Window() << ") vs (" << state->AppId() << ", "
+               << state->Window() << ") ";
+    DCHECK(false);
+    return;
+  }
+  if (!delta->LaunchId().empty()) {
+    state->SetLaunchId(delta->LaunchId());
+  }
+  if (delta->State() != InstanceState::kUnknown) {
+    state->UpdateState(delta->State(), delta->LastUpdatedTime());
+  }
+  // When adding new fields to the Instance class, this function should also be
+  // updated.
+}
+
+InstanceUpdate::InstanceUpdate(Instance* state, Instance* delta)
+    : state_(state), delta_(delta) {
+  DCHECK(state_ || delta_);
+  if (state_ && delta_) {
+    DCHECK(state_->AppId() == delta->AppId());
+    DCHECK(state_->Window() == delta->Window());
+  }
+}
+
+bool InstanceUpdate::StateIsNull() const {
+  return state_ == nullptr;
+}
+
+const std::string& InstanceUpdate::AppId() const {
+  return delta_ ? delta_->AppId() : state_->AppId();
+}
+
+const aura::Window* InstanceUpdate::Window() const {
+  return delta_ ? delta_->Window() : state_->Window();
+}
+
+const std::string& InstanceUpdate::LaunchId() const {
+  if (delta_ && !delta_->LaunchId().empty()) {
+    return delta_->LaunchId();
+  }
+  if (state_ && !state_->LaunchId().empty()) {
+    return state_->LaunchId();
+  }
+  return base::EmptyString();
+}
+
+bool InstanceUpdate::LaunchIdChanged() const {
+  return delta_ && !delta_->LaunchId().empty() &&
+         (!state_ || (delta_->LaunchId() != state_->LaunchId()));
+}
+
+InstanceState InstanceUpdate::State() const {
+  if (delta_ && (delta_->State() != InstanceState::kUnknown)) {
+    return delta_->State();
+  }
+  if (state_) {
+    return state_->State();
+  }
+  return InstanceState::kUnknown;
+}
+
+bool InstanceUpdate::StateChanged() const {
+  return delta_ && (delta_->State() != InstanceState::kUnknown) &&
+         (!state_ || (delta_->State() != state_->State()));
+}
+
+base::Time InstanceUpdate::LastUpdatedTime() const {
+  if (delta_ && !delta_->LastUpdatedTime().is_null()) {
+    return delta_->LastUpdatedTime();
+  }
+  if (state_ && !state_->LastUpdatedTime().is_null()) {
+    return state_->LastUpdatedTime();
+  }
+
+  return base::Time();
+}
+
+bool InstanceUpdate::LastUpdatedTimeChanged() const {
+  return delta_ && !delta_->LastUpdatedTime().is_null() &&
+         (!state_ || (delta_->LastUpdatedTime() != state_->LastUpdatedTime()));
+}
+
+}  // namespace apps
diff --git a/chrome/services/app_service/public/cpp/instance_update.h b/chrome/services/app_service/public/cpp/instance_update.h
new file mode 100644
index 0000000..53d3d82
--- /dev/null
+++ b/chrome/services/app_service/public/cpp/instance_update.h
@@ -0,0 +1,80 @@
+// 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_SERVICES_APP_SERVICE_PUBLIC_CPP_INSTANCE_UPDATE_H_
+#define CHROME_SERVICES_APP_SERVICE_PUBLIC_CPP_INSTANCE_UPDATE_H_
+
+#include <string>
+
+#include "base/time/time.h"
+#include "chrome/services/app_service/public/cpp/instance.h"
+
+namespace aura {
+class Window;
+}
+
+namespace apps {
+
+class Instance;
+
+// Wraps two Instance's, a prior state and a delta on top of that state. The
+// state is conceptually the "sum" of all of the previous deltas, with
+// "addition" or "merging" simply being that the most recent version of each
+// field "wins".
+//
+// The state may be nullptr, meaning that there are no previous deltas.
+// Alternatively, the delta may be nullptr, meaning that there is no change in
+// state. At least one of state and delta must be non-nullptr.
+//
+// The combination of the two (state and delta) can answer questions such as:
+//  - What is the app's launch_id? If the delta knows, that's the answer.
+//  Otherwise, ask the state.
+//  - Is the app launched? Likewise, if the delta says yes or no, that's the
+//  answer. Otherwise, the delta says "unknown", ask the state.
+//
+// An InstanceUpdate is read-only once constructed. All of its fields and
+// methods are const. The constructor caller must guarantee that the Instance
+// pointer remain valid for the lifetime of the AppUpdate.
+//
+// See the below two design docs for more details:
+// go/app-service-instance-registry-design-doc
+// go/appservice-for-per-app-time-limit-design-doc
+class InstanceUpdate {
+ public:
+  // Modifies |state| by copying over all of |delta|'s known fields: those
+  // fields whose values aren't "unknown" or invalid. The |state| may not be
+  // nullptr.
+  static void Merge(Instance* state, const Instance* delta);
+
+  // At most one of |state| or |delta| may be nullptr.
+  InstanceUpdate(Instance* state, Instance* delta);
+
+  InstanceUpdate(const InstanceUpdate&) = delete;
+  InstanceUpdate& operator=(const InstanceUpdate&) = delete;
+
+  // Returns whether this is the first update for the given window.
+  // Equivalently, there are no previous deltas for the window.
+  bool StateIsNull() const;
+
+  const std::string& AppId() const;
+
+  const aura::Window* Window() const;
+
+  const std::string& LaunchId() const;
+  bool LaunchIdChanged() const;
+
+  InstanceState State() const;
+  bool StateChanged() const;
+
+  base::Time LastUpdatedTime() const;
+  bool LastUpdatedTimeChanged() const;
+
+ private:
+  Instance* state_;
+  Instance* delta_;
+};
+
+}  // namespace apps
+
+#endif  // CHROME_SERVICES_APP_SERVICE_PUBLIC_CPP_INSTANCE_UPDATE_H_
diff --git a/chrome/services/app_service/public/cpp/instance_update_unittest.cc b/chrome/services/app_service/public/cpp/instance_update_unittest.cc
new file mode 100644
index 0000000..107cbdb1
--- /dev/null
+++ b/chrome/services/app_service/public/cpp/instance_update_unittest.cc
@@ -0,0 +1,128 @@
+// 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/services/app_service/public/cpp/instance_update.h"
+#include "chrome/services/app_service/public/cpp/instance.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "ui/aura/window.h"
+
+namespace {
+
+const char app_id[] = "abcdefgh";
+const char test_launch_id0[] = "abc";
+const char test_launch_id1[] = "xyz";
+
+}  // namespace
+
+class InstanceUpdateTest : public testing::Test {
+ protected:
+  void ExpectNoChange() {
+    expect_launch_id_changed_ = false;
+    expect_state_changed_ = false;
+    expect_last_updated_time_changed_ = false;
+  }
+
+  void CheckExpects(const apps::InstanceUpdate& u) {
+    EXPECT_EQ(expect_launch_id_, u.LaunchId());
+    EXPECT_EQ(expect_launch_id_changed_, u.LaunchIdChanged());
+    EXPECT_EQ(expect_state_, u.State());
+    EXPECT_EQ(expect_state_changed_, u.StateChanged());
+    EXPECT_EQ(expect_last_updated_time_, u.LastUpdatedTime());
+    EXPECT_EQ(expect_last_updated_time_changed_, u.LastUpdatedTimeChanged());
+  }
+
+  void TestInstanceUpdate(apps::Instance* state, apps::Instance* delta) {
+    apps::InstanceUpdate u(state, delta);
+    EXPECT_EQ(app_id, u.AppId());
+    EXPECT_EQ(state == nullptr, u.StateIsNull());
+    expect_launch_id_ = base::EmptyString();
+    expect_state_ = apps::InstanceState::kUnknown;
+    expect_last_updated_time_ = base::Time();
+
+    ExpectNoChange();
+    CheckExpects(u);
+
+    if (delta) {
+      delta->SetLaunchId(test_launch_id0);
+      expect_launch_id_ = test_launch_id0;
+      expect_launch_id_changed_ = true;
+      CheckExpects(u);
+    }
+    if (state) {
+      state->SetLaunchId(test_launch_id0);
+      expect_launch_id_ = test_launch_id0;
+      expect_launch_id_changed_ = false;
+      CheckExpects(u);
+    }
+    if (state) {
+      apps::InstanceUpdate::Merge(state, delta);
+      ExpectNoChange();
+      CheckExpects(u);
+    }
+    if (delta) {
+      delta->SetLaunchId(test_launch_id1);
+      expect_launch_id_ = test_launch_id1;
+      expect_launch_id_changed_ = true;
+      CheckExpects(u);
+    }
+
+    // State and StateTime tests.
+    if (state) {
+      state->UpdateState(apps::InstanceState::kRunning,
+                         base::Time::FromDoubleT(1000.0));
+      expect_state_ = apps::InstanceState::kRunning;
+      expect_last_updated_time_ = base::Time::FromDoubleT(1000.0);
+      expect_state_changed_ = false;
+      expect_last_updated_time_changed_ = false;
+      CheckExpects(u);
+    }
+    if (delta) {
+      delta->UpdateState(apps::InstanceState::kActive,
+                         base::Time::FromDoubleT(2000.0));
+      expect_state_ = apps::InstanceState::kActive;
+      expect_last_updated_time_ = base::Time::FromDoubleT(2000.0);
+      expect_state_changed_ = true;
+      expect_last_updated_time_changed_ = true;
+      CheckExpects(u);
+    }
+    if (state) {
+      apps::InstanceUpdate::Merge(state, delta);
+      ExpectNoChange();
+      CheckExpects(u);
+    }
+  }
+
+  std::string expect_launch_id_;
+  bool expect_launch_id_changed_;
+  apps::InstanceState expect_state_;
+  bool expect_state_changed_;
+  base::Time expect_last_updated_time_;
+  bool expect_last_updated_time_changed_;
+};
+
+TEST_F(InstanceUpdateTest, StateIsNonNull) {
+  aura::Window window(nullptr);
+  window.Init(ui::LAYER_NOT_DRAWN);
+  std::unique_ptr<apps::Instance> state =
+      std::make_unique<apps::Instance>(app_id, &window);
+  TestInstanceUpdate(state.get(), nullptr);
+}
+
+TEST_F(InstanceUpdateTest, DeltaIsNonNull) {
+  aura::Window window(nullptr);
+  window.Init(ui::LAYER_NOT_DRAWN);
+  std::unique_ptr<apps::Instance> delta =
+      std::make_unique<apps::Instance>(app_id, &window);
+  TestInstanceUpdate(nullptr, delta.get());
+}
+
+TEST_F(InstanceUpdateTest, BothAreNonNull) {
+  aura::Window window(nullptr);
+  window.Init(ui::LAYER_NOT_DRAWN);
+  std::unique_ptr<apps::Instance> state =
+      std::make_unique<apps::Instance>(app_id, &window);
+  std::unique_ptr<apps::Instance> delta =
+      std::make_unique<apps::Instance>(app_id, &window);
+  TestInstanceUpdate(state.get(), delta.get());
+}
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn
index 8129540d..c14d79d 100644
--- a/chrome/test/BUILD.gn
+++ b/chrome/test/BUILD.gn
@@ -1125,7 +1125,7 @@
       "../browser/sharing/sharing_browsertest.cc",
       "../browser/sharing/sharing_browsertest.h",
       "../browser/signin/consistency_cookie_browsertest.cc",
-      "../browser/signin/e2e_tests/demo_signin_e2e_test.cc",
+      "../browser/signin/e2e_tests/live_sign_in_test.cc",
       "../browser/signin/e2e_tests/live_test.cc",
       "../browser/signin/e2e_tests/live_test.h",
       "../browser/site_isolation/chrome_site_per_process_browsertest.cc",
diff --git a/chrome/test/chromedriver/js/call_function.js b/chrome/test/chromedriver/js/call_function.js
index 99ed6cf..c76cd01 100644
--- a/chrome/test/chromedriver/js/call_function.js
+++ b/chrome/test/chromedriver/js/call_function.js
@@ -82,7 +82,7 @@
  * @constructor
  */
 function CacheWithUUID() {
-  this.cache_ = {};
+  this.cache_ = Object.create(null);
 }
 
 CacheWithUUID.prototype = {
@@ -146,7 +146,7 @@
  * @constructor
  */
 function Cache() {
-  this.cache_ = {};
+  this.cache_ = Object.create(null);
   this.nextId_ = 1;
   this.idPrefix_ = Math.random().toString();
 }
diff --git a/chrome/test/chromedriver/js/call_function_test.html b/chrome/test/chromedriver/js/call_function_test.html
index 25dd7401..2b97c29 100644
--- a/chrome/test/chromedriver/js/call_function_test.html
+++ b/chrome/test/chromedriver/js/call_function_test.html
@@ -11,8 +11,9 @@
   return jsonDeserialize(value, [], opt_cache);
 }
 
+var initialCache = getPageCache().cache_;
 function clearCache() {
-  getPageCache().cache_ = {};
+  getPageCache().cache_ = Object.create(initialCache);
 }
 
 function isValidUUID(uuid) {
@@ -349,6 +350,26 @@
   runner.waitForAsync();
 }
 
+function testCallWithModifiedObjectProto(runner) {
+  var callCount = 0;
+  Object.defineProperty(Object.prototype, 'f', {
+    enumerable: true,
+    configurable: true,
+    get: function() {
+      callCount += 1;
+    }
+  });
+
+  callFunction(function() {}, []).then(() => {
+    try {
+      assertEquals(callCount, 0);
+      runner.continueTesting();
+    } finally {
+      delete Object.prototype.f;
+    }
+  });
+  runner.waitForAsync();
+}
 </script>
 <body>
 <div><span></span></div>
diff --git a/chrome/test/data/android/render_tests/StartSurfaceLayoutTest.10_web_tabs-select_last.Nexus_5-19.png.sha1 b/chrome/test/data/android/render_tests/StartSurfaceLayoutTest.10_web_tabs-select_last.Nexus_5-19.png.sha1
new file mode 100644
index 0000000..15055971
--- /dev/null
+++ b/chrome/test/data/android/render_tests/StartSurfaceLayoutTest.10_web_tabs-select_last.Nexus_5-19.png.sha1
@@ -0,0 +1 @@
+79e8fa909fc5955968b1cab646173ffa1796447d
\ No newline at end of file
diff --git a/chrome/test/data/android/render_tests/StartSurfaceLayoutTest.10_web_tabs.Nexus_5-19.png.sha1 b/chrome/test/data/android/render_tests/StartSurfaceLayoutTest.10_web_tabs.Nexus_5-19.png.sha1
new file mode 100644
index 0000000..342f35a
--- /dev/null
+++ b/chrome/test/data/android/render_tests/StartSurfaceLayoutTest.10_web_tabs.Nexus_5-19.png.sha1
@@ -0,0 +1 @@
+43c97d59e40785dc7ea53e9982dcee293dbfd360
\ No newline at end of file
diff --git a/chrome/test/data/android/render_tests/StartSurfaceLayoutTest.3_incognito_web_tabs.Nexus_5-19.png.sha1 b/chrome/test/data/android/render_tests/StartSurfaceLayoutTest.3_incognito_web_tabs.Nexus_5-19.png.sha1
new file mode 100644
index 0000000..ade38dc
--- /dev/null
+++ b/chrome/test/data/android/render_tests/StartSurfaceLayoutTest.3_incognito_web_tabs.Nexus_5-19.png.sha1
@@ -0,0 +1 @@
+3e5157295e5ebba07d11d8bae3bc4e1eeeced8f9
\ No newline at end of file
diff --git a/chrome/test/data/android/render_tests/StartSurfaceLayoutTest.3_web_tabs.Nexus_5-19.png.sha1 b/chrome/test/data/android/render_tests/StartSurfaceLayoutTest.3_web_tabs.Nexus_5-19.png.sha1
new file mode 100644
index 0000000..9f91812
--- /dev/null
+++ b/chrome/test/data/android/render_tests/StartSurfaceLayoutTest.3_web_tabs.Nexus_5-19.png.sha1
@@ -0,0 +1 @@
+bafdd41367d78118cc377f20099a554268572f69
\ No newline at end of file
diff --git a/chromecast/BUILD.gn b/chromecast/BUILD.gn
index 33f90ec..e482914 100644
--- a/chromecast/BUILD.gn
+++ b/chromecast/BUILD.gn
@@ -661,7 +661,7 @@
 
     shared_libraries = [ "//chromecast/android:libcast_shell_android" ]
 
-    locale_config_java_packages = [ "org.chromium.chromecast.shell" ]
+    product_config_java_packages = [ "org.chromium.chromecast.shell" ]
 
     deps = [
       ":cast_shell_apk_assets",
diff --git a/chromecast/browser/android/BUILD.gn b/chromecast/browser/android/BUILD.gn
index 7a071ae..fb7b0e64 100644
--- a/chromecast/browser/android/BUILD.gn
+++ b/chromecast/browser/android/BUILD.gn
@@ -112,7 +112,7 @@
   ]
 }
 
-generate_locale_config_srcjar("chromecast_locale_config") {
+generate_product_config_srcjar("chromecast_product_config") {
   java_package = "org.chromium.chromecast.shell"
 }
 
@@ -148,12 +148,12 @@
 
   srcjar_deps = [
     ":cast_shell_build_config_gen",
-    ":chromecast_locale_config",
+    ":chromecast_product_config",
     ":logs_provider_aidl",
     "//chromecast/browser:java_enums",
   ]
 
-  jar_excluded_patterns = [ "*/LocaleConfig.class" ]
+  jar_excluded_patterns = [ "*/ProductConfig.class" ]
 
   deps = [
     ":cast_audio_manager_java",
diff --git a/chromecast/browser/android/apk/src/org/chromium/chromecast/shell/CastApplication.java b/chromecast/browser/android/apk/src/org/chromium/chromecast/shell/CastApplication.java
index 2d11076..16b8f1b2 100644
--- a/chromecast/browser/android/apk/src/org/chromium/chromecast/shell/CastApplication.java
+++ b/chromecast/browser/android/apk/src/org/chromium/chromecast/shell/CastApplication.java
@@ -10,6 +10,7 @@
 import org.chromium.base.ApplicationStatus;
 import org.chromium.base.ContextUtils;
 import org.chromium.base.PathUtils;
+import org.chromium.base.library_loader.LibraryLoader;
 import org.chromium.ui.base.ResourceBundle;
 
 /**
@@ -28,7 +29,9 @@
         super.attachBaseContext(base);
         ContextUtils.initApplicationContext(this);
         ResourceBundle.setAvailablePakLocales(
-                LocaleConfig.COMPRESSED_LOCALES, LocaleConfig.UNCOMPRESSED_LOCALES);
+                ProductConfig.COMPRESSED_LOCALES, ProductConfig.UNCOMPRESSED_LOCALES);
+        LibraryLoader.getInstance().setConfiguration(
+                ProductConfig.USE_CHROMIUM_LINKER, ProductConfig.USE_MODERN_LINKER);
     }
 
     @Override
diff --git a/chromecast/browser/cast_content_window.h b/chromecast/browser/cast_content_window.h
index 09ede75..3973f6e 100644
--- a/chromecast/browser/cast_content_window.h
+++ b/chromecast/browser/cast_content_window.h
@@ -32,11 +32,16 @@
   // Window occupies a portion of the screen, supporting user interaction.
   PARTIAL_OUT = 2,
 
-  // Window is hidden, and cannot be interacted with via touch.
+  // Window is hidden after dismissal by back gesture, and cannot be interacted
+  // with via touch.
   HIDDEN = 3,
 
   // Window is being displayed as a small visible tile.
-  TILE = 4
+  TILE = 4,
+
+  // Window is covered by other activities and cannot be interacted with via
+  // touch.
+  TRANSIENTLY_HIDDEN = 5
 };
 
 // Represents requested activity windowing behavior. Behavior includes:
diff --git a/chromeos/dbus/debug_daemon/debug_daemon_client.cc b/chromeos/dbus/debug_daemon/debug_daemon_client.cc
index eb9333e..437e4c39 100644
--- a/chromeos/dbus/debug_daemon/debug_daemon_client.cc
+++ b/chromeos/dbus/debug_daemon/debug_daemon_client.cc
@@ -521,10 +521,12 @@
                        weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
   }
 
-  void StartPluginVmDispatcher(PluginVmDispatcherCallback callback) override {
+  void StartPluginVmDispatcher(const std::string& owner_id,
+                               PluginVmDispatcherCallback callback) override {
     dbus::MethodCall method_call(debugd::kDebugdInterface,
                                  debugd::kStartVmPluginDispatcher);
     dbus::MessageWriter writer(&method_call);
+    writer.AppendString(owner_id);
     debugdaemon_proxy_->CallMethod(
         &method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
         base::BindOnce(&DebugDaemonClientImpl::OnStartPluginVmDispatcher,
diff --git a/chromeos/dbus/debug_daemon/debug_daemon_client.h b/chromeos/dbus/debug_daemon/debug_daemon_client.h
index 8854292..ad4a7f3 100644
--- a/chromeos/dbus/debug_daemon/debug_daemon_client.h
+++ b/chromeos/dbus/debug_daemon/debug_daemon_client.h
@@ -238,8 +238,10 @@
   // StartPluginVmDispatcher/StopPluginVmDispatcher.
   using PluginVmDispatcherCallback = base::OnceCallback<void(bool success)>;
   // Calls debugd::kStartVmPluginDispatcher, which starts the PluginVm
-  // dispatcher service. |callback| is called when the method finishes.
-  virtual void StartPluginVmDispatcher(PluginVmDispatcherCallback callback) = 0;
+  // dispatcher service on behalf of |owner_id|. |callback| is called
+  // when the method finishes.
+  virtual void StartPluginVmDispatcher(const std::string& owner_id,
+                                       PluginVmDispatcherCallback callback) = 0;
   // Calls debug::kStopVmPluginDispatcher, which stops the PluginVm dispatcher
   // service. |callback| is called when the method finishes.
   virtual void StopPluginVmDispatcher(PluginVmDispatcherCallback callback) = 0;
diff --git a/chromeos/dbus/debug_daemon/fake_debug_daemon_client.cc b/chromeos/dbus/debug_daemon/fake_debug_daemon_client.cc
index 0040b64d8a..46a4593d 100644
--- a/chromeos/dbus/debug_daemon/fake_debug_daemon_client.cc
+++ b/chromeos/dbus/debug_daemon/fake_debug_daemon_client.cc
@@ -256,6 +256,7 @@
 }
 
 void FakeDebugDaemonClient::StartPluginVmDispatcher(
+    const std::string& owner_id,
     PluginVmDispatcherCallback callback) {
   base::ThreadTaskRunnerHandle::Get()->PostTask(
       FROM_HERE, base::BindOnce(std::move(callback), true));
diff --git a/chromeos/dbus/debug_daemon/fake_debug_daemon_client.h b/chromeos/dbus/debug_daemon/fake_debug_daemon_client.h
index cd59ab4..dae150f 100644
--- a/chromeos/dbus/debug_daemon/fake_debug_daemon_client.h
+++ b/chromeos/dbus/debug_daemon/fake_debug_daemon_client.h
@@ -90,7 +90,8 @@
                          const base::Closure& error_callback) override;
   void StartConcierge(ConciergeCallback callback) override;
   void StopConcierge(ConciergeCallback callback) override;
-  void StartPluginVmDispatcher(PluginVmDispatcherCallback callback) override;
+  void StartPluginVmDispatcher(const std::string& owner_id,
+                               PluginVmDispatcherCallback callback) override;
   void StopPluginVmDispatcher(PluginVmDispatcherCallback callback) override;
   void SetRlzPingSent(SetRlzPingSentCallback callback) override;
   void SetSchedulerConfigurationV2(
diff --git a/chromeos/dbus/kerberos/fake_kerberos_client.cc b/chromeos/dbus/kerberos/fake_kerberos_client.cc
index 790b5f3..03d9ca32 100644
--- a/chromeos/dbus/kerberos/fake_kerberos_client.cc
+++ b/chromeos/dbus/kerberos/fake_kerberos_client.cc
@@ -336,6 +336,10 @@
   return result;
 }
 
+std::size_t FakeKerberosClient::GetNumberOfAccounts() const {
+  return accounts_.size();
+}
+
 void FakeKerberosClient::MaybeRecordFunctionCallForTesting(
     const char* function_name) {
   if (!recorded_function_calls_)
diff --git a/chromeos/dbus/kerberos/fake_kerberos_client.h b/chromeos/dbus/kerberos/fake_kerberos_client.h
index d889d2e..22d1e2d 100644
--- a/chromeos/dbus/kerberos/fake_kerberos_client.h
+++ b/chromeos/dbus/kerberos/fake_kerberos_client.h
@@ -52,6 +52,7 @@
   void SetTaskDelay(base::TimeDelta delay) override;
   void StartRecordingFunctionCalls() override;
   std::string StopRecordingAndGetRecordedFunctionCalls() override;
+  std::size_t GetNumberOfAccounts() const override;
 
  private:
   struct AccountData {
diff --git a/chromeos/dbus/kerberos/kerberos_client.h b/chromeos/dbus/kerberos/kerberos_client.h
index e574a3a1..54b7523 100644
--- a/chromeos/dbus/kerberos/kerberos_client.h
+++ b/chromeos/dbus/kerberos/kerberos_client.h
@@ -58,6 +58,9 @@
     // comma separated list, e.g. "AddAccount,ListAccounts".
     virtual std::string StopRecordingAndGetRecordedFunctionCalls() = 0;
 
+    // Returns the number of accounts currently saved.
+    virtual std::size_t GetNumberOfAccounts() const = 0;
+
    protected:
     virtual ~TestInterface() {}
   };
diff --git a/components/arc/bluetooth/bluetooth_mojom_traits.cc b/components/arc/bluetooth/bluetooth_mojom_traits.cc
index 9272ce6..05bb3c9c 100644
--- a/components/arc/bluetooth/bluetooth_mojom_traits.cc
+++ b/components/arc/bluetooth/bluetooth_mojom_traits.cc
@@ -10,7 +10,6 @@
 #include <utility>
 #include <vector>
 
-#include "base/stl_util.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/stringprintf.h"
 #include "device/bluetooth/bluetooth_advertisement.h"
@@ -21,16 +20,6 @@
 // BluetoothUUID helpers.
 constexpr size_t kUUIDSize = 16;
 
-bool IsNonHex(char c) {
-  return !isxdigit(c);
-}
-
-std::string StripNonHex(const std::string& str) {
-  std::string result = str;
-  base::EraseIf(result, IsNonHex);
-  return result;
-}
-
 // BluetoothAdvertisement helpers.
 struct AdvertisementEntry {
   virtual ~AdvertisementEntry() {}
@@ -115,11 +104,7 @@
     const device::BluetoothUUID& input) {
   // TODO(dcheng): Figure out what to do here, this is called twice on
   // serialization. Building a vector is a little inefficient.
-  std::string uuid_str = StripNonHex(input.canonical_value());
-
-  std::vector<uint8_t> address_bytes;
-  base::HexStringToBytes(uuid_str, &address_bytes);
-  return address_bytes;
+  return input.GetBytes();
 }
 
 // static
diff --git a/components/offline_pages/core/archive_validator_unittest.cc b/components/offline_pages/core/archive_validator_unittest.cc
index b226a2c..20de231 100644
--- a/components/offline_pages/core/archive_validator_unittest.cc
+++ b/components/offline_pages/core/archive_validator_unittest.cc
@@ -143,7 +143,8 @@
 }
 
 #if defined(OS_ANDROID)
-TEST_F(ArchiveValidatorTest, GetSizeAndComputeDigestOnContentUri) {
+// Flaky. https://crbug.com/1022323
+TEST_F(ArchiveValidatorTest, DISABLED_GetSizeAndComputeDigestOnContentUri) {
   base::FilePath content_uri_path = GetContentUriPathForTest();
   std::pair<int64_t, std::string> actual_size_and_digest =
       ArchiveValidator::GetSizeAndComputeDigest(content_uri_path);
diff --git a/components/password_manager/core/browser/form_parsing/form_parser.cc b/components/password_manager/core/browser/form_parsing/form_parser.cc
index bf178c4..6c7f61b7 100644
--- a/components/password_manager/core/browser/form_parsing/form_parser.cc
+++ b/components/password_manager/core/browser/form_parsing/form_parser.cc
@@ -913,6 +913,10 @@
 bool GetMayUsePrefilledPlaceholder(
     const base::Optional<FormPredictions>& form_predictions,
     const SignificantFields& significant_fields) {
+  if (!base::FeatureList::IsEnabled(
+          password_manager::features::kEnableOverwritingPlaceholderUsernames))
+    return false;
+
   if (!form_predictions || !significant_fields.username)
     return false;
 
diff --git a/components/password_manager/core/browser/form_parsing/form_parser_unittest.cc b/components/password_manager/core/browser/form_parsing/form_parser_unittest.cc
index 7306b8e2..8098634d 100644
--- a/components/password_manager/core/browser/form_parsing/form_parser_unittest.cc
+++ b/components/password_manager/core/browser/form_parsing/form_parser_unittest.cc
@@ -1197,6 +1197,33 @@
   });
 }
 
+TEST(FormParserTest, ServerHintsForDisabledPrefilledPlaceholderFeature) {
+  base::test::ScopedFeatureList feature_list;
+  feature_list.InitAndDisableFeature(
+      password_manager::features::kEnableOverwritingPlaceholderUsernames);
+  CheckTestData({
+      {
+          .description_for_logging = "Simple predictions work",
+          .fields =
+              {
+                  {.role = ElementRole::USERNAME,
+                   .form_control_type = "text",
+                   .prediction = {.type = autofill::USERNAME_AND_EMAIL_ADDRESS,
+                                  .may_use_prefilled_placeholder = true}},
+                  {.form_control_type = "text"},
+                  {.role_saving = ElementRole::CURRENT_PASSWORD,
+                   .form_control_type = "password"},
+                  {.role_filling = ElementRole::CURRENT_PASSWORD,
+                   .role_saving = ElementRole::NEW_PASSWORD,
+                   .form_control_type = "password",
+                   .prediction = {.type = autofill::PASSWORD,
+                                  .may_use_prefilled_placeholder = true}},
+              },
+          .username_may_use_prefilled_placeholder = false,
+      },
+  });
+}
+
 TEST(FormParserTest, ServerHints) {
   CheckTestData({
       {
diff --git a/components/password_manager/core/common/password_manager_features.cc b/components/password_manager/core/common/password_manager_features.cc
index b4fc5ac9..c5e7ac9 100644
--- a/components/password_manager/core/common/password_manager_features.cc
+++ b/components/password_manager/core/common/password_manager_features.cc
@@ -20,6 +20,11 @@
 const base::Feature kDeleteCorruptedPasswords = {
     "DeleteCorruptedPasswords", base::FEATURE_DISABLED_BY_DEFAULT};
 
+// Enables the overwriting of prefilled username fields if the server predicted
+// the field to contain a placeholder value.
+const base::Feature kEnableOverwritingPlaceholderUsernames{
+    "EnableOverwritingPlaceholderUsernames", base::FEATURE_ENABLED_BY_DEFAULT};
+
 // Enables a second, Gaia-account-scoped password store for users who are signed
 // in but not syncing.
 const base::Feature kEnablePasswordsAccountStorage = {
diff --git a/components/password_manager/core/common/password_manager_features.h b/components/password_manager/core/common/password_manager_features.h
index c211ea7..50802fe 100644
--- a/components/password_manager/core/common/password_manager_features.h
+++ b/components/password_manager/core/common/password_manager_features.h
@@ -19,6 +19,7 @@
 
 extern const base::Feature kEditPasswordsInDesktopSettings;
 extern const base::Feature kDeleteCorruptedPasswords;
+extern const base::Feature kEnableOverwritingPlaceholderUsernames;
 extern const base::Feature kEnablePasswordsAccountStorage;
 extern const base::Feature KEnablePasswordGenerationForClearTextFields;
 extern const base::Feature kFillOnAccountSelect;
diff --git a/components/password_manager/ios/resources/password_controller.js b/components/password_manager/ios/resources/password_controller.js
index 1a647d6..d2f4d266 100644
--- a/components/password_manager/ios/resources/password_controller.js
+++ b/components/password_manager/ios/resources/password_controller.js
@@ -30,8 +30,8 @@
  */
 __gCrWeb.passwords['findPasswordForms'] = function() {
   var formDataList = [];
-  if (hasPasswordField_(window)) {
-    getPasswordFormDataList_(formDataList, window);
+  if (hasPasswordField(window)) {
+    getPasswordFormDataList(formDataList, window);
   }
   return __gCrWeb.stringify(formDataList);
 };
@@ -43,7 +43,7 @@
  * contain an input field of type 'password'.
  * @return {boolean}
  */
-var hasPasswordField_ = function(win) {
+var hasPasswordField = function(win) {
   var doc = win.document;
 
   // We may will not be allowed to read the 'document' property from a frame
@@ -56,16 +56,16 @@
     return true;
   }
 
-  return getSameOriginFrames_(win).some(hasPasswordField_);
+  return getSameOriginFrames(win).some(hasPasswordField);
 };
 
 /**
  * Returns the contentWindow of all iframes that are from the the same origin
  * as the containing window.
  * @param {Window} win The window in which to look for frames.
- * @return {Array.<Window>} Array of the same-origin frames found.
+ * @return {Array<Window>} Array of the same-origin frames found.
  */
-var getSameOriginFrames_ = function(win) {
+var getSameOriginFrames = function(win) {
   var frames = win.document.getElementsByTagName('iframe');
   var result = [];
   for (var i = 0; i < frames.length; i++) {
@@ -84,7 +84,7 @@
  * a proxy for onclick event because onclick handling might be prevented by
  * the site JavaScript.
  */
-var addSubmitButtonTouchEndHandler_ = function(form) {
+var addSubmitButtonTouchEndHandler = function(form) {
   if (form.querySelector('input[type=submit]')) {
     return;
   }
@@ -99,7 +99,7 @@
     }
   }
   for (var i = 0; i < buttons.length; ++i) {
-    buttons[0].addEventListener('touchend', onSubmitButtonTouchEnd_);
+    buttons[0].addEventListener('touchend', onSubmitButtonTouchEnd);
   }
 };
 
@@ -107,7 +107,7 @@
  * Click handler for the submit button. It sends to the host
  * form.submitButtonClick command.
  */
-var onSubmitButtonTouchEnd_ = function(evt) {
+var onSubmitButtonTouchEnd = function(evt) {
   var form = evt.currentTarget.form;
   var formData = __gCrWeb.passwords.getPasswordFormData(form);
   if (!formData) {
@@ -120,11 +120,11 @@
 /**
  * Returns the element from |inputs| which has the field identifier equal to
  * |identifier| and null if there is no such element.
- * @param {Array.<HTMLInputElement>} inputs
+ * @param {Array<HTMLInputElement>} inputs
  * @param {string} identifier
  * @return {HTMLInputElement}
  */
-var findInputByFieldIdentifier_ = function(inputs, identifier) {
+var findInputByFieldIdentifier = function(inputs, identifier) {
   for (var i = 0; i < inputs.length; ++i) {
     if (identifier == __gCrWeb.form.getFieldIdentifier(inputs[i])) {
       return inputs[i];
@@ -140,14 +140,14 @@
  * @param {string} identifier The name of the form to extract.
  * @return {HTMLFormElement} The password form.
  */
-var getPasswordFormElement_ = function(win, identifier) {
+var getPasswordFormElement = function(win, identifier) {
   var el = win.__gCrWeb.form.getFormElementFromIdentifier(identifier);
   if (el) {
     return el;
   }
-  var frames = getSameOriginFrames_(win);
+  var frames = getSameOriginFrames(win);
   for (var i = 0; i < frames.length; ++i) {
-    el = getPasswordFormElement_(frames[i], identifier);
+    el = getPasswordFormElement(frames[i], identifier);
     if (el) {
       return el;
     }
@@ -161,7 +161,7 @@
  *   are returned.
  * @return {Array<HTMLInputElement>}
  */
-var getFormInputElements_ = function(form) {
+var getFormInputElements = function(form) {
   return __gCrWeb.form.getFormControlElements(form).filter(function(element) {
     return element.tagName === 'INPUT';
   });
@@ -173,7 +173,7 @@
  * @return {string} The password form.
  */
 __gCrWeb.passwords['getPasswordFormDataAsString'] = function(identifier) {
-  var el = getPasswordFormElement_(window, identifier);
+  var el = getPasswordFormElement(window, identifier);
   if (!el) {
     return '{}';
   }
@@ -205,7 +205,7 @@
   if (!__gCrWeb.common.isSameOrigin(origin, normalizedOrigin)) {
     return false;
   }
-  return fillPasswordFormWithData_(
+  return fillPasswordFormWithData(
       formData, username, password, window, opt_normalizedOrigin);
 };
 
@@ -226,9 +226,9 @@
   if (!form) {
     return false;
   }
-  var inputs = getFormInputElements_(form);
+  var inputs = getFormInputElements(form);
   var newPasswordField =
-      findInputByFieldIdentifier_(inputs, newPasswordIdentifier);
+      findInputByFieldIdentifier(inputs, newPasswordIdentifier);
   if (!newPasswordField) {
     return false;
   }
@@ -237,7 +237,7 @@
     __gCrWeb.fill.setInputElementValue(password, newPasswordField);
   }
   var confirmPasswordField =
-      findInputByFieldIdentifier_(inputs, confirmPasswordIdentifier);
+      findInputByFieldIdentifier(inputs, confirmPasswordIdentifier);
   if (confirmPasswordField && confirmPasswordField.value != password) {
     __gCrWeb.fill.setInputElementValue(password, confirmPasswordField);
   }
@@ -256,7 +256,7 @@
  * @param {string=} opt_normalizedOrigin The origin URL to compare to.
  * @return {boolean} Whether a form field has been filled.
  */
-var fillPasswordFormWithData_ = function(
+var fillPasswordFormWithData = function(
     formData, username, password, win, opt_normalizedOrigin) {
   var doc = win.document;
   var forms = doc.forms;
@@ -269,15 +269,15 @@
     if (formData.action != normalizedFormAction) {
       continue;
     }
-    var inputs = getFormInputElements_(form);
+    var inputs = getFormInputElements(form);
     var usernameInput =
-        findInputByFieldIdentifier_(inputs, formData.fields[0].name);
+        findInputByFieldIdentifier(inputs, formData.fields[0].name);
     if (usernameInput == null || !__gCrWeb.common.isTextField(usernameInput) ||
         usernameInput.disabled) {
       continue;
     }
     var passwordInput =
-        findInputByFieldIdentifier_(inputs, formData.fields[1].name);
+        findInputByFieldIdentifier(inputs, formData.fields[1].name);
     if (passwordInput == null || passwordInput.type != 'password' ||
         passwordInput.readOnly || passwordInput.disabled) {
       continue;
@@ -298,9 +298,9 @@
   }
 
   // Recursively invoke for all iframes.
-  var frames = getSameOriginFrames_(win);
+  var frames = getSameOriginFrames(win);
   for (var i = 0; i < frames.length; i++) {
-    if (fillPasswordFormWithData_(
+    if (fillPasswordFormWithData(
             formData, username, password, frames[i], opt_normalizedOrigin)) {
       filled = true;
     }
@@ -312,26 +312,26 @@
 /**
  * Finds all forms with passwords in the supplied window or frame and appends
  * JS objects containing the form data to |formDataList|.
- * @param {!Array.<Object>} formDataList A list that this function populates
+ * @param {!Array<Object>} formDataList A list that this function populates
  *     with descriptions of discovered forms.
  * @param {Window} win A window (or frame) in which the function should
  *    look for password forms.
  */
-var getPasswordFormDataList_ = function(formDataList, win) {
+var getPasswordFormDataList = function(formDataList, win) {
   var doc = win.document;
   var forms = doc.forms;
   for (var i = 0; i < forms.length; i++) {
     var formData = __gCrWeb.passwords.getPasswordFormData(forms[i]);
     if (formData) {
       formDataList.push(formData);
-      addSubmitButtonTouchEndHandler_(forms[i]);
+      addSubmitButtonTouchEndHandler(forms[i]);
     }
   }
 
   // Recursively invoke for all iframes.
-  var frames = getSameOriginFrames_(win);
+  var frames = getSameOriginFrames(win);
   for (var i = 0; i < frames.length; i++) {
-    getPasswordFormDataList_(formDataList, frames[i]);
+    getPasswordFormDataList(formDataList, frames[i]);
   }
 };
 
diff --git a/components/performance_manager/BUILD.gn b/components/performance_manager/BUILD.gn
index 6d618818..cb1d23a 100644
--- a/components/performance_manager/BUILD.gn
+++ b/components/performance_manager/BUILD.gn
@@ -103,6 +103,7 @@
     "performance_manager_tab_helper_unittest.cc",
     "performance_manager_test_harness.cc",
     "performance_manager_test_harness.h",
+    "performance_manager_unittest.cc",
     "shared_worker_watcher_unittest.cc",
     "web_contents_proxy_unittest.cc",
   ]
diff --git a/components/performance_manager/graph/page_node_impl.cc b/components/performance_manager/graph/page_node_impl.cc
index b9a4c4d..04a4aa21 100644
--- a/components/performance_manager/graph/page_node_impl.cc
+++ b/components/performance_manager/graph/page_node_impl.cc
@@ -271,6 +271,12 @@
 
 void PageNodeImpl::JoinGraph() {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+#if DCHECK_IS_ON()
+  // Dereferencing the WeakPtr associated with this node will bind it to the
+  // current sequence (all subsequent calls to |GetWeakPtr| will return the
+  // same WeakPtr).
+  GetWeakPtr()->GetImpl();
+#endif
 
   NodeBase::JoinGraph();
 }
diff --git a/components/performance_manager/graph/page_node_impl.h b/components/performance_manager/graph/page_node_impl.h
index 706d8a5..cc5e656 100644
--- a/components/performance_manager/graph/page_node_impl.h
+++ b/components/performance_manager/graph/page_node_impl.h
@@ -11,6 +11,7 @@
 
 #include "base/containers/flat_set.h"
 #include "base/macros.h"
+#include "base/memory/weak_ptr.h"
 #include "base/time/time.h"
 #include "components/performance_manager/graph/node_attached_data.h"
 #include "components/performance_manager/graph/node_base.h"
@@ -106,6 +107,14 @@
     SetPageAlmostIdle(page_almost_idle);
   }
 
+  void SetIsHoldingWebLockForTesting(bool is_holding_weblock) {
+    SetIsHoldingWebLock(is_holding_weblock);
+  }
+
+  base::WeakPtr<PageNodeImpl> GetWeakPtr() {
+    return weak_factory_.GetWeakPtr();
+  }
+
  private:
   friend class FrameNodeImpl;
   friend class PageAggregatorAccess;
@@ -251,6 +260,8 @@
   // Inline storage for PageAggregatorAccess user data.
   InternalNodeAttachedDataStorage<sizeof(uintptr_t) + 24> page_aggregator_data_;
 
+  base::WeakPtrFactory<PageNodeImpl> weak_factory_{this};
+
   DISALLOW_COPY_AND_ASSIGN(PageNodeImpl);
 };
 
diff --git a/components/performance_manager/performance_manager.cc b/components/performance_manager/performance_manager.cc
index 60d8000..c97f85c 100644
--- a/components/performance_manager/performance_manager.cc
+++ b/components/performance_manager/performance_manager.cc
@@ -4,7 +4,9 @@
 
 #include "components/performance_manager/public/performance_manager.h"
 
+#include "components/performance_manager/graph/page_node_impl.h"
 #include "components/performance_manager/performance_manager_impl.h"
+#include "components/performance_manager/performance_manager_tab_helper.h"
 
 namespace performance_manager {
 
@@ -45,4 +47,16 @@
           std::move(graph_owned)));
 }
 
+// static
+base::WeakPtr<PageNode> PerformanceManager::GetPageNodeForWebContents(
+    content::WebContents* wc) {
+  DCHECK(wc);
+  PerformanceManagerTabHelper* helper =
+      PerformanceManagerTabHelper::FromWebContents(wc);
+  if (!helper)
+    return nullptr;
+
+  return helper->page_node()->GetWeakPtr();
+}
+
 }  // namespace performance_manager
diff --git a/components/performance_manager/performance_manager_impl_unittest.cc b/components/performance_manager/performance_manager_impl_unittest.cc
index 38e008c..1810374 100644
--- a/components/performance_manager/performance_manager_impl_unittest.cc
+++ b/components/performance_manager/performance_manager_impl_unittest.cc
@@ -19,11 +19,11 @@
 
 namespace performance_manager {
 
-class PerformanceManagerTest : public testing::Test {
+class PerformanceManagerImplTest : public testing::Test {
  public:
-  PerformanceManagerTest() {}
+  PerformanceManagerImplTest() {}
 
-  ~PerformanceManagerTest() override {}
+  ~PerformanceManagerImplTest() override {}
 
   void SetUp() override {
     EXPECT_EQ(nullptr, PerformanceManagerImpl::GetInstance());
@@ -52,10 +52,10 @@
   std::unique_ptr<PerformanceManagerImpl> performance_manager_;
   base::test::TaskEnvironment task_environment_;
 
-  DISALLOW_COPY_AND_ASSIGN(PerformanceManagerTest);
+  DISALLOW_COPY_AND_ASSIGN(PerformanceManagerImplTest);
 };
 
-TEST_F(PerformanceManagerTest, InstantiateNodes) {
+TEST_F(PerformanceManagerImplTest, InstantiateNodes) {
   int next_render_frame_id = 0;
 
   std::unique_ptr<ProcessNodeImpl> process_node =
@@ -78,7 +78,7 @@
   performance_manager()->DeleteNode(std::move(process_node));
 }
 
-TEST_F(PerformanceManagerTest, BatchDeleteNodes) {
+TEST_F(PerformanceManagerImplTest, BatchDeleteNodes) {
   int next_render_frame_id = 0;
   // Create a page node and a small hierarchy of frames.
   std::unique_ptr<ProcessNodeImpl> process_node =
@@ -125,7 +125,7 @@
   performance_manager()->BatchDeleteNodes(std::move(nodes));
 }
 
-TEST_F(PerformanceManagerTest, CallOnGraphImpl) {
+TEST_F(PerformanceManagerImplTest, CallOnGraphImpl) {
   // Create a page node for something to target.
   std::unique_ptr<PageNodeImpl> page_node =
       performance_manager()->CreatePageNode(WebContentsProxy(), std::string(),
@@ -147,7 +147,7 @@
   performance_manager()->DeleteNode(std::move(page_node));
 }
 
-TEST_F(PerformanceManagerTest, CallOnGraphAndReplyWithResult) {
+TEST_F(PerformanceManagerImplTest, CallOnGraphAndReplyWithResult) {
   // Create a page node for something to target.
   std::unique_ptr<PageNodeImpl> page_node =
       performance_manager()->CreatePageNode(WebContentsProxy(), std::string(),
diff --git a/components/performance_manager/performance_manager_unittest.cc b/components/performance_manager/performance_manager_unittest.cc
new file mode 100644
index 0000000..e26bce6
--- /dev/null
+++ b/components/performance_manager/performance_manager_unittest.cc
@@ -0,0 +1,84 @@
+// 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 "components/performance_manager/public/performance_manager.h"
+
+#include "base/run_loop.h"
+#include "base/task/post_task.h"
+#include "base/test/bind_test_util.h"
+#include "components/performance_manager/performance_manager_test_harness.h"
+#include "components/performance_manager/public/graph/page_node.h"
+#include "components/performance_manager/public/web_contents_proxy.h"
+#include "content/public/browser/browser_task_traits.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace performance_manager {
+
+class PerformanceManagerTest : public PerformanceManagerTestHarness {
+ public:
+  using Super = PerformanceManagerTestHarness;
+
+  PerformanceManagerTest() {}
+
+  void SetUp() override {
+    EXPECT_FALSE(PerformanceManager::IsAvailable());
+    Super::SetUp();
+    EXPECT_TRUE(PerformanceManager::IsAvailable());
+  }
+
+  void TearDown() override {
+    EXPECT_TRUE(PerformanceManager::IsAvailable());
+    Super::TearDown();
+    EXPECT_FALSE(PerformanceManager::IsAvailable());
+  }
+
+  ~PerformanceManagerTest() override {}
+
+  DISALLOW_COPY_AND_ASSIGN(PerformanceManagerTest);
+};
+
+TEST_F(PerformanceManagerTest, GetPageNodeForWebContents) {
+  auto contents = CreateTestWebContents();
+
+  base::WeakPtr<PageNode> page_node =
+      PerformanceManager::GetPageNodeForWebContents(contents.get());
+
+  // Post a task to the Graph and make it call a function on the UI thread that
+  // will ensure that |page_node| is really associated with |contents|.
+
+  base::RunLoop run_loop;
+  auto check_wc_on_main_thread =
+      base::BindLambdaForTesting([&](const WebContentsProxy& wc_proxy) {
+        EXPECT_EQ(contents.get(), wc_proxy.Get());
+        run_loop.Quit();
+      });
+
+  auto call_on_graph_cb = base::BindLambdaForTesting([&](Graph* unused) {
+    EXPECT_TRUE(page_node.get());
+    base::PostTask(FROM_HERE, {content::BrowserThread::UI},
+                   base::BindOnce(std::move(check_wc_on_main_thread),
+                                  page_node->GetContentsProxy()));
+  });
+
+  PerformanceManager::CallOnGraph(FROM_HERE, call_on_graph_cb);
+
+  // Wait for |check_wc_on_main_thread| to be called.
+  run_loop.Run();
+
+  contents.reset();
+
+  // After deleting |contents| the corresponding PageNode WeakPtr should be
+  // invalid.
+  base::RunLoop run_loop_after_contents_reset;
+  auto quit_closure = run_loop_after_contents_reset.QuitClosure();
+  auto call_on_graph_cb_2 = base::BindLambdaForTesting([&](Graph* unused) {
+    EXPECT_FALSE(page_node.get());
+    std::move(quit_closure).Run();
+  });
+
+  PerformanceManager::CallOnGraph(FROM_HERE, call_on_graph_cb_2);
+  run_loop_after_contents_reset.Run();
+}
+
+}  // namespace performance_manager
diff --git a/components/performance_manager/public/performance_manager.h b/components/performance_manager/public/performance_manager.h
index 4686bf3..f25c9017 100644
--- a/components/performance_manager/public/performance_manager.h
+++ b/components/performance_manager/public/performance_manager.h
@@ -7,11 +7,17 @@
 
 #include "base/callback.h"
 #include "base/location.h"
+#include "base/memory/weak_ptr.h"
+
+namespace content {
+class WebContents;
+}
 
 namespace performance_manager {
 
 class Graph;
 class GraphOwned;
+class PageNode;
 
 // The performance manager is a rendezvous point for communicating with the
 // performance manager graph on its dedicated sequence.
@@ -33,6 +39,14 @@
   static void PassToGraph(const base::Location& from_here,
                           std::unique_ptr<GraphOwned> graph_owned);
 
+  // Returns a WeakPtr to the PageNode associated with a given WebContents,
+  // or a null WeakPtr if there's no PageNode for this WebContents.
+  // Valid to call from the main thread only, the returned WeakPtr should only
+  // be dereferenced on the PM sequence (e.g. it can be used in a
+  // CallOnGraph callback).
+  static base::WeakPtr<PageNode> GetPageNodeForWebContents(
+      content::WebContents* wc);
+
  protected:
   PerformanceManager();
   virtual ~PerformanceManager();
diff --git a/components/plugins/renderer/webview_plugin.cc b/components/plugins/renderer/webview_plugin.cc
index 2adb1541..172849f 100644
--- a/components/plugins/renderer/webview_plugin.cc
+++ b/components/plugins/renderer/webview_plugin.cc
@@ -21,7 +21,6 @@
 #include "mojo/public/cpp/bindings/pending_remote.h"
 #include "skia/ext/platform_canvas.h"
 #include "third_party/blink/public/common/page/page_zoom.h"
-#include "third_party/blink/public/mojom/frame/document_interface_broker.mojom.h"
 #include "third_party/blink/public/platform/scheduler/web_thread_scheduler.h"
 #include "third_party/blink/public/platform/web_coalesced_input_event.h"
 #include "third_party/blink/public/platform/web_url.h"
@@ -267,12 +266,8 @@
   // ApplyWebPreferences before making a WebLocalFrame so that the frame sees a
   // consistent view of our preferences.
   content::RenderView::ApplyWebPreferences(preferences, web_view_);
-  mojo::PendingRemote<blink::mojom::DocumentInterfaceBroker>
-      document_interface_broker;
-  WebLocalFrame* web_frame = WebLocalFrame::CreateMainFrame(
-      web_view_, this, nullptr,
-      document_interface_broker.InitWithNewPipeAndPassReceiver().PassPipe(),
-      nullptr);
+  WebLocalFrame* web_frame =
+      WebLocalFrame::CreateMainFrame(web_view_, this, nullptr, nullptr);
   // The created WebFrameWidget is owned by the |web_frame|.
   WebFrameWidget::CreateForMainFrame(this, web_frame);
 
diff --git a/components/printing/renderer/print_render_frame_helper.cc b/components/printing/renderer/print_render_frame_helper.cc
index ca398efa..4d158128 100644
--- a/components/printing/renderer/print_render_frame_helper.cc
+++ b/components/printing/renderer/print_render_frame_helper.cc
@@ -43,7 +43,6 @@
 #include "third_party/blink/public/common/associated_interfaces/associated_interface_registry.h"
 #include "third_party/blink/public/common/frame/frame_owner_element_type.h"
 #include "third_party/blink/public/common/frame/sandbox_flags.h"
-#include "third_party/blink/public/mojom/frame/document_interface_broker.mojom.h"
 #include "third_party/blink/public/platform/platform.h"
 #include "third_party/blink/public/platform/web_data.h"
 #include "third_party/blink/public/platform/web_double_size.h"
@@ -726,12 +725,8 @@
   };
 
   HeaderAndFooterClient frame_client;
-  mojo::PendingRemote<blink::mojom::DocumentInterfaceBroker>
-      document_interface_broker;
   blink::WebLocalFrame* frame = blink::WebLocalFrame::CreateMainFrame(
-      web_view, &frame_client, nullptr,
-      document_interface_broker.InitWithNewPipeAndPassReceiver().PassPipe(),
-      nullptr);
+      web_view, &frame_client, nullptr, nullptr);
 
   blink::WebWidgetClient web_widget_client;
   blink::WebFrameWidget::CreateForMainFrame(&web_widget_client, frame);
@@ -966,12 +961,8 @@
       /*compositing_enabled=*/false,
       /*opener=*/nullptr);
   content::RenderView::ApplyWebPreferences(prefs, web_view);
-  mojo::PendingRemote<blink::mojom::DocumentInterfaceBroker>
-      document_interface_broker;
-  blink::WebLocalFrame* main_frame = blink::WebLocalFrame::CreateMainFrame(
-      web_view, this, nullptr,
-      document_interface_broker.InitWithNewPipeAndPassReceiver().PassPipe(),
-      nullptr);
+  blink::WebLocalFrame* main_frame =
+      blink::WebLocalFrame::CreateMainFrame(web_view, this, nullptr, nullptr);
   frame_.Reset(main_frame);
   blink::WebFrameWidget::CreateForMainFrame(this, main_frame);
   node_to_print_.Reset();
diff --git a/components/remote_cocoa/app_shim/native_widget_ns_window_bridge.h b/components/remote_cocoa/app_shim/native_widget_ns_window_bridge.h
index 958efeb..b102035 100644
--- a/components/remote_cocoa/app_shim/native_widget_ns_window_bridge.h
+++ b/components/remote_cocoa/app_shim/native_widget_ns_window_bridge.h
@@ -187,6 +187,8 @@
   bool RedispatchKeyEvent(NSEvent* event);
 
   // display::DisplayObserver:
+  void OnDisplayAdded(const display::Display& new_display) override;
+  void OnDisplayRemoved(const display::Display& old_display) override;
   void OnDisplayMetricsChanged(const display::Display& display,
                                uint32_t metrics) override;
 
diff --git a/components/remote_cocoa/app_shim/native_widget_ns_window_bridge.mm b/components/remote_cocoa/app_shim/native_widget_ns_window_bridge.mm
index c91034e..53659a0 100644
--- a/components/remote_cocoa/app_shim/native_widget_ns_window_bridge.mm
+++ b/components/remote_cocoa/app_shim/native_widget_ns_window_bridge.mm
@@ -312,9 +312,11 @@
       text_input_host_(text_input_host) {
   DCHECK(GetIdToWidgetImplMap().find(id_) == GetIdToWidgetImplMap().end());
   GetIdToWidgetImplMap().insert(std::make_pair(id_, this));
+  display::Screen::GetScreen()->AddObserver(this);
 }
 
 NativeWidgetNSWindowBridge::~NativeWidgetNSWindowBridge() {
+  display::Screen::GetScreen()->RemoveObserver(this);
   // The delegate should be cleared already. Note this enforces the precondition
   // that -[NSWindow close] is invoked on the hosted window before the
   // destructor is called.
@@ -1122,7 +1124,17 @@
 }
 
 ////////////////////////////////////////////////////////////////////////////////
-// NativeWidgetNSWindowBridge, ui::CATransactionObserver
+// NativeWidgetNSWindowBridge, display::DisplayObserver:
+
+void NativeWidgetNSWindowBridge::OnDisplayAdded(
+    const display::Display& display) {
+  UpdateWindowDisplay();
+}
+
+void NativeWidgetNSWindowBridge::OnDisplayRemoved(
+    const display::Display& display) {
+  UpdateWindowDisplay();
+}
 
 void NativeWidgetNSWindowBridge::OnDisplayMetricsChanged(
     const display::Display& display,
diff --git a/components/security_interstitials/core/browser/resources/interstitial_large.js b/components/security_interstitials/core/browser/resources/interstitial_large.js
index 93751cb..e6befc3 100644
--- a/components/security_interstitials/core/browser/resources/interstitial_large.js
+++ b/components/security_interstitials/core/browser/resources/interstitial_large.js
@@ -135,18 +135,17 @@
   }
 
   if (lookalike) {
-    var proceed_button = 'proceed-button';
-    var dont_proceed_link = 'dont-proceed-link';
-    $(proceed_button).classList.remove(HIDDEN_CLASS);
+    var proceedButton = 'proceed-button';
+    var dontProceedLink = 'dont-proceed-link';
+    $(proceedButton).classList.remove(HIDDEN_CLASS);
 
-    $(proceed_button).textContent =
-        loadTimeData.getString('proceedButtonText');
+    $(proceedButton).textContent = loadTimeData.getString('proceedButtonText');
 
-    $(proceed_button).addEventListener('click', function(event) {
+    $(proceedButton).addEventListener('click', function(event) {
       sendCommand(SecurityInterstitialCommandId.CMD_PROCEED);
     });
 
-    $(dont_proceed_link).addEventListener('click', function(event) {
+    $(dontProceedLink).addEventListener('click', function(event) {
       sendCommand(SecurityInterstitialCommandId.CMD_DONT_PROCEED);
     });
   }
@@ -188,7 +187,6 @@
     });
   }
 
-  var details_id = null;
   if (captivePortal || billing || lookalike) {
     // Captive portal, billing and lookalike pages don't have details buttons.
     $('details-button').classList.add('hidden');
diff --git a/components/services/quarantine/public/cpp/quarantine_features_win.cc b/components/services/quarantine/public/cpp/quarantine_features_win.cc
index 48e64df..0d85db2 100644
--- a/components/services/quarantine/public/cpp/quarantine_features_win.cc
+++ b/components/services/quarantine/public/cpp/quarantine_features_win.cc
@@ -12,10 +12,4 @@
 const base::Feature kOutOfProcessQuarantine{"OutOfProcessQuarantine",
                                             base::FEATURE_DISABLED_BY_DEFAULT};
 
-// This feature controls whether the InvokeAttachmentServices function will be
-// called. Has no effect on machines that are domain-joined, where the function
-// is always called.
-const base::Feature kInvokeAttachmentServices{"InvokeAttachmentServices",
-                                              base::FEATURE_ENABLED_BY_DEFAULT};
-
 }  // namespace quarantine
diff --git a/components/services/quarantine/public/cpp/quarantine_features_win.h b/components/services/quarantine/public/cpp/quarantine_features_win.h
index 7ca3925..b97fd1e6 100644
--- a/components/services/quarantine/public/cpp/quarantine_features_win.h
+++ b/components/services/quarantine/public/cpp/quarantine_features_win.h
@@ -11,8 +11,6 @@
 
 extern const base::Feature kOutOfProcessQuarantine;
 
-extern const base::Feature kInvokeAttachmentServices;
-
 }  // namespace quarantine
 
 #endif  // COMPONENTS_SERVICES_QUARANTINE_PUBLIC_CPP_QUARANTINE_FEATURES_WIN_H_
diff --git a/components/services/quarantine/quarantine_win.cc b/components/services/quarantine/quarantine_win.cc
index 3ff31d9..a81c9b0e 100644
--- a/components/services/quarantine/quarantine_win.cc
+++ b/components/services/quarantine/quarantine_win.cc
@@ -16,7 +16,6 @@
 
 #include <vector>
 
-#include "base/enterprise_util.h"
 #include "base/feature_list.h"
 #include "base/files/file_util.h"
 #include "base/guid.h"
@@ -258,21 +257,8 @@
     return SetInternetZoneIdentifierDirectly(file, source_url, referrer_url);
   }
 
-  // Check if the attachment services should be invoked based on the experiment
-  // state. Not invoking the attachment services means that the Zone Identifier
-  // will always be set to 3 (Internet), regardless of URL zones configurations.
-  //
-  // Note: The attachment services must always be invoked on domain-joined
-  // machines.
-  // TODO(pmonette): Move the InvokeAttachmentServices() call to a utility
-  //                 process and remove the feature.
-  bool should_invoke_attachment_services =
-      base::IsMachineExternallyManaged() ||
-      base::FeatureList::IsEnabled(kInvokeAttachmentServices);
-
   QuarantineFileResult attachment_services_result = QuarantineFileResult::OK;
-  if (should_invoke_attachment_services &&
-      InvokeAttachmentServices(file, source_url, referrer_url, guid,
+  if (InvokeAttachmentServices(file, source_url, referrer_url, guid,
                                &attachment_services_result)) {
     return attachment_services_result;
   }
diff --git a/components/services/quarantine/quarantine_win_unittest.cc b/components/services/quarantine/quarantine_win_unittest.cc
index de648483..cf0a33002 100644
--- a/components/services/quarantine/quarantine_win_unittest.cc
+++ b/components/services/quarantine/quarantine_win_unittest.cc
@@ -424,6 +424,7 @@
   GURL referrer_url_clean = GURL(
       base::StringPrintf(L"https://%ls/folder/index?x#y", GetInternetSite()));
 
+  // An invalid GUID will cause QuarantineFile() to apply the MOTW directly.
   EXPECT_EQ(QuarantineFileResult::OK,
             QuarantineFile(test_file, host_url, referrer_url, std::string()));
 
@@ -431,9 +432,6 @@
 }
 
 TEST_F(QuarantineWinTest, MetaData_InvokeAS) {
-  base::test::ScopedFeatureList invoke_as_feature;
-  invoke_as_feature.InitAndEnableFeature(kInvokeAttachmentServices);
-
   base::FilePath test_file = GetTempDir().AppendASCII("foo.exe");
   ASSERT_TRUE(CreateFile(test_file));
 
diff --git a/components/sync/driver/profile_sync_service.cc b/components/sync/driver/profile_sync_service.cc
index d403d892..5a3e214e 100644
--- a/components/sync/driver/profile_sync_service.cc
+++ b/components/sync/driver/profile_sync_service.cc
@@ -17,6 +17,7 @@
 #include "base/run_loop.h"
 #include "base/strings/strcat.h"
 #include "base/task/post_task.h"
+#include "base/threading/thread.h"
 #include "components/invalidation/public/invalidation_service.h"
 #include "components/signin/public/base/signin_metrics.h"
 #include "components/signin/public/identity_manager/account_info.h"
@@ -51,9 +52,6 @@
 
 namespace {
 
-const base::Feature kProfileSyncServiceUsesTaskScheduler{
-    "ProfileSyncServiceUsesThreadPool", base::FEATURE_DISABLED_BY_DEFAULT};
-
 // The initial state of sync, for the Sync.InitialState histogram. Even if
 // this value is CAN_START, sync startup might fail for reasons that we may
 // want to consider logging in the future, such as a passphrase needed for
@@ -435,18 +433,28 @@
     return;
   }
 
-  if (base::FeatureList::IsEnabled(kProfileSyncServiceUsesTaskScheduler)) {
+  if (base::FeatureList::IsEnabled(
+          switches::kProfileSyncServiceUsesThreadPool)) {
     backend_task_runner_ = base::CreateSequencedTaskRunner(
         {base::ThreadPool(), base::MayBlock(), base::TaskPriority::USER_VISIBLE,
          base::TaskShutdownBehavior::BLOCK_SHUTDOWN});
   } else {
-    sync_thread_ = std::make_unique<base::Thread>("Chrome_SyncThread");
+    // The thread where all the sync operations happen. This thread is kept
+    // alive until browser shutdown and reused if sync is turned off and on
+    // again. It is joined during the shutdown process, but there is an abort
+    // mechanism in place to prevent slow HTTP requests from blocking browser
+    // shutdown.
+    auto sync_thread = std::make_unique<base::Thread>("Chrome_SyncThread");
     base::Thread::Options options;
     options.timer_slack = base::TIMER_SLACK_MAXIMUM;
-    bool success = sync_thread_->StartWithOptions(options);
+    bool success = sync_thread->StartWithOptions(options);
     DCHECK(success);
+    backend_task_runner_ = sync_thread->task_runner();
 
-    backend_task_runner_ = sync_thread_->task_runner();
+    // Transfer ownership of the thread to the stopper closure that gets
+    // executed at shutdown.
+    sync_thread_stopper_ =
+        base::BindOnce(&base::Thread::Stop, std::move(sync_thread));
   }
 }
 
@@ -536,8 +544,9 @@
 
   auth_manager_.reset();
 
-  if (sync_thread_)
-    sync_thread_->Stop();
+  if (sync_thread_stopper_) {
+    std::move(sync_thread_stopper_).Run();
+  }
 }
 
 void ProfileSyncService::ShutdownImpl(ShutdownReason reason) {
diff --git a/components/sync/driver/profile_sync_service.h b/components/sync/driver/profile_sync_service.h
index 9d319faa..2dea4ad 100644
--- a/components/sync/driver/profile_sync_service.h
+++ b/components/sync/driver/profile_sync_service.h
@@ -400,13 +400,10 @@
   // A utility object containing logic and state relating to encryption.
   SyncServiceCrypto crypto_;
 
-  // The thread where all the sync operations happen. This thread is kept alive
-  // until browser shutdown and reused if sync is turned off and on again. It is
-  // joined during the shutdown process, but there is an abort mechanism in
-  // place to prevent slow HTTP requests from blocking browser shutdown.
+  // Owns the sync thread and takes care of its destruction.
   // TODO(https://crbug.com/1014464): Remove once we have switched to
   // Threadpool.
-  std::unique_ptr<base::Thread> sync_thread_;
+  base::OnceClosure sync_thread_stopper_;
 
   // TODO(crbug.com/923287): Move out of this class. Possibly to SyncEngineImpl.
   scoped_refptr<base::SequencedTaskRunner> backend_task_runner_;
diff --git a/components/sync/driver/sync_driver_switches.cc b/components/sync/driver/sync_driver_switches.cc
index 6ecf8b45..ddff8b9 100644
--- a/components/sync/driver/sync_driver_switches.cc
+++ b/components/sync/driver/sync_driver_switches.cc
@@ -76,4 +76,8 @@
 const base::Feature kSyncDeviceInfoInTransportMode{
     "SyncDeviceInfoInTransportMode", base::FEATURE_DISABLED_BY_DEFAULT};
 
+// Enables the running of backend ProfileSyncService tasks on the ThreadPool.
+const base::Feature kProfileSyncServiceUsesThreadPool{
+    "ProfileSyncServiceUsesThreadPool", base::FEATURE_DISABLED_BY_DEFAULT};
+
 }  // namespace switches
diff --git a/components/sync/driver/sync_driver_switches.h b/components/sync/driver/sync_driver_switches.h
index 114f876..bfb14065 100644
--- a/components/sync/driver/sync_driver_switches.h
+++ b/components/sync/driver/sync_driver_switches.h
@@ -35,6 +35,7 @@
 extern const base::Feature kUpdateBookmarkGUIDWithNodeReplacement;
 extern const base::Feature kMergeBookmarksUsingGUIDs;
 extern const base::Feature kSyncDeviceInfoInTransportMode;
+extern const base::Feature kProfileSyncServiceUsesThreadPool;
 
 }  // namespace switches
 
diff --git a/components/sync/engine_impl/model_type_worker.cc b/components/sync/engine_impl/model_type_worker.cc
index df14223..5938332 100644
--- a/components/sync/engine_impl/model_type_worker.cc
+++ b/components/sync/engine_impl/model_type_worker.cc
@@ -18,6 +18,7 @@
 #include "base/metrics/histogram_functions.h"
 #include "base/metrics/histogram_macros.h"
 #include "base/strings/stringprintf.h"
+#include "base/threading/thread_restrictions.h"
 #include "base/trace_event/memory_usage_estimator.h"
 #include "components/sync/base/cancelation_signal.h"
 #include "components/sync/base/client_tag_hash.h"
@@ -789,7 +790,12 @@
   if (!cancelation_signal_->TryRegisterHandler(this)) {
     return;
   }
-  response_accepted_.Wait();
+
+  {
+    base::ScopedAllowBaseSyncPrimitives allow_wait;
+    response_accepted_.Wait();
+  }
+
   cancelation_signal_->UnregisterHandler(this);
 }
 
diff --git a/components/sync/test/fake_server/fake_server_http_post_provider.cc b/components/sync/test/fake_server/fake_server_http_post_provider.cc
index eaba3c65..d633cc73 100644
--- a/components/sync/test/fake_server/fake_server_http_post_provider.cc
+++ b/components/sync/test/fake_server/fake_server_http_post_provider.cc
@@ -8,6 +8,7 @@
 
 #include "base/bind.h"
 #include "base/location.h"
+#include "base/threading/thread_restrictions.h"
 #include "base/time/time.h"
 #include "components/sync/test/fake_server/fake_server.h"
 #include "net/base/net_errors.h"
@@ -107,7 +108,10 @@
     return false;
   }
 
-  synchronous_post_completion_.Wait();
+  {
+    base::ScopedAllowBaseSyncPrimitivesForTesting allow_wait;
+    synchronous_post_completion_.Wait();
+  }
 
   if (aborted_) {
     *net_error_code = net::ERR_ABORTED;
diff --git a/components/test/data/password_manager/form_annotation_extension/README b/components/test/data/password_manager/form_annotation_extension/README
index 609ee3f..abcd4e7 100644
--- a/components/test/data/password_manager/form_annotation_extension/README
+++ b/components/test/data/password_manager/form_annotation_extension/README
@@ -15,7 +15,7 @@
 current site and will get the next site.
 6. Repeat steps 4-5 till all sites have been visited.
 7. You can output the tests for all sites you visited, if you print the value
-of the variable "all_tests" in background page's console.
+of the variable "allTests" in background page's console.
 8. Copy the generated code into the source code of the form classifier tests
 (components/test/data/password_manager/form_classification_tests).
 
@@ -23,4 +23,4 @@
 put focus into password field. The backgound page will output a Python test to
 the console, but then the extension will get to the next unvisited site.
 
-See this video for more info: goo.gl/nDTIOc.
\ No newline at end of file
+See this video for more info: goo.gl/nDTIOc.
diff --git a/components/test/data/password_manager/form_annotation_extension/background.js b/components/test/data/password_manager/form_annotation_extension/background.js
index be78ca4..972f5df 100644
--- a/components/test/data/password_manager/form_annotation_extension/background.js
+++ b/components/test/data/password_manager/form_annotation_extension/background.js
@@ -33,11 +33,11 @@
 var steps = [];
 
 /**
- * The index of the last visited site from |sites_to_visit| (sites_to_visit.js).
+ * The index of the last visited site from |sitesToVisit| (sites_to_visit.js).
  *
  * @type {number}
  */
-var last_visited_site_index = 0;
+var lastVisitedSiteIndex = 0;
 
 /**
  * Generated Python tests.
@@ -51,7 +51,7 @@
  *
  * @type {string}
  */
-var all_tests = '\n';
+var allTests = '\n';
 
 /**
  * Return the name of the test based on the form's url |url|
@@ -116,7 +116,7 @@
 
 /**
  * Outputs to the console the code of a Python test based on script steps
- * accumulated in |steps|. Also appends the test code to |all_tests|.
+ * accumulated in |steps|. Also appends the test code to |allTests|.
  */
 function outputPythonTestCode() {
   var lastStepUrl = stripUrl(steps[steps.length - 1].url);
@@ -139,7 +139,7 @@
   test += '\n';
 
   console.log(test);
-  all_tests += test;
+  allTests += test;
   steps = [];
 }
 
@@ -147,12 +147,12 @@
  * Moves the current tab to the next site.
  */
 function visitNextSite() {
-  console.log('next site: ' + sites_to_visit[last_visited_site_index] + ' ' +
-              last_visited_site_index);
-  chrome.tabs.update(
-      {url: 'http://' + sites_to_visit[last_visited_site_index]});
+  console.log(
+      'next site: ' + sitesToVisit[lastVisitedSiteIndex] + ' ' +
+      lastVisitedSiteIndex);
+  chrome.tabs.update({url: 'http://' + sitesToVisit[lastVisitedSiteIndex]});
   steps = [];
-  last_visited_site_index += 1;
+  lastVisitedSiteIndex += 1;
 }
 
 /**
diff --git a/components/test/data/password_manager/form_annotation_extension/content.js b/components/test/data/password_manager/form_annotation_extension/content.js
index e8158d108..04972ce 100644
--- a/components/test/data/password_manager/form_annotation_extension/content.js
+++ b/components/test/data/password_manager/form_annotation_extension/content.js
@@ -126,8 +126,8 @@
       console.error('frameElement is null. Unable to fetch data about iframes');
       break;
     }
-    var iframe_selector = getSmartSelector(frameElement);
-    frames.unshift(iframe_selector);
+    var iframeSelector = getSmartSelector(frameElement);
+    frames.unshift(iframeSelector);
     elem = elem.ownerDocument.defaultView.frameElement;
   }
   return frames;
diff --git a/components/user_manager/fake_user_manager.cc b/components/user_manager/fake_user_manager.cc
index 100b76b..6a8f609 100644
--- a/components/user_manager/fake_user_manager.cc
+++ b/components/user_manager/fake_user_manager.cc
@@ -260,6 +260,11 @@
                      : false;
 }
 
+bool FakeUserManager::IsLoggedInAsAnyKioskApp() const {
+  const User* active_user = GetActiveUser();
+  return active_user && active_user->IsKioskType();
+}
+
 bool FakeUserManager::IsLoggedInAsStub() const {
   return false;
 }
diff --git a/components/user_manager/fake_user_manager.h b/components/user_manager/fake_user_manager.h
index 8b42c8f..a985a3c 100644
--- a/components/user_manager/fake_user_manager.h
+++ b/components/user_manager/fake_user_manager.h
@@ -96,6 +96,7 @@
   bool IsLoggedInAsSupervisedUser() const override;
   bool IsLoggedInAsKioskApp() const override;
   bool IsLoggedInAsArcKioskApp() const override;
+  bool IsLoggedInAsAnyKioskApp() const override;
   bool IsLoggedInAsStub() const override;
   bool IsUserNonCryptohomeDataEphemeral(
       const AccountId& account_id) const override;
diff --git a/components/viz/service/BUILD.gn b/components/viz/service/BUILD.gn
index 67941e5a..79aa878 100644
--- a/components/viz/service/BUILD.gn
+++ b/components/viz/service/BUILD.gn
@@ -17,14 +17,10 @@
     "display/bsp_tree.h",
     "display/bsp_walk_action.cc",
     "display/bsp_walk_action.h",
-    "display/ca_layer_overlay.cc",
-    "display/ca_layer_overlay.h",
     "display/color_lut_cache.cc",
     "display/color_lut_cache.h",
     "display/damage_frame_annotator.cc",
     "display/damage_frame_annotator.h",
-    "display/dc_layer_overlay.cc",
-    "display/dc_layer_overlay.h",
     "display/direct_renderer.cc",
     "display/direct_renderer.h",
     "display/display.cc",
@@ -245,6 +241,8 @@
 
   if (is_mac) {
     sources += [
+      "display/ca_layer_overlay.cc",
+      "display/ca_layer_overlay.h",
       "display_embedder/overlay_candidate_validator_mac.cc",
       "display_embedder/overlay_candidate_validator_mac.h",
       "display_embedder/software_output_device_mac.cc",
@@ -289,6 +287,8 @@
 
   if (is_win) {
     sources += [
+      "display/dc_layer_overlay.cc",
+      "display/dc_layer_overlay.h",
       "display_embedder/output_device_backing.cc",
       "display_embedder/output_device_backing.h",
       "display_embedder/overlay_candidate_validator_win.cc",
@@ -409,7 +409,6 @@
     "display/gl_renderer_copier_unittest.cc",
     "display/gl_renderer_unittest.cc",
     "display/layer_quad_unittest.cc",
-    "display/overlay_unittest.cc",
     "display/renderer_pixeltest.cc",
     "display/shader_unittest.cc",
     "display/software_renderer_unittest.cc",
@@ -473,7 +472,10 @@
   ]
 
   if (use_ozone) {
-    sources += [ "display_embedder/software_output_device_ozone_unittest.cc" ]
+    sources += [
+      "display/overlay_unittest.cc",
+      "display_embedder/software_output_device_ozone_unittest.cc",
+    ]
     deps += [
       "//ui/compositor:test_support",
       "//ui/ozone",
@@ -481,16 +483,23 @@
   }
 
   if (is_mac) {
-    sources += [ "display_embedder/software_output_device_mac_unittest.mm" ]
+    sources += [
+      "display/overlay_ca_unittest.cc",
+      "display_embedder/software_output_device_mac_unittest.mm",
+    ]
     libs = [ "IOSurface.framework" ]
   }
 
   if (is_win) {
-    sources += [ "display_embedder/output_device_backing_unittest.cc" ]
+    sources += [
+      "display/overlay_dc_unittest.cc",
+      "display_embedder/output_device_backing_unittest.cc",
+    ]
   }
 
   if (is_android) {
     sources += [
+      "display/overlay_unittest.cc",
       "display_embedder/overlay_candidate_validator_surface_control_unittest.cc",
       "frame_sinks/external_begin_frame_source_android_unittest.cc",
     ]
diff --git a/components/viz/service/display/DEPS b/components/viz/service/display/DEPS
index 7dc1aeb6..7c4078cc 100644
--- a/components/viz/service/display/DEPS
+++ b/components/viz/service/display/DEPS
@@ -34,7 +34,7 @@
 ]
 
 specific_include_rules = {
-  "dc_layer_overlay.cc|overlay_unittest.cc": [
+  "dc_layer_overlay.cc|overlay_dc_unittest.cc": [
     # TODO(danakj): gl_switches.h brings runtime flags into the compositor which
     # makes testing coverage harder/less clear, it should not be here.
     "+ui/gl/gl_switches.h",
diff --git a/components/viz/service/display/direct_renderer.cc b/components/viz/service/display/direct_renderer.cc
index e80a645..c63144c 100644
--- a/components/viz/service/display/direct_renderer.cc
+++ b/components/viz/service/display/direct_renderer.cc
@@ -71,10 +71,12 @@
   return canvas;
 }
 
+#if defined(OS_WIN)
 // Switching between enabling DC layers and not is expensive, so only
 // switch away after a large number of frames not needing DC layers have
 // been produced.
 constexpr int kNumberOfFramesBeforeDisablingDCLayers = 60;
+#endif  // defined(OS_WIN)
 
 // Returns the bounding box that contains the specified rounded corner.
 gfx::RectF ComputeRoundedCornerBoundingBox(const gfx::RRectF& rrect,
@@ -130,7 +132,9 @@
 
   use_partial_swap_ = settings_->partial_swap_enabled && CanPartialSwap();
   allow_empty_swap_ = use_partial_swap_;
+#if defined(OS_WIN)
   supports_dc_layers_ = output_surface_->capabilities().supports_dc_layers;
+#endif
   if (context_provider) {
     if (context_provider->ContextCapabilities().commit_overlay_planes)
       allow_empty_swap_ = true;
@@ -348,16 +352,15 @@
       resource_provider_, render_passes_in_draw_order,
       output_surface_->color_matrix(), render_pass_filters_,
       render_pass_backdrop_filters_, primary_plane,
-      &current_frame()->overlay_list, &current_frame()->ca_layer_overlay_list,
-      &current_frame()->dc_layer_overlay_list,
-      &current_frame()->root_damage_rect,
+      &current_frame()->overlay_list, &current_frame()->root_damage_rect,
       &current_frame()->root_content_bounds);
 
   overlay_processor_->AdjustOutputSurfaceOverlay(
       &(current_frame()->output_surface_plane));
 
+#if defined(OS_WIN)
   bool was_using_dc_layers = using_dc_layers_;
-  if (!current_frame()->dc_layer_overlay_list.empty()) {
+  if (!current_frame()->overlay_list.empty()) {
     DCHECK(supports_dc_layers_);
     using_dc_layers_ = true;
     frames_since_using_dc_layers_ = 0;
@@ -368,6 +371,7 @@
 
   if (supports_dc_layers_ && (was_using_dc_layers != using_dc_layers_))
     SetEnableDCLayers(using_dc_layers_);
+#endif
 
   // Draw all non-root render passes except for the root render pass.
   for (const auto& pass : *render_passes_in_draw_order) {
@@ -376,6 +380,7 @@
     DrawRenderPassAndExecuteCopyRequests(pass.get());
   }
 
+#if defined(OS_WIN)
   if (supports_dc_layers_ &&
       (did_reshape || (was_using_dc_layers != using_dc_layers_))) {
     // The entire surface has to be redrawn if it was reshaped or if switching
@@ -383,6 +388,7 @@
     // discarded and some contents would otherwise be undefined.
     current_frame()->root_damage_rect = gfx::Rect(device_viewport_size);
   }
+#endif
 
   // We can skip all drawing if the damage rect is now empty.
   bool skip_drawing_root_render_pass =
@@ -611,8 +617,10 @@
   // set on the root framebuffer or else the rendering may modify something
   // outside the damage rectangle, even if the damage rectangle is the size of
   // the full backbuffer.
-  bool render_pass_requires_scissor =
-      (supports_dc_layers_ && is_root_render_pass) || render_pass_is_clipped;
+  bool render_pass_requires_scissor = render_pass_is_clipped;
+#if defined(OS_WIN)
+  render_pass_requires_scissor |= (supports_dc_layers_ && is_root_render_pass);
+#endif
   bool has_external_stencil_test =
       is_root_render_pass && output_surface_->HasExternalStencilTest();
   bool should_clear_surface =
@@ -720,8 +728,10 @@
   current_frame()->current_render_pass = render_pass;
   if (render_pass == current_frame()->root_render_pass) {
     BindFramebufferToOutputSurface();
+#if defined(OS_WIN)
     if (supports_dc_layers_)
       output_surface_->SetDrawRectangle(current_frame()->root_damage_rect);
+#endif
     InitializeViewport(current_frame(), render_pass->output_rect,
                        gfx::Rect(current_frame()->device_viewport_size),
                        current_frame()->device_viewport_size);
diff --git a/components/viz/service/display/direct_renderer.h b/components/viz/service/display/direct_renderer.h
index c12b9d1d..347a9094 100644
--- a/components/viz/service/display/direct_renderer.h
+++ b/components/viz/service/display/direct_renderer.h
@@ -12,9 +12,8 @@
 #include "base/containers/circular_deque.h"
 #include "base/containers/flat_map.h"
 #include "base/macros.h"
+#include "build/build_config.h"
 #include "components/viz/common/quads/tile_draw_quad.h"
-#include "components/viz/service/display/ca_layer_overlay.h"
-#include "components/viz/service/display/dc_layer_overlay.h"
 #include "components/viz/service/display/display_resource_provider.h"
 #include "components/viz/service/display/overlay_candidate_list.h"
 #include "components/viz/service/display/overlay_processor.h"
@@ -92,9 +91,7 @@
     gfx::Transform projection_matrix;
     gfx::Transform window_matrix;
 
-    OverlayCandidateList overlay_list;
-    CALayerOverlayList ca_layer_overlay_list;
-    DCLayerOverlayList dc_layer_overlay_list;
+    OverlayProcessor::CandidateList overlay_list;
     // When we have a buffer queue, the output surface could be treated as an
     // overlay plane, and the struct to store that information is in
     // |output_surface_plane|.
@@ -219,7 +216,9 @@
   virtual void CopyDrawnRenderPass(
       const copy_output::RenderPassGeometry& geometry,
       std::unique_ptr<CopyOutputRequest> request) = 0;
+#if defined(OS_WIN)
   virtual void SetEnableDCLayers(bool enable) = 0;
+#endif
   virtual void GenerateMipmap() = 0;
 
   gfx::Size surface_size_for_swap_buffers() const {
@@ -241,6 +240,7 @@
   bool use_partial_swap_ = false;
   // Whether overdraw feedback is enabled and can be used.
   bool overdraw_feedback_ = false;
+#if defined(OS_WIN)
   // Whether the SetDrawRectangle and EnableDCLayers commands are in
   // use.
   bool supports_dc_layers_ = false;
@@ -249,6 +249,7 @@
   // This counts the number of draws since the last time
   // DirectComposition layers needed to be used.
   int frames_since_using_dc_layers_ = 0;
+#endif
 
   // A map from RenderPass id to the single quad present in and replacing the
   // RenderPass. The DrawQuads are owned by their RenderPasses, which outlive
diff --git a/components/viz/service/display/gl_renderer.cc b/components/viz/service/display/gl_renderer.cc
index cf811dc..c0c6613 100644
--- a/components/viz/service/display/gl_renderer.cc
+++ b/components/viz/service/display/gl_renderer.cc
@@ -2305,6 +2305,8 @@
 
   gfx::ColorSpace dst_color_space =
       current_frame()->current_render_pass->color_space;
+
+#if defined(OS_WIN)
   // Force sRGB output on Windows for overlay candidate video quads to match
   // DirectComposition behavior in case these switch between overlays and
   // compositing. See https://crbug.com/811118 for details.
@@ -2313,6 +2315,7 @@
     DCHECK(resource_provider_->IsOverlayCandidate(quad->u_plane_resource_id()));
     dst_color_space = gfx::ColorSpace::CreateSRGB();
   }
+#endif
 
   // TODO(jbauman): Use base::Optional when available.
   std::unique_ptr<DisplayResourceProvider::ScopedSamplerGL> v_plane_lock;
@@ -2742,9 +2745,13 @@
   // semantics during overlay refactoring.
   ScheduleOutputSurfaceAsOverlay();
 
-  ScheduleCALayers();
-  ScheduleDCLayers();
+#if defined(OS_ANDROID) || defined(USE_OZONE)
   ScheduleOverlays();
+#elif defined(OS_MACOSX)
+  ScheduleCALayers();
+#elif defined(OS_WIN)
+  ScheduleDCLayers();
+#endif
 
   TRACE_COUNTER1(TRACE_DISABLED_BY_DEFAULT("viz.triangles"), "Triangles Drawn",
                  num_triangles_drawn_);
@@ -2759,9 +2766,11 @@
   current_framebuffer_texture_->set_generate_mipmap();
 }
 
+#if defined(OS_WIN)
 void GLRenderer::SetEnableDCLayers(bool enable) {
   gl_->SetEnableDCLayersCHROMIUM(enable);
 }
+#endif
 
 bool GLRenderer::FlippedFramebuffer() const {
   if (force_drawing_frame_framebuffer_unflipped_)
@@ -3385,6 +3394,7 @@
   return gl_->GetGraphicsResetStatusKHR() != GL_NO_ERROR;
 }
 
+#if defined(OS_MACOSX)
 void GLRenderer::ScheduleCALayers() {
   // The use of OverlayTextures for RenderPasses is only supported on the code
   // paths for |release_overlay_resources_after_gpu_query| at the moment. See
@@ -3397,8 +3407,7 @@
   scoped_refptr<CALayerOverlaySharedState> shared_state;
   size_t copied_render_pass_count = 0;
 
-  for (const CALayerOverlay& ca_layer_overlay :
-       current_frame()->ca_layer_overlay_list) {
+  for (const CALayerOverlay& ca_layer_overlay : current_frame()->overlay_list) {
     if (ca_layer_overlay.rpdq) {
       std::unique_ptr<OverlayTexture> overlay_texture =
           ScheduleRenderPassDrawQuad(&ca_layer_overlay);
@@ -3458,10 +3467,11 @@
 
   ReduceAvailableOverlayTextures();
 }
+#endif  // defined(OS_MACOSX)
 
+#if defined(OS_WIN)
 void GLRenderer::ScheduleDCLayers() {
-  for (DCLayerOverlay& dc_layer_overlay :
-       current_frame()->dc_layer_overlay_list) {
+  for (DCLayerOverlay& dc_layer_overlay : current_frame()->overlay_list) {
     DCHECK_EQ(DCLayerOverlay::kNumResources, 2u);
     GLuint texture_ids[DCLayerOverlay::kNumResources] = {};
     for (size_t i = 0; i < DCLayerOverlay::kNumResources; i++) {
@@ -3499,7 +3509,9 @@
         clip_rect.height(), protected_video_type);
   }
 }
+#endif  // defined (OS_WIN)
 
+#if defined(OS_ANDROID) || defined(USE_OZONE)
 void GLRenderer::ScheduleOverlays() {
   if (current_frame()->overlay_list.empty())
     return;
@@ -3518,6 +3530,7 @@
         overlay_candidate.gpu_fence_id);
   }
 }
+#endif  // defined(OS_ANDROID) || defined(USE_OZONE)
 
 void GLRenderer::ScheduleOutputSurfaceAsOverlay() {
   if (!current_frame()->output_surface_plane)
@@ -3538,6 +3551,7 @@
       overlay_candidate.enable_blending, overlay_candidate.gpu_fence_id);
 }
 
+#if defined(OS_MACOSX)
 // This function draws the RenderPassDrawQuad into a temporary
 // texture/framebuffer, and then copies the result into an IOSurface. The
 // inefficient (but simple) way to do this would be to:
@@ -3811,6 +3825,7 @@
                                filter);
   return overlay_texture;
 }
+#endif  // defined(OS_MACOSX)
 
 void GLRenderer::SetupOverdrawFeedback() {
   gl_->StencilFunc(GL_ALWAYS, 1, 0xffffffff);
diff --git a/components/viz/service/display/gl_renderer.h b/components/viz/service/display/gl_renderer.h
index ad7718b..e4ef191 100644
--- a/components/viz/service/display/gl_renderer.h
+++ b/components/viz/service/display/gl_renderer.h
@@ -13,6 +13,7 @@
 #include "base/cancelable_callback.h"
 #include "base/containers/circular_deque.h"
 #include "base/macros.h"
+#include "build/build_config.h"
 #include "components/viz/common/gpu/context_cache_controller.h"
 #include "components/viz/common/quads/debug_border_draw_quad.h"
 #include "components/viz/common/quads/render_pass_draw_quad.h"
@@ -31,6 +32,14 @@
 #include "ui/gfx/geometry/quad_f.h"
 #include "ui/latency/latency_info.h"
 
+#if defined(OS_MACOSX)
+#include "components/viz/service/display/ca_layer_overlay.h"
+#endif
+
+#if defined(OS_WIN)
+#include "components/viz/service/display/dc_layer_overlay.h"
+#endif
+
 namespace base {
 class SingleThreadTaskRunner;
 }
@@ -109,7 +118,9 @@
   void EnsureScissorTestDisabled() override;
   void CopyDrawnRenderPass(const copy_output::RenderPassGeometry& geometry,
                            std::unique_ptr<CopyOutputRequest> request) override;
+#if defined(OS_WIN)
   void SetEnableDCLayers(bool enable) override;
+#endif
   void FinishDrawingQuadList() override;
   void GenerateMipmap() override;
 
@@ -303,9 +314,16 @@
   // nothing.
   void ScheduleOutputSurfaceAsOverlay();
   // Schedule overlays sends overlay candidate to the GPU.
-  void ScheduleCALayers();
-  void ScheduleDCLayers();
+#if defined(OS_ANDROID) || defined(USE_OZONE)
   void ScheduleOverlays();
+#elif defined(OS_MACOSX)
+  void ScheduleCALayers();
+
+  // Schedules the |ca_layer_overlay|, which is guaranteed to have a non-null
+  // |rpdq| parameter. Returns ownership of a GL texture that contains the
+  // output of the RenderPassDrawQuad.
+  std::unique_ptr<OverlayTexture> ScheduleRenderPassDrawQuad(
+      const CALayerOverlay* ca_layer_overlay);
 
   // Copies the contents of the render pass draw quad, including filter effects,
   // to a GL texture, returned in |overlay_texture|. The resulting texture may
@@ -322,11 +340,9 @@
       const gfx::ColorSpace& color_space);
   void ReduceAvailableOverlayTextures();
 
-  // Schedules the |ca_layer_overlay|, which is guaranteed to have a non-null
-  // |rpdq| parameter. Returns ownership of a GL texture that contains the
-  // output of the RenderPassDrawQuad.
-  std::unique_ptr<OverlayTexture> ScheduleRenderPassDrawQuad(
-      const CALayerOverlay* ca_layer_overlay);
+#elif defined(OS_WIN)
+  void ScheduleDCLayers();
+#endif
 
   // Setup/flush all pending overdraw feedback to framebuffer.
   void SetupOverdrawFeedback();
diff --git a/components/viz/service/display/gl_renderer_unittest.cc b/components/viz/service/display/gl_renderer_unittest.cc
index fd7d8e8..ed9f319 100644
--- a/components/viz/service/display/gl_renderer_unittest.cc
+++ b/components/viz/service/display/gl_renderer_unittest.cc
@@ -2343,9 +2343,13 @@
   // added a fake strategy, so checking for Attempt calls checks if there was
   // any attempt to overlay, which there shouldn't be. We can't use the quad
   // list because the render pass is cleaned up by DrawFrame.
+#if defined(USE_OZONE) || defined(OS_ANDROID)
   EXPECT_CALL(processor->strategy(), Attempt(_, _, _, _, _, _, _)).Times(0);
+#elif defined(OS_MACOSX)
   EXPECT_CALL(*validator, AllowCALayerOverlays()).Times(0);
+#elif defined(OS_WIN)
   EXPECT_CALL(*validator, AllowDCLayerOverlays()).Times(0);
+#endif
   DrawFrame(&renderer, viewport_size);
   Mock::VerifyAndClearExpectations(&processor->strategy());
   Mock::VerifyAndClearExpectations(
@@ -2364,13 +2368,17 @@
       premultiplied_alpha, gfx::PointF(0, 0), gfx::PointF(1, 1),
       SK_ColorTRANSPARENT, vertex_opacity, flipped, nearest_neighbor,
       /*secure_output_only=*/false, gfx::ProtectedVideoType::kClear);
+#if defined(USE_OZONE) || defined(OS_ANDROID)
+  EXPECT_CALL(processor->strategy(), Attempt(_, _, _, _, _, _, _)).Times(1);
+#elif defined(OS_MACOSX)
   EXPECT_CALL(*validator, AllowCALayerOverlays())
       .Times(1)
       .WillOnce(::testing::Return(false));
+#elif defined(OS_WIN)
   EXPECT_CALL(*validator, AllowDCLayerOverlays())
       .Times(1)
       .WillOnce(::testing::Return(false));
-  EXPECT_CALL(processor->strategy(), Attempt(_, _, _, _, _, _, _)).Times(1);
+#endif
   DrawFrame(&renderer, viewport_size);
 
   // If the CALayerOverlay path is taken, then the ordinary overlay path should
@@ -2387,10 +2395,17 @@
       premultiplied_alpha, gfx::PointF(0, 0), gfx::PointF(1, 1),
       SK_ColorTRANSPARENT, vertex_opacity, flipped, nearest_neighbor,
       /*secure_output_only=*/false, gfx::ProtectedVideoType::kClear);
+#if defined(OS_MACOSX)
   EXPECT_CALL(*validator, AllowCALayerOverlays())
       .Times(1)
       .WillOnce(::testing::Return(true));
-  EXPECT_CALL(processor->strategy(), Attempt(_, _, _, _, _, _, _)).Times(0);
+#elif defined(USE_OZONE) || defined(OS_ANDROID)
+  EXPECT_CALL(processor->strategy(), Attempt(_, _, _, _, _, _, _)).Times(1);
+#elif defined(OS_WIN)
+  EXPECT_CALL(*validator, AllowDCLayerOverlays())
+      .Times(1)
+      .WillOnce(::testing::Return(true));
+#endif
   DrawFrame(&renderer, viewport_size);
 
   // Transfer resources back from the parent to the child. Set no resources as
@@ -2448,6 +2463,7 @@
   MOCK_METHOD1(WaitSyncTokenCHROMIUM, void(const GLbyte* sync_token));
 };
 
+#if defined(USE_OZONE) || defined(OS_ANDROID)
 class MockOverlayScheduler {
  public:
   MOCK_METHOD7(Schedule,
@@ -2578,6 +2594,7 @@
   child_resource_provider->RemoveImportedResource(resource_id);
   child_resource_provider->ShutdownAndReleaseAllResources();
 }
+#endif  // defined(USE_OZONE) || defined(OS_ANDROID)
 
 class OutputColorMatrixMockGLES2Interface : public TestGLES2Interface {
  public:
@@ -2832,6 +2849,7 @@
   RunTest(false, false);
 }
 
+#if defined(OS_WIN)
 TEST_F(GLRendererPartialSwapTest, SetDrawRectangle_PartialSwap) {
   RunTest(true, true);
 }
@@ -2969,6 +2987,7 @@
   child_resource_provider->RemoveImportedResource(resource_id);
   child_resource_provider->ShutdownAndReleaseAllResources();
 }
+#endif
 
 class GLRendererWithMockContextTest : public ::testing::Test {
  protected:
@@ -3015,6 +3034,7 @@
   Mock::VerifyAndClearExpectations(context_support_ptr_);
 }
 
+#if defined(USE_OZONE) || defined(OS_ANDROID)
 class ContentBoundsOverlayProcessor : public OverlayProcessor {
  public:
   class Strategy : public OverlayProcessor::Strategy {
@@ -3049,7 +3069,8 @@
           std::make_unique<Strategy>(std::move(content_bounds_)));
     }
 
-    // Returns true if draw quads can be represented as CALayers (Mac only).
+    // Empty mock methods since this test set up uses strategies, which are only
+    // for ozone and android.
     MOCK_CONST_METHOD0(AllowCALayerOverlays, bool());
     MOCK_CONST_METHOD0(AllowDCLayerOverlays, bool());
     MOCK_CONST_METHOD0(NeedsSurfaceOccludingDamageRect, bool());
@@ -3141,7 +3162,9 @@
   content_bounds.push_back(gfx::Rect(20, 20, 30, 30));
   RunTest(content_bounds);
 }
+#endif  // defined(USE_OZONE) || defined(OS_ANDROID)
 
+#if defined(OS_MACOSX)
 class CALayerValidator : public OverlayCandidateValidator {
  public:
   bool AllowCALayerOverlays() const override { return true; }
@@ -4056,6 +4079,7 @@
   Mock::VerifyAndClearExpectations(&gl());
   renderer().SwapBuffers(std::vector<ui::LatencyInfo>());
 }
+#endif
 
 class FramebufferWatchingGLRenderer : public FakeRendererGL {
  public:
@@ -4187,6 +4211,7 @@
   }
 }
 
+#if defined(USE_OZONE) || defined(OS_ANDROID)
 class GLRendererWithGpuFenceTest : public GLRendererTest {
  protected:
   GLRendererWithGpuFenceTest() {
@@ -4310,6 +4335,7 @@
       .Times(1);
   DrawFrame(renderer_.get(), viewport_size);
 }
+#endif  // defined(USE_OZONE) || defined(OS_ANDROID)
 
 }  // namespace
 }  // namespace viz
diff --git a/components/viz/service/display/overlay_ca_unittest.cc b/components/viz/service/display/overlay_ca_unittest.cc
new file mode 100644
index 0000000..c70621c0
--- /dev/null
+++ b/components/viz/service/display/overlay_ca_unittest.cc
@@ -0,0 +1,520 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <stddef.h>
+
+#include <utility>
+#include <vector>
+
+#include "base/bind.h"
+#include "base/containers/flat_map.h"
+#include "base/test/scoped_feature_list.h"
+#include "base/unguessable_token.h"
+#include "build/build_config.h"
+#include "cc/test/fake_output_surface_client.h"
+#include "cc/test/geometry_test_utils.h"
+#include "cc/test/resource_provider_test_utils.h"
+#include "components/viz/client/client_resource_provider.h"
+#include "components/viz/common/quads/render_pass.h"
+#include "components/viz/common/quads/render_pass_draw_quad.h"
+#include "components/viz/common/quads/solid_color_draw_quad.h"
+#include "components/viz/common/quads/stream_video_draw_quad.h"
+#include "components/viz/common/quads/texture_draw_quad.h"
+#include "components/viz/common/quads/video_hole_draw_quad.h"
+#include "components/viz/common/resources/transferable_resource.h"
+#include "components/viz/service/display/ca_layer_overlay.h"
+#include "components/viz/service/display/display_resource_provider.h"
+#include "components/viz/service/display/gl_renderer.h"
+#include "components/viz/service/display/output_surface.h"
+#include "components/viz/service/display/output_surface_client.h"
+#include "components/viz/service/display/output_surface_frame.h"
+#include "components/viz/service/display/overlay_candidate_validator.h"
+#include "components/viz/service/display/overlay_processor.h"
+#include "components/viz/test/test_context_provider.h"
+#include "components/viz/test/test_gles2_interface.h"
+#include "components/viz/test/test_shared_bitmap_manager.h"
+#include "gpu/config/gpu_finch_features.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "ui/gfx/geometry/rect_conversions.h"
+#include "ui/latency/latency_info.h"
+
+using testing::_;
+using testing::Mock;
+
+namespace viz {
+namespace {
+
+const gfx::Rect kOverlayRect(0, 0, 256, 256);
+const gfx::PointF kUVTopLeft(0.1f, 0.2f);
+const gfx::PointF kUVBottomRight(1.0f, 1.0f);
+
+class CALayerValidator : public OverlayCandidateValidator {
+ public:
+  bool AllowCALayerOverlays() const override { return true; }
+  bool AllowDCLayerOverlays() const override { return false; }
+  bool NeedsSurfaceOccludingDamageRect() const override { return false; }
+  void CheckOverlaySupport(const PrimaryPlane* primary_plane,
+                           OverlayCandidateList* surfaces) override {}
+};
+
+class OverlayOutputSurface : public OutputSurface {
+ public:
+  explicit OverlayOutputSurface(
+      scoped_refptr<TestContextProvider> context_provider)
+      : OutputSurface(std::move(context_provider)) {}
+
+  // OutputSurface implementation.
+  void BindToClient(OutputSurfaceClient* client) override {}
+  void EnsureBackbuffer() override {}
+  void DiscardBackbuffer() override {}
+  void BindFramebuffer() override { bind_framebuffer_count_ += 1; }
+  void SetDrawRectangle(const gfx::Rect& rect) override {}
+  void Reshape(const gfx::Size& size,
+               float device_scale_factor,
+               const gfx::ColorSpace& color_space,
+               bool has_alpha,
+               bool use_stencil) override {}
+  void SwapBuffers(OutputSurfaceFrame frame) override {}
+  uint32_t GetFramebufferCopyTextureFormat() override {
+    // TestContextProvider has no real framebuffer, just use RGB.
+    return GL_RGB;
+  }
+  bool HasExternalStencilTest() const override { return false; }
+  void ApplyExternalStencil() override {}
+  bool IsDisplayedAsOverlayPlane() const override { return false; }
+  unsigned GetOverlayTextureId() const override { return 10000; }
+  gfx::BufferFormat GetOverlayBufferFormat() const override {
+    return gfx::BufferFormat::RGBX_8888;
+  }
+  unsigned UpdateGpuFence() override { return 0; }
+  void SetUpdateVSyncParametersCallback(
+      UpdateVSyncParametersCallback callback) override {}
+  void SetDisplayTransformHint(gfx::OverlayTransform transform) override {}
+  gfx::OverlayTransform GetDisplayTransform() override {
+    return gfx::OVERLAY_TRANSFORM_NONE;
+  }
+
+  unsigned bind_framebuffer_count() const { return bind_framebuffer_count_; }
+
+ private:
+  unsigned bind_framebuffer_count_ = 0;
+};
+
+class CATestOverlayProcessor : public OverlayProcessor {
+ public:
+  CATestOverlayProcessor()
+      : OverlayProcessor(std::make_unique<CALayerValidator>()) {}
+};
+
+std::unique_ptr<RenderPass> CreateRenderPass() {
+  int render_pass_id = 1;
+  gfx::Rect output_rect(0, 0, 256, 256);
+
+  std::unique_ptr<RenderPass> pass = RenderPass::Create();
+  pass->SetNew(render_pass_id, output_rect, output_rect, gfx::Transform());
+
+  SharedQuadState* shared_state = pass->CreateAndAppendSharedQuadState();
+  shared_state->opacity = 1.f;
+  return pass;
+}
+
+static ResourceId CreateResourceInLayerTree(
+    ClientResourceProvider* child_resource_provider,
+    const gfx::Size& size,
+    bool is_overlay_candidate) {
+  auto resource = TransferableResource::MakeGL(
+      gpu::Mailbox::Generate(), GL_LINEAR, GL_TEXTURE_2D, gpu::SyncToken(),
+      size, is_overlay_candidate);
+  auto release_callback = SingleReleaseCallback::Create(
+      base::BindRepeating([](const gpu::SyncToken&, bool) {}));
+
+  ResourceId resource_id = child_resource_provider->ImportResource(
+      resource, std::move(release_callback));
+
+  return resource_id;
+}
+
+ResourceId CreateResource(DisplayResourceProvider* parent_resource_provider,
+                          ClientResourceProvider* child_resource_provider,
+                          ContextProvider* child_context_provider,
+                          const gfx::Size& size,
+                          bool is_overlay_candidate) {
+  ResourceId resource_id = CreateResourceInLayerTree(
+      child_resource_provider, size, is_overlay_candidate);
+
+  int child_id = parent_resource_provider->CreateChild(
+      base::BindRepeating([](const std::vector<ReturnedResource>&) {}), true);
+
+  // Transfer resource to the parent.
+  std::vector<ResourceId> resource_ids_to_transfer;
+  resource_ids_to_transfer.push_back(resource_id);
+  std::vector<TransferableResource> list;
+  child_resource_provider->PrepareSendToParent(resource_ids_to_transfer, &list,
+                                               child_context_provider);
+  parent_resource_provider->ReceiveFromChild(child_id, list);
+
+  // Delete it in the child so it won't be leaked, and will be released once
+  // returned from the parent.
+  child_resource_provider->RemoveImportedResource(resource_id);
+
+  // In DisplayResourceProvider's namespace, use the mapped resource id.
+  std::unordered_map<ResourceId, ResourceId> resource_map =
+      parent_resource_provider->GetChildToParentMap(child_id);
+  return resource_map[list[0].id];
+}
+
+TextureDrawQuad* CreateCandidateQuadAt(
+    DisplayResourceProvider* parent_resource_provider,
+    ClientResourceProvider* child_resource_provider,
+    ContextProvider* child_context_provider,
+    const SharedQuadState* shared_quad_state,
+    RenderPass* render_pass,
+    const gfx::Rect& rect,
+    gfx::ProtectedVideoType protected_video_type) {
+  bool needs_blending = false;
+  bool premultiplied_alpha = false;
+  bool flipped = false;
+  bool nearest_neighbor = false;
+  float vertex_opacity[4] = {1.0f, 1.0f, 1.0f, 1.0f};
+  gfx::Size resource_size_in_pixels = rect.size();
+  bool is_overlay_candidate = true;
+  ResourceId resource_id = CreateResource(
+      parent_resource_provider, child_resource_provider, child_context_provider,
+      resource_size_in_pixels, is_overlay_candidate);
+
+  auto* overlay_quad = render_pass->CreateAndAppendDrawQuad<TextureDrawQuad>();
+  overlay_quad->SetNew(shared_quad_state, rect, rect, needs_blending,
+                       resource_id, premultiplied_alpha, kUVTopLeft,
+                       kUVBottomRight, SK_ColorTRANSPARENT, vertex_opacity,
+                       flipped, nearest_neighbor, /*secure_output_only=*/false,
+                       protected_video_type);
+  overlay_quad->set_resource_size_in_pixels(resource_size_in_pixels);
+
+  return overlay_quad;
+}
+
+TextureDrawQuad* CreateCandidateQuadAt(
+    DisplayResourceProvider* parent_resource_provider,
+    ClientResourceProvider* child_resource_provider,
+    ContextProvider* child_context_provider,
+    const SharedQuadState* shared_quad_state,
+    RenderPass* render_pass,
+    const gfx::Rect& rect) {
+  return CreateCandidateQuadAt(
+      parent_resource_provider, child_resource_provider, child_context_provider,
+      shared_quad_state, render_pass, rect, gfx::ProtectedVideoType::kClear);
+}
+
+TextureDrawQuad* CreateFullscreenCandidateQuad(
+    DisplayResourceProvider* parent_resource_provider,
+    ClientResourceProvider* child_resource_provider,
+    ContextProvider* child_context_provider,
+    const SharedQuadState* shared_quad_state,
+    RenderPass* render_pass) {
+  return CreateCandidateQuadAt(
+      parent_resource_provider, child_resource_provider, child_context_provider,
+      shared_quad_state, render_pass, render_pass->output_rect);
+}
+
+SkMatrix44 GetIdentityColorMatrix() {
+  return SkMatrix44(SkMatrix44::kIdentity_Constructor);
+}
+
+class CALayerOverlayTest : public testing::Test {
+ protected:
+  void SetUp() override {
+    provider_ = TestContextProvider::Create();
+    provider_->BindToCurrentThread();
+    output_surface_ = std::make_unique<OverlayOutputSurface>(provider_);
+    output_surface_->BindToClient(&client_);
+
+    shared_bitmap_manager_ = std::make_unique<TestSharedBitmapManager>();
+    resource_provider_ = std::make_unique<DisplayResourceProvider>(
+        DisplayResourceProvider::kGpu, provider_.get(),
+        shared_bitmap_manager_.get());
+
+    child_provider_ = TestContextProvider::Create();
+    child_provider_->BindToCurrentThread();
+    child_resource_provider_ = std::make_unique<ClientResourceProvider>(true);
+
+    overlay_processor_ = std::make_unique<CATestOverlayProcessor>();
+  }
+
+  void TearDown() override {
+    overlay_processor_ = nullptr;
+    child_resource_provider_->ShutdownAndReleaseAllResources();
+    child_resource_provider_ = nullptr;
+    child_provider_ = nullptr;
+    resource_provider_ = nullptr;
+    shared_bitmap_manager_ = nullptr;
+    output_surface_ = nullptr;
+    provider_ = nullptr;
+  }
+
+  scoped_refptr<TestContextProvider> provider_;
+  std::unique_ptr<OverlayOutputSurface> output_surface_;
+  cc::FakeOutputSurfaceClient client_;
+  std::unique_ptr<SharedBitmapManager> shared_bitmap_manager_;
+  std::unique_ptr<DisplayResourceProvider> resource_provider_;
+  scoped_refptr<TestContextProvider> child_provider_;
+  std::unique_ptr<ClientResourceProvider> child_resource_provider_;
+  std::unique_ptr<CATestOverlayProcessor> overlay_processor_;
+  gfx::Rect damage_rect_;
+  std::vector<gfx::Rect> content_bounds_;
+};
+
+TEST_F(CALayerOverlayTest, AllowNonAxisAlignedTransform) {
+  std::unique_ptr<RenderPass> pass = CreateRenderPass();
+  CreateFullscreenCandidateQuad(
+      resource_provider_.get(), child_resource_provider_.get(),
+      child_provider_.get(), pass->shared_quad_state_list.back(), pass.get());
+  pass->shared_quad_state_list.back()
+      ->quad_to_target_transform.RotateAboutZAxis(45.f);
+
+  gfx::Rect damage_rect;
+  CALayerOverlayList ca_layer_list;
+  OverlayProcessor::FilterOperationsMap render_pass_filters;
+  OverlayProcessor::FilterOperationsMap render_pass_backdrop_filters;
+  RenderPassList pass_list;
+  pass_list.push_back(std::move(pass));
+  overlay_processor_->ProcessForOverlays(
+      resource_provider_.get(), &pass_list, GetIdentityColorMatrix(),
+      render_pass_filters, render_pass_backdrop_filters, nullptr,
+      &ca_layer_list, &damage_rect_, &content_bounds_);
+  EXPECT_EQ(gfx::Rect(), damage_rect);
+  EXPECT_EQ(1U, ca_layer_list.size());
+  EXPECT_EQ(0U, output_surface_->bind_framebuffer_count());
+}
+
+TEST_F(CALayerOverlayTest, ThreeDTransform) {
+  std::unique_ptr<RenderPass> pass = CreateRenderPass();
+  CreateFullscreenCandidateQuad(
+      resource_provider_.get(), child_resource_provider_.get(),
+      child_provider_.get(), pass->shared_quad_state_list.back(), pass.get());
+  pass->shared_quad_state_list.back()
+      ->quad_to_target_transform.RotateAboutXAxis(45.f);
+
+  CALayerOverlayList ca_layer_list;
+  OverlayProcessor::FilterOperationsMap render_pass_filters;
+  OverlayProcessor::FilterOperationsMap render_pass_backdrop_filters;
+  RenderPassList pass_list;
+  pass_list.push_back(std::move(pass));
+  overlay_processor_->ProcessForOverlays(
+      resource_provider_.get(), &pass_list, GetIdentityColorMatrix(),
+      render_pass_filters, render_pass_backdrop_filters, nullptr,
+      &ca_layer_list, &damage_rect_, &content_bounds_);
+  EXPECT_EQ(1U, ca_layer_list.size());
+  gfx::Transform expected_transform;
+  expected_transform.RotateAboutXAxis(45.f);
+  gfx::Transform actual_transform(ca_layer_list.back().shared_state->transform);
+  EXPECT_EQ(expected_transform.ToString(), actual_transform.ToString());
+  EXPECT_EQ(0U, output_surface_->bind_framebuffer_count());
+}
+
+TEST_F(CALayerOverlayTest, AllowContainingClip) {
+  std::unique_ptr<RenderPass> pass = CreateRenderPass();
+  CreateFullscreenCandidateQuad(
+      resource_provider_.get(), child_resource_provider_.get(),
+      child_provider_.get(), pass->shared_quad_state_list.back(), pass.get());
+  pass->shared_quad_state_list.back()->is_clipped = true;
+  pass->shared_quad_state_list.back()->clip_rect = kOverlayRect;
+
+  gfx::Rect damage_rect;
+  CALayerOverlayList ca_layer_list;
+  OverlayProcessor::FilterOperationsMap render_pass_filters;
+  OverlayProcessor::FilterOperationsMap render_pass_backdrop_filters;
+  RenderPassList pass_list;
+  pass_list.push_back(std::move(pass));
+  overlay_processor_->ProcessForOverlays(
+      resource_provider_.get(), &pass_list, GetIdentityColorMatrix(),
+      render_pass_filters, render_pass_backdrop_filters, nullptr,
+      &ca_layer_list, &damage_rect_, &content_bounds_);
+  EXPECT_EQ(gfx::Rect(), damage_rect);
+  EXPECT_EQ(1U, ca_layer_list.size());
+  EXPECT_EQ(0U, output_surface_->bind_framebuffer_count());
+}
+
+TEST_F(CALayerOverlayTest, NontrivialClip) {
+  std::unique_ptr<RenderPass> pass = CreateRenderPass();
+  CreateFullscreenCandidateQuad(
+      resource_provider_.get(), child_resource_provider_.get(),
+      child_provider_.get(), pass->shared_quad_state_list.back(), pass.get());
+  pass->shared_quad_state_list.back()->is_clipped = true;
+  pass->shared_quad_state_list.back()->clip_rect = gfx::Rect(64, 64, 128, 128);
+
+  gfx::Rect damage_rect;
+  CALayerOverlayList ca_layer_list;
+  OverlayProcessor::FilterOperationsMap render_pass_filters;
+  OverlayProcessor::FilterOperationsMap render_pass_backdrop_filters;
+  RenderPassList pass_list;
+  pass_list.push_back(std::move(pass));
+  overlay_processor_->ProcessForOverlays(
+      resource_provider_.get(), &pass_list, GetIdentityColorMatrix(),
+      render_pass_filters, render_pass_backdrop_filters, nullptr,
+      &ca_layer_list, &damage_rect_, &content_bounds_);
+  EXPECT_EQ(gfx::Rect(), damage_rect);
+  EXPECT_EQ(1U, ca_layer_list.size());
+  EXPECT_TRUE(ca_layer_list.back().shared_state->is_clipped);
+  EXPECT_EQ(gfx::RectF(64, 64, 128, 128),
+            ca_layer_list.back().shared_state->clip_rect);
+  EXPECT_EQ(0U, output_surface_->bind_framebuffer_count());
+}
+
+TEST_F(CALayerOverlayTest, SkipTransparent) {
+  std::unique_ptr<RenderPass> pass = CreateRenderPass();
+  CreateFullscreenCandidateQuad(
+      resource_provider_.get(), child_resource_provider_.get(),
+      child_provider_.get(), pass->shared_quad_state_list.back(), pass.get());
+  pass->shared_quad_state_list.back()->opacity = 0;
+
+  gfx::Rect damage_rect;
+  CALayerOverlayList ca_layer_list;
+  OverlayProcessor::FilterOperationsMap render_pass_filters;
+  OverlayProcessor::FilterOperationsMap render_pass_backdrop_filters;
+  RenderPassList pass_list;
+  pass_list.push_back(std::move(pass));
+  overlay_processor_->ProcessForOverlays(
+      resource_provider_.get(), &pass_list, GetIdentityColorMatrix(),
+      render_pass_filters, render_pass_backdrop_filters, nullptr,
+      &ca_layer_list, &damage_rect_, &content_bounds_);
+  EXPECT_EQ(gfx::Rect(), damage_rect);
+  EXPECT_EQ(0U, ca_layer_list.size());
+  EXPECT_EQ(0U, output_surface_->bind_framebuffer_count());
+}
+
+class CALayerOverlayRPDQTest : public CALayerOverlayTest {
+ protected:
+  void SetUp() override {
+    CALayerOverlayTest::SetUp();
+    pass_list_.push_back(CreateRenderPass());
+    pass_ = pass_list_.back().get();
+    quad_ = pass_->CreateAndAppendDrawQuad<RenderPassDrawQuad>();
+    render_pass_id_ = 3;
+  }
+
+  void ProcessForOverlays() {
+    overlay_processor_->ProcessForOverlays(
+        resource_provider_.get(), &pass_list_, GetIdentityColorMatrix(),
+        render_pass_filters_, render_pass_backdrop_filters_, nullptr,
+        &ca_layer_list_, &damage_rect_, &content_bounds_);
+  }
+  RenderPassList pass_list_;
+  RenderPass* pass_;
+  RenderPassDrawQuad* quad_;
+  int render_pass_id_;
+  cc::FilterOperations filters_;
+  cc::FilterOperations backdrop_filters_;
+  OverlayProcessor::FilterOperationsMap render_pass_filters_;
+  OverlayProcessor::FilterOperationsMap render_pass_backdrop_filters_;
+  CALayerOverlayList ca_layer_list_;
+};
+
+TEST_F(CALayerOverlayRPDQTest, RenderPassDrawQuadNoFilters) {
+  quad_->SetNew(pass_->shared_quad_state_list.back(), kOverlayRect,
+                kOverlayRect, render_pass_id_, 0, gfx::RectF(), gfx::Size(),
+                gfx::Vector2dF(1, 1), gfx::PointF(), gfx::RectF(), false, 1.0f);
+  ProcessForOverlays();
+
+  EXPECT_EQ(1U, ca_layer_list_.size());
+}
+
+TEST_F(CALayerOverlayRPDQTest, RenderPassDrawQuadAllValidFilters) {
+  filters_.Append(cc::FilterOperation::CreateGrayscaleFilter(0.1f));
+  filters_.Append(cc::FilterOperation::CreateSepiaFilter(0.2f));
+  filters_.Append(cc::FilterOperation::CreateSaturateFilter(0.3f));
+  filters_.Append(cc::FilterOperation::CreateHueRotateFilter(0.4f));
+  filters_.Append(cc::FilterOperation::CreateInvertFilter(0.5f));
+  filters_.Append(cc::FilterOperation::CreateBrightnessFilter(0.6f));
+  filters_.Append(cc::FilterOperation::CreateContrastFilter(0.7f));
+  filters_.Append(cc::FilterOperation::CreateOpacityFilter(0.8f));
+  filters_.Append(cc::FilterOperation::CreateBlurFilter(0.9f));
+  filters_.Append(cc::FilterOperation::CreateDropShadowFilter(
+      gfx::Point(10, 20), 1.0f, SK_ColorGREEN));
+  render_pass_filters_[render_pass_id_] = &filters_;
+  quad_->SetNew(pass_->shared_quad_state_list.back(), kOverlayRect,
+                kOverlayRect, render_pass_id_, 0, gfx::RectF(), gfx::Size(),
+                gfx::Vector2dF(1, 1), gfx::PointF(), gfx::RectF(), false, 1.0f);
+  ProcessForOverlays();
+
+  EXPECT_EQ(1U, ca_layer_list_.size());
+}
+
+TEST_F(CALayerOverlayRPDQTest, RenderPassDrawQuadOpacityFilterScale) {
+  filters_.Append(cc::FilterOperation::CreateOpacityFilter(0.8f));
+  render_pass_filters_[render_pass_id_] = &filters_;
+  quad_->SetNew(pass_->shared_quad_state_list.back(), kOverlayRect,
+                kOverlayRect, render_pass_id_, 0, gfx::RectF(), gfx::Size(),
+                gfx::Vector2dF(1, 2), gfx::PointF(), gfx::RectF(), false, 1.0f);
+  ProcessForOverlays();
+  EXPECT_EQ(1U, ca_layer_list_.size());
+}
+
+TEST_F(CALayerOverlayRPDQTest, RenderPassDrawQuadBlurFilterScale) {
+  filters_.Append(cc::FilterOperation::CreateBlurFilter(0.8f));
+  render_pass_filters_[render_pass_id_] = &filters_;
+  quad_->SetNew(pass_->shared_quad_state_list.back(), kOverlayRect,
+                kOverlayRect, render_pass_id_, 0, gfx::RectF(), gfx::Size(),
+                gfx::Vector2dF(1, 2), gfx::PointF(), gfx::RectF(), false, 1.0f);
+  ProcessForOverlays();
+  EXPECT_EQ(1U, ca_layer_list_.size());
+}
+
+TEST_F(CALayerOverlayRPDQTest, RenderPassDrawQuadDropShadowFilterScale) {
+  filters_.Append(cc::FilterOperation::CreateDropShadowFilter(
+      gfx::Point(10, 20), 1.0f, SK_ColorGREEN));
+  render_pass_filters_[render_pass_id_] = &filters_;
+  quad_->SetNew(pass_->shared_quad_state_list.back(), kOverlayRect,
+                kOverlayRect, render_pass_id_, 0, gfx::RectF(), gfx::Size(),
+                gfx::Vector2dF(1, 2), gfx::PointF(), gfx::RectF(), false, 1.0f);
+  ProcessForOverlays();
+  EXPECT_EQ(1U, ca_layer_list_.size());
+}
+
+TEST_F(CALayerOverlayRPDQTest, RenderPassDrawQuadBackgroundFilter) {
+  backdrop_filters_.Append(cc::FilterOperation::CreateGrayscaleFilter(0.1f));
+  render_pass_backdrop_filters_[render_pass_id_] = &backdrop_filters_;
+  quad_->SetNew(pass_->shared_quad_state_list.back(), kOverlayRect,
+                kOverlayRect, render_pass_id_, 0, gfx::RectF(), gfx::Size(),
+                gfx::Vector2dF(1, 1), gfx::PointF(), gfx::RectF(), false, 1.0f);
+  ProcessForOverlays();
+  EXPECT_EQ(0U, ca_layer_list_.size());
+}
+
+TEST_F(CALayerOverlayRPDQTest, RenderPassDrawQuadMask) {
+  quad_->SetNew(pass_->shared_quad_state_list.back(), kOverlayRect,
+                kOverlayRect, render_pass_id_, 2, gfx::RectF(), gfx::Size(),
+                gfx::Vector2dF(1, 1), gfx::PointF(), gfx::RectF(), false, 1.0f);
+  ProcessForOverlays();
+  EXPECT_EQ(1U, ca_layer_list_.size());
+}
+
+TEST_F(CALayerOverlayRPDQTest, RenderPassDrawQuadUnsupportedFilter) {
+  filters_.Append(cc::FilterOperation::CreateZoomFilter(0.9f, 1));
+  render_pass_filters_[render_pass_id_] = &filters_;
+  quad_->SetNew(pass_->shared_quad_state_list.back(), kOverlayRect,
+                kOverlayRect, render_pass_id_, 0, gfx::RectF(), gfx::Size(),
+                gfx::Vector2dF(1, 1), gfx::PointF(), gfx::RectF(), false, 1.0f);
+  ProcessForOverlays();
+  EXPECT_EQ(0U, ca_layer_list_.size());
+}
+
+TEST_F(CALayerOverlayRPDQTest, TooManyRenderPassDrawQuads) {
+  filters_.Append(cc::FilterOperation::CreateBlurFilter(0.8f));
+  int count = 35;
+
+  for (int i = 0; i < count; ++i) {
+    auto* quad = pass_->CreateAndAppendDrawQuad<RenderPassDrawQuad>();
+    quad->SetNew(pass_->shared_quad_state_list.back(), kOverlayRect,
+                 kOverlayRect, render_pass_id_, 2, gfx::RectF(), gfx::Size(),
+                 gfx::Vector2dF(1, 1), gfx::PointF(), gfx::RectF(), false,
+                 1.0f);
+  }
+
+  ProcessForOverlays();
+  EXPECT_EQ(0U, ca_layer_list_.size());
+}
+
+}  // namespace
+}  // namespace viz
diff --git a/components/viz/service/display/overlay_dc_unittest.cc b/components/viz/service/display/overlay_dc_unittest.cc
new file mode 100644
index 0000000..6e84cad
--- /dev/null
+++ b/components/viz/service/display/overlay_dc_unittest.cc
@@ -0,0 +1,868 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <stddef.h>
+
+#include <utility>
+#include <vector>
+
+#include "base/bind.h"
+#include "base/containers/flat_map.h"
+#include "base/test/scoped_feature_list.h"
+#include "base/unguessable_token.h"
+#include "cc/test/fake_output_surface_client.h"
+#include "cc/test/geometry_test_utils.h"
+#include "cc/test/resource_provider_test_utils.h"
+#include "components/viz/client/client_resource_provider.h"
+#include "components/viz/common/quads/render_pass.h"
+#include "components/viz/common/quads/render_pass_draw_quad.h"
+#include "components/viz/common/quads/solid_color_draw_quad.h"
+#include "components/viz/common/quads/stream_video_draw_quad.h"
+#include "components/viz/common/quads/texture_draw_quad.h"
+#include "components/viz/common/quads/video_hole_draw_quad.h"
+#include "components/viz/common/resources/transferable_resource.h"
+#include "components/viz/service/display/dc_layer_overlay.h"
+#include "components/viz/service/display/display_resource_provider.h"
+#include "components/viz/service/display/gl_renderer.h"
+#include "components/viz/service/display/output_surface.h"
+#include "components/viz/service/display/output_surface_client.h"
+#include "components/viz/service/display/output_surface_frame.h"
+#include "components/viz/service/display/overlay_candidate_validator.h"
+#include "components/viz/service/display/overlay_processor.h"
+#include "components/viz/test/test_context_provider.h"
+#include "components/viz/test/test_gles2_interface.h"
+#include "components/viz/test/test_shared_bitmap_manager.h"
+#include "gpu/config/gpu_finch_features.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "ui/gfx/geometry/rect_conversions.h"
+#include "ui/gl/gl_switches.h"
+#include "ui/latency/latency_info.h"
+
+using testing::_;
+using testing::Mock;
+
+namespace viz {
+namespace {
+
+const gfx::Rect kOverlayRect(0, 0, 256, 256);
+const gfx::Rect kOverlayBottomRightRect(128, 128, 128, 128);
+
+class DCLayerValidator : public OverlayCandidateValidator {
+ public:
+  bool AllowCALayerOverlays() const override { return false; }
+  bool AllowDCLayerOverlays() const override { return true; }
+  bool NeedsSurfaceOccludingDamageRect() const override { return true; }
+  void CheckOverlaySupport(const PrimaryPlane* primary_plane,
+                           OverlayCandidateList* surfaces) override {}
+};
+
+class OverlayOutputSurface : public OutputSurface {
+ public:
+  explicit OverlayOutputSurface(
+      scoped_refptr<TestContextProvider> context_provider)
+      : OutputSurface(std::move(context_provider)) {
+    is_displayed_as_overlay_plane_ = true;
+  }
+
+  // OutputSurface implementation.
+  void BindToClient(OutputSurfaceClient* client) override {}
+  void EnsureBackbuffer() override {}
+  void DiscardBackbuffer() override {}
+  void BindFramebuffer() override { bind_framebuffer_count_ += 1; }
+  void SetDrawRectangle(const gfx::Rect& rect) override {}
+  void Reshape(const gfx::Size& size,
+               float device_scale_factor,
+               const gfx::ColorSpace& color_space,
+               bool has_alpha,
+               bool use_stencil) override {}
+  void SwapBuffers(OutputSurfaceFrame frame) override {}
+  uint32_t GetFramebufferCopyTextureFormat() override {
+    // TestContextProvider has no real framebuffer, just use RGB.
+    return GL_RGB;
+  }
+  bool HasExternalStencilTest() const override { return false; }
+  void ApplyExternalStencil() override {}
+  bool IsDisplayedAsOverlayPlane() const override {
+    return is_displayed_as_overlay_plane_;
+  }
+  unsigned GetOverlayTextureId() const override { return 10000; }
+  gfx::BufferFormat GetOverlayBufferFormat() const override {
+    return gfx::BufferFormat::RGBX_8888;
+  }
+  unsigned UpdateGpuFence() override { return 0; }
+  void SetUpdateVSyncParametersCallback(
+      UpdateVSyncParametersCallback callback) override {}
+  void SetDisplayTransformHint(gfx::OverlayTransform transform) override {}
+  gfx::OverlayTransform GetDisplayTransform() override {
+    return gfx::OVERLAY_TRANSFORM_NONE;
+  }
+
+  void set_is_displayed_as_overlay_plane(bool value) {
+    is_displayed_as_overlay_plane_ = value;
+  }
+
+  unsigned bind_framebuffer_count() const { return bind_framebuffer_count_; }
+
+ private:
+  bool is_displayed_as_overlay_plane_;
+  unsigned bind_framebuffer_count_ = 0;
+};
+
+class DCTestOverlayProcessor : public OverlayProcessor {
+ public:
+  DCTestOverlayProcessor()
+      : OverlayProcessor(std::make_unique<DCLayerValidator>()) {}
+};
+
+std::unique_ptr<RenderPass> CreateRenderPass() {
+  int render_pass_id = 1;
+  gfx::Rect output_rect(0, 0, 256, 256);
+
+  std::unique_ptr<RenderPass> pass = RenderPass::Create();
+  pass->SetNew(render_pass_id, output_rect, output_rect, gfx::Transform());
+
+  SharedQuadState* shared_state = pass->CreateAndAppendSharedQuadState();
+  shared_state->opacity = 1.f;
+  return pass;
+}
+
+static ResourceId CreateResourceInLayerTree(
+    ClientResourceProvider* child_resource_provider,
+    const gfx::Size& size,
+    bool is_overlay_candidate) {
+  auto resource = TransferableResource::MakeGL(
+      gpu::Mailbox::Generate(), GL_LINEAR, GL_TEXTURE_2D, gpu::SyncToken(),
+      size, is_overlay_candidate);
+  auto release_callback = SingleReleaseCallback::Create(
+      base::BindRepeating([](const gpu::SyncToken&, bool) {}));
+
+  ResourceId resource_id = child_resource_provider->ImportResource(
+      resource, std::move(release_callback));
+
+  return resource_id;
+}
+
+ResourceId CreateResource(DisplayResourceProvider* parent_resource_provider,
+                          ClientResourceProvider* child_resource_provider,
+                          ContextProvider* child_context_provider,
+                          const gfx::Size& size,
+                          bool is_overlay_candidate) {
+  ResourceId resource_id = CreateResourceInLayerTree(
+      child_resource_provider, size, is_overlay_candidate);
+
+  int child_id = parent_resource_provider->CreateChild(
+      base::BindRepeating([](const std::vector<ReturnedResource>&) {}), true);
+
+  // Transfer resource to the parent.
+  std::vector<ResourceId> resource_ids_to_transfer;
+  resource_ids_to_transfer.push_back(resource_id);
+  std::vector<TransferableResource> list;
+  child_resource_provider->PrepareSendToParent(resource_ids_to_transfer, &list,
+                                               child_context_provider);
+  parent_resource_provider->ReceiveFromChild(child_id, list);
+
+  // Delete it in the child so it won't be leaked, and will be released once
+  // returned from the parent.
+  child_resource_provider->RemoveImportedResource(resource_id);
+
+  // In DisplayResourceProvider's namespace, use the mapped resource id.
+  std::unordered_map<ResourceId, ResourceId> resource_map =
+      parent_resource_provider->GetChildToParentMap(child_id);
+  return resource_map[list[0].id];
+}
+
+SolidColorDrawQuad* CreateSolidColorQuadAt(
+    const SharedQuadState* shared_quad_state,
+    SkColor color,
+    RenderPass* render_pass,
+    const gfx::Rect& rect) {
+  SolidColorDrawQuad* quad =
+      render_pass->CreateAndAppendDrawQuad<SolidColorDrawQuad>();
+  quad->SetNew(shared_quad_state, rect, rect, color, false);
+  return quad;
+}
+
+void CreateOpaqueQuadAt(DisplayResourceProvider* resource_provider,
+                        const SharedQuadState* shared_quad_state,
+                        RenderPass* render_pass,
+                        const gfx::Rect& rect,
+                        SkColor color) {
+  DCHECK_EQ(255u, SkColorGetA(color));
+  auto* color_quad = render_pass->CreateAndAppendDrawQuad<SolidColorDrawQuad>();
+  color_quad->SetNew(shared_quad_state, rect, rect, color, false);
+}
+
+YUVVideoDrawQuad* CreateFullscreenCandidateYUVVideoQuad(
+    DisplayResourceProvider* parent_resource_provider,
+    ClientResourceProvider* child_resource_provider,
+    ContextProvider* child_context_provider,
+    const SharedQuadState* shared_quad_state,
+    RenderPass* render_pass) {
+  bool needs_blending = false;
+  gfx::RectF tex_coord_rect(0, 0, 1, 1);
+  gfx::Rect rect = render_pass->output_rect;
+  gfx::Size resource_size_in_pixels = rect.size();
+  bool is_overlay_candidate = true;
+  ResourceId resource_id = CreateResource(
+      parent_resource_provider, child_resource_provider, child_context_provider,
+      resource_size_in_pixels, is_overlay_candidate);
+
+  auto* overlay_quad = render_pass->CreateAndAppendDrawQuad<YUVVideoDrawQuad>();
+  overlay_quad->SetNew(shared_quad_state, rect, rect, needs_blending,
+                       tex_coord_rect, tex_coord_rect, resource_size_in_pixels,
+                       resource_size_in_pixels, resource_id, resource_id,
+                       resource_id, resource_id,
+                       gfx::ColorSpace::CreateREC601(), 0, 1.0, 8);
+
+  return overlay_quad;
+}
+
+SkMatrix44 GetIdentityColorMatrix() {
+  return SkMatrix44(SkMatrix44::kIdentity_Constructor);
+}
+
+class DCLayerOverlayTest : public testing::Test {
+ protected:
+  void SetUp() override {
+    provider_ = TestContextProvider::Create();
+    provider_->BindToCurrentThread();
+    output_surface_ = std::make_unique<OverlayOutputSurface>(provider_);
+    output_surface_->BindToClient(&client_);
+
+    shared_bitmap_manager_ = std::make_unique<TestSharedBitmapManager>();
+    resource_provider_ = std::make_unique<DisplayResourceProvider>(
+        DisplayResourceProvider::kGpu, provider_.get(),
+        shared_bitmap_manager_.get());
+
+    child_provider_ = TestContextProvider::Create();
+    child_provider_->BindToCurrentThread();
+    child_resource_provider_ = std::make_unique<ClientResourceProvider>(true);
+
+    overlay_processor_ = std::make_unique<DCTestOverlayProcessor>();
+  }
+
+  void TearDown() override {
+    overlay_processor_ = nullptr;
+    child_resource_provider_->ShutdownAndReleaseAllResources();
+    child_resource_provider_ = nullptr;
+    child_provider_ = nullptr;
+    resource_provider_ = nullptr;
+    shared_bitmap_manager_ = nullptr;
+    output_surface_ = nullptr;
+    provider_ = nullptr;
+  }
+
+  scoped_refptr<TestContextProvider> provider_;
+  std::unique_ptr<OverlayOutputSurface> output_surface_;
+  cc::FakeOutputSurfaceClient client_;
+  std::unique_ptr<SharedBitmapManager> shared_bitmap_manager_;
+  std::unique_ptr<DisplayResourceProvider> resource_provider_;
+  scoped_refptr<TestContextProvider> child_provider_;
+  std::unique_ptr<ClientResourceProvider> child_resource_provider_;
+  std::unique_ptr<DCTestOverlayProcessor> overlay_processor_;
+  gfx::Rect damage_rect_;
+  std::vector<gfx::Rect> content_bounds_;
+};
+
+TEST_F(DCLayerOverlayTest, AllowNonAxisAlignedTransform) {
+  base::test::ScopedFeatureList feature_list;
+  feature_list.InitAndEnableFeature(
+      features::kDirectCompositionComplexOverlays);
+  std::unique_ptr<RenderPass> pass = CreateRenderPass();
+  CreateFullscreenCandidateYUVVideoQuad(
+      resource_provider_.get(), child_resource_provider_.get(),
+      child_provider_.get(), pass->shared_quad_state_list.back(), pass.get());
+  pass->shared_quad_state_list.back()
+      ->quad_to_target_transform.RotateAboutZAxis(45.f);
+
+  DCLayerOverlayList dc_layer_list;
+  OverlayProcessor::FilterOperationsMap render_pass_filters;
+  OverlayProcessor::FilterOperationsMap render_pass_backdrop_filters;
+  damage_rect_ = gfx::Rect(1, 1, 10, 10);
+  RenderPassList pass_list;
+  pass_list.push_back(std::move(pass));
+  overlay_processor_->ProcessForOverlays(
+      resource_provider_.get(), &pass_list, GetIdentityColorMatrix(),
+      render_pass_filters, render_pass_backdrop_filters, nullptr,
+      &dc_layer_list, &damage_rect_, &content_bounds_);
+  EXPECT_EQ(1U, dc_layer_list.size());
+  EXPECT_EQ(1, dc_layer_list.back().z_order);
+  EXPECT_EQ(0U, output_surface_->bind_framebuffer_count());
+  EXPECT_EQ(gfx::Rect(1, 1, 10, 10), damage_rect_);
+}
+
+TEST_F(DCLayerOverlayTest, AllowRequiredNonAxisAlignedTransform) {
+  base::test::ScopedFeatureList feature_list;
+  feature_list.InitAndEnableFeature(
+      features::kDirectCompositionNonrootOverlays);
+  std::unique_ptr<RenderPass> pass = CreateRenderPass();
+  YUVVideoDrawQuad* yuv_quad = CreateFullscreenCandidateYUVVideoQuad(
+      resource_provider_.get(), child_resource_provider_.get(),
+      child_provider_.get(), pass->shared_quad_state_list.back(), pass.get());
+  // Set the protected video flag will force DCLayerOverlay to use hw overlay
+  yuv_quad->protected_video_type = gfx::ProtectedVideoType::kHardwareProtected;
+  pass->shared_quad_state_list.back()
+      ->quad_to_target_transform.RotateAboutZAxis(45.f);
+
+  gfx::Rect damage_rect;
+  DCLayerOverlayList dc_layer_list;
+  OverlayProcessor::FilterOperationsMap render_pass_filters;
+  OverlayProcessor::FilterOperationsMap render_pass_backdrop_filters;
+  damage_rect_ = gfx::Rect(1, 1, 10, 10);
+  RenderPassList pass_list;
+  pass_list.push_back(std::move(pass));
+  overlay_processor_->ProcessForOverlays(
+      resource_provider_.get(), &pass_list, GetIdentityColorMatrix(),
+      render_pass_filters, render_pass_backdrop_filters, nullptr,
+      &dc_layer_list, &damage_rect_, &content_bounds_);
+  EXPECT_EQ(gfx::Rect(), damage_rect);
+  ASSERT_EQ(1U, dc_layer_list.size());
+  EXPECT_EQ(1, dc_layer_list.back().z_order);
+  EXPECT_EQ(0U, output_surface_->bind_framebuffer_count());
+  EXPECT_EQ(gfx::Rect(1, 1, 10, 10), damage_rect_);
+}
+
+TEST_F(DCLayerOverlayTest, Occluded) {
+  base::test::ScopedFeatureList feature_list;
+  feature_list.InitAndEnableFeature(features::kDirectCompositionUnderlays);
+  {
+    std::unique_ptr<RenderPass> pass = CreateRenderPass();
+    SharedQuadState* first_shared_state = pass->shared_quad_state_list.back();
+    first_shared_state->occluding_damage_rect = gfx::Rect(1, 1, 10, 10);
+    CreateOpaqueQuadAt(resource_provider_.get(),
+                       pass->shared_quad_state_list.back(), pass.get(),
+                       gfx::Rect(0, 3, 100, 100), SK_ColorWHITE);
+    CreateFullscreenCandidateYUVVideoQuad(
+        resource_provider_.get(), child_resource_provider_.get(),
+        child_provider_.get(), pass->shared_quad_state_list.back(), pass.get());
+
+    SharedQuadState* second_shared_state =
+        pass->CreateAndAppendSharedQuadState();
+    second_shared_state->occluding_damage_rect = gfx::Rect(1, 1, 10, 10);
+    auto* second_video_quad = CreateFullscreenCandidateYUVVideoQuad(
+        resource_provider_.get(), child_resource_provider_.get(),
+        child_provider_.get(), pass->shared_quad_state_list.back(), pass.get());
+    // Set the protected video flag will force DCLayerOverlay to use hw overlay
+    second_video_quad->protected_video_type =
+        gfx::ProtectedVideoType::kHardwareProtected;
+    second_video_quad->rect.set_origin(gfx::Point(2, 2));
+    second_video_quad->visible_rect.set_origin(gfx::Point(2, 2));
+
+    DCLayerOverlayList dc_layer_list;
+    OverlayProcessor::FilterOperationsMap render_pass_filters;
+    OverlayProcessor::FilterOperationsMap render_pass_backdrop_filters;
+    damage_rect_ = gfx::Rect(1, 1, 10, 10);
+    RenderPassList pass_list;
+    pass_list.push_back(std::move(pass));
+    overlay_processor_->ProcessForOverlays(
+        resource_provider_.get(), &pass_list, GetIdentityColorMatrix(),
+        render_pass_filters, render_pass_backdrop_filters, nullptr,
+        &dc_layer_list, &damage_rect_, &content_bounds_);
+    EXPECT_EQ(2U, dc_layer_list.size());
+    EXPECT_EQ(0U, output_surface_->bind_framebuffer_count());
+    EXPECT_EQ(-1, dc_layer_list.front().z_order);
+    EXPECT_EQ(-2, dc_layer_list.back().z_order);
+    // Entire underlay rect must be redrawn.
+    EXPECT_EQ(gfx::Rect(0, 0, 256, 256), damage_rect_);
+  }
+  {
+    std::unique_ptr<RenderPass> pass = CreateRenderPass();
+    SharedQuadState* first_shared_state = pass->shared_quad_state_list.back();
+    first_shared_state->occluding_damage_rect = gfx::Rect(1, 1, 10, 10);
+    CreateOpaqueQuadAt(resource_provider_.get(),
+                       pass->shared_quad_state_list.back(), pass.get(),
+                       gfx::Rect(3, 3, 100, 100), SK_ColorWHITE);
+    CreateFullscreenCandidateYUVVideoQuad(
+        resource_provider_.get(), child_resource_provider_.get(),
+        child_provider_.get(), pass->shared_quad_state_list.back(), pass.get());
+
+    SharedQuadState* second_shared_state =
+        pass->CreateAndAppendSharedQuadState();
+    second_shared_state->occluding_damage_rect = gfx::Rect(1, 1, 10, 10);
+    auto* second_video_quad = CreateFullscreenCandidateYUVVideoQuad(
+        resource_provider_.get(), child_resource_provider_.get(),
+        child_provider_.get(), pass->shared_quad_state_list.back(), pass.get());
+    second_video_quad->protected_video_type =
+        gfx::ProtectedVideoType::kHardwareProtected;
+    second_video_quad->rect.set_origin(gfx::Point(2, 2));
+    second_video_quad->visible_rect.set_origin(gfx::Point(2, 2));
+
+    DCLayerOverlayList dc_layer_list;
+    OverlayProcessor::FilterOperationsMap render_pass_filters;
+    OverlayProcessor::FilterOperationsMap render_pass_backdrop_filters;
+    damage_rect_ = gfx::Rect(1, 1, 10, 10);
+    RenderPassList pass_list;
+    pass_list.push_back(std::move(pass));
+    overlay_processor_->ProcessForOverlays(
+        resource_provider_.get(), &pass_list, GetIdentityColorMatrix(),
+        render_pass_filters, render_pass_backdrop_filters, nullptr,
+        &dc_layer_list, &damage_rect_, &content_bounds_);
+    EXPECT_EQ(2U, dc_layer_list.size());
+    EXPECT_EQ(0U, output_surface_->bind_framebuffer_count());
+    EXPECT_EQ(-1, dc_layer_list.front().z_order);
+    EXPECT_EQ(-2, dc_layer_list.back().z_order);
+    // The underlay rectangle is the same, so the damage for first video quad is
+    // contained within the combined occluding rects for this and the last
+    // frame. Second video quad also adds its damage.
+
+    // This is calculated by carving out the underlay rect size from the
+    // damage_rect, adding back the quads on top and then the overlay/underlay
+    // rects from the previous frame. The damage rect carried over from  the
+    // revious frame with multiple overlays cannot be skipped.
+    EXPECT_EQ(gfx::Rect(0, 0, 256, 256), damage_rect_);
+  }
+}
+
+TEST_F(DCLayerOverlayTest, DamageRectWithoutVideoDamage) {
+  base::test::ScopedFeatureList feature_list;
+  feature_list.InitAndEnableFeature(features::kDirectCompositionUnderlays);
+  {
+    std::unique_ptr<RenderPass> pass = CreateRenderPass();
+    SharedQuadState* shared_quad_state = pass->shared_quad_state_list.back();
+    shared_quad_state->occluding_damage_rect = gfx::Rect(210, 210, 20, 20);
+    // Occluding quad fully contained in video rect.
+    CreateOpaqueQuadAt(resource_provider_.get(),
+                       pass->shared_quad_state_list.back(), pass.get(),
+                       gfx::Rect(0, 3, 100, 100), SK_ColorWHITE);
+    // Non-occluding quad fully outside video rect
+    CreateOpaqueQuadAt(resource_provider_.get(),
+                       pass->shared_quad_state_list.back(), pass.get(),
+                       gfx::Rect(210, 210, 20, 20), SK_ColorWHITE);
+    // Underlay video quad
+    auto* video_quad = CreateFullscreenCandidateYUVVideoQuad(
+        resource_provider_.get(), child_resource_provider_.get(),
+        child_provider_.get(), pass->shared_quad_state_list.back(), pass.get());
+    video_quad->rect = gfx::Rect(0, 0, 200, 200);
+    video_quad->visible_rect = video_quad->rect;
+
+    DCLayerOverlayList dc_layer_list;
+    OverlayProcessor::FilterOperationsMap render_pass_filters;
+    OverlayProcessor::FilterOperationsMap render_pass_backdrop_filters;
+    // Damage rect fully outside video quad
+    damage_rect_ = gfx::Rect(210, 210, 20, 20);
+    RenderPassList pass_list;
+    pass_list.push_back(std::move(pass));
+    overlay_processor_->ProcessForOverlays(
+        resource_provider_.get(), &pass_list, GetIdentityColorMatrix(),
+        render_pass_filters, render_pass_backdrop_filters, nullptr,
+        &dc_layer_list, &damage_rect_, &content_bounds_);
+    EXPECT_EQ(1U, dc_layer_list.size());
+    EXPECT_EQ(0U, output_surface_->bind_framebuffer_count());
+    EXPECT_EQ(-1, dc_layer_list.back().z_order);
+    // All rects must be redrawn at the first frame.
+    EXPECT_EQ(gfx::Rect(0, 0, 230, 230), damage_rect_);
+  }
+  {
+    std::unique_ptr<RenderPass> pass = CreateRenderPass();
+    SharedQuadState* shared_quad_state = pass->shared_quad_state_list.back();
+    shared_quad_state->occluding_damage_rect = gfx::Rect(210, 210, 20, 20);
+    // Occluding quad fully contained in video rect.
+    CreateOpaqueQuadAt(resource_provider_.get(),
+                       pass->shared_quad_state_list.back(), pass.get(),
+                       gfx::Rect(0, 3, 100, 100), SK_ColorWHITE);
+    // Non-occluding quad fully outside video rect
+    CreateOpaqueQuadAt(resource_provider_.get(),
+                       pass->shared_quad_state_list.back(), pass.get(),
+                       gfx::Rect(210, 210, 20, 20), SK_ColorWHITE);
+    // Underlay video quad
+    auto* video_quad = CreateFullscreenCandidateYUVVideoQuad(
+        resource_provider_.get(), child_resource_provider_.get(),
+        child_provider_.get(), pass->shared_quad_state_list.back(), pass.get());
+    video_quad->rect = gfx::Rect(0, 0, 200, 200);
+    video_quad->visible_rect = video_quad->rect;
+
+    DCLayerOverlayList dc_layer_list;
+    OverlayProcessor::FilterOperationsMap render_pass_filters;
+    OverlayProcessor::FilterOperationsMap render_pass_backdrop_filters;
+    // Damage rect fully outside video quad
+    damage_rect_ = gfx::Rect(210, 210, 20, 20);
+    RenderPassList pass_list;
+    pass_list.push_back(std::move(pass));
+    overlay_processor_->ProcessForOverlays(
+        resource_provider_.get(), &pass_list, GetIdentityColorMatrix(),
+        render_pass_filters, render_pass_backdrop_filters, nullptr,
+        &dc_layer_list, &damage_rect_, &content_bounds_);
+    EXPECT_EQ(1U, dc_layer_list.size());
+    EXPECT_EQ(0U, output_surface_->bind_framebuffer_count());
+    EXPECT_EQ(-1, dc_layer_list.back().z_order);
+    // Only the non-overlay damaged rect need to be drawn by the gl compositor
+    EXPECT_EQ(gfx::Rect(210, 210, 20, 20), damage_rect_);
+  }
+}
+
+TEST_F(DCLayerOverlayTest, DamageRectWithNonRootOverlay) {
+  base::test::ScopedFeatureList feature_list;
+  feature_list.InitWithFeatures({features::kDirectCompositionUnderlays,
+                                 features::kDirectCompositionNonrootOverlays},
+                                {});
+  {
+    // A root solid quad
+    std::unique_ptr<RenderPass> root_pass = CreateRenderPass();
+    CreateOpaqueQuadAt(
+        resource_provider_.get(), root_pass->shared_quad_state_list.back(),
+        root_pass.get(), gfx::Rect(210, 0, 20, 20), SK_ColorWHITE);
+
+    // A non-root video quad
+    std::unique_ptr<RenderPass> nonroot_pass = CreateRenderPass();
+    auto* video_quad = CreateFullscreenCandidateYUVVideoQuad(
+        resource_provider_.get(), child_resource_provider_.get(),
+        child_provider_.get(), nonroot_pass->shared_quad_state_list.back(),
+        nonroot_pass.get());
+    video_quad->rect = gfx::Rect(0, 0, 200, 200);
+    video_quad->visible_rect = video_quad->rect;
+
+    DCLayerOverlayList dc_layer_list;
+    OverlayProcessor::FilterOperationsMap render_pass_filters;
+    OverlayProcessor::FilterOperationsMap render_pass_backdrop_filters;
+    // Damage rect fully outside video quad
+    damage_rect_ = gfx::Rect(210, 0, 20, 20);
+    RenderPassList pass_list;
+    pass_list.push_back(std::move(nonroot_pass));
+    pass_list.push_back(std::move(root_pass));
+    overlay_processor_->ProcessForOverlays(
+        resource_provider_.get(), &pass_list, GetIdentityColorMatrix(),
+        render_pass_filters, render_pass_backdrop_filters, nullptr,
+        &dc_layer_list, &damage_rect_, &content_bounds_);
+    EXPECT_EQ(1U, dc_layer_list.size());
+    EXPECT_EQ(0U, output_surface_->bind_framebuffer_count());
+    EXPECT_EQ(-1, dc_layer_list.back().z_order);
+    // damage_rect returned from ProcessForOverlays() is for root render pass
+    // only. Non-root damage rect is not included.
+    EXPECT_EQ(gfx::Rect(210, 0, 20, 20), damage_rect_);
+  }
+  {
+    // A root solid quad
+    std::unique_ptr<RenderPass> root_pass = CreateRenderPass();
+    CreateOpaqueQuadAt(
+        resource_provider_.get(), root_pass->shared_quad_state_list.back(),
+        root_pass.get(), gfx::Rect(210, 0, 20, 20), SK_ColorWHITE);
+
+    // A non-root video quad
+    std::unique_ptr<RenderPass> nonroot_pass = CreateRenderPass();
+    auto* video_quad = CreateFullscreenCandidateYUVVideoQuad(
+        resource_provider_.get(), child_resource_provider_.get(),
+        child_provider_.get(), nonroot_pass->shared_quad_state_list.back(),
+        nonroot_pass.get());
+    video_quad->rect = gfx::Rect(0, 0, 200, 200);
+    video_quad->visible_rect = video_quad->rect;
+
+    DCLayerOverlayList dc_layer_list;
+    OverlayProcessor::FilterOperationsMap render_pass_filters;
+    OverlayProcessor::FilterOperationsMap render_pass_backdrop_filters;
+    // Damage rect fully outside video quad
+    damage_rect_ = gfx::Rect(210, 0, 20, 20);
+    RenderPassList pass_list;
+    pass_list.push_back(std::move(nonroot_pass));
+    pass_list.push_back(std::move(root_pass));
+    overlay_processor_->ProcessForOverlays(
+        resource_provider_.get(), &pass_list, GetIdentityColorMatrix(),
+        render_pass_filters, render_pass_backdrop_filters, nullptr,
+        &dc_layer_list, &damage_rect_, &content_bounds_);
+    EXPECT_EQ(1U, dc_layer_list.size());
+    EXPECT_EQ(0U, output_surface_->bind_framebuffer_count());
+    EXPECT_EQ(-1, dc_layer_list.back().z_order);
+    // Nonroot damage_rect from the previous frame should be added to this frame
+    EXPECT_EQ(gfx::Rect(0, 0, 230, 200), damage_rect_);
+  }
+}
+
+TEST_F(DCLayerOverlayTest, DamageRect) {
+  for (int i = 0; i < 2; i++) {
+    std::unique_ptr<RenderPass> pass = CreateRenderPass();
+    CreateFullscreenCandidateYUVVideoQuad(
+        resource_provider_.get(), child_resource_provider_.get(),
+        child_provider_.get(), pass->shared_quad_state_list.back(), pass.get());
+
+    gfx::Rect damage_rect;
+    DCLayerOverlayList dc_layer_list;
+    OverlayProcessor::FilterOperationsMap render_pass_filters;
+    OverlayProcessor::FilterOperationsMap render_pass_backdrop_filters;
+    damage_rect_ = gfx::Rect(1, 1, 10, 10);
+    RenderPassList pass_list;
+    pass_list.push_back(std::move(pass));
+    overlay_processor_->ProcessForOverlays(
+        resource_provider_.get(), &pass_list, GetIdentityColorMatrix(),
+        render_pass_filters, render_pass_backdrop_filters, nullptr,
+        &dc_layer_list, &damage_rect_, &content_bounds_);
+    EXPECT_EQ(gfx::Rect(), damage_rect);
+    EXPECT_EQ(1U, dc_layer_list.size());
+    EXPECT_EQ(0U, output_surface_->bind_framebuffer_count());
+    EXPECT_EQ(1, dc_layer_list.back().z_order);
+    // Damage rect should be unchanged on initial frame because of resize, but
+    // should be empty on the second frame because everything was put in a
+    // layer.
+    if (i == 1)
+      EXPECT_TRUE(damage_rect_.IsEmpty());
+    else
+      EXPECT_EQ(gfx::Rect(1, 1, 10, 10), damage_rect_);
+  }
+}
+
+TEST_F(DCLayerOverlayTest, MultiplePassDamageRect) {
+  gfx::Transform child_pass1_transform;
+  child_pass1_transform.Translate(0, 100);
+
+  RenderPassId child_pass1_id(5);
+  std::unique_ptr<RenderPass> child_pass1 = CreateRenderPass();
+  ASSERT_EQ(child_pass1->shared_quad_state_list.size(), 1u);
+  child_pass1->id = child_pass1_id;
+  child_pass1->damage_rect = gfx::Rect();
+  child_pass1->transform_to_root_target = child_pass1_transform;
+  child_pass1->shared_quad_state_list.back()->opacity = 0.9f;
+  child_pass1->shared_quad_state_list.back()->blend_mode =
+      SkBlendMode::kSrcOver;
+
+  YUVVideoDrawQuad* yuv_quad_required = CreateFullscreenCandidateYUVVideoQuad(
+      resource_provider_.get(), child_resource_provider_.get(),
+      child_provider_.get(), child_pass1->shared_quad_state_list.back(),
+      child_pass1.get());
+  // Set the protected video flag will force DCLayerOverlay to use hw overlay
+  yuv_quad_required->protected_video_type =
+      gfx::ProtectedVideoType::kHardwareProtected;
+
+  RenderPassId child_pass2_id(6);
+  std::unique_ptr<RenderPass> child_pass2 = CreateRenderPass();
+  ASSERT_EQ(child_pass2->shared_quad_state_list.size(), 1u);
+  child_pass2->id = child_pass2_id;
+  child_pass2->damage_rect = gfx::Rect();
+  child_pass2->transform_to_root_target = gfx::Transform();
+  child_pass2->shared_quad_state_list.back()->opacity = 0.8f;
+
+  YUVVideoDrawQuad* yuv_quad_not_required =
+      CreateFullscreenCandidateYUVVideoQuad(
+          resource_provider_.get(), child_resource_provider_.get(),
+          child_provider_.get(), child_pass2->shared_quad_state_list.back(),
+          child_pass2.get());
+
+  std::unique_ptr<RenderPass> root_pass = CreateRenderPass();
+  root_pass->CreateAndAppendSharedQuadState();
+  ASSERT_EQ(root_pass->shared_quad_state_list.size(), 2u);
+
+  SharedQuadState* child_pass1_sqs =
+      root_pass->shared_quad_state_list.ElementAt(0);
+  child_pass1_sqs->quad_to_target_transform =
+      child_pass1->transform_to_root_target;
+  child_pass1_sqs->opacity = 0.7f;
+
+  gfx::Rect unit_rect(0, 0, 1, 1);
+  auto* child_pass1_rpdq =
+      root_pass->CreateAndAppendDrawQuad<RenderPassDrawQuad>();
+  child_pass1_rpdq->SetNew(child_pass1_sqs, unit_rect, unit_rect,
+                           child_pass1_id, 0, gfx::RectF(), gfx::Size(),
+                           gfx::Vector2dF(), gfx::PointF(),
+                           gfx::RectF(0, 0, 1, 1), false, 1.0f);
+
+  SharedQuadState* child_pass2_sqs =
+      root_pass->shared_quad_state_list.ElementAt(1);
+  child_pass2_sqs->quad_to_target_transform =
+      child_pass2->transform_to_root_target;
+  child_pass2_sqs->opacity = 0.6f;
+
+  auto* child_pass2_rpdq =
+      root_pass->CreateAndAppendDrawQuad<RenderPassDrawQuad>();
+  child_pass2_rpdq->SetNew(child_pass2_sqs, unit_rect, unit_rect,
+                           child_pass2_id, 0, gfx::RectF(), gfx::Size(),
+                           gfx::Vector2dF(), gfx::PointF(),
+                           gfx::RectF(0, 0, 1, 1), false, 1.0f);
+
+  root_pass->damage_rect = gfx::Rect();
+
+  gfx::Rect root_damage_rect;
+  DCLayerOverlayList dc_layer_list;
+  OverlayProcessor::FilterOperationsMap render_pass_filters;
+  OverlayProcessor::FilterOperationsMap render_pass_backdrop_filters;
+  RenderPassList pass_list;
+  pass_list.push_back(std::move(child_pass1));
+  pass_list.push_back(std::move(child_pass2));
+  pass_list.push_back(std::move(root_pass));
+  overlay_processor_->ProcessForOverlays(
+      resource_provider_.get(), &pass_list, GetIdentityColorMatrix(),
+      render_pass_filters, render_pass_backdrop_filters, nullptr,
+      &dc_layer_list, &root_damage_rect, &content_bounds_);
+  EXPECT_EQ(0U, output_surface_->bind_framebuffer_count());
+
+  // Only the kHardwareProtectedVideo video quad produces damage.
+  ASSERT_EQ(1U, dc_layer_list.size());
+  EXPECT_EQ(-1, dc_layer_list.back().z_order);
+  EXPECT_EQ(gfx::Rect(0, 0, 256, 256), pass_list[0]->damage_rect);
+  EXPECT_EQ(gfx::Rect(), pass_list[1]->damage_rect);
+  EXPECT_EQ(gfx::Rect(0, 100, 256, 156), root_damage_rect);
+  // Overlay damage handling is done entirely within DCOverlayProcessor so this
+  // is expected to return an empty rect
+  gfx::Rect overlay_damage = overlay_processor_->GetAndResetOverlayDamage();
+  EXPECT_EQ(gfx::Rect(0, 0, 0, 0), overlay_damage);
+
+  EXPECT_EQ(1u, pass_list[0]->quad_list.size());
+  EXPECT_EQ(DrawQuad::Material::kSolidColor,
+            pass_list[0]->quad_list.ElementAt(0)->material);
+
+  // The kHardwareProtectedVideo video quad is put into an underlay, and
+  // replaced by a solid color quad.
+  auto* yuv_solid_color_quad =
+      static_cast<SolidColorDrawQuad*>(pass_list[0]->quad_list.ElementAt(0));
+  EXPECT_EQ(SK_ColorBLACK, yuv_solid_color_quad->color);
+  EXPECT_EQ(gfx::Rect(0, 0, 256, 256), yuv_solid_color_quad->rect);
+  EXPECT_TRUE(yuv_solid_color_quad->shared_quad_state->quad_to_target_transform
+                  .IsIdentity());
+  EXPECT_EQ(0.9f, yuv_solid_color_quad->shared_quad_state->opacity);
+  EXPECT_EQ(SkBlendMode::kDstOut,
+            yuv_solid_color_quad->shared_quad_state->blend_mode);
+
+  // The non required video quad is not put into an underlay.
+  EXPECT_EQ(1u, pass_list[1]->quad_list.size());
+  EXPECT_EQ(yuv_quad_not_required, pass_list[1]->quad_list.ElementAt(0));
+
+  EXPECT_EQ(3u, pass_list[2]->quad_list.size());
+
+  // The RPDQs are not modified.
+  EXPECT_EQ(DrawQuad::Material::kRenderPass,
+            pass_list[2]->quad_list.ElementAt(0)->material);
+  EXPECT_EQ(child_pass1_id, static_cast<RenderPassDrawQuad*>(
+                                pass_list[2]->quad_list.ElementAt(0))
+                                ->render_pass_id);
+
+  // A solid color quad is put behind the RPDQ containing the video.
+  EXPECT_EQ(DrawQuad::Material::kSolidColor,
+            pass_list[2]->quad_list.ElementAt(1)->material);
+  auto* rpdq_solid_color_quad =
+      static_cast<SolidColorDrawQuad*>(pass_list[2]->quad_list.ElementAt(1));
+  EXPECT_EQ(SK_ColorTRANSPARENT, rpdq_solid_color_quad->color);
+  EXPECT_EQ(child_pass1_transform,
+            rpdq_solid_color_quad->shared_quad_state->quad_to_target_transform);
+  EXPECT_EQ(1.f, rpdq_solid_color_quad->shared_quad_state->opacity);
+  EXPECT_FALSE(rpdq_solid_color_quad->ShouldDrawWithBlending());
+
+  EXPECT_EQ(DrawQuad::Material::kRenderPass,
+            pass_list[2]->quad_list.ElementAt(2)->material);
+  EXPECT_EQ(child_pass2_id, static_cast<RenderPassDrawQuad*>(
+                                pass_list[2]->quad_list.ElementAt(2))
+                                ->render_pass_id);
+}
+
+TEST_F(DCLayerOverlayTest, ClipRect) {
+  base::test::ScopedFeatureList feature_list;
+  feature_list.InitAndEnableFeature(features::kDirectCompositionUnderlays);
+
+  // Process twice. The second time through the overlay list shouldn't change,
+  // which will allow the damage rect to reflect just the changes in that
+  // frame.
+  for (size_t i = 0; i < 2; ++i) {
+    std::unique_ptr<RenderPass> pass = CreateRenderPass();
+    CreateOpaqueQuadAt(resource_provider_.get(),
+                       pass->shared_quad_state_list.back(), pass.get(),
+                       gfx::Rect(0, 2, 100, 100), SK_ColorWHITE);
+    pass->shared_quad_state_list.back()->is_clipped = true;
+    pass->shared_quad_state_list.back()->clip_rect = gfx::Rect(0, 3, 100, 100);
+    SharedQuadState* shared_state = pass->CreateAndAppendSharedQuadState();
+    shared_state->opacity = 1.f;
+    CreateFullscreenCandidateYUVVideoQuad(
+        resource_provider_.get(), child_resource_provider_.get(),
+        child_provider_.get(), shared_state, pass.get());
+    shared_state->is_clipped = true;
+    // Clipped rect shouldn't be overlapped by clipped opaque quad rect.
+    shared_state->clip_rect = gfx::Rect(0, 0, 100, 3);
+
+    DCLayerOverlayList dc_layer_list;
+    OverlayProcessor::FilterOperationsMap render_pass_filters;
+    OverlayProcessor::FilterOperationsMap render_pass_backdrop_filters;
+    RenderPassList pass_list;
+    pass_list.push_back(std::move(pass));
+    damage_rect_ = gfx::Rect(1, 1, 10, 10);
+    overlay_processor_->ProcessForOverlays(
+        resource_provider_.get(), &pass_list, GetIdentityColorMatrix(),
+        render_pass_filters, render_pass_backdrop_filters, nullptr,
+        &dc_layer_list, &damage_rect_, &content_bounds_);
+    EXPECT_EQ(1U, dc_layer_list.size());
+    // Because of clip rects the overlay isn't occluded and shouldn't be an
+    // underlay.
+    EXPECT_EQ(1, dc_layer_list.back().z_order);
+    EXPECT_TRUE(dc_layer_list.back().is_clipped);
+    EXPECT_EQ(gfx::Rect(0, 0, 100, 3), dc_layer_list.back().clip_rect);
+    if (i == 1) {
+      // The damage rect should only contain contents that aren't in the
+      // clipped overlay rect.
+      EXPECT_EQ(gfx::Rect(1, 3, 10, 8), damage_rect_);
+    }
+  }
+}
+
+TEST_F(DCLayerOverlayTest, TransparentOnTop) {
+  // Process twice. The second time through the overlay list shouldn't change,
+  // which will allow the damage rect to reflect just the changes in that
+  // frame.
+  for (size_t i = 0; i < 2; ++i) {
+    std::unique_ptr<RenderPass> pass = CreateRenderPass();
+    CreateFullscreenCandidateYUVVideoQuad(
+        resource_provider_.get(), child_resource_provider_.get(),
+        child_provider_.get(), pass->shared_quad_state_list.back(), pass.get());
+    pass->shared_quad_state_list.back()->opacity = 0.5f;
+
+    DCLayerOverlayList dc_layer_list;
+    OverlayProcessor::FilterOperationsMap render_pass_filters;
+    OverlayProcessor::FilterOperationsMap render_pass_backdrop_filters;
+    damage_rect_ = gfx::Rect(1, 1, 10, 10);
+    RenderPassList pass_list;
+    pass_list.push_back(std::move(pass));
+    overlay_processor_->ProcessForOverlays(
+        resource_provider_.get(), &pass_list, GetIdentityColorMatrix(),
+        render_pass_filters, render_pass_backdrop_filters, nullptr,
+        &dc_layer_list, &damage_rect_, &content_bounds_);
+    EXPECT_EQ(1U, dc_layer_list.size());
+    EXPECT_EQ(1, dc_layer_list.back().z_order);
+    // Quad isn't opaque, so underlying damage must remain the same.
+    EXPECT_EQ(gfx::Rect(1, 1, 10, 10), damage_rect_);
+  }
+}
+
+TEST_F(DCLayerOverlayTest, UnderlayDamageRectWithQuadOnTopUnchanged) {
+  base::test::ScopedFeatureList feature_list;
+  feature_list.InitAndEnableFeature(features::kDirectCompositionUnderlays);
+
+  for (int i = 0; i < 3; i++) {
+    std::unique_ptr<RenderPass> pass = CreateRenderPass();
+    // Add a solid color quad on top
+    SharedQuadState* shared_state_on_top = pass->shared_quad_state_list.back();
+    CreateSolidColorQuadAt(shared_state_on_top, SK_ColorRED, pass.get(),
+                           kOverlayBottomRightRect);
+
+    SharedQuadState* shared_state = pass->CreateAndAppendSharedQuadState();
+    shared_state->opacity = 1.f;
+    CreateFullscreenCandidateYUVVideoQuad(
+        resource_provider_.get(), child_resource_provider_.get(),
+        child_provider_.get(), shared_state, pass.get());
+
+    DCLayerOverlayList dc_layer_list;
+    OverlayProcessor::FilterOperationsMap render_pass_filters;
+    OverlayProcessor::FilterOperationsMap render_pass_backdrop_filters;
+    RenderPassList pass_list;
+    pass_list.push_back(std::move(pass));
+    gfx::Rect damage_rect_ = kOverlayRect;
+
+    // The quad on top does not give damage on the third frame
+    if (i == 2)
+      shared_state->occluding_damage_rect = gfx::Rect();
+    else
+      shared_state->occluding_damage_rect = kOverlayBottomRightRect;
+
+    overlay_processor_->ProcessForOverlays(
+        resource_provider_.get(), &pass_list, GetIdentityColorMatrix(),
+        render_pass_filters, render_pass_backdrop_filters, nullptr,
+        &dc_layer_list, &damage_rect_, &content_bounds_);
+    EXPECT_EQ(1U, dc_layer_list.size());
+    EXPECT_EQ(0U, output_surface_->bind_framebuffer_count());
+    EXPECT_EQ(-1, dc_layer_list.back().z_order);
+    // Damage rect should be unchanged on initial frame, but should be reduced
+    // to the size of quad on top, and empty on the third frame.
+    if (i == 0)
+      EXPECT_EQ(kOverlayRect, damage_rect_);
+    else if (i == 1)
+      EXPECT_EQ(kOverlayBottomRightRect, damage_rect_);
+    else if (i == 2)
+      EXPECT_EQ(gfx::Rect(), damage_rect_);
+  }
+}
+
+}  // namespace
+}  // namespace viz
diff --git a/components/viz/service/display/overlay_processor.cc b/components/viz/service/display/overlay_processor.cc
index 992f5a2..fa600dd9 100644
--- a/components/viz/service/display/overlay_processor.cc
+++ b/components/viz/service/display/overlay_processor.cc
@@ -26,6 +26,7 @@
 
 namespace {
 
+#if defined(OS_ANDROID)
 // Utility class to make sure that we notify resource that they're promotable
 // before returning from ProcessForOverlays.
 class SendPromotionHintsBeforeReturning {
@@ -64,7 +65,7 @@
 
   DISALLOW_COPY_AND_ASSIGN(SendPromotionHintsBeforeReturning);
 };
-
+#endif
 }  // namespace
 
 // Default implementation of whether a strategy would remove the output surface
@@ -129,32 +130,53 @@
     gpu::SurfaceHandle surface_handle,
     const OutputSurface::Capabilities& capabilities,
     const RendererSettings& renderer_settings) {
-  return base::WrapUnique(
-      new OverlayProcessor(skia_output_surface,
-                           OverlayCandidateValidator::Create(
-                               surface_handle, capabilities, renderer_settings),
-                           std::make_unique<DCLayerOverlayProcessor>(
-                               capabilities, renderer_settings)));
+  auto processor = base::WrapUnique(new OverlayProcessor(
+      skia_output_surface,
+      OverlayCandidateValidator::Create(surface_handle, capabilities,
+                                        renderer_settings)));
+#if defined(OS_WIN)
+  processor->InitializeDCOverlayProcessor(
+      std::make_unique<DCLayerOverlayProcessor>(capabilities,
+                                                renderer_settings));
+#endif
+  return processor;
 }
 
+#if defined(OS_ANDROID)
 OverlayProcessor::OverlayProcessor(
     SkiaOutputSurface* skia_output_surface,
-    std::unique_ptr<OverlayCandidateValidator> overlay_validator,
-    std::unique_ptr<DCLayerOverlayProcessor> dc_layer_overlay_processor)
+    std::unique_ptr<OverlayCandidateValidator> overlay_validator)
     : overlay_validator_(std::move(overlay_validator)),
-      dc_layer_overlay_processor_(std::move(dc_layer_overlay_processor)),
       skia_output_surface_(skia_output_surface) {
-  DCHECK(dc_layer_overlay_processor_);
   if (overlay_validator_)
     overlay_validator_->InitializeStrategies();
 }
+#else
+OverlayProcessor::OverlayProcessor(
+    SkiaOutputSurface* skia_output_surface,
+    std::unique_ptr<OverlayCandidateValidator> overlay_validator)
+    : overlay_validator_(std::move(overlay_validator)) {
+  if (overlay_validator_)
+    overlay_validator_->InitializeStrategies();
+}
+#endif
 
 // For testing.
 OverlayProcessor::OverlayProcessor(
     std::unique_ptr<OverlayCandidateValidator> overlay_validator)
-    : OverlayProcessor(nullptr,
-                       std::move(overlay_validator),
-                       std::make_unique<DCLayerOverlayProcessor>()) {}
+    : OverlayProcessor(nullptr, std::move(overlay_validator)) {
+#if defined(OS_WIN)
+  InitializeDCOverlayProcessor(std::make_unique<DCLayerOverlayProcessor>());
+#endif
+}
+
+#if defined(OS_WIN)
+void OverlayProcessor::InitializeDCOverlayProcessor(
+    std::unique_ptr<DCLayerOverlayProcessor> dc_layer_overlay_processor) {
+  DCHECK(!dc_layer_overlay_processor_);
+  dc_layer_overlay_processor_.swap(dc_layer_overlay_processor);
+}
+#endif  // defined(OS_WIN)
 
 OverlayProcessor::~OverlayProcessor() = default;
 
@@ -169,8 +191,13 @@
     RenderPass* render_pass,
     const OverlayProcessor::FilterOperationsMap& render_pass_filters,
     const OverlayProcessor::FilterOperationsMap& render_pass_backdrop_filters,
-    CALayerOverlayList* ca_layer_overlays,
+    CandidateList* ca_layer_overlays,
     gfx::Rect* damage_rect) {
+#if defined(OS_MACOSX)
+  // Skip overlay processing if we have copy request.
+  if (!render_pass->copy_requests.empty())
+    return true;
+
   if (!overlay_validator_ || !overlay_validator_->AllowCALayerOverlays())
     return false;
 
@@ -181,12 +208,13 @@
     return false;
 
   // CALayer overlays are all-or-nothing. If all quads were replaced with
-  // layers then clear the list and remove the backbuffer from the overcandidate
-  // list.
+  // layers then mark the output surface as already handled.
   output_surface_already_handled_ = true;
   overlay_damage_rect_ = render_pass->output_rect;
   *damage_rect = gfx::Rect();
   return true;
+#endif  // defined(OS_MACOSX)
+  return false;
 }
 
 bool OverlayProcessor::ProcessForDCLayers(
@@ -194,8 +222,19 @@
     RenderPassList* render_passes,
     const OverlayProcessor::FilterOperationsMap& render_pass_filters,
     const OverlayProcessor::FilterOperationsMap& render_pass_backdrop_filters,
-    DCLayerOverlayList* dc_layer_overlays,
+    CandidateList* dc_layer_overlays,
     gfx::Rect* damage_rect) {
+#if defined(OS_WIN)
+  // Skip overlay processing if we have copy request.
+  if (!render_passes->back()->copy_requests.empty()) {
+    damage_rect->Union(dc_layer_overlay_processor_
+                           ->previous_frame_overlay_damage_contribution());
+    // Update damage rect before calling ClearOverlayState, otherwise
+    // previous_frame_overlay_rect_union will be empty.
+    dc_layer_overlay_processor_->ClearOverlayState();
+    return true;
+  }
+
   if (!overlay_validator_ || !overlay_validator_->AllowDCLayerOverlays())
     return false;
 
@@ -203,6 +242,8 @@
       resource_provider, gfx::RectF(render_passes->back()->output_rect),
       render_passes, damage_rect, dc_layer_overlays);
   return true;
+#endif  // defined(OS_WIN)
+  return false;
 }
 
 void OverlayProcessor::ProcessForOverlays(
@@ -212,57 +253,50 @@
     const OverlayProcessor::FilterOperationsMap& render_pass_filters,
     const OverlayProcessor::FilterOperationsMap& render_pass_backdrop_filters,
     OutputSurfaceOverlayPlane* output_surface_plane,
-    OverlayCandidateList* candidates,
-    CALayerOverlayList* ca_layer_overlays,
-    DCLayerOverlayList* dc_layer_overlays,
+    CandidateList* candidates,
     gfx::Rect* damage_rect,
     std::vector<gfx::Rect>* content_bounds) {
   TRACE_EVENT0("viz", "OverlayProcessor::ProcessForOverlays");
+#if defined(OS_ANDROID)
   // Be sure to send out notifications, regardless of whether we get to
   // processing for overlays or not.  If we don't, then we should notify that
   // they are not promotable.
   SendPromotionHintsBeforeReturning notifier(resource_provider, candidates,
                                              skia_output_surface_);
+#endif
 
   // Clear to get ready to handle output surface as overlay.
   output_surface_already_handled_ = false;
 
-  // Reset |previous_frame_underlay_rect_| in case UpdateDamageRect() not being
-  // invoked.  Also reset |previous_frame_underlay_was_unoccluded_|.
-  const gfx::Rect previous_frame_underlay_rect = previous_frame_underlay_rect_;
-  previous_frame_underlay_rect_ = gfx::Rect();
-  bool previous_frame_underlay_was_unoccluded =
-      previous_frame_underlay_was_unoccluded_;
-  previous_frame_underlay_was_unoccluded_ = false;
-
-  RenderPass* root_render_pass = render_passes->back().get();
-
-  // If we have any copy requests, we can't remove any quads for overlays,
-  // CALayers, or DCLayers because the framebuffer would be missing the removed
-  // quads' contents.
-  if (!root_render_pass->copy_requests.empty()) {
-    damage_rect->Union(dc_layer_overlay_processor_
-                           ->previous_frame_overlay_damage_contribution());
-    // Update damage rect before calling ClearOverlayState, otherwise
-    // previous_frame_overlay_rect_union will be empty.
-    dc_layer_overlay_processor_->ClearOverlayState();
-    return;
-  }
-
   // First attempt to process for CALayers.
-  if (ProcessForCALayers(resource_provider, root_render_pass,
+  if (ProcessForCALayers(resource_provider, render_passes->back().get(),
                          render_pass_filters, render_pass_backdrop_filters,
-                         ca_layer_overlays, damage_rect)) {
+                         candidates, damage_rect)) {
     return;
   }
 
   if (ProcessForDCLayers(resource_provider, render_passes, render_pass_filters,
-                         render_pass_backdrop_filters, dc_layer_overlays,
+                         render_pass_backdrop_filters, candidates,
                          damage_rect)) {
     return;
   }
 
   DCHECK(candidates->empty());
+
+#if !defined(OS_MACOSX) && !defined(OS_WIN)
+  RenderPass* render_pass = render_passes->back().get();
+
+  // If we have any copy requests, we can't remove any quads for overlays or
+  // CALayers because the framebuffer would be missing the removed quads'
+  // contents.
+  if (!render_pass->copy_requests.empty()) {
+    // Reset |previous_frame_underlay_rect_| in case UpdateDamageRect() not
+    // being invoked.  Also reset |previous_frame_underlay_was_unoccluded_|.
+    previous_frame_underlay_rect_ = gfx::Rect();
+    previous_frame_underlay_was_unoccluded_ = false;
+    return;
+  }
+
   // Only if that fails, attempt hardware overlay strategies.
   bool success = false;
   if (overlay_validator_) {
@@ -272,22 +306,27 @@
   }
 
   if (success) {
-    UpdateDamageRect(candidates, previous_frame_underlay_rect,
-                     previous_frame_underlay_was_unoccluded,
-                     &root_render_pass->quad_list, damage_rect);
+    UpdateDamageRect(candidates, previous_frame_underlay_rect_,
+                     previous_frame_underlay_was_unoccluded_,
+                     &render_pass->quad_list, damage_rect);
   } else {
-    if (!previous_frame_underlay_rect.IsEmpty())
-      damage_rect->Union(previous_frame_underlay_rect);
+    if (!previous_frame_underlay_rect_.IsEmpty())
+      damage_rect->Union(previous_frame_underlay_rect_);
 
     DCHECK(candidates->empty());
+
+    previous_frame_underlay_rect_ = gfx::Rect();
+    previous_frame_underlay_was_unoccluded_ = false;
   }
 
   TRACE_COUNTER1(TRACE_DISABLED_BY_DEFAULT("viz.debug.overlay_planes"),
                  "Scheduled overlay planes", candidates->size());
+
+#endif
 }
 
-// Subtract on-top opaque overlays from the damage rect, unless the overlays use
-// the backbuffer as their content (in which case, add their combined rect
+// Subtract on-top opaque overlays from the damage rect, unless the overlays
+// use the backbuffer as their content (in which case, add their combined rect
 // back to the damage at the end).
 // Also subtract unoccluded underlays from the damage rect if we know that the
 // same underlay was scheduled on the previous frame. If the renderer decides
@@ -314,13 +353,12 @@
       // Track the underlay_rect from frame to frame. If it is the same and
       // nothing is on top of it then that rect doesn't need to be damaged
       // because the drawing is occurring on a different plane. If it is
-      // different then that indicates that a different underlay has been chosen
-      // and the previous underlay rect should be damaged because it has changed
-      // planes from the underlay plane to the main plane.
-      // It then checks that this is not a transition from occluded to
-      // unoccluded.
+      // different then that indicates that a different underlay has been
+      // chosen and the previous underlay rect should be damaged because it
+      // has changed planes from the underlay plane to the main plane. It then
+      // checks that this is not a transition from occluded to unoccluded.
       //
-      // We also insist that the underlay is unoccluded for at leat one frame,
+      // We also insist that the underlay is unoccluded for at least one frame,
       // else when content above the overlay transitions from not fully
       // transparent to fully transparent, we still need to erase it from the
       // framebuffer.  Otherwise, the last non-transparent frame will remain.
diff --git a/components/viz/service/display/overlay_processor.h b/components/viz/service/display/overlay_processor.h
index 2ca7db4..cca3ffd2 100644
--- a/components/viz/service/display/overlay_processor.h
+++ b/components/viz/service/display/overlay_processor.h
@@ -9,13 +9,22 @@
 
 #include "base/containers/flat_map.h"
 #include "base/macros.h"
+#include "build/build_config.h"
 #include "components/viz/common/display/overlay_strategy.h"
 #include "components/viz/common/quads/render_pass.h"
-#include "components/viz/service/display/ca_layer_overlay.h"
-#include "components/viz/service/display/dc_layer_overlay.h"
+#include "components/viz/service/display/output_surface.h"
+#include "components/viz/service/display/overlay_candidate.h"
 #include "components/viz/service/viz_service_export.h"
 #include "gpu/ipc/common/surface_handle.h"
 
+#if defined(OS_WIN)
+#include "components/viz/service/display/dc_layer_overlay.h"
+#endif
+
+#if defined(OS_MACOSX)
+#include "components/viz/service/display/ca_layer_overlay.h"
+#endif
+
 namespace cc {
 class DisplayResourceProvider;
 }
@@ -28,6 +37,19 @@
 
 class VIZ_SERVICE_EXPORT OverlayProcessor {
  public:
+#if defined(OS_ANDROID)
+  using CandidateList = OverlayCandidateList;
+#elif defined(OS_MACOSX)
+  using CandidateList = CALayerOverlayList;
+#elif defined(OS_WIN)
+  using CandidateList = DCLayerOverlayList;
+#elif defined(USE_OZONE)
+  using CandidateList = OverlayCandidateList;
+#else
+  // Default.
+  using CandidateList = OverlayCandidateList;
+#endif
+
   using FilterOperationsMap =
       base::flat_map<RenderPassId, cc::FilterOperations*>;
 
@@ -123,9 +145,7 @@
       const FilterOperationsMap& render_pass_filters,
       const FilterOperationsMap& render_pass_backdrop_filters,
       OutputSurfaceOverlayPlane* output_surface_plane,
-      OverlayCandidateList* overlay_candidates,
-      CALayerOverlayList* ca_layer_overlays,
-      DCLayerOverlayList* dc_layer_overlays,
+      CandidateList* overlay_candidates,
       gfx::Rect* damage_rect,
       std::vector<gfx::Rect>* content_bounds);
 
@@ -161,22 +181,26 @@
  private:
   OverlayProcessor(
       SkiaOutputSurface* skia_output_surface,
-      std::unique_ptr<OverlayCandidateValidator> overlay_validator,
+      std::unique_ptr<OverlayCandidateValidator> overlay_validator);
+
+#if defined(OS_WIN)
+  void InitializeDCOverlayProcessor(
       std::unique_ptr<DCLayerOverlayProcessor> dc_layer_overlay_processor);
+#endif
 
   bool ProcessForCALayers(
       DisplayResourceProvider* resource_provider,
       RenderPass* render_pass,
       const FilterOperationsMap& render_pass_filters,
       const FilterOperationsMap& render_pass_backdrop_filters,
-      CALayerOverlayList* ca_layer_overlays,
+      CandidateList* overlay_candidates,
       gfx::Rect* damage_rect);
   bool ProcessForDCLayers(
       DisplayResourceProvider* resource_provider,
       RenderPassList* render_passes,
       const FilterOperationsMap& render_pass_filters,
       const FilterOperationsMap& render_pass_backdrop_filters,
-      DCLayerOverlayList* dc_layer_overlays,
+      CandidateList* overlay_candidates,
       gfx::Rect* damage_rect);
   // Update |damage_rect| by removing damage casued by |candidates|.
   void UpdateDamageRect(OverlayCandidateList* candidates,
@@ -185,9 +209,13 @@
                         const QuadList* quad_list,
                         gfx::Rect* damage_rect);
 
+#if defined(OS_WIN)
   std::unique_ptr<DCLayerOverlayProcessor> dc_layer_overlay_processor_;
+#endif
 
+#if defined(OS_ANDROID)
   SkiaOutputSurface* skia_output_surface_;
+#endif
   bool output_surface_already_handled_;
   DISALLOW_COPY_AND_ASSIGN(OverlayProcessor);
 };
diff --git a/components/viz/service/display/overlay_unittest.cc b/components/viz/service/display/overlay_unittest.cc
index 48c5c3f..4ff0d61 100644
--- a/components/viz/service/display/overlay_unittest.cc
+++ b/components/viz/service/display/overlay_unittest.cc
@@ -11,6 +11,7 @@
 #include "base/containers/flat_map.h"
 #include "base/test/scoped_feature_list.h"
 #include "base/unguessable_token.h"
+#include "build/build_config.h"
 #include "cc/test/fake_output_surface_client.h"
 #include "cc/test/geometry_test_utils.h"
 #include "cc/test/resource_provider_test_utils.h"
@@ -41,7 +42,6 @@
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "ui/gfx/geometry/rect_conversions.h"
-#include "ui/gl/gl_switches.h"
 #include "ui/latency/latency_info.h"
 
 using testing::_;
@@ -453,31 +453,6 @@
       shared_quad_state, render_pass, render_pass->output_rect);
 }
 
-YUVVideoDrawQuad* CreateFullscreenCandidateYUVVideoQuad(
-    DisplayResourceProvider* parent_resource_provider,
-    ClientResourceProvider* child_resource_provider,
-    ContextProvider* child_context_provider,
-    const SharedQuadState* shared_quad_state,
-    RenderPass* render_pass) {
-  bool needs_blending = false;
-  gfx::RectF tex_coord_rect(0, 0, 1, 1);
-  gfx::Rect rect = render_pass->output_rect;
-  gfx::Size resource_size_in_pixels = rect.size();
-  bool is_overlay_candidate = true;
-  ResourceId resource_id = CreateResource(
-      parent_resource_provider, child_resource_provider, child_context_provider,
-      resource_size_in_pixels, is_overlay_candidate);
-
-  auto* overlay_quad = render_pass->CreateAndAppendDrawQuad<YUVVideoDrawQuad>();
-  overlay_quad->SetNew(shared_quad_state, rect, rect, needs_blending,
-                       tex_coord_rect, tex_coord_rect, resource_size_in_pixels,
-                       resource_size_in_pixels, resource_id, resource_id,
-                       resource_id, resource_id,
-                       gfx::ColorSpace::CreateREC601(), 0, 1.0, 8);
-
-  return overlay_quad;
-}
-
 void CreateOpaqueQuadAt(DisplayResourceProvider* resource_provider,
                         const SharedQuadState* shared_quad_state,
                         RenderPass* render_pass,
@@ -621,6 +596,7 @@
   EXPECT_GE(2U, overlay_processor->GetStrategyCount());
 }
 
+#if !defined(OS_MACOSX) && !defined(OS_WIN)
 TEST_F(FullscreenOverlayTest, SuccessfulOverlay) {
   std::unique_ptr<RenderPass> pass = CreateRenderPass();
   gfx::Rect output_rect = pass->output_rect;
@@ -644,7 +620,8 @@
   overlay_processor_->ProcessForOverlays(
       resource_provider_.get(), &pass_list, GetIdentityColorMatrix(),
       render_pass_filters, render_pass_backdrop_filters, nullptr,
-      &candidate_list, nullptr, nullptr, &damage_rect_, &content_bounds_);
+      &candidate_list, &damage_rect_, &content_bounds_);
+
   ASSERT_EQ(1U, candidate_list.size());
 
   // Check that all the quads are gone.
@@ -681,7 +658,7 @@
   overlay_processor_->ProcessForOverlays(
       resource_provider_.get(), &pass_list, GetNonIdentityColorMatrix(),
       render_pass_filters, render_pass_backdrop_filters, nullptr,
-      &candidate_list, nullptr, nullptr, &damage_rect_, &content_bounds_);
+      &candidate_list, &damage_rect_, &content_bounds_);
   ASSERT_EQ(0U, candidate_list.size());
 
   // Check that the 2 quads are not gone.
@@ -706,7 +683,7 @@
   overlay_processor_->ProcessForOverlays(
       resource_provider_.get(), &pass_list, GetIdentityColorMatrix(),
       render_pass_filters, render_pass_backdrop_filters, nullptr,
-      &candidate_list, nullptr, nullptr, &damage_rect_, &content_bounds_);
+      &candidate_list, &damage_rect_, &content_bounds_);
 
   // Check that all the quads are gone.
   EXPECT_EQ(1U, main_pass->quad_list.size());
@@ -732,7 +709,7 @@
   overlay_processor_->ProcessForOverlays(
       resource_provider_.get(), &pass_list, GetIdentityColorMatrix(),
       render_pass_filters, render_pass_backdrop_filters, nullptr,
-      &candidate_list, nullptr, nullptr, &damage_rect_, &content_bounds_);
+      &candidate_list, &damage_rect_, &content_bounds_);
   ASSERT_EQ(1U, candidate_list.size());
 
   // Check that the quad is gone.
@@ -762,7 +739,7 @@
   overlay_processor_->ProcessForOverlays(
       resource_provider_.get(), &pass_list, GetIdentityColorMatrix(),
       render_pass_filters, render_pass_backdrop_filters, nullptr,
-      &candidate_list, nullptr, nullptr, &damage_rect_, &content_bounds_);
+      &candidate_list, &damage_rect_, &content_bounds_);
   ASSERT_EQ(0U, candidate_list.size());
 
   // Check that the 2 quads are not gone.
@@ -788,7 +765,7 @@
   overlay_processor_->ProcessForOverlays(
       resource_provider_.get(), &pass_list, GetIdentityColorMatrix(),
       render_pass_filters, render_pass_backdrop_filters, nullptr,
-      &candidate_list, nullptr, nullptr, &damage_rect_, &content_bounds_);
+      &candidate_list, &damage_rect_, &content_bounds_);
   ASSERT_EQ(0U, candidate_list.size());
 
   // Check that the quad is not gone.
@@ -820,7 +797,7 @@
   overlay_processor_->ProcessForOverlays(
       resource_provider_.get(), &pass_list, GetIdentityColorMatrix(),
       render_pass_filters, render_pass_backdrop_filters, nullptr,
-      &candidate_list, nullptr, nullptr, &damage_rect_, &content_bounds_);
+      &candidate_list, &damage_rect_, &content_bounds_);
   ASSERT_EQ(1U, candidate_list.size());
 
   // Check that the fullscreen quad is gone.
@@ -852,7 +829,7 @@
   overlay_processor_->ProcessForOverlays(
       resource_provider_.get(), &pass_list, GetIdentityColorMatrix(),
       render_pass_filters, render_pass_backdrop_filters, nullptr,
-      &candidate_list, nullptr, nullptr, &damage_rect_, &content_bounds_);
+      &candidate_list, &damage_rect_, &content_bounds_);
   ASSERT_EQ(1U, candidate_list.size());
 
   // Check that the quad is gone.
@@ -901,7 +878,7 @@
   overlay_processor_->ProcessForOverlays(
       resource_provider_.get(), &pass_list, GetIdentityColorMatrix(),
       render_pass_filters, render_pass_backdrop_filters, nullptr,
-      &candidate_list, nullptr, nullptr, &damage_rect_, &content_bounds_);
+      &candidate_list, &damage_rect_, &content_bounds_);
   ASSERT_EQ(1U, candidate_list.size());
 
   // Check that one quad is gone.
@@ -935,7 +912,7 @@
   overlay_processor_->ProcessForOverlays(
       resource_provider_.get(), &pass_list, GetIdentityColorMatrix(),
       render_pass_filters, render_pass_backdrop_filters, nullptr,
-      &candidate_list, nullptr, nullptr, &damage_rect_, &content_bounds_);
+      &candidate_list, &damage_rect_, &content_bounds_);
   EXPECT_TRUE(damage_rect_.IsEmpty());
 }
 
@@ -958,7 +935,7 @@
   overlay_processor_->ProcessForOverlays(
       resource_provider_.get(), &pass_list, GetIdentityColorMatrix(),
       render_pass_filters, render_pass_backdrop_filters, nullptr,
-      &candidate_list, nullptr, nullptr, &damage_rect_, &content_bounds_);
+      &candidate_list, &damage_rect_, &content_bounds_);
   EXPECT_EQ(0U, candidate_list.size());
   // There should be nothing new here.
   CompareRenderPassLists(pass_list, original_pass_list);
@@ -987,7 +964,7 @@
   overlay_processor_->ProcessForOverlays(
       resource_provider_.get(), &pass_list, GetIdentityColorMatrix(),
       render_pass_filters, render_pass_backdrop_filters, nullptr,
-      &candidate_list, nullptr, nullptr, &damage_rect_, &content_bounds_);
+      &candidate_list, &damage_rect_, &content_bounds_);
   EXPECT_EQ(0U, candidate_list.size());
   // There should be nothing new here.
   CompareRenderPassLists(pass_list, original_pass_list);
@@ -1015,7 +992,7 @@
   overlay_processor_->ProcessForOverlays(
       resource_provider_.get(), &pass_list, GetIdentityColorMatrix(),
       render_pass_filters, render_pass_backdrop_filters, nullptr,
-      &candidate_list, nullptr, nullptr, &damage_rect_, &content_bounds_);
+      &candidate_list, &damage_rect_, &content_bounds_);
   EXPECT_EQ(1U, candidate_list.size());
 }
 
@@ -1035,7 +1012,7 @@
   overlay_processor_->ProcessForOverlays(
       resource_provider_.get(), &pass_list, GetIdentityColorMatrix(),
       render_pass_filters, render_pass_backdrop_filters, nullptr,
-      &candidate_list, nullptr, nullptr, &damage_rect_, &content_bounds_);
+      &candidate_list, &damage_rect_, &content_bounds_);
   EXPECT_EQ(1U, candidate_list.size());
   EXPECT_FALSE(damage_rect_.IsEmpty());
   gfx::Rect overlay_damage_rect =
@@ -1058,7 +1035,7 @@
   overlay_processor_->ProcessForOverlays(
       resource_provider_.get(), &pass_list, GetIdentityColorMatrix(),
       render_pass_filters, render_pass_backdrop_filters, nullptr,
-      &candidate_list, nullptr, nullptr, &damage_rect_, &content_bounds_);
+      &candidate_list, &damage_rect_, &content_bounds_);
   EXPECT_EQ(0U, candidate_list.size());
 }
 
@@ -1077,7 +1054,7 @@
   overlay_processor_->ProcessForOverlays(
       resource_provider_.get(), &pass_list, GetIdentityColorMatrix(),
       render_pass_filters, render_pass_backdrop_filters, nullptr,
-      &candidate_list, nullptr, nullptr, &damage_rect_, &content_bounds_);
+      &candidate_list, &damage_rect_, &content_bounds_);
   EXPECT_EQ(1U, candidate_list.size());
 }
 
@@ -1097,7 +1074,7 @@
   overlay_processor_->ProcessForOverlays(
       resource_provider_.get(), &pass_list, GetIdentityColorMatrix(),
       render_pass_filters, render_pass_backdrop_filters, nullptr,
-      &candidate_list, nullptr, nullptr, &damage_rect_, &content_bounds_);
+      &candidate_list, &damage_rect_, &content_bounds_);
   EXPECT_EQ(0U, candidate_list.size());
 }
 
@@ -1116,7 +1093,7 @@
   overlay_processor_->ProcessForOverlays(
       resource_provider_.get(), &pass_list, GetIdentityColorMatrix(),
       render_pass_filters, render_pass_backdrop_filters, nullptr,
-      &candidate_list, nullptr, nullptr, &damage_rect_, &content_bounds_);
+      &candidate_list, &damage_rect_, &content_bounds_);
   EXPECT_EQ(0U, candidate_list.size());
 }
 
@@ -1135,7 +1112,7 @@
   overlay_processor_->ProcessForOverlays(
       resource_provider_.get(), &pass_list, GetIdentityColorMatrix(),
       render_pass_filters, render_pass_backdrop_filters, nullptr,
-      &candidate_list, nullptr, nullptr, &damage_rect_, &content_bounds_);
+      &candidate_list, &damage_rect_, &content_bounds_);
   EXPECT_EQ(0U, candidate_list.size());
 }
 
@@ -1155,7 +1132,7 @@
   overlay_processor_->ProcessForOverlays(
       resource_provider_.get(), &pass_list, GetIdentityColorMatrix(),
       render_pass_filters, render_pass_backdrop_filters, nullptr,
-      &candidate_list, nullptr, nullptr, &damage_rect_, &content_bounds_);
+      &candidate_list, &damage_rect_, &content_bounds_);
   EXPECT_EQ(0U, candidate_list.size());
 }
 
@@ -1175,7 +1152,7 @@
   overlay_processor_->ProcessForOverlays(
       resource_provider_.get(), &pass_list, GetIdentityColorMatrix(),
       render_pass_filters, render_pass_backdrop_filters, nullptr,
-      &candidate_list, nullptr, nullptr, &damage_rect_, &content_bounds_);
+      &candidate_list, &damage_rect_, &content_bounds_);
   EXPECT_EQ(1U, candidate_list.size());
 }
 
@@ -1197,7 +1174,7 @@
   overlay_processor_->ProcessForOverlays(
       resource_provider_.get(), &pass_list, GetIdentityColorMatrix(),
       render_pass_filters, render_pass_backdrop_filters, nullptr,
-      &candidate_list, nullptr, nullptr, &damage_rect_, &content_bounds_);
+      &candidate_list, &damage_rect_, &content_bounds_);
   ASSERT_EQ(1U, candidate_list.size());
   EXPECT_EQ(gfx::OVERLAY_TRANSFORM_FLIP_VERTICAL,
             candidate_list.back().transform);
@@ -1222,7 +1199,7 @@
   overlay_processor_->ProcessForOverlays(
       resource_provider_.get(), &pass_list, GetIdentityColorMatrix(),
       render_pass_filters, render_pass_backdrop_filters, nullptr,
-      &candidate_list, nullptr, nullptr, &damage_rect_, &content_bounds_);
+      &candidate_list, &damage_rect_, &content_bounds_);
   ASSERT_EQ(1U, candidate_list.size());
   EXPECT_EQ(gfx::OVERLAY_TRANSFORM_FLIP_HORIZONTAL,
             candidate_list.back().transform);
@@ -1245,7 +1222,7 @@
   overlay_processor_->ProcessForOverlays(
       resource_provider_.get(), &pass_list, GetIdentityColorMatrix(),
       render_pass_filters, render_pass_backdrop_filters, nullptr,
-      &candidate_list, nullptr, nullptr, &damage_rect_, &content_bounds_);
+      &candidate_list, &damage_rect_, &content_bounds_);
   EXPECT_EQ(1U, candidate_list.size());
 }
 
@@ -1267,7 +1244,7 @@
   overlay_processor_->ProcessForOverlays(
       resource_provider_.get(), &pass_list, GetIdentityColorMatrix(),
       render_pass_filters, render_pass_backdrop_filters, nullptr,
-      &candidate_list, nullptr, nullptr, &damage_rect_, &content_bounds_);
+      &candidate_list, &damage_rect_, &content_bounds_);
   ASSERT_EQ(1U, candidate_list.size());
 }
 
@@ -1289,7 +1266,7 @@
   overlay_processor_->ProcessForOverlays(
       resource_provider_.get(), &pass_list, GetIdentityColorMatrix(),
       render_pass_filters, render_pass_backdrop_filters, nullptr,
-      &candidate_list, nullptr, nullptr, &damage_rect_, &content_bounds_);
+      &candidate_list, &damage_rect_, &content_bounds_);
   ASSERT_EQ(1U, candidate_list.size());
   EXPECT_EQ(gfx::OVERLAY_TRANSFORM_ROTATE_90, candidate_list.back().transform);
 }
@@ -1312,7 +1289,7 @@
   overlay_processor_->ProcessForOverlays(
       resource_provider_.get(), &pass_list, GetIdentityColorMatrix(),
       render_pass_filters, render_pass_backdrop_filters, nullptr,
-      &candidate_list, nullptr, nullptr, &damage_rect_, &content_bounds_);
+      &candidate_list, &damage_rect_, &content_bounds_);
   ASSERT_EQ(1U, candidate_list.size());
   EXPECT_EQ(gfx::OVERLAY_TRANSFORM_ROTATE_180, candidate_list.back().transform);
 }
@@ -1335,7 +1312,7 @@
   overlay_processor_->ProcessForOverlays(
       resource_provider_.get(), &pass_list, GetIdentityColorMatrix(),
       render_pass_filters, render_pass_backdrop_filters, nullptr,
-      &candidate_list, nullptr, nullptr, &damage_rect_, &content_bounds_);
+      &candidate_list, &damage_rect_, &content_bounds_);
   ASSERT_EQ(1U, candidate_list.size());
   EXPECT_EQ(gfx::OVERLAY_TRANSFORM_ROTATE_270, candidate_list.back().transform);
 }
@@ -1356,7 +1333,7 @@
   overlay_processor_->ProcessForOverlays(
       resource_provider_.get(), &pass_list, GetIdentityColorMatrix(),
       render_pass_filters, render_pass_backdrop_filters, nullptr,
-      &candidate_list, nullptr, nullptr, &damage_rect_, &content_bounds_);
+      &candidate_list, &damage_rect_, &content_bounds_);
   ASSERT_EQ(1U, candidate_list.size());
 }
 
@@ -1375,7 +1352,7 @@
   overlay_processor_->ProcessForOverlays(
       resource_provider_.get(), &pass_list, GetIdentityColorMatrix(),
       render_pass_filters, render_pass_backdrop_filters, nullptr,
-      &candidate_list, nullptr, nullptr, &damage_rect_, &content_bounds_);
+      &candidate_list, &damage_rect_, &content_bounds_);
   ASSERT_EQ(0U, candidate_list.size());
 }
 
@@ -1409,7 +1386,7 @@
   overlay_processor_->ProcessForOverlays(
       resource_provider_.get(), &pass_list, GetIdentityColorMatrix(),
       render_pass_filters, render_pass_backdrop_filters, nullptr,
-      &candidate_list, nullptr, nullptr, &damage_rect_, &content_bounds_);
+      &candidate_list, &damage_rect_, &content_bounds_);
   ASSERT_EQ(0U, candidate_list.size());
 }
 
@@ -1429,7 +1406,7 @@
   overlay_processor_->ProcessForOverlays(
       resource_provider_.get(), &pass_list, GetIdentityColorMatrix(),
       render_pass_filters, render_pass_backdrop_filters, nullptr,
-      &candidate_list, nullptr, nullptr, &damage_rect_, &content_bounds_);
+      &candidate_list, &damage_rect_, &content_bounds_);
   ASSERT_EQ(1U, candidate_list.size());
 }
 
@@ -1448,7 +1425,7 @@
   overlay_processor_->ProcessForOverlays(
       resource_provider_.get(), &pass_list, GetIdentityColorMatrix(),
       render_pass_filters, render_pass_backdrop_filters, nullptr,
-      &candidate_list, nullptr, nullptr, &damage_rect_, &content_bounds_);
+      &candidate_list, &damage_rect_, &content_bounds_);
   ASSERT_EQ(1U, candidate_list.size());
 }
 
@@ -1472,7 +1449,7 @@
   overlay_processor_->ProcessForOverlays(
       resource_provider_.get(), &pass_list, GetIdentityColorMatrix(),
       render_pass_filters, render_pass_backdrop_filters, nullptr,
-      &candidate_list, nullptr, nullptr, &damage_rect_, &content_bounds_);
+      &candidate_list, &damage_rect_, &content_bounds_);
   EXPECT_EQ(1U, candidate_list.size());
 }
 
@@ -1498,7 +1475,7 @@
   overlay_processor_->ProcessForOverlays(
       resource_provider_.get(), &pass_list, GetIdentityColorMatrix(),
       render_pass_filters, render_pass_backdrop_filters, nullptr,
-      &candidate_list, nullptr, nullptr, &damage_rect_, &content_bounds_);
+      &candidate_list, &damage_rect_, &content_bounds_);
   EXPECT_EQ(1U, candidate_list.size());
 }
 
@@ -1522,7 +1499,7 @@
   overlay_processor_->ProcessForOverlays(
       resource_provider_.get(), &pass_list, GetIdentityColorMatrix(),
       render_pass_filters, render_pass_backdrop_filters, nullptr,
-      &candidate_list, nullptr, nullptr, &damage_rect_, &content_bounds_);
+      &candidate_list, &damage_rect_, &content_bounds_);
   EXPECT_EQ(1U, candidate_list.size());
 }
 
@@ -1546,7 +1523,7 @@
   overlay_processor_->ProcessForOverlays(
       resource_provider_.get(), &pass_list, GetIdentityColorMatrix(),
       render_pass_filters, render_pass_backdrop_filters, nullptr,
-      &candidate_list, nullptr, nullptr, &damage_rect_, &content_bounds_);
+      &candidate_list, &damage_rect_, &content_bounds_);
   EXPECT_EQ(0U, candidate_list.size());
 }
 
@@ -1568,7 +1545,7 @@
   overlay_processor_->ProcessForOverlays(
       resource_provider_.get(), &pass_list, GetIdentityColorMatrix(),
       render_pass_filters, render_pass_backdrop_filters, nullptr,
-      &candidate_list, nullptr, nullptr, &damage_rect_, &content_bounds_);
+      &candidate_list, &damage_rect_, &content_bounds_);
   EXPECT_EQ(0U, candidate_list.size());
 }
 
@@ -1621,7 +1598,7 @@
     overlay_processor_->ProcessForOverlays(
         resource_provider_.get(), &pass_list, GetIdentityColorMatrix(),
         render_pass_filters, render_pass_backdrop_filters, nullptr,
-        &candidate_list, nullptr, nullptr, &damage_rect_, &content_bounds_);
+        &candidate_list, &damage_rect_, &content_bounds_);
 
     if (i <= kFramesSkippedBeforeNotPromoting) {
       EXPECT_EQ(1U, candidate_list.size());
@@ -1656,7 +1633,7 @@
   overlay_processor_->ProcessForOverlays(
       resource_provider_.get(), &pass_list, GetIdentityColorMatrix(),
       render_pass_filters, render_pass_backdrop_filters, nullptr,
-      &candidate_list, nullptr, nullptr, &damage_rect_, &content_bounds_);
+      &candidate_list, &damage_rect_, &content_bounds_);
   ASSERT_EQ(1U, candidate_list.size());
   EXPECT_EQ(-1, candidate_list[0].plane_z_order);
   EXPECT_EQ(2U, main_pass->quad_list.size());
@@ -1687,7 +1664,7 @@
   overlay_processor_->ProcessForOverlays(
       resource_provider_.get(), &pass_list, GetIdentityColorMatrix(),
       render_pass_filters, render_pass_backdrop_filters, nullptr,
-      &candidate_list, nullptr, nullptr, &damage_rect_, &content_bounds_);
+      &candidate_list, &damage_rect_, &content_bounds_);
   ASSERT_EQ(1U, candidate_list.size());
   EXPECT_EQ(-1, candidate_list[0].plane_z_order);
   // The overlay quad should have changed to a SOLID_COLOR quad.
@@ -1716,7 +1693,7 @@
   overlay_processor_->ProcessForOverlays(
       resource_provider_.get(), &pass_list, GetIdentityColorMatrix(),
       render_pass_filters, render_pass_backdrop_filters, nullptr,
-      &candidate_list, nullptr, nullptr, &damage_rect_, &content_bounds_);
+      &candidate_list, &damage_rect_, &content_bounds_);
 
   EXPECT_EQ(kOverlayRect, damage_rect_);
 }
@@ -1744,7 +1721,7 @@
     overlay_processor_->ProcessForOverlays(
         resource_provider_.get(), &pass_list, GetIdentityColorMatrix(),
         render_pass_filters, render_pass_backdrop_filters, nullptr,
-        &candidate_list, nullptr, nullptr, &damage_rect_, &content_bounds_);
+        &candidate_list, &damage_rect_, &content_bounds_);
   }
 
   // The second time the same overlay rect is scheduled it will be subtracted
@@ -1776,7 +1753,7 @@
     overlay_processor_->ProcessForOverlays(
         resource_provider_.get(), &pass_list, GetIdentityColorMatrix(),
         render_pass_filters, render_pass_backdrop_filters, nullptr,
-        &candidate_list, nullptr, nullptr, &damage_rect_, &content_bounds_);
+        &candidate_list, &damage_rect_, &content_bounds_);
 
     EXPECT_EQ(overlay_rects[i], damage_rect_);
   }
@@ -1811,7 +1788,7 @@
     overlay_processor_->ProcessForOverlays(
         resource_provider_.get(), &pass_list, GetIdentityColorMatrix(),
         render_pass_filters, render_pass_backdrop_filters, nullptr,
-        &candidate_list, nullptr, nullptr, &damage_rect_, &content_bounds_);
+        &candidate_list, &damage_rect_, &content_bounds_);
   }
 
   EXPECT_EQ(kOverlayRect, damage_rect_);
@@ -1842,7 +1819,7 @@
     overlay_processor_->ProcessForOverlays(
         resource_provider_.get(), &pass_list, GetIdentityColorMatrix(),
         render_pass_filters, render_pass_backdrop_filters, nullptr,
-        &candidate_list, nullptr, nullptr, &damage_rect_, &content_bounds_);
+        &candidate_list, &damage_rect_, &content_bounds_);
 
     // The damage rect should not be subtracted if the underlay is occluded
     // (i==0) or it is unoccluded for the first time (i==1).
@@ -1874,7 +1851,7 @@
     overlay_processor_->ProcessForOverlays(
         resource_provider_.get(), &pass_list, GetIdentityColorMatrix(),
         render_pass_filters, render_pass_backdrop_filters, nullptr,
-        &candidate_list, nullptr, nullptr, &damage_rect_, &content_bounds_);
+        &candidate_list, &damage_rect_, &content_bounds_);
   }
 
   EXPECT_EQ(kOverlayRect, damage_rect_);
@@ -1904,7 +1881,7 @@
     overlay_processor_->ProcessForOverlays(
         resource_provider_.get(), &pass_list, GetIdentityColorMatrix(),
         render_pass_filters, render_pass_backdrop_filters, nullptr,
-        &candidate_list, nullptr, nullptr, &damage_rect_, &content_bounds_);
+        &candidate_list, &damage_rect_, &content_bounds_);
   }
 
   EXPECT_TRUE(damage_rect_.IsEmpty());
@@ -1938,7 +1915,7 @@
   overlay_processor_->ProcessForOverlays(
       resource_provider_.get(), &pass_list, GetIdentityColorMatrix(),
       render_pass_filters, render_pass_backdrop_filters, primary_plane,
-      &candidate_list, nullptr, nullptr, &damage_rect_, &content_bounds_);
+      &candidate_list, &damage_rect_, &content_bounds_);
 
   EXPECT_EQ(1U, candidate_list.size());
   ASSERT_EQ(true, output_surface_plane.enable_blending);
@@ -1970,7 +1947,7 @@
     overlay_processor_->ProcessForOverlays(
         resource_provider_.get(), &pass_list, GetIdentityColorMatrix(),
         render_pass_filters, render_pass_backdrop_filters, nullptr,
-        &candidate_list, nullptr, nullptr, &damage_rect_, &content_bounds_);
+        &candidate_list, &damage_rect_, &content_bounds_);
   }
 
   EXPECT_EQ(kOverlayRect, damage_rect_);
@@ -2012,7 +1989,7 @@
     overlay_processor_->ProcessForOverlays(
         resource_provider_.get(), &pass_list, GetIdentityColorMatrix(),
         render_pass_filters, render_pass_background_filters, nullptr,
-        &candidate_list, nullptr, nullptr, &damage_rect, &content_bounds_);
+        &candidate_list, &damage_rect, &content_bounds_);
 
     EXPECT_EQ(expected_damages[i], damage_rect);
     ASSERT_EQ(expected_candidate_size[i], candidate_list.size());
@@ -2055,7 +2032,7 @@
     overlay_processor_->ProcessForOverlays(
         resource_provider_.get(), &pass_list, GetIdentityColorMatrix(),
         render_pass_filters, render_pass_backdrop_filters, nullptr,
-        &candidate_list, nullptr, nullptr, &damage_rect_, &content_bounds_);
+        &candidate_list, &damage_rect_, &content_bounds_);
 
     if (i == 0 || i == 1 || i == 3)
       EXPECT_FALSE(damage_rect_.IsEmpty());
@@ -2079,7 +2056,7 @@
   overlay_processor_->ProcessForOverlays(
       resource_provider_.get(), &pass_list, GetIdentityColorMatrix(),
       render_pass_filters, render_pass_backdrop_filters, nullptr,
-      &candidate_list, nullptr, nullptr, &damage_rect_, &content_bounds_);
+      &candidate_list, &damage_rect_, &content_bounds_);
   EXPECT_EQ(0U, content_bounds_.size());
 }
 
@@ -2096,7 +2073,7 @@
   overlay_processor_->ProcessForOverlays(
       resource_provider_.get(), &pass_list, GetIdentityColorMatrix(),
       render_pass_filters, render_pass_backdrop_filters, nullptr,
-      &candidate_list, nullptr, nullptr, &damage_rect_, &content_bounds_);
+      &candidate_list, &damage_rect_, &content_bounds_);
 
   EXPECT_EQ(1U, content_bounds_.size());
   EXPECT_TRUE(content_bounds_[0].IsEmpty());
@@ -2126,7 +2103,7 @@
   overlay_processor_->ProcessForOverlays(
       resource_provider_.get(), &pass_list, GetIdentityColorMatrix(),
       render_pass_filters, render_pass_backdrop_filters, nullptr,
-      &candidate_list, nullptr, nullptr, &damage_rect_, &content_bounds_);
+      &candidate_list, &damage_rect_, &content_bounds_);
 
   EXPECT_EQ(1U, content_bounds_.size());
   EXPECT_TRUE(content_bounds_[0].IsEmpty());
@@ -2148,7 +2125,7 @@
   overlay_processor_->ProcessForOverlays(
       resource_provider_.get(), &pass_list, GetIdentityColorMatrix(),
       render_pass_filters, render_pass_backdrop_filters, nullptr,
-      &candidate_list, nullptr, nullptr, &damage_rect_, &content_bounds_);
+      &candidate_list, &damage_rect_, &content_bounds_);
 
   EXPECT_EQ(1U, content_bounds_.size());
   EXPECT_EQ(kOverlayTopLeftRect, content_bounds_[0]);
@@ -2173,7 +2150,7 @@
   overlay_processor_->ProcessForOverlays(
       resource_provider_.get(), &pass_list, GetIdentityColorMatrix(),
       render_pass_filters, render_pass_backdrop_filters, nullptr,
-      &candidate_list, nullptr, nullptr, &damage_rect_, &content_bounds_);
+      &candidate_list, &damage_rect_, &content_bounds_);
 
   EXPECT_EQ(1U, content_bounds_.size());
   EXPECT_EQ(kOverlayRect, content_bounds_[0]);
@@ -2203,7 +2180,7 @@
   overlay_processor_->ProcessForOverlays(
       resource_provider_.get(), &pass_list, GetIdentityColorMatrix(),
       render_pass_filters, render_pass_backdrop_filters, nullptr,
-      &candidate_list, nullptr, nullptr, &damage_rect_, &content_bounds_);
+      &candidate_list, &damage_rect_, &content_bounds_);
 
   EXPECT_EQ(1U, content_bounds_.size());
   EXPECT_EQ(gfx::Rect(0, 0, 11, 11), content_bounds_[0]);
@@ -2234,12 +2211,34 @@
   overlay_processor_->ProcessForOverlays(
       resource_provider_.get(), &pass_list, GetIdentityColorMatrix(),
       render_pass_filters, render_pass_backdrop_filters, nullptr,
-      &candidate_list, nullptr, nullptr, &damage_rect_, &content_bounds_);
+      &candidate_list, &damage_rect_, &content_bounds_);
 
   EXPECT_EQ(1U, content_bounds_.size());
   EXPECT_EQ(kOverlayRect, content_bounds_[0]);
 }
 
+TEST_F(UnderlayCastTest, NoOverlayPromotionWithoutProtectedContent) {
+  std::unique_ptr<RenderPass> pass = CreateRenderPass();
+  CreateCandidateQuadAt(resource_provider_.get(),
+                        child_resource_provider_.get(), child_provider_.get(),
+                        pass->shared_quad_state_list.back(), pass.get(),
+                        kOverlayRect);
+
+  OverlayCandidateList candidate_list;
+  OverlayProcessor::FilterOperationsMap render_pass_filters;
+  OverlayProcessor::FilterOperationsMap render_pass_backdrop_filters;
+  RenderPassList pass_list;
+  pass_list.push_back(std::move(pass));
+  overlay_processor_->ProcessForOverlays(
+      resource_provider_.get(), &pass_list, GetIdentityColorMatrix(),
+      render_pass_filters, render_pass_backdrop_filters, nullptr,
+      &candidate_list, &damage_rect_, &content_bounds_);
+
+  ASSERT_TRUE(candidate_list.empty());
+  EXPECT_TRUE(content_bounds_.empty());
+}
+#endif
+
 #if defined(ALWAYS_ENABLE_BLENDING_FOR_PRIMARY)
 TEST_F(UnderlayCastTest, PrimaryPlaneOverlayIsAlwaysTransparent) {
   std::unique_ptr<RenderPass> pass = CreateRenderPass();
@@ -2260,812 +2259,14 @@
   overlay_processor_->ProcessForOverlays(
       resource_provider_.get(), &pass_list, GetIdentityColorMatrix(),
       render_pass_filters, render_pass_backdrop_filters, &output_surface_plane,
-      &candidate_list, nullptr, nullptr, &damage_rect_, &content_bounds_);
+      &candidate_list, &damage_rect_, &content_bounds_);
 
   ASSERT_EQ(true, output_surface_plane.enable_blending);
   EXPECT_EQ(0U, content_bounds_.size());
 }
 #endif
 
-TEST_F(UnderlayCastTest, NoOverlayPromotionWithoutProtectedContent) {
-  std::unique_ptr<RenderPass> pass = CreateRenderPass();
-  CreateCandidateQuadAt(resource_provider_.get(),
-                        child_resource_provider_.get(), child_provider_.get(),
-                        pass->shared_quad_state_list.back(), pass.get(),
-                        kOverlayRect);
-
-  OverlayCandidateList candidate_list;
-  OverlayProcessor::FilterOperationsMap render_pass_filters;
-  OverlayProcessor::FilterOperationsMap render_pass_backdrop_filters;
-  RenderPassList pass_list;
-  pass_list.push_back(std::move(pass));
-  overlay_processor_->ProcessForOverlays(
-      resource_provider_.get(), &pass_list, GetIdentityColorMatrix(),
-      render_pass_filters, render_pass_backdrop_filters, nullptr,
-      &candidate_list, nullptr, nullptr, &damage_rect_, &content_bounds_);
-
-  ASSERT_TRUE(candidate_list.empty());
-  EXPECT_TRUE(content_bounds_.empty());
-}
-
-TEST_F(CALayerOverlayTest, AllowNonAxisAlignedTransform) {
-  std::unique_ptr<RenderPass> pass = CreateRenderPass();
-  CreateFullscreenCandidateQuad(
-      resource_provider_.get(), child_resource_provider_.get(),
-      child_provider_.get(), pass->shared_quad_state_list.back(), pass.get());
-  pass->shared_quad_state_list.back()
-      ->quad_to_target_transform.RotateAboutZAxis(45.f);
-  gfx::Size display_size(pass->output_rect.width(), pass->output_rect.height());
-  gfx::Rect damage_rect;
-  CALayerOverlayList ca_layer_list;
-  OverlayCandidateList overlay_list;
-  OverlayProcessor::FilterOperationsMap render_pass_filters;
-  OverlayProcessor::FilterOperationsMap render_pass_backdrop_filters;
-  RenderPassList pass_list;
-  pass_list.push_back(std::move(pass));
-
-  overlay_processor_->ProcessForOverlays(
-      resource_provider_.get(), &pass_list, GetIdentityColorMatrix(),
-      render_pass_filters, render_pass_backdrop_filters, nullptr, &overlay_list,
-      &ca_layer_list, nullptr, &damage_rect_, &content_bounds_);
-
-  EXPECT_EQ(gfx::Rect(), damage_rect);
-  EXPECT_EQ(0U, overlay_list.size());
-  EXPECT_EQ(1U, ca_layer_list.size());
-  EXPECT_EQ(0U, output_surface_->bind_framebuffer_count());
-}
-
-TEST_F(CALayerOverlayTest, ThreeDTransform) {
-  std::unique_ptr<RenderPass> pass = CreateRenderPass();
-  CreateFullscreenCandidateQuad(
-      resource_provider_.get(), child_resource_provider_.get(),
-      child_provider_.get(), pass->shared_quad_state_list.back(), pass.get());
-  pass->shared_quad_state_list.back()
-      ->quad_to_target_transform.RotateAboutXAxis(45.f);
-  gfx::Size display_size(pass->output_rect.width(), pass->output_rect.height());
-
-  CALayerOverlayList ca_layer_list;
-  OverlayCandidateList overlay_list;
-  OverlayProcessor::FilterOperationsMap render_pass_filters;
-  OverlayProcessor::FilterOperationsMap render_pass_backdrop_filters;
-  RenderPassList pass_list;
-  pass_list.push_back(std::move(pass));
-
-  overlay_processor_->ProcessForOverlays(
-      resource_provider_.get(), &pass_list, GetIdentityColorMatrix(),
-      render_pass_filters, render_pass_backdrop_filters, nullptr, &overlay_list,
-      &ca_layer_list, nullptr, &damage_rect_, &content_bounds_);
-  EXPECT_EQ(0U, overlay_list.size());
-  EXPECT_EQ(1U, ca_layer_list.size());
-  gfx::Transform expected_transform;
-  expected_transform.RotateAboutXAxis(45.f);
-  gfx::Transform actual_transform(ca_layer_list.back().shared_state->transform);
-
-  EXPECT_EQ(expected_transform.ToString(), actual_transform.ToString());
-  EXPECT_EQ(0U, output_surface_->bind_framebuffer_count());
-}
-
-TEST_F(CALayerOverlayTest, AllowContainingClip) {
-  std::unique_ptr<RenderPass> pass = CreateRenderPass();
-  CreateFullscreenCandidateQuad(
-      resource_provider_.get(), child_resource_provider_.get(),
-      child_provider_.get(), pass->shared_quad_state_list.back(), pass.get());
-  pass->shared_quad_state_list.back()->is_clipped = true;
-  pass->shared_quad_state_list.back()->clip_rect = kOverlayRect;
-  gfx::Size display_size(pass->output_rect.width(), pass->output_rect.height());
-
-  gfx::Rect damage_rect;
-  CALayerOverlayList ca_layer_list;
-  OverlayCandidateList overlay_list;
-  OverlayProcessor::FilterOperationsMap render_pass_filters;
-  OverlayProcessor::FilterOperationsMap render_pass_backdrop_filters;
-  RenderPassList pass_list;
-  pass_list.push_back(std::move(pass));
-
-  overlay_processor_->ProcessForOverlays(
-      resource_provider_.get(), &pass_list, GetIdentityColorMatrix(),
-      render_pass_filters, render_pass_backdrop_filters, nullptr, &overlay_list,
-      &ca_layer_list, nullptr, &damage_rect_, &content_bounds_);
-
-  EXPECT_EQ(gfx::Rect(), damage_rect);
-  EXPECT_EQ(0U, overlay_list.size());
-  EXPECT_EQ(1U, ca_layer_list.size());
-  EXPECT_EQ(0U, output_surface_->bind_framebuffer_count());
-}
-
-TEST_F(CALayerOverlayTest, NontrivialClip) {
-  std::unique_ptr<RenderPass> pass = CreateRenderPass();
-  CreateFullscreenCandidateQuad(
-      resource_provider_.get(), child_resource_provider_.get(),
-      child_provider_.get(), pass->shared_quad_state_list.back(), pass.get());
-  pass->shared_quad_state_list.back()->is_clipped = true;
-  pass->shared_quad_state_list.back()->clip_rect = gfx::Rect(64, 64, 128, 128);
-  gfx::Size display_size(pass->output_rect.width(), pass->output_rect.height());
-
-  gfx::Rect damage_rect;
-  CALayerOverlayList ca_layer_list;
-  OverlayCandidateList overlay_list;
-  OverlayProcessor::FilterOperationsMap render_pass_filters;
-  OverlayProcessor::FilterOperationsMap render_pass_backdrop_filters;
-  RenderPassList pass_list;
-  pass_list.push_back(std::move(pass));
-
-  overlay_processor_->ProcessForOverlays(
-      resource_provider_.get(), &pass_list, GetIdentityColorMatrix(),
-      render_pass_filters, render_pass_backdrop_filters, nullptr, &overlay_list,
-      &ca_layer_list, nullptr, &damage_rect_, &content_bounds_);
-
-  EXPECT_EQ(gfx::Rect(), damage_rect);
-  EXPECT_EQ(0U, overlay_list.size());
-  EXPECT_EQ(1U, ca_layer_list.size());
-  EXPECT_TRUE(ca_layer_list.back().shared_state->is_clipped);
-  EXPECT_EQ(gfx::RectF(64, 64, 128, 128),
-            ca_layer_list.back().shared_state->clip_rect);
-  EXPECT_EQ(0U, output_surface_->bind_framebuffer_count());
-}
-
-TEST_F(CALayerOverlayTest, SkipTransparent) {
-  std::unique_ptr<RenderPass> pass = CreateRenderPass();
-  CreateFullscreenCandidateQuad(
-      resource_provider_.get(), child_resource_provider_.get(),
-      child_provider_.get(), pass->shared_quad_state_list.back(), pass.get());
-  pass->shared_quad_state_list.back()->opacity = 0;
-  gfx::Size display_size(pass->output_rect.width(), pass->output_rect.height());
-
-  gfx::Rect damage_rect;
-  CALayerOverlayList ca_layer_list;
-  OverlayCandidateList overlay_list;
-  OverlayProcessor::FilterOperationsMap render_pass_filters;
-  OverlayProcessor::FilterOperationsMap render_pass_backdrop_filters;
-  RenderPassList pass_list;
-  pass_list.push_back(std::move(pass));
-
-  overlay_processor_->ProcessForOverlays(
-      resource_provider_.get(), &pass_list, GetIdentityColorMatrix(),
-      render_pass_filters, render_pass_backdrop_filters, nullptr, &overlay_list,
-      &ca_layer_list, nullptr, &damage_rect_, &content_bounds_);
-  EXPECT_EQ(gfx::Rect(), damage_rect);
-  EXPECT_EQ(0U, overlay_list.size());
-  EXPECT_EQ(0U, ca_layer_list.size());
-  EXPECT_EQ(0U, output_surface_->bind_framebuffer_count());
-}
-
-class DCLayerOverlayTest : public OverlayTest<DCLayerValidator> {};
-
-TEST_F(DCLayerOverlayTest, AllowNonAxisAlignedTransform) {
-  base::test::ScopedFeatureList feature_list;
-  feature_list.InitAndEnableFeature(
-      features::kDirectCompositionComplexOverlays);
-  std::unique_ptr<RenderPass> pass = CreateRenderPass();
-  CreateFullscreenCandidateYUVVideoQuad(
-      resource_provider_.get(), child_resource_provider_.get(),
-      child_provider_.get(), pass->shared_quad_state_list.back(), pass.get());
-  pass->shared_quad_state_list.back()
-      ->quad_to_target_transform.RotateAboutZAxis(45.f);
-
-  DCLayerOverlayList dc_layer_list;
-  OverlayCandidateList overlay_list;
-  OverlayProcessor::FilterOperationsMap render_pass_filters;
-  OverlayProcessor::FilterOperationsMap render_pass_backdrop_filters;
-  damage_rect_ = gfx::Rect(1, 1, 10, 10);
-  RenderPassList pass_list;
-  pass_list.push_back(std::move(pass));
-  overlay_processor_->ProcessForOverlays(
-      resource_provider_.get(), &pass_list, GetIdentityColorMatrix(),
-      render_pass_filters, render_pass_backdrop_filters, nullptr, &overlay_list,
-      nullptr, &dc_layer_list, &damage_rect_, &content_bounds_);
-  EXPECT_EQ(0U, overlay_list.size());
-  EXPECT_EQ(1U, dc_layer_list.size());
-  EXPECT_EQ(1, dc_layer_list.back().z_order);
-  EXPECT_EQ(0U, output_surface_->bind_framebuffer_count());
-  EXPECT_EQ(gfx::Rect(1, 1, 10, 10), damage_rect_);
-}
-
-TEST_F(DCLayerOverlayTest, AllowRequiredNonAxisAlignedTransform) {
-  base::test::ScopedFeatureList feature_list;
-  feature_list.InitAndEnableFeature(
-      features::kDirectCompositionNonrootOverlays);
-  std::unique_ptr<RenderPass> pass = CreateRenderPass();
-  YUVVideoDrawQuad* yuv_quad = CreateFullscreenCandidateYUVVideoQuad(
-      resource_provider_.get(), child_resource_provider_.get(),
-      child_provider_.get(), pass->shared_quad_state_list.back(), pass.get());
-  // Set the protected video flag will force DCLayerOverlay to use hw overlay
-  yuv_quad->protected_video_type = gfx::ProtectedVideoType::kHardwareProtected;
-  pass->shared_quad_state_list.back()
-      ->quad_to_target_transform.RotateAboutZAxis(45.f);
-
-  gfx::Rect damage_rect;
-  DCLayerOverlayList dc_layer_list;
-  OverlayCandidateList overlay_list;
-  OverlayProcessor::FilterOperationsMap render_pass_filters;
-  OverlayProcessor::FilterOperationsMap render_pass_backdrop_filters;
-  damage_rect_ = gfx::Rect(1, 1, 10, 10);
-  RenderPassList pass_list;
-  pass_list.push_back(std::move(pass));
-  overlay_processor_->ProcessForOverlays(
-      resource_provider_.get(), &pass_list, GetIdentityColorMatrix(),
-      render_pass_filters, render_pass_backdrop_filters, nullptr, &overlay_list,
-      nullptr, &dc_layer_list, &damage_rect_, &content_bounds_);
-  EXPECT_EQ(gfx::Rect(), damage_rect);
-  EXPECT_EQ(0U, overlay_list.size());
-  ASSERT_EQ(1U, dc_layer_list.size());
-  EXPECT_EQ(1, dc_layer_list.back().z_order);
-  EXPECT_EQ(0U, output_surface_->bind_framebuffer_count());
-  EXPECT_EQ(gfx::Rect(1, 1, 10, 10), damage_rect_);
-}
-
-TEST_F(DCLayerOverlayTest, Occluded) {
-  base::test::ScopedFeatureList feature_list;
-  feature_list.InitAndEnableFeature(features::kDirectCompositionUnderlays);
-  {
-    std::unique_ptr<RenderPass> pass = CreateRenderPass();
-    SharedQuadState* first_shared_state = pass->shared_quad_state_list.back();
-    first_shared_state->occluding_damage_rect = gfx::Rect(1, 1, 10, 10);
-    CreateOpaqueQuadAt(resource_provider_.get(),
-                       pass->shared_quad_state_list.back(), pass.get(),
-                       gfx::Rect(0, 3, 100, 100), SK_ColorWHITE);
-    CreateFullscreenCandidateYUVVideoQuad(
-        resource_provider_.get(), child_resource_provider_.get(),
-        child_provider_.get(), pass->shared_quad_state_list.back(), pass.get());
-
-    SharedQuadState* second_shared_state =
-        pass->CreateAndAppendSharedQuadState();
-    second_shared_state->occluding_damage_rect = gfx::Rect(1, 1, 10, 10);
-    auto* second_video_quad = CreateFullscreenCandidateYUVVideoQuad(
-        resource_provider_.get(), child_resource_provider_.get(),
-        child_provider_.get(), pass->shared_quad_state_list.back(), pass.get());
-    // Set the protected video flag will force DCLayerOverlay to use hw overlay
-    second_video_quad->protected_video_type =
-        gfx::ProtectedVideoType::kHardwareProtected;
-    second_video_quad->rect.set_origin(gfx::Point(2, 2));
-    second_video_quad->visible_rect.set_origin(gfx::Point(2, 2));
-
-    DCLayerOverlayList dc_layer_list;
-    OverlayCandidateList overlay_list;
-    OverlayProcessor::FilterOperationsMap render_pass_filters;
-    OverlayProcessor::FilterOperationsMap render_pass_backdrop_filters;
-    damage_rect_ = gfx::Rect(1, 1, 10, 10);
-    RenderPassList pass_list;
-    pass_list.push_back(std::move(pass));
-    overlay_processor_->ProcessForOverlays(
-        resource_provider_.get(), &pass_list, GetIdentityColorMatrix(),
-        render_pass_filters, render_pass_backdrop_filters, nullptr,
-        &overlay_list, nullptr, &dc_layer_list, &damage_rect_,
-        &content_bounds_);
-    EXPECT_EQ(0U, overlay_list.size());
-    EXPECT_EQ(2U, dc_layer_list.size());
-    EXPECT_EQ(0U, output_surface_->bind_framebuffer_count());
-    EXPECT_EQ(-1, dc_layer_list.front().z_order);
-    EXPECT_EQ(-2, dc_layer_list.back().z_order);
-    // Entire underlay rect must be redrawn.
-    EXPECT_EQ(gfx::Rect(0, 0, 256, 256), damage_rect_);
-  }
-  {
-    std::unique_ptr<RenderPass> pass = CreateRenderPass();
-    SharedQuadState* first_shared_state = pass->shared_quad_state_list.back();
-    first_shared_state->occluding_damage_rect = gfx::Rect(1, 1, 10, 10);
-    CreateOpaqueQuadAt(resource_provider_.get(),
-                       pass->shared_quad_state_list.back(), pass.get(),
-                       gfx::Rect(3, 3, 100, 100), SK_ColorWHITE);
-    CreateFullscreenCandidateYUVVideoQuad(
-        resource_provider_.get(), child_resource_provider_.get(),
-        child_provider_.get(), pass->shared_quad_state_list.back(), pass.get());
-
-    SharedQuadState* second_shared_state =
-        pass->CreateAndAppendSharedQuadState();
-    second_shared_state->occluding_damage_rect = gfx::Rect(1, 1, 10, 10);
-    auto* second_video_quad = CreateFullscreenCandidateYUVVideoQuad(
-        resource_provider_.get(), child_resource_provider_.get(),
-        child_provider_.get(), pass->shared_quad_state_list.back(), pass.get());
-    second_video_quad->protected_video_type =
-        gfx::ProtectedVideoType::kHardwareProtected;
-    second_video_quad->rect.set_origin(gfx::Point(2, 2));
-    second_video_quad->visible_rect.set_origin(gfx::Point(2, 2));
-
-    DCLayerOverlayList dc_layer_list;
-    OverlayCandidateList overlay_list;
-    OverlayProcessor::FilterOperationsMap render_pass_filters;
-    OverlayProcessor::FilterOperationsMap render_pass_backdrop_filters;
-    damage_rect_ = gfx::Rect(1, 1, 10, 10);
-    RenderPassList pass_list;
-    pass_list.push_back(std::move(pass));
-    overlay_processor_->ProcessForOverlays(
-        resource_provider_.get(), &pass_list, GetIdentityColorMatrix(),
-        render_pass_filters, render_pass_backdrop_filters, nullptr,
-        &overlay_list, nullptr, &dc_layer_list, &damage_rect_,
-        &content_bounds_);
-    EXPECT_EQ(0U, overlay_list.size());
-    EXPECT_EQ(2U, dc_layer_list.size());
-    EXPECT_EQ(0U, output_surface_->bind_framebuffer_count());
-    EXPECT_EQ(-1, dc_layer_list.front().z_order);
-    EXPECT_EQ(-2, dc_layer_list.back().z_order);
-    // The underlay rectangle is the same, so the damage for first video quad is
-    // contained within the combined occluding rects for this and the last
-    // frame. Second video quad also adds its damage.
-
-    // This is calculated by carving out the underlay rect size from the
-    // damage_rect, adding back the quads on top and then the overlay/underlay
-    // rects from the previous frame. The damage rect carried over from  the
-    // revious frame with multiple overlays cannot be skipped.
-    EXPECT_EQ(gfx::Rect(0, 0, 256, 256), damage_rect_);
-  }
-}
-
-TEST_F(DCLayerOverlayTest, DamageRectWithoutVideoDamage) {
-  base::test::ScopedFeatureList feature_list;
-  feature_list.InitAndEnableFeature(features::kDirectCompositionUnderlays);
-  {
-    std::unique_ptr<RenderPass> pass = CreateRenderPass();
-    SharedQuadState* shared_quad_state = pass->shared_quad_state_list.back();
-    shared_quad_state->occluding_damage_rect = gfx::Rect(210, 210, 20, 20);
-    // Occluding quad fully contained in video rect.
-    CreateOpaqueQuadAt(resource_provider_.get(),
-                       pass->shared_quad_state_list.back(), pass.get(),
-                       gfx::Rect(0, 3, 100, 100), SK_ColorWHITE);
-    // Non-occluding quad fully outside video rect
-    CreateOpaqueQuadAt(resource_provider_.get(),
-                       pass->shared_quad_state_list.back(), pass.get(),
-                       gfx::Rect(210, 210, 20, 20), SK_ColorWHITE);
-    // Underlay video quad
-    auto* video_quad = CreateFullscreenCandidateYUVVideoQuad(
-        resource_provider_.get(), child_resource_provider_.get(),
-        child_provider_.get(), pass->shared_quad_state_list.back(), pass.get());
-    video_quad->rect = gfx::Rect(0, 0, 200, 200);
-    video_quad->visible_rect = video_quad->rect;
-
-    DCLayerOverlayList dc_layer_list;
-    OverlayCandidateList overlay_list;
-    OverlayProcessor::FilterOperationsMap render_pass_filters;
-    OverlayProcessor::FilterOperationsMap render_pass_backdrop_filters;
-    // Damage rect fully outside video quad
-    damage_rect_ = gfx::Rect(210, 210, 20, 20);
-    RenderPassList pass_list;
-    pass_list.push_back(std::move(pass));
-    overlay_processor_->ProcessForOverlays(
-        resource_provider_.get(), &pass_list, GetIdentityColorMatrix(),
-        render_pass_filters, render_pass_backdrop_filters, nullptr,
-        &overlay_list, nullptr, &dc_layer_list, &damage_rect_,
-        &content_bounds_);
-    EXPECT_EQ(0U, overlay_list.size());
-    EXPECT_EQ(1U, dc_layer_list.size());
-    EXPECT_EQ(0U, output_surface_->bind_framebuffer_count());
-    EXPECT_EQ(-1, dc_layer_list.back().z_order);
-    // All rects must be redrawn at the first frame.
-    EXPECT_EQ(gfx::Rect(0, 0, 230, 230), damage_rect_);
-  }
-  {
-    std::unique_ptr<RenderPass> pass = CreateRenderPass();
-    SharedQuadState* shared_quad_state = pass->shared_quad_state_list.back();
-    shared_quad_state->occluding_damage_rect = gfx::Rect(210, 210, 20, 20);
-    // Occluding quad fully contained in video rect.
-    CreateOpaqueQuadAt(resource_provider_.get(),
-                       pass->shared_quad_state_list.back(), pass.get(),
-                       gfx::Rect(0, 3, 100, 100), SK_ColorWHITE);
-    // Non-occluding quad fully outside video rect
-    CreateOpaqueQuadAt(resource_provider_.get(),
-                       pass->shared_quad_state_list.back(), pass.get(),
-                       gfx::Rect(210, 210, 20, 20), SK_ColorWHITE);
-    // Underlay video quad
-    auto* video_quad = CreateFullscreenCandidateYUVVideoQuad(
-        resource_provider_.get(), child_resource_provider_.get(),
-        child_provider_.get(), pass->shared_quad_state_list.back(), pass.get());
-    video_quad->rect = gfx::Rect(0, 0, 200, 200);
-    video_quad->visible_rect = video_quad->rect;
-
-    DCLayerOverlayList dc_layer_list;
-    OverlayCandidateList overlay_list;
-    OverlayProcessor::FilterOperationsMap render_pass_filters;
-    OverlayProcessor::FilterOperationsMap render_pass_backdrop_filters;
-    // Damage rect fully outside video quad
-    damage_rect_ = gfx::Rect(210, 210, 20, 20);
-    RenderPassList pass_list;
-    pass_list.push_back(std::move(pass));
-    overlay_processor_->ProcessForOverlays(
-        resource_provider_.get(), &pass_list, GetIdentityColorMatrix(),
-        render_pass_filters, render_pass_backdrop_filters, nullptr,
-        &overlay_list, nullptr, &dc_layer_list, &damage_rect_,
-        &content_bounds_);
-    EXPECT_EQ(0U, overlay_list.size());
-    EXPECT_EQ(1U, dc_layer_list.size());
-    EXPECT_EQ(0U, output_surface_->bind_framebuffer_count());
-    EXPECT_EQ(-1, dc_layer_list.back().z_order);
-    // Only the non-overlay damaged rect need to be drawn by the gl compositor
-    EXPECT_EQ(gfx::Rect(210, 210, 20, 20), damage_rect_);
-  }
-}
-
-TEST_F(DCLayerOverlayTest, DamageRectWithNonRootOverlay) {
-  base::test::ScopedFeatureList feature_list;
-  feature_list.InitWithFeatures({features::kDirectCompositionUnderlays,
-                                 features::kDirectCompositionNonrootOverlays},
-                                {});
-  {
-    // A root solid quad
-    std::unique_ptr<RenderPass> root_pass = CreateRenderPass();
-    CreateOpaqueQuadAt(
-        resource_provider_.get(), root_pass->shared_quad_state_list.back(),
-        root_pass.get(), gfx::Rect(210, 0, 20, 20), SK_ColorWHITE);
-
-    // A non-root video quad
-    std::unique_ptr<RenderPass> nonroot_pass = CreateRenderPass();
-    auto* video_quad = CreateFullscreenCandidateYUVVideoQuad(
-        resource_provider_.get(), child_resource_provider_.get(),
-        child_provider_.get(), nonroot_pass->shared_quad_state_list.back(),
-        nonroot_pass.get());
-    video_quad->rect = gfx::Rect(0, 0, 200, 200);
-    video_quad->visible_rect = video_quad->rect;
-
-    DCLayerOverlayList dc_layer_list;
-    OverlayCandidateList overlay_list;
-    OverlayProcessor::FilterOperationsMap render_pass_filters;
-    OverlayProcessor::FilterOperationsMap render_pass_backdrop_filters;
-    // Damage rect fully outside video quad
-    damage_rect_ = gfx::Rect(210, 0, 20, 20);
-    RenderPassList pass_list;
-    pass_list.push_back(std::move(nonroot_pass));
-    pass_list.push_back(std::move(root_pass));
-    overlay_processor_->ProcessForOverlays(
-        resource_provider_.get(), &pass_list, GetIdentityColorMatrix(),
-        render_pass_filters, render_pass_backdrop_filters, nullptr,
-        &overlay_list, nullptr, &dc_layer_list, &damage_rect_,
-        &content_bounds_);
-    EXPECT_EQ(0U, overlay_list.size());
-    EXPECT_EQ(1U, dc_layer_list.size());
-    EXPECT_EQ(0U, output_surface_->bind_framebuffer_count());
-    EXPECT_EQ(-1, dc_layer_list.back().z_order);
-    // damage_rect returned from ProcessForOverlays() is for root render pass
-    // only. Non-root damage rect is not included.
-    EXPECT_EQ(gfx::Rect(210, 0, 20, 20), damage_rect_);
-  }
-  {
-    // A root solid quad
-    std::unique_ptr<RenderPass> root_pass = CreateRenderPass();
-    CreateOpaqueQuadAt(
-        resource_provider_.get(), root_pass->shared_quad_state_list.back(),
-        root_pass.get(), gfx::Rect(210, 0, 20, 20), SK_ColorWHITE);
-
-    // A non-root video quad
-    std::unique_ptr<RenderPass> nonroot_pass = CreateRenderPass();
-    auto* video_quad = CreateFullscreenCandidateYUVVideoQuad(
-        resource_provider_.get(), child_resource_provider_.get(),
-        child_provider_.get(), nonroot_pass->shared_quad_state_list.back(),
-        nonroot_pass.get());
-    video_quad->rect = gfx::Rect(0, 0, 200, 200);
-    video_quad->visible_rect = video_quad->rect;
-
-    DCLayerOverlayList dc_layer_list;
-    OverlayCandidateList overlay_list;
-    OverlayProcessor::FilterOperationsMap render_pass_filters;
-    OverlayProcessor::FilterOperationsMap render_pass_backdrop_filters;
-    // Damage rect fully outside video quad
-    damage_rect_ = gfx::Rect(210, 0, 20, 20);
-    RenderPassList pass_list;
-    pass_list.push_back(std::move(nonroot_pass));
-    pass_list.push_back(std::move(root_pass));
-    overlay_processor_->ProcessForOverlays(
-        resource_provider_.get(), &pass_list, GetIdentityColorMatrix(),
-        render_pass_filters, render_pass_backdrop_filters, nullptr,
-        &overlay_list, nullptr, &dc_layer_list, &damage_rect_,
-        &content_bounds_);
-    EXPECT_EQ(0U, overlay_list.size());
-    EXPECT_EQ(1U, dc_layer_list.size());
-    EXPECT_EQ(0U, output_surface_->bind_framebuffer_count());
-    EXPECT_EQ(-1, dc_layer_list.back().z_order);
-    // Nonroot damage_rect from the previous frame should be added to this frame
-    EXPECT_EQ(gfx::Rect(0, 0, 230, 200), damage_rect_);
-  }
-}
-
-TEST_F(DCLayerOverlayTest, DamageRect) {
-  for (int i = 0; i < 2; i++) {
-    std::unique_ptr<RenderPass> pass = CreateRenderPass();
-    CreateFullscreenCandidateYUVVideoQuad(
-        resource_provider_.get(), child_resource_provider_.get(),
-        child_provider_.get(), pass->shared_quad_state_list.back(), pass.get());
-
-    gfx::Rect damage_rect;
-    DCLayerOverlayList dc_layer_list;
-    OverlayCandidateList overlay_list;
-    OverlayProcessor::FilterOperationsMap render_pass_filters;
-    OverlayProcessor::FilterOperationsMap render_pass_backdrop_filters;
-    damage_rect_ = gfx::Rect(1, 1, 10, 10);
-    RenderPassList pass_list;
-    pass_list.push_back(std::move(pass));
-    overlay_processor_->ProcessForOverlays(
-        resource_provider_.get(), &pass_list, GetIdentityColorMatrix(),
-        render_pass_filters, render_pass_backdrop_filters, nullptr,
-        &overlay_list, nullptr, &dc_layer_list, &damage_rect_,
-        &content_bounds_);
-    EXPECT_EQ(gfx::Rect(), damage_rect);
-    EXPECT_EQ(0U, overlay_list.size());
-    EXPECT_EQ(1U, dc_layer_list.size());
-    EXPECT_EQ(0U, output_surface_->bind_framebuffer_count());
-    EXPECT_EQ(1, dc_layer_list.back().z_order);
-    // Damage rect should be unchanged on initial frame because of resize, but
-    // should be empty on the second frame because everything was put in a
-    // layer.
-    if (i == 1)
-      EXPECT_TRUE(damage_rect_.IsEmpty());
-    else
-      EXPECT_EQ(gfx::Rect(1, 1, 10, 10), damage_rect_);
-  }
-}
-
-TEST_F(DCLayerOverlayTest, MultiplePassDamageRect) {
-  gfx::Transform child_pass1_transform;
-  child_pass1_transform.Translate(0, 100);
-
-  RenderPassId child_pass1_id(5);
-  std::unique_ptr<RenderPass> child_pass1 = CreateRenderPass();
-  ASSERT_EQ(child_pass1->shared_quad_state_list.size(), 1u);
-  child_pass1->id = child_pass1_id;
-  child_pass1->damage_rect = gfx::Rect();
-  child_pass1->transform_to_root_target = child_pass1_transform;
-  child_pass1->shared_quad_state_list.back()->opacity = 0.9f;
-  child_pass1->shared_quad_state_list.back()->blend_mode =
-      SkBlendMode::kSrcOver;
-
-  YUVVideoDrawQuad* yuv_quad_required = CreateFullscreenCandidateYUVVideoQuad(
-      resource_provider_.get(), child_resource_provider_.get(),
-      child_provider_.get(), child_pass1->shared_quad_state_list.back(),
-      child_pass1.get());
-  // Set the protected video flag will force DCLayerOverlay to use hw overlay
-  yuv_quad_required->protected_video_type =
-      gfx::ProtectedVideoType::kHardwareProtected;
-
-  RenderPassId child_pass2_id(6);
-  std::unique_ptr<RenderPass> child_pass2 = CreateRenderPass();
-  ASSERT_EQ(child_pass2->shared_quad_state_list.size(), 1u);
-  child_pass2->id = child_pass2_id;
-  child_pass2->damage_rect = gfx::Rect();
-  child_pass2->transform_to_root_target = gfx::Transform();
-  child_pass2->shared_quad_state_list.back()->opacity = 0.8f;
-
-  YUVVideoDrawQuad* yuv_quad_not_required =
-      CreateFullscreenCandidateYUVVideoQuad(
-          resource_provider_.get(), child_resource_provider_.get(),
-          child_provider_.get(), child_pass2->shared_quad_state_list.back(),
-          child_pass2.get());
-
-  std::unique_ptr<RenderPass> root_pass = CreateRenderPass();
-  root_pass->CreateAndAppendSharedQuadState();
-  ASSERT_EQ(root_pass->shared_quad_state_list.size(), 2u);
-
-  SharedQuadState* child_pass1_sqs =
-      root_pass->shared_quad_state_list.ElementAt(0);
-  child_pass1_sqs->quad_to_target_transform =
-      child_pass1->transform_to_root_target;
-  child_pass1_sqs->opacity = 0.7f;
-
-  gfx::Rect unit_rect(0, 0, 1, 1);
-  auto* child_pass1_rpdq =
-      root_pass->CreateAndAppendDrawQuad<RenderPassDrawQuad>();
-  child_pass1_rpdq->SetNew(child_pass1_sqs, unit_rect, unit_rect,
-                           child_pass1_id, 0, gfx::RectF(), gfx::Size(),
-                           gfx::Vector2dF(), gfx::PointF(),
-                           gfx::RectF(0, 0, 1, 1), false, 1.0f);
-
-  SharedQuadState* child_pass2_sqs =
-      root_pass->shared_quad_state_list.ElementAt(1);
-  child_pass2_sqs->quad_to_target_transform =
-      child_pass2->transform_to_root_target;
-  child_pass2_sqs->opacity = 0.6f;
-
-  auto* child_pass2_rpdq =
-      root_pass->CreateAndAppendDrawQuad<RenderPassDrawQuad>();
-  child_pass2_rpdq->SetNew(child_pass2_sqs, unit_rect, unit_rect,
-                           child_pass2_id, 0, gfx::RectF(), gfx::Size(),
-                           gfx::Vector2dF(), gfx::PointF(),
-                           gfx::RectF(0, 0, 1, 1), false, 1.0f);
-
-  root_pass->damage_rect = gfx::Rect();
-
-  gfx::Rect root_damage_rect;
-  DCLayerOverlayList dc_layer_list;
-  OverlayCandidateList overlay_list;
-  OverlayProcessor::FilterOperationsMap render_pass_filters;
-  OverlayProcessor::FilterOperationsMap render_pass_backdrop_filters;
-  RenderPassList pass_list;
-  pass_list.push_back(std::move(child_pass1));
-  pass_list.push_back(std::move(child_pass2));
-  pass_list.push_back(std::move(root_pass));
-  overlay_processor_->ProcessForOverlays(
-      resource_provider_.get(), &pass_list, GetIdentityColorMatrix(),
-      render_pass_filters, render_pass_backdrop_filters, nullptr, &overlay_list,
-      nullptr, &dc_layer_list, &root_damage_rect, &content_bounds_);
-  EXPECT_EQ(0U, overlay_list.size());
-  EXPECT_EQ(0U, output_surface_->bind_framebuffer_count());
-
-  // Only the kHardwareProtectedVideo video quad produces damage.
-  ASSERT_EQ(1U, dc_layer_list.size());
-  EXPECT_EQ(-1, dc_layer_list.back().z_order);
-  EXPECT_EQ(gfx::Rect(0, 0, 256, 256), pass_list[0]->damage_rect);
-  EXPECT_EQ(gfx::Rect(), pass_list[1]->damage_rect);
-  EXPECT_EQ(gfx::Rect(0, 100, 256, 156), root_damage_rect);
-  // Overlay damage handling is done entirely within DCOverlayProcessor so this
-  // is expected to return an empty rect
-  gfx::Rect overlay_damage = overlay_processor_->GetAndResetOverlayDamage();
-  EXPECT_EQ(gfx::Rect(0, 0, 0, 0), overlay_damage);
-
-  EXPECT_EQ(1u, pass_list[0]->quad_list.size());
-  EXPECT_EQ(DrawQuad::Material::kSolidColor,
-            pass_list[0]->quad_list.ElementAt(0)->material);
-
-  // The kHardwareProtectedVideo video quad is put into an underlay, and
-  // replaced by a solid color quad.
-  auto* yuv_solid_color_quad =
-      static_cast<SolidColorDrawQuad*>(pass_list[0]->quad_list.ElementAt(0));
-  EXPECT_EQ(SK_ColorBLACK, yuv_solid_color_quad->color);
-  EXPECT_EQ(gfx::Rect(0, 0, 256, 256), yuv_solid_color_quad->rect);
-  EXPECT_TRUE(yuv_solid_color_quad->shared_quad_state->quad_to_target_transform
-                  .IsIdentity());
-  EXPECT_EQ(0.9f, yuv_solid_color_quad->shared_quad_state->opacity);
-  EXPECT_EQ(SkBlendMode::kDstOut,
-            yuv_solid_color_quad->shared_quad_state->blend_mode);
-
-  // The non required video quad is not put into an underlay.
-  EXPECT_EQ(1u, pass_list[1]->quad_list.size());
-  EXPECT_EQ(yuv_quad_not_required, pass_list[1]->quad_list.ElementAt(0));
-
-  EXPECT_EQ(3u, pass_list[2]->quad_list.size());
-
-  // The RPDQs are not modified.
-  EXPECT_EQ(DrawQuad::Material::kRenderPass,
-            pass_list[2]->quad_list.ElementAt(0)->material);
-  EXPECT_EQ(child_pass1_id, static_cast<RenderPassDrawQuad*>(
-                                pass_list[2]->quad_list.ElementAt(0))
-                                ->render_pass_id);
-
-  // A solid color quad is put behind the RPDQ containing the video.
-  EXPECT_EQ(DrawQuad::Material::kSolidColor,
-            pass_list[2]->quad_list.ElementAt(1)->material);
-  auto* rpdq_solid_color_quad =
-      static_cast<SolidColorDrawQuad*>(pass_list[2]->quad_list.ElementAt(1));
-  EXPECT_EQ(SK_ColorTRANSPARENT, rpdq_solid_color_quad->color);
-  EXPECT_EQ(child_pass1_transform,
-            rpdq_solid_color_quad->shared_quad_state->quad_to_target_transform);
-  EXPECT_EQ(1.f, rpdq_solid_color_quad->shared_quad_state->opacity);
-  EXPECT_FALSE(rpdq_solid_color_quad->ShouldDrawWithBlending());
-
-  EXPECT_EQ(DrawQuad::Material::kRenderPass,
-            pass_list[2]->quad_list.ElementAt(2)->material);
-  EXPECT_EQ(child_pass2_id, static_cast<RenderPassDrawQuad*>(
-                                pass_list[2]->quad_list.ElementAt(2))
-                                ->render_pass_id);
-}
-
-TEST_F(DCLayerOverlayTest, ClipRect) {
-  base::test::ScopedFeatureList feature_list;
-  feature_list.InitAndEnableFeature(features::kDirectCompositionUnderlays);
-
-  // Process twice. The second time through the overlay list shouldn't change,
-  // which will allow the damage rect to reflect just the changes in that
-  // frame.
-  for (size_t i = 0; i < 2; ++i) {
-    std::unique_ptr<RenderPass> pass = CreateRenderPass();
-    CreateOpaqueQuadAt(resource_provider_.get(),
-                       pass->shared_quad_state_list.back(), pass.get(),
-                       gfx::Rect(0, 2, 100, 100), SK_ColorWHITE);
-    pass->shared_quad_state_list.back()->is_clipped = true;
-    pass->shared_quad_state_list.back()->clip_rect = gfx::Rect(0, 3, 100, 100);
-    SharedQuadState* shared_state = pass->CreateAndAppendSharedQuadState();
-    shared_state->opacity = 1.f;
-    CreateFullscreenCandidateYUVVideoQuad(
-        resource_provider_.get(), child_resource_provider_.get(),
-        child_provider_.get(), shared_state, pass.get());
-    shared_state->is_clipped = true;
-    // Clipped rect shouldn't be overlapped by clipped opaque quad rect.
-    shared_state->clip_rect = gfx::Rect(0, 0, 100, 3);
-
-    DCLayerOverlayList dc_layer_list;
-    OverlayCandidateList overlay_list;
-    OverlayProcessor::FilterOperationsMap render_pass_filters;
-    OverlayProcessor::FilterOperationsMap render_pass_backdrop_filters;
-    RenderPassList pass_list;
-    pass_list.push_back(std::move(pass));
-    damage_rect_ = gfx::Rect(1, 1, 10, 10);
-    overlay_processor_->ProcessForOverlays(
-        resource_provider_.get(), &pass_list, GetIdentityColorMatrix(),
-        render_pass_filters, render_pass_backdrop_filters, nullptr,
-        &overlay_list, nullptr, &dc_layer_list, &damage_rect_,
-        &content_bounds_);
-    EXPECT_EQ(0U, overlay_list.size());
-    EXPECT_EQ(1U, dc_layer_list.size());
-    // Because of clip rects the overlay isn't occluded and shouldn't be an
-    // underlay.
-    EXPECT_EQ(1, dc_layer_list.back().z_order);
-    EXPECT_TRUE(dc_layer_list.back().is_clipped);
-    EXPECT_EQ(gfx::Rect(0, 0, 100, 3), dc_layer_list.back().clip_rect);
-    if (i == 1) {
-      // The damage rect should only contain contents that aren't in the
-      // clipped overlay rect.
-      EXPECT_EQ(gfx::Rect(1, 3, 10, 8), damage_rect_);
-    }
-  }
-}
-
-TEST_F(DCLayerOverlayTest, TransparentOnTop) {
-  // Process twice. The second time through the overlay list shouldn't change,
-  // which will allow the damage rect to reflect just the changes in that
-  // frame.
-  for (size_t i = 0; i < 2; ++i) {
-    std::unique_ptr<RenderPass> pass = CreateRenderPass();
-    CreateFullscreenCandidateYUVVideoQuad(
-        resource_provider_.get(), child_resource_provider_.get(),
-        child_provider_.get(), pass->shared_quad_state_list.back(), pass.get());
-    pass->shared_quad_state_list.back()->opacity = 0.5f;
-
-    DCLayerOverlayList dc_layer_list;
-    OverlayCandidateList overlay_list;
-    OverlayProcessor::FilterOperationsMap render_pass_filters;
-    OverlayProcessor::FilterOperationsMap render_pass_backdrop_filters;
-    damage_rect_ = gfx::Rect(1, 1, 10, 10);
-    RenderPassList pass_list;
-    pass_list.push_back(std::move(pass));
-    overlay_processor_->ProcessForOverlays(
-        resource_provider_.get(), &pass_list, GetIdentityColorMatrix(),
-        render_pass_filters, render_pass_backdrop_filters, nullptr,
-        &overlay_list, nullptr, &dc_layer_list, &damage_rect_,
-        &content_bounds_);
-    EXPECT_EQ(0U, overlay_list.size());
-    EXPECT_EQ(1U, dc_layer_list.size());
-    EXPECT_EQ(1, dc_layer_list.back().z_order);
-    // Quad isn't opaque, so underlying damage must remain the same.
-    EXPECT_EQ(gfx::Rect(1, 1, 10, 10), damage_rect_);
-  }
-}
-
-TEST_F(DCLayerOverlayTest, UnderlayDamageRectWithQuadOnTopUnchanged) {
-  base::test::ScopedFeatureList feature_list;
-  feature_list.InitAndEnableFeature(features::kDirectCompositionUnderlays);
-
-  for (int i = 0; i < 3; i++) {
-    std::unique_ptr<RenderPass> pass = CreateRenderPass();
-    // Add a solid color quad on top
-    SharedQuadState* shared_state_on_top = pass->shared_quad_state_list.back();
-    CreateSolidColorQuadAt(shared_state_on_top, SK_ColorRED, pass.get(),
-                           kOverlayBottomRightRect);
-
-    SharedQuadState* shared_state = pass->CreateAndAppendSharedQuadState();
-    shared_state->opacity = 1.f;
-    CreateFullscreenCandidateYUVVideoQuad(
-        resource_provider_.get(), child_resource_provider_.get(),
-        child_provider_.get(), shared_state, pass.get());
-
-    DCLayerOverlayList dc_layer_list;
-    OverlayCandidateList overlay_list;
-    OverlayProcessor::FilterOperationsMap render_pass_filters;
-    OverlayProcessor::FilterOperationsMap render_pass_backdrop_filters;
-    RenderPassList pass_list;
-    pass_list.push_back(std::move(pass));
-    gfx::Rect damage_rect_ = kOverlayRect;
-
-    // The quad on top does not give damage on the third frame
-    if (i == 2)
-      shared_state->occluding_damage_rect = gfx::Rect();
-    else
-      shared_state->occluding_damage_rect = kOverlayBottomRightRect;
-
-    overlay_processor_->ProcessForOverlays(
-        resource_provider_.get(), &pass_list, GetIdentityColorMatrix(),
-        render_pass_filters, render_pass_backdrop_filters, nullptr,
-        &overlay_list, nullptr, &dc_layer_list, &damage_rect_,
-        &content_bounds_);
-    EXPECT_EQ(0U, overlay_list.size());
-    EXPECT_EQ(1U, dc_layer_list.size());
-    EXPECT_EQ(0U, output_surface_->bind_framebuffer_count());
-    EXPECT_EQ(-1, dc_layer_list.back().z_order);
-    // Damage rect should be unchanged on initial frame, but should be reduced
-    // to the size of quad on top, and empty on the third frame.
-    if (i == 0)
-      EXPECT_EQ(kOverlayRect, damage_rect_);
-    else if (i == 1)
-      EXPECT_EQ(kOverlayBottomRightRect, damage_rect_);
-    else if (i == 2)
-      EXPECT_EQ(gfx::Rect(), damage_rect_);
-  }
-}
-
+#if defined(USE_OZONE) || defined(OS_ANDROID) || defined(OS_MACOSX)
 class OverlayInfoRendererGL : public GLRenderer {
   using OverlayProcessorType = TypedOverlayProcessor<SingleOverlayValidator>;
 
@@ -3678,7 +2879,9 @@
   EXPECT_FALSE(resource_provider_->InUse(mapped_resource2));
   EXPECT_FALSE(resource_provider_->InUse(mapped_resource3));
 }
+#endif
 
+#if defined(OS_MACOSX)
 class CALayerOverlayRPDQTest : public CALayerOverlayTest {
  protected:
   void SetUp() override {
@@ -3690,12 +2893,10 @@
   }
 
   void ProcessForOverlays() {
-    overlay_list_.clear();
     overlay_processor_->ProcessForOverlays(
         resource_provider_.get(), &pass_list_, GetIdentityColorMatrix(),
-        render_pass_filters_, render_pass_backdrop_filters_, nullptr,
-        &overlay_list_, &ca_layer_list_, nullptr, &damage_rect_,
-        &content_bounds_);
+        render_pass_filters_, render_pass_backdrop_filters_, nullity,
+        &ca_layer_list_, &damage_rect_, &content_bounds_);
   }
   RenderPassList pass_list_;
   RenderPass* pass_;
@@ -3706,7 +2907,6 @@
   OverlayProcessor::FilterOperationsMap render_pass_filters_;
   OverlayProcessor::FilterOperationsMap render_pass_backdrop_filters_;
   CALayerOverlayList ca_layer_list_;
-  OverlayCandidateList overlay_list_;
 };
 
 TEST_F(CALayerOverlayRPDQTest, RenderPassDrawQuadNoFilters) {
@@ -3813,6 +3013,7 @@
   ProcessForOverlays();
   EXPECT_EQ(0U, ca_layer_list_.size());
 }
+#endif
 
 }  // namespace
 }  // namespace viz
diff --git a/components/viz/service/display/skia_output_surface.h b/components/viz/service/display/skia_output_surface.h
index ae7dbf5..17bc68ff2 100644
--- a/components/viz/service/display/skia_output_surface.h
+++ b/components/viz/service/display/skia_output_surface.h
@@ -8,6 +8,7 @@
 #include <memory>
 #include <vector>
 
+#include "build/build_config.h"
 #include "components/viz/common/resources/resource_format.h"
 #include "components/viz/common/resources/resource_id.h"
 #include "components/viz/service/display/external_use_client.h"
@@ -26,7 +27,9 @@
 
 class ContextLostObserver;
 class CopyOutputRequest;
+#if defined(OS_WIN)
 class DCLayerOverlay;
+#endif
 
 namespace copy_output {
 struct RenderPassGeometry;
@@ -129,12 +132,14 @@
                           const gfx::ColorSpace& color_space,
                           std::unique_ptr<CopyOutputRequest> request) = 0;
 
+#if defined(OS_WIN)
   // Enables/disables drawing with DC layers. Should be enabled before
   // ScheduleDCLayers() will be called.
   virtual void SetEnableDCLayers(bool enable) = 0;
 
   // Schedule drawing DC layer overlays at next SkiaSwapBuffers() call.
   virtual void ScheduleDCLayers(std::vector<DCLayerOverlay> dc_layers) = 0;
+#endif
 
   // Add context lost observer.
   virtual void AddContextLostObserver(ContextLostObserver* observer) = 0;
diff --git a/components/viz/service/display/skia_renderer.cc b/components/viz/service/display/skia_renderer.cc
index d110134..48373566 100644
--- a/components/viz/service/display/skia_renderer.cc
+++ b/components/viz/service/display/skia_renderer.cc
@@ -805,8 +805,10 @@
         current_frame()->output_surface_plane.value());
   }
 
+#if defined(OS_WIN)
   // Schedule overlay planes to be presented before SwapBuffers().
   ScheduleDCLayers();
+#endif
 }
 
 void SkiaRenderer::SwapBuffers(std::vector<ui::LatencyInfo> latency_info) {
@@ -2040,11 +2042,12 @@
 #endif
 }
 
+#if defined(OS_WIN)
 void SkiaRenderer::ScheduleDCLayers() {
-  if (current_frame()->dc_layer_overlay_list.empty())
+  if (current_frame()->overlay_list.empty())
     return;
 
-  for (auto& dc_layer_overlay : current_frame()->dc_layer_overlay_list) {
+  for (auto& dc_layer_overlay : current_frame()->overlay_list) {
     for (size_t i = 0; i < DCLayerOverlay::kNumResources; ++i) {
       ResourceId resource_id = dc_layer_overlay.resources[i];
       if (resource_id == kInvalidResourceId)
@@ -2060,8 +2063,9 @@
 
   has_locked_overlay_resources_ = true;
   skia_output_surface_->ScheduleDCLayers(
-      std::move(current_frame()->dc_layer_overlay_list));
+      std::move(current_frame()->overlay_list));
 }
+#endif
 
 sk_sp<SkColorFilter> SkiaRenderer::GetColorFilter(const gfx::ColorSpace& src,
                                                   const gfx::ColorSpace& dst,
@@ -2344,9 +2348,11 @@
   }
 }
 
+#if defined(OS_WIN)
 void SkiaRenderer::SetEnableDCLayers(bool enable) {
   skia_output_surface_->SetEnableDCLayers(enable);
 }
+#endif
 
 void SkiaRenderer::DidChangeVisibility() {
   if (visible_)
diff --git a/components/viz/service/display/skia_renderer.h b/components/viz/service/display/skia_renderer.h
index 0379ebd..f106c95 100644
--- a/components/viz/service/display/skia_renderer.h
+++ b/components/viz/service/display/skia_renderer.h
@@ -11,6 +11,7 @@
 #include <vector>
 
 #include "base/macros.h"
+#include "build/build_config.h"
 #include "cc/cc_export.h"
 #include "components/viz/service/display/direct_renderer.h"
 #include "components/viz/service/display/sync_query_collection.h"
@@ -84,7 +85,9 @@
   void EnsureScissorTestDisabled() override;
   void CopyDrawnRenderPass(const copy_output::RenderPassGeometry& geometry,
                            std::unique_ptr<CopyOutputRequest> request) override;
+#if defined(OS_WIN)
   void SetEnableDCLayers(bool enable) override;
+#endif
   void DidChangeVisibility() override;
   void FinishDrawingQuadList() override;
   void GenerateMipmap() override;
@@ -208,8 +211,10 @@
                            const DrawRPDQParams* rpdq_params,
                            DrawQuadParams* params);
 
+#if defined(OS_WIN)
   // Schedule overlay candidates for presentation at next SwapBuffers().
   void ScheduleDCLayers();
+#endif
 
   // skia_renderer can draw most single-quad passes directly, regardless of
   // blend mode or image filtering.
diff --git a/components/viz/service/display/software_renderer.cc b/components/viz/service/display/software_renderer.cc
index 76a7693..19f0d94 100644
--- a/components/viz/service/display/software_renderer.cc
+++ b/components/viz/service/display/software_renderer.cc
@@ -633,9 +633,11 @@
       result_format, geometry.result_selection, bitmap));
 }
 
+#if defined(OS_WIN)
 void SoftwareRenderer::SetEnableDCLayers(bool enable) {
   NOTIMPLEMENTED();
 }
+#endif
 
 void SoftwareRenderer::DidChangeVisibility() {
   if (visible_)
diff --git a/components/viz/service/display/software_renderer.h b/components/viz/service/display/software_renderer.h
index 11b3998..b78a724 100644
--- a/components/viz/service/display/software_renderer.h
+++ b/components/viz/service/display/software_renderer.h
@@ -6,6 +6,7 @@
 #define COMPONENTS_VIZ_SERVICE_DISPLAY_SOFTWARE_RENDERER_H_
 
 #include "base/macros.h"
+#include "build/build_config.h"
 #include "components/viz/service/display/direct_renderer.h"
 #include "components/viz/service/viz_service_export.h"
 #include "ui/latency/latency_info.h"
@@ -61,7 +62,9 @@
   void EnsureScissorTestDisabled() override;
   void CopyDrawnRenderPass(const copy_output::RenderPassGeometry& geometry,
                            std::unique_ptr<CopyOutputRequest> request) override;
+#if defined(OS_WIN)
   void SetEnableDCLayers(bool enable) override;
+#endif
   void DidChangeVisibility() override;
   void GenerateMipmap() override;
 
diff --git a/components/viz/service/display_embedder/skia_output_device.cc b/components/viz/service/display_embedder/skia_output_device.cc
index 2541bb0..f9732b7d 100644
--- a/components/viz/service/display_embedder/skia_output_device.cc
+++ b/components/viz/service/display_embedder/skia_output_device.cc
@@ -38,6 +38,7 @@
   NOTIMPLEMENTED();
 }
 
+#if defined(OS_WIN)
 void SkiaOutputDevice::SetEnableDCLayers(bool enable) {
   NOTIMPLEMENTED();
 }
@@ -45,6 +46,7 @@
 void SkiaOutputDevice::ScheduleDCLayers(std::vector<DCLayerOverlay> dc_layers) {
   NOTIMPLEMENTED();
 }
+#endif
 
 void SkiaOutputDevice::StartSwapBuffers(
     base::Optional<BufferPresentedCallback> feedback) {
diff --git a/components/viz/service/display_embedder/skia_output_device.h b/components/viz/service/display_embedder/skia_output_device.h
index bec5cc0..3199b9a 100644
--- a/components/viz/service/display_embedder/skia_output_device.h
+++ b/components/viz/service/display_embedder/skia_output_device.h
@@ -12,6 +12,7 @@
 #include "base/containers/queue.h"
 #include "base/macros.h"
 #include "base/optional.h"
+#include "build/build_config.h"
 #include "components/viz/service/display/output_surface.h"
 #include "gpu/command_buffer/common/swap_buffers_complete_params.h"
 #include "third_party/skia/include/core/SkRefCnt.h"
@@ -29,12 +30,16 @@
 struct PresentationFeedback;
 }  // namespace gfx
 
+// TODO(crbug.com/996004): Remove this once we use BufferQueue SharedImage
+// implementation.
 namespace gl {
 class GLImage;
 }
 
 namespace viz {
+#if defined(OS_WIN)
 class DCLayerOverlay;
+#endif
 
 class SkiaOutputDevice {
  public:
@@ -85,6 +90,8 @@
                              BufferPresentedCallback feedback,
                              std::vector<ui::LatencyInfo> latency_info);
 
+  // TODO(crbug.com/996004): Should use BufferQueue SharedImage
+  // implementation instead of GLImage.
   virtual gl::GLImage* GetOverlayImage();
   virtual std::unique_ptr<gfx::GpuFence> SubmitOverlayGpuFence();
 
@@ -92,8 +99,10 @@
   virtual void SetDrawRectangle(const gfx::Rect& draw_rectangle);
 
   virtual void SetGpuVSyncEnabled(bool enabled);
+#if defined(OS_WIN)
   virtual void SetEnableDCLayers(bool enabled);
   virtual void ScheduleDCLayers(std::vector<DCLayerOverlay> dc_layers);
+#endif
 
   const OutputSurface::Capabilities& capabilities() const {
     return capabilities_;
diff --git a/components/viz/service/display_embedder/skia_output_device_gl.cc b/components/viz/service/display_embedder/skia_output_device_gl.cc
index 88319a5..a0a47d9 100644
--- a/components/viz/service/display_embedder/skia_output_device_gl.cc
+++ b/components/viz/service/display_embedder/skia_output_device_gl.cc
@@ -176,6 +176,7 @@
   gl_surface_->SetGpuVSyncEnabled(enabled);
 }
 
+#if defined(OS_WIN)
 void SkiaOutputDeviceGL::SetEnableDCLayers(bool enable) {
   gl_surface_->SetEnableDCLayers(enable);
 }
@@ -220,6 +221,7 @@
       DLOG(ERROR) << "ScheduleDCLayer failed";
   }
 }
+#endif
 
 void SkiaOutputDeviceGL::EnsureBackbuffer() {
   gl_surface_->SetBackbufferAllocation(true);
diff --git a/components/viz/service/display_embedder/skia_output_device_gl.h b/components/viz/service/display_embedder/skia_output_device_gl.h
index c31edc57..7ef0321 100644
--- a/components/viz/service/display_embedder/skia_output_device_gl.h
+++ b/components/viz/service/display_embedder/skia_output_device_gl.h
@@ -65,8 +65,10 @@
                      std::vector<ui::LatencyInfo> latency_info) override;
   void SetDrawRectangle(const gfx::Rect& draw_rectangle) override;
   void SetGpuVSyncEnabled(bool enabled) override;
+#if defined(OS_WIN)
   void SetEnableDCLayers(bool enable) override;
   void ScheduleDCLayers(std::vector<DCLayerOverlay> dc_layers) override;
+#endif
   void EnsureBackbuffer() override;
   void DiscardBackbuffer() override;
   SkSurface* BeginPaint() override;
diff --git a/components/viz/service/display_embedder/skia_output_surface_impl.cc b/components/viz/service/display_embedder/skia_output_surface_impl.cc
index 511da6ff..568a71a 100644
--- a/components/viz/service/display_embedder/skia_output_surface_impl.cc
+++ b/components/viz/service/display_embedder/skia_output_surface_impl.cc
@@ -614,6 +614,7 @@
   ScheduleGpuTask(std::move(callback), std::move(resource_sync_tokens_));
 }
 
+#if defined(OS_WIN)
 void SkiaOutputSurfaceImpl::SetEnableDCLayers(bool enable) {
   auto task = base::BindOnce(&SkiaOutputSurfaceImplOnGpu::SetEnableDCLayers,
                              base::Unretained(impl_on_gpu_.get()), enable);
@@ -627,6 +628,7 @@
                      base::Unretained(impl_on_gpu_.get()), std::move(overlays));
   ScheduleGpuTask(std::move(task), {});
 }
+#endif
 
 void SkiaOutputSurfaceImpl::SetCapabilitiesForTesting(
     bool flipped_output_surface) {
diff --git a/components/viz/service/display_embedder/skia_output_surface_impl.h b/components/viz/service/display_embedder/skia_output_surface_impl.h
index acc264f4..0af491dc 100644
--- a/components/viz/service/display_embedder/skia_output_surface_impl.h
+++ b/components/viz/service/display_embedder/skia_output_surface_impl.h
@@ -13,6 +13,7 @@
 #include "base/optional.h"
 #include "base/threading/thread_checker.h"
 #include "base/util/type_safety/pass_key.h"
+#include "build/build_config.h"
 #include "components/viz/common/display/renderer_settings.h"
 #include "components/viz/common/resources/resource_id.h"
 #include "components/viz/service/display/skia_output_surface.h"
@@ -114,8 +115,10 @@
       sk_sp<SkColorSpace> color_space) override;
 
   void RemoveRenderPassResource(std::vector<RenderPassId> ids) override;
+#if defined(OS_WIN)
   void SetEnableDCLayers(bool enable) override;
   void ScheduleDCLayers(std::vector<DCLayerOverlay> overlays) override;
+#endif
   void CopyOutput(RenderPassId id,
                   const copy_output::RenderPassGeometry& geometry,
                   const gfx::ColorSpace& color_space,
diff --git a/components/viz/service/display_embedder/skia_output_surface_impl_on_gpu.cc b/components/viz/service/display_embedder/skia_output_surface_impl_on_gpu.cc
index a5a35121..56975ad2 100644
--- a/components/viz/service/display_embedder/skia_output_surface_impl_on_gpu.cc
+++ b/components/viz/service/display_embedder/skia_output_surface_impl_on_gpu.cc
@@ -1243,6 +1243,7 @@
   // |image_contexts| goes out of scope here.
 }
 
+#if defined(OS_WIN)
 void SkiaOutputSurfaceImplOnGpu::SetEnableDCLayers(bool enable) {
   if (!MakeCurrent(false /* need_fbo0 */))
     return;
@@ -1253,6 +1254,7 @@
     std::vector<DCLayerOverlay> dc_layers) {
   output_device_->ScheduleDCLayers(std::move(dc_layers));
 }
+#endif
 
 void SkiaOutputSurfaceImplOnGpu::SetGpuVSyncEnabled(bool enabled) {
   output_device_->SetGpuVSyncEnabled(enabled);
diff --git a/components/viz/service/display_embedder/skia_output_surface_impl_on_gpu.h b/components/viz/service/display_embedder/skia_output_surface_impl_on_gpu.h
index e225d47..f3b5952 100644
--- a/components/viz/service/display_embedder/skia_output_surface_impl_on_gpu.h
+++ b/components/viz/service/display_embedder/skia_output_surface_impl_on_gpu.h
@@ -161,8 +161,10 @@
   void ReleaseImageContexts(
       std::vector<std::unique_ptr<ExternalUseClient::ImageContext>>
           image_contexts);
+#if defined(OS_WIN)
   void SetEnableDCLayers(bool enable);
   void ScheduleDCLayers(std::vector<DCLayerOverlay> dc_layers);
+#endif
   void SetGpuVSyncEnabled(bool enabled);
 
   bool was_context_lost() { return context_state_->context_lost(); }
diff --git a/components/viz/test/fake_skia_output_surface.h b/components/viz/test/fake_skia_output_surface.h
index cf291e2..42ec0ad 100644
--- a/components/viz/test/fake_skia_output_surface.h
+++ b/components/viz/test/fake_skia_output_surface.h
@@ -14,6 +14,7 @@
 #include "base/memory/ptr_util.h"
 #include "base/memory/weak_ptr.h"
 #include "base/threading/thread_checker.h"
+#include "build/build_config.h"
 #include "components/viz/service/display/skia_output_surface.h"
 #include "components/viz/test/test_context_provider.h"
 #include "gpu/command_buffer/common/sync_token.h"
@@ -89,8 +90,10 @@
       bool mipmap,
       sk_sp<SkColorSpace> color_space) override;
   void RemoveRenderPassResource(std::vector<RenderPassId> ids) override;
+#if defined(OS_WIN)
   void SetEnableDCLayers(bool enable) override {}
   void ScheduleDCLayers(std::vector<DCLayerOverlay> overlays) override {}
+#endif
   void CopyOutput(RenderPassId id,
                   const copy_output::RenderPassGeometry& geometry,
                   const gfx::ColorSpace& color_space,
diff --git a/content/browser/accessibility/accessibility_tree_formatter_base.cc b/content/browser/accessibility/accessibility_tree_formatter_base.cc
index 150cc77..90ac4e5 100644
--- a/content/browser/accessibility/accessibility_tree_formatter_base.cc
+++ b/content/browser/accessibility/accessibility_tree_formatter_base.cc
@@ -40,7 +40,8 @@
   return passes[index];
 }
 
-base::string16 AccessibilityTreeFormatter::DumpAccessibilityTreeFromManager(
+// static
+base::string16 AccessibilityTreeFormatterBase::DumpAccessibilityTreeFromManager(
     BrowserAccessibilityManager* ax_mgr,
     bool internal,
     std::vector<PropertyFilter> property_filters) {
diff --git a/content/browser/accessibility/accessibility_tree_formatter_base.h b/content/browser/accessibility/accessibility_tree_formatter_base.h
index fc779030..7b1f6b5 100644
--- a/content/browser/accessibility/accessibility_tree_formatter_base.h
+++ b/content/browser/accessibility/accessibility_tree_formatter_base.h
@@ -37,6 +37,35 @@
   AccessibilityTreeFormatterBase();
   ~AccessibilityTreeFormatterBase() override;
 
+  static base::string16 DumpAccessibilityTreeFromManager(
+      BrowserAccessibilityManager* ax_mgr,
+      bool internal,
+      std::vector<PropertyFilter> property_filters);
+
+  // Populates the given DictionaryValue with the accessibility tree.
+  // The dictionary contains a key/value pair for each attribute of the node,
+  // plus a "children" attribute containing a list of all child nodes.
+  // {
+  //   "AXName": "node",  /* actual attributes will vary by platform */
+  //   "position": {  /* some attributes may be dictionaries */
+  //     "x": 0,
+  //     "y": 0
+  //   },
+  //   /* ... more attributes of |node| */
+  //   "children": [ {  /* list of children created recursively */
+  //     "AXName": "child node 1",
+  //     /* ... more attributes */
+  //     "children": [ ]
+  //   }, {
+  //     "AXName": "child name 2",
+  //     /* ... more attributes */
+  //     "children": [ ]
+  //   } ]
+  // }
+  // Build an accessibility tree for the current Chrome app.
+  virtual std::unique_ptr<base::DictionaryValue> BuildAccessibilityTree(
+      BrowserAccessibility* root) = 0;
+
   // AccessibilityTreeFormatter overrides.
   void AddDefaultFilters(
       std::vector<PropertyFilter>* property_filters) override;
diff --git a/content/browser/accessibility/accessibility_tree_formatter_uia_win.h b/content/browser/accessibility/accessibility_tree_formatter_uia_win.h
index 6a53870..45fbaaa 100644
--- a/content/browser/accessibility/accessibility_tree_formatter_uia_win.h
+++ b/content/browser/accessibility/accessibility_tree_formatter_uia_win.h
@@ -3,35 +3,36 @@
 // found in the LICENSE file.
 #ifndef CONTENT_BROWSER_ACCESSIBILITY_ACCESSIBILITY_TREE_FORMATTER_UIA_WIN_H_
 #define CONTENT_BROWSER_ACCESSIBILITY_ACCESSIBILITY_TREE_FORMATTER_UIA_WIN_H_
+
 #include "content/browser/accessibility/accessibility_tree_formatter_base.h"
 
 #include <ole2.h>
 #include <stdint.h>
 #include <uiautomation.h>
 #include <wrl/client.h>
+
 #include <memory>
 #include <string>
 #include <vector>
 
 #include "base/win/scoped_variant.h"
+
 namespace content {
 
 class AccessibilityTreeFormatterUia : public AccessibilityTreeFormatterBase {
  public:
   AccessibilityTreeFormatterUia();
-
   ~AccessibilityTreeFormatterUia() override;
 
   static std::unique_ptr<AccessibilityTreeFormatter> CreateUia();
 
-  void AddDefaultFilters(
-      std::vector<PropertyFilter>* property_filters) override;
-
   static void SetUpCommandLineForTestPass(base::CommandLine* command_line);
 
+  // AccessibilityTreeFormatterBase:
+  void AddDefaultFilters(
+      std::vector<PropertyFilter>* property_filters) override;
   base::FilePath::StringType GetExpectedFileSuffix() override;
   base::FilePath::StringType GetVersionSpecificExpectedFileSuffix() override;
-
   std::unique_ptr<base::DictionaryValue> BuildAccessibilityTree(
       BrowserAccessibility* start) override;
   std::unique_ptr<base::DictionaryValue> BuildAccessibilityTreeForProcess(
@@ -114,5 +115,7 @@
   Microsoft::WRL::ComPtr<IUIAutomationCacheRequest> element_cache_request_;
   Microsoft::WRL::ComPtr<IUIAutomationCacheRequest> children_cache_request_;
 };
+
 }  // namespace content
+
 #endif  // CONTENT_BROWSER_ACCESSIBILITY_ACCESSIBILITY_TREE_FORMATTER_UIA_WIN_H_
diff --git a/content/browser/accessibility/accessibility_win_browsertest.cc b/content/browser/accessibility/accessibility_win_browsertest.cc
index 06afe720..aad8de5 100644
--- a/content/browser/accessibility/accessibility_win_browsertest.cc
+++ b/content/browser/accessibility/accessibility_win_browsertest.cc
@@ -894,7 +894,7 @@
     // Test navigating to the parent node via UIA
     Microsoft::WRL::ComPtr<IUIAutomationElement> parent;
     tree_walker_->GetParentElement(root_.Get(), &parent);
-    CHECK(parent.Get());
+    CHECK(parent.Get() == nullptr);
 
     run_loop_.Quit();
   }
diff --git a/content/browser/accessibility/dump_accessibility_browsertest_base.cc b/content/browser/accessibility/dump_accessibility_browsertest_base.cc
index 6d01281..d7078fd9 100644
--- a/content/browser/accessibility/dump_accessibility_browsertest_base.cc
+++ b/content/browser/accessibility/dump_accessibility_browsertest_base.cc
@@ -309,8 +309,19 @@
     AccessibilityNotificationWaiter waiter(shell()->web_contents(),
                                            ui::kAXModeComplete,
                                            ax::mojom::Event::kClicked);
+    BrowserAccessibility* action_element;
 
-    BrowserAccessibility* action_element = FindNode(str);
+    size_t parent_node_delimiter_index = str.find(",");
+    if (parent_node_delimiter_index != std::string::npos) {
+      auto node_name = str.substr(0, parent_node_delimiter_index);
+      auto parent_node_name = str.substr(parent_node_delimiter_index + 1);
+
+      BrowserAccessibility* parent_node = FindNode(parent_node_name);
+      DCHECK(parent_node) << "Parent node name provided but not found";
+      action_element = FindNode(node_name, parent_node);
+    } else {
+      action_element = FindNode(str);
+    }
 
     ui::AXActionData action_data;
     action_data.action = ax::mojom::Action::kDoDefault;
@@ -430,10 +441,14 @@
 }
 
 BrowserAccessibility* DumpAccessibilityTestBase::FindNode(
-    const std::string& name) {
-  BrowserAccessibility* root = GetManager()->GetRoot();
-  CHECK(root);
-  return FindNodeInSubtree(*root, name);
+    const std::string& name,
+    BrowserAccessibility* search_root) {
+  if (!search_root)
+    search_root = GetManager()->GetRoot();
+
+  CHECK(search_root);
+  BrowserAccessibility* node = FindNodeInSubtree(*search_root, name);
+  return node;
 }
 
 BrowserAccessibilityManager* DumpAccessibilityTestBase::GetManager() {
@@ -445,9 +460,8 @@
 BrowserAccessibility* DumpAccessibilityTestBase::FindNodeInSubtree(
     BrowserAccessibility& node,
     const std::string& name) {
-  if (node.GetStringAttribute(ax::mojom::StringAttribute::kName) == name) {
+  if (node.GetStringAttribute(ax::mojom::StringAttribute::kName) == name)
     return &node;
-  }
 
   for (unsigned int i = 0; i < node.PlatformChildCount(); ++i) {
     BrowserAccessibility* result =
diff --git a/content/browser/accessibility/dump_accessibility_browsertest_base.h b/content/browser/accessibility/dump_accessibility_browsertest_base.h
index 45da92b..cbea701b 100644
--- a/content/browser/accessibility/dump_accessibility_browsertest_base.h
+++ b/content/browser/accessibility/dump_accessibility_browsertest_base.h
@@ -96,9 +96,11 @@
 
   void RunTestForPlatform(const base::FilePath file_path, const char* file_dir);
 
-  // Retrieve the accessibility node, starting from the root node, that matches
-  // the accessibility name.
-  BrowserAccessibility* FindNode(const std::string& name);
+  // Retrieve the accessibility node that matches the accessibility name. There
+  // is an optional search_root parameter that defaults to the document root if
+  // not provided.
+  BrowserAccessibility* FindNode(const std::string& name,
+                                 BrowserAccessibility* search_root = nullptr);
 
   // Retrieve the browser accessibility manager object for the current web
   // contents.
diff --git a/content/browser/accessibility/dump_accessibility_tree_browsertest.cc b/content/browser/accessibility/dump_accessibility_tree_browsertest.cc
index c791084..82be5b9f 100644
--- a/content/browser/accessibility/dump_accessibility_tree_browsertest.cc
+++ b/content/browser/accessibility/dump_accessibility_tree_browsertest.cc
@@ -1560,6 +1560,11 @@
   RunHtmlTest(FILE_PATH_LITERAL("input-date-with-popup-open.html"));
 }
 
+IN_PROC_BROWSER_TEST_P(DumpAccessibilityTreeTest,
+                       AccessibilityInputDateWithPopupOpenMultiple) {
+  RunHtmlTest(FILE_PATH_LITERAL("input-date-with-popup-open-multiple.html"));
+}
+
 IN_PROC_BROWSER_TEST_P(DumpAccessibilityTreeTest, AccessibilityInputDateTime) {
   RunHtmlTest(FILE_PATH_LITERAL("input-datetime.html"));
 }
diff --git a/content/browser/accessibility/one_shot_accessibility_tree_search.cc b/content/browser/accessibility/one_shot_accessibility_tree_search.cc
index 467b2793..41b49ab 100644
--- a/content/browser/accessibility/one_shot_accessibility_tree_search.cc
+++ b/content/browser/accessibility/one_shot_accessibility_tree_search.cc
@@ -414,6 +414,11 @@
   return tag == "audio" || tag == "video";
 }
 
+bool AccessibilityPopupButtonPredicate(BrowserAccessibility* start,
+                                       BrowserAccessibility* node) {
+  return (node->GetRole() == ax::mojom::Role::kPopUpButton);
+}
+
 bool AccessibilityRadioButtonPredicate(BrowserAccessibility* start,
                                        BrowserAccessibility* node) {
   return (node->GetRole() == ax::mojom::Role::kRadioButton ||
diff --git a/content/browser/accessibility/one_shot_accessibility_tree_search.h b/content/browser/accessibility/one_shot_accessibility_tree_search.h
index 27128a0..fe0864c 100644
--- a/content/browser/accessibility/one_shot_accessibility_tree_search.h
+++ b/content/browser/accessibility/one_shot_accessibility_tree_search.h
@@ -51,6 +51,7 @@
 DECLARE_ACCESSIBILITY_PREDICATE(AccessibilityLiveRegionPredicate);
 DECLARE_ACCESSIBILITY_PREDICATE(AccessibilityMainPredicate);
 DECLARE_ACCESSIBILITY_PREDICATE(AccessibilityMediaPredicate);
+DECLARE_ACCESSIBILITY_PREDICATE(AccessibilityPopupButtonPredicate);
 DECLARE_ACCESSIBILITY_PREDICATE(AccessibilityRadioButtonPredicate);
 DECLARE_ACCESSIBILITY_PREDICATE(AccessibilityRadioGroupPredicate);
 DECLARE_ACCESSIBILITY_PREDICATE(AccessibilityTablePredicate);
diff --git a/content/browser/bad_message.h b/content/browser/bad_message.h
index 131b6f6..d29ce2e 100644
--- a/content/browser/bad_message.h
+++ b/content/browser/bad_message.h
@@ -236,7 +236,7 @@
   RFH_CHILD_FRAME_NEEDS_OWNER_ELEMENT_TYPE = 208,
   OBSOLETE_RFH_INVALID_WEB_REPORTING_CRASH_ID = 209,
   RFH_DETACH_MAIN_FRAME = 210,
-  RFH_DOCUMENT_INTERFACE_BROKER_MISSING = 211,
+  RFH_BROWSER_INTERFACE_BROKER_MISSING = 211,
   RFPH_POST_MESSAGE_INVALID_SOURCE_ORIGIN = 212,
   INVALID_INITIATOR_ORIGIN = 213,
   RFHI_BEGIN_NAVIGATION_MISSING_INITIATOR_ORIGIN = 214,
diff --git a/content/browser/client_hints/client_hints.cc b/content/browser/client_hints/client_hints.cc
index ed270d8c..3c28e55 100644
--- a/content/browser/client_hints/client_hints.cc
+++ b/content/browser/client_hints/client_hints.cc
@@ -16,6 +16,8 @@
 #include "base/strings/stringprintf.h"
 #include "base/time/time.h"
 #include "build/build_config.h"
+#include "content/browser/frame_host/frame_tree.h"
+#include "content/browser/frame_host/frame_tree_node.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/host_zoom_map.h"
 #include "content/public/common/content_features.h"
@@ -26,6 +28,7 @@
 #include "services/network/public/cpp/network_quality_tracker.h"
 #include "third_party/blink/public/common/client_hints/client_hints.h"
 #include "third_party/blink/public/common/device_memory/approximated_device_memory.h"
+#include "third_party/blink/public/common/feature_policy/feature_policy.h"
 #include "third_party/blink/public/common/page/page_zoom.h"
 #include "third_party/blink/public/common/user_agent/user_agent_metadata.h"
 #include "third_party/blink/public/platform/web_client_hints_type.h"
@@ -361,6 +364,26 @@
   SetHeaderToString(headers, type, value);
 }
 
+bool IsFeaturePolicyForClientHintsEnabled() {
+  return base::FeatureList::IsEnabled(features::kFeaturePolicyForClientHints) ||
+         base::CommandLine::ForCurrentProcess()->HasSwitch(
+             switches::kEnableExperimentalWebPlatformFeatures);
+}
+
+bool ShouldAddClientHint(
+    const blink::WebEnabledClientHints& main_frame_client_hints,
+    blink::FeaturePolicy* feature_policy,
+    const url::Origin& resource_origin,
+    blink::mojom::WebClientHintsType type,
+    blink::mojom::FeaturePolicyFeature feature) {
+  if (!main_frame_client_hints.IsEnabled(type))
+    return false;
+  if (!IsFeaturePolicyForClientHintsEnabled())
+    return true;
+  return feature_policy &&
+         feature_policy->IsFeatureEnabledForOrigin(feature, resource_origin);
+}
+
 }  // namespace
 
 namespace content {
@@ -380,13 +403,16 @@
     net::HttpRequestHeaders* headers,
     BrowserContext* context,
     bool javascript_enabled,
-    ClientHintsControllerDelegate* delegate) {
-  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+    ClientHintsControllerDelegate* delegate,
+    FrameTreeNode* frame_tree_node) {
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   DCHECK_EQ(blink::kWebEffectiveConnectionTypeMappingCount,
             net::EFFECTIVE_CONNECTION_TYPE_4G + 1u);
   DCHECK_EQ(blink::kWebEffectiveConnectionTypeMappingCount,
             static_cast<size_t>(net::EFFECTIVE_CONNECTION_TYPE_LAST));
   DCHECK(context);
+  RenderFrameHostImpl* main_frame =
+      frame_tree_node->frame_tree()->GetMainFrame();
 
   if (!IsValidURLForClientHints(url))
     return;
@@ -400,32 +426,59 @@
     return;
 
   blink::WebEnabledClientHints web_client_hints;
-  delegate->GetAllowedClientHintsFromSource(url, &web_client_hints);
+
+  // If the current frame is the main frame, the URL wasn't committed yet, so in
+  // order to get the main frame URL, we should use the provided URL instead.
+  // Otherwise, the current frame is an iframe and the main frame URL was
+  // committed, so we can safely get it from it.
+  GURL main_frame_url =
+      frame_tree_node->IsMainFrame() ? url : main_frame->GetLastCommittedURL();
+
+  delegate->GetAllowedClientHintsFromSource(main_frame_url, &web_client_hints);
+
+  url::Origin resource_origin = url::Origin::Create(url);
+  blink::FeaturePolicy* feature_policy = main_frame->feature_policy();
 
   // Add Headers
-  if (web_client_hints.IsEnabled(
-          blink::mojom::WebClientHintsType::kDeviceMemory)) {
+  if (ShouldAddClientHint(
+          web_client_hints, feature_policy, resource_origin,
+          blink::mojom::WebClientHintsType::kDeviceMemory,
+          blink::mojom::FeaturePolicyFeature::kClientHintDeviceMemory)) {
     AddDeviceMemoryHeader(headers);
   }
-  if (web_client_hints.IsEnabled(blink::mojom::WebClientHintsType::kDpr)) {
+  if (ShouldAddClientHint(web_client_hints, feature_policy, resource_origin,
+                          blink::mojom::WebClientHintsType::kDpr,
+                          blink::mojom::FeaturePolicyFeature::kClientHintDPR)) {
     AddDPRHeader(headers, context, url);
   }
-  if (web_client_hints.IsEnabled(
-          blink::mojom::WebClientHintsType::kViewportWidth)) {
+  if (ShouldAddClientHint(
+          web_client_hints, feature_policy, resource_origin,
+          blink::mojom::WebClientHintsType::kViewportWidth,
+          blink::mojom::FeaturePolicyFeature::kClientHintViewportWidth)) {
     AddViewportWidthHeader(headers, context, url);
   }
   network::NetworkQualityTracker* network_quality_tracker =
       delegate->GetNetworkQualityTracker();
-  if (web_client_hints.IsEnabled(blink::mojom::WebClientHintsType::kRtt)) {
+  if (ShouldAddClientHint(web_client_hints, feature_policy, resource_origin,
+                          blink::mojom::WebClientHintsType::kRtt,
+                          blink::mojom::FeaturePolicyFeature::kClientHintRTT)) {
     AddRttHeader(headers, network_quality_tracker, url);
   }
-  if (web_client_hints.IsEnabled(blink::mojom::WebClientHintsType::kDownlink)) {
+  if (ShouldAddClientHint(
+          web_client_hints, feature_policy, resource_origin,
+          blink::mojom::WebClientHintsType::kDownlink,
+          blink::mojom::FeaturePolicyFeature::kClientHintDownlink)) {
     AddDownlinkHeader(headers, network_quality_tracker, url);
   }
-  if (web_client_hints.IsEnabled(blink::mojom::WebClientHintsType::kEct)) {
+  if (ShouldAddClientHint(web_client_hints, feature_policy, resource_origin,
+                          blink::mojom::WebClientHintsType::kEct,
+                          blink::mojom::FeaturePolicyFeature::kClientHintECT)) {
     AddEctHeader(headers, network_quality_tracker, url);
   }
-  if (web_client_hints.IsEnabled(blink::mojom::WebClientHintsType::kLang)) {
+  if (ShouldAddClientHint(
+          web_client_hints, feature_policy, resource_origin,
+          blink::mojom::WebClientHintsType::kLang,
+          blink::mojom::FeaturePolicyFeature::kClientHintLang)) {
     AddLangHeader(headers, delegate);
   }
 
@@ -446,19 +499,26 @@
                                 : base::StringPrintf("%s %s", ua.brand.c_str(),
                                                      version.c_str()));
 
-    if (web_client_hints.IsEnabled(blink::mojom::WebClientHintsType::kUAArch)) {
+    if (ShouldAddClientHint(
+            web_client_hints, feature_policy, resource_origin,
+            blink::mojom::WebClientHintsType::kUAArch,
+            blink::mojom::FeaturePolicyFeature::kClientHintUAArch)) {
       AddUAHeader(headers, blink::mojom::WebClientHintsType::kUAArch,
                   ua.architecture);
     }
 
-    if (web_client_hints.IsEnabled(
-            blink::mojom::WebClientHintsType::kUAPlatform)) {
+    if (ShouldAddClientHint(
+            web_client_hints, feature_policy, resource_origin,
+            blink::mojom::WebClientHintsType::kUAPlatform,
+            blink::mojom::FeaturePolicyFeature::kClientHintUAPlatform)) {
       AddUAHeader(headers, blink::mojom::WebClientHintsType::kUAPlatform,
                   ua.platform);
     }
 
-    if (web_client_hints.IsEnabled(
-            blink::mojom::WebClientHintsType::kUAModel)) {
+    if (ShouldAddClientHint(
+            web_client_hints, feature_policy, resource_origin,
+            blink::mojom::WebClientHintsType::kUAModel,
+            blink::mojom::FeaturePolicyFeature::kClientHintUAModel)) {
       AddUAHeader(headers, blink::mojom::WebClientHintsType::kUAModel,
                   ua.model);
     }
diff --git a/content/browser/client_hints/client_hints.h b/content/browser/client_hints/client_hints.h
index 90e83ab64..af53d71 100644
--- a/content/browser/client_hints/client_hints.h
+++ b/content/browser/client_hints/client_hints.h
@@ -12,6 +12,7 @@
 #include "net/http/http_request_headers.h"
 
 class BrowserContext;
+class FrameTreeNode;
 
 namespace content {
 
@@ -33,7 +34,8 @@
     net::HttpRequestHeaders* headers,
     BrowserContext* context,
     bool javascript_enabled,
-    ClientHintsControllerDelegate* delegate);
+    ClientHintsControllerDelegate* delegate,
+    FrameTreeNode*);
 
 }  // namespace content
 
diff --git a/content/browser/devtools/devtools_url_loader_interceptor.cc b/content/browser/devtools/devtools_url_loader_interceptor.cc
index cea3a30a..d7cc1cbb 100644
--- a/content/browser/devtools/devtools_url_loader_interceptor.cc
+++ b/content/browser/devtools/devtools_url_loader_interceptor.cc
@@ -1224,8 +1224,8 @@
   // TODO(arthursonzogni, juncai): This seems to be correctly implemented, but
   // not used nor tested so far. Add tests and remove this DCHECK to support
   // this feature if needed. See https://crbug.com/845683.
-  DCHECK(removed_headers.empty() && modified_headers.IsEmpty())
-      << "Redirect with removed or modified headers is not supported yet. See "
+  DCHECK(removed_headers.empty())
+      << "Redirect with removed headers is not supported yet. See "
          "https://crbug.com/845683";
   DCHECK(!new_url.has_value()) << "Redirect with modified url was not "
                                   "supported yet. crbug.com/845683";
diff --git a/content/browser/frame_host/ancestor_throttle.cc b/content/browser/frame_host/ancestor_throttle.cc
index 361ac112..85923e3 100644
--- a/content/browser/frame_host/ancestor_throttle.cc
+++ b/content/browser/frame_host/ancestor_throttle.cc
@@ -114,8 +114,8 @@
   // so we can't log reliably to the console. We should be able to work around
   // this iff we decide to ship the redirect-blocking behavior, but for now
   // we'll just skip the console-logging bits to collect metrics.
-  NavigationThrottle::ThrottleCheckResult result =
-      ProcessResponseImpl(LoggingDisposition::DO_NOT_LOG_TO_CONSOLE);
+  NavigationThrottle::ThrottleCheckResult result = ProcessResponseImpl(
+      LoggingDisposition::DO_NOT_LOG_TO_CONSOLE, false /* is_response_check */);
 
   if (result.action() == NavigationThrottle::BLOCK_RESPONSE)
     RecordXFrameOptionsUsage(XFrameOptionsHistogram::REDIRECT_WOULD_BE_BLOCKED);
@@ -129,11 +129,13 @@
 
 NavigationThrottle::ThrottleCheckResult
 AncestorThrottle::WillProcessResponse() {
-  return ProcessResponseImpl(LoggingDisposition::LOG_TO_CONSOLE);
+  return ProcessResponseImpl(LoggingDisposition::LOG_TO_CONSOLE,
+                             true /* is_response_check */);
 }
 
 NavigationThrottle::ThrottleCheckResult AncestorThrottle::ProcessResponseImpl(
-    LoggingDisposition logging) {
+    LoggingDisposition logging,
+    bool is_response_check) {
   DCHECK(!navigation_handle()->IsInMainFrame());
 
   NavigationRequest* request = NavigationRequest::From(navigation_handle());
@@ -150,9 +152,13 @@
     if (network::mojom::ContentSecurityPolicyPtr policy =
             request->response()->head.content_security_policy) {
       if (auto& frame_ancestors = policy->frame_ancestors) {
+        // TODO(lfg, arthursonzogni): Move the frame-ancestors check to a common
+        // ContentSecurityPolicy object instead of checking directly against the
+        // CSPSourceList.
         CSPSourceList frame_ancestors_list(*frame_ancestors);
         frame_ancestors_list.allow_response_redirects = true;
         FrameTreeNode* parent = request->frame_tree_node()->parent();
+        bool has_followed_redirect = navigation_handle()->WasServerRedirect();
         // Since the navigation hasn't committed yet, we need to create a
         // CSPContext for the navigation handle.
         CSPContext csp_context;
@@ -162,12 +168,36 @@
                                    parent->current_frame_host()
                                        ->GetLastCommittedOrigin()
                                        .GetURL(),
-                                   &csp_context,
-                                   false /* has_followed_redirect */,
-                                   true /* is_response_check) */)) {
+                                   &csp_context, has_followed_redirect,
+                                   is_response_check)) {
             parent = parent->parent();
             continue;
           }
+          auto* frame_to_commit = static_cast<RenderFrameHostImpl*>(
+              navigation_handle()->GetRenderFrameHost());
+          GURL blocked_url = navigation_handle()->GetURL();
+          SourceLocation source_location;
+          frame_to_commit->SanitizeDataForUseInCspViolation(
+              has_followed_redirect, CSPDirective::FrameAncestors, &blocked_url,
+              &source_location);
+          std::vector<std::string> report_endpoints;
+          for (auto& url : policy->report_endpoints)
+            report_endpoints.push_back(url.spec());
+
+          frame_to_commit->ReportContentSecurityPolicyViolation(
+              // The browser doesn't have the raw CSP text to report in the
+              // message.
+              CSPViolationParams(
+                  "frame-ancestors", "frame-ancestors",
+                  base::StringPrintf(
+                      "Refused to display '%s' in a frame because an ancestor "
+                      "violates the frame-ancestors Content Security Policy.",
+                      blocked_url.spec().c_str()),
+                  blocked_url, report_endpoints, policy->use_reporting_api,
+                  "" /* header */,
+                  blink::mojom::ContentSecurityPolicyType::kEnforce,
+                  has_followed_redirect, source_location));
+
           return NavigationThrottle::BLOCK_RESPONSE;
         }
         return NavigationThrottle::PROCEED;
diff --git a/content/browser/frame_host/ancestor_throttle.h b/content/browser/frame_host/ancestor_throttle.h
index c0c3d7d..2e1cbda 100644
--- a/content/browser/frame_host/ancestor_throttle.h
+++ b/content/browser/frame_host/ancestor_throttle.h
@@ -52,7 +52,8 @@
 
   explicit AncestorThrottle(NavigationHandle* handle);
   NavigationThrottle::ThrottleCheckResult ProcessResponseImpl(
-      LoggingDisposition);
+      LoggingDisposition logging,
+      bool is_response_check);
   void ParseError(const std::string& value, HeaderDisposition disposition);
   void ConsoleError(HeaderDisposition disposition);
 
diff --git a/content/browser/frame_host/cross_process_frame_connector.cc b/content/browser/frame_host/cross_process_frame_connector.cc
index ba6f3f4..a0dbc77 100644
--- a/content/browser/frame_host/cross_process_frame_connector.cc
+++ b/content/browser/frame_host/cross_process_frame_connector.cc
@@ -94,7 +94,6 @@
                         OnSynchronizeVisualProperties)
     IPC_MESSAGE_HANDLER(FrameHostMsg_UpdateViewportIntersection,
                         OnUpdateViewportIntersection)
-    IPC_MESSAGE_HANDLER(FrameHostMsg_VisibilityChanged, OnVisibilityChanged)
     IPC_MESSAGE_HANDLER(FrameHostMsg_SetIsInert, OnSetIsInert)
     IPC_MESSAGE_HANDLER(FrameHostMsg_UpdateRenderThrottlingStatus,
                         OnUpdateRenderThrottlingStatus)
diff --git a/content/browser/frame_host/cross_process_frame_connector.h b/content/browser/frame_host/cross_process_frame_connector.h
index 396eddf..3f2c546 100644
--- a/content/browser/frame_host/cross_process_frame_connector.h
+++ b/content/browser/frame_host/cross_process_frame_connector.h
@@ -117,6 +117,7 @@
   // Handlers for messages received from the parent frame called
   // from RenderFrameProxyHost to be sent to |view_|.
   void OnSetInheritedEffectiveTouchAction(cc::TouchAction);
+  void OnVisibilityChanged(blink::mojom::FrameVisibility visibility);
 
   // Exposed for tests.
   RenderWidgetHostViewBase* GetRootRenderWidgetHostViewForTesting() {
@@ -176,7 +177,6 @@
       const FrameVisualProperties& visual_properties);
   void OnUpdateViewportIntersection(
       const blink::ViewportIntersectionState& viewport_intersection);
-  void OnVisibilityChanged(blink::mojom::FrameVisibility visibility);
   void OnSetIsInert(bool);
   void OnUpdateRenderThrottlingStatus(bool is_throttled,
                                       bool subtree_throttled);
diff --git a/content/browser/frame_host/frame_tree.cc b/content/browser/frame_host/frame_tree.cc
index fd588f51..579d5d4 100644
--- a/content/browser/frame_host/frame_tree.cc
+++ b/content/browser/frame_host/frame_tree.cc
@@ -178,10 +178,6 @@
     int process_id,
     int new_routing_id,
     service_manager::mojom::InterfaceProviderRequest interface_provider_request,
-    mojo::PendingReceiver<blink::mojom::DocumentInterfaceBroker>
-        document_interface_broker_content_receiver,
-    mojo::PendingReceiver<blink::mojom::DocumentInterfaceBroker>
-        document_interface_broker_blink_receiver,
     mojo::PendingReceiver<blink::mojom::BrowserInterfaceBroker>
         browser_interface_broker_receiver,
     blink::WebTreeScopeType scope,
@@ -226,12 +222,6 @@
   added_node->current_frame_host()->BindInterfaceProviderRequest(
       std::move(interface_provider_request));
 
-  DCHECK(document_interface_broker_content_receiver.is_valid());
-  DCHECK(document_interface_broker_blink_receiver.is_valid());
-  added_node->current_frame_host()->BindDocumentInterfaceBrokerReceiver(
-      std::move(document_interface_broker_content_receiver),
-      std::move(document_interface_broker_blink_receiver));
-
   DCHECK(browser_interface_broker_receiver.is_valid());
   added_node->current_frame_host()->BindBrowserInterfaceBrokerReceiver(
       std::move(browser_interface_broker_receiver));
diff --git a/content/browser/frame_host/frame_tree.h b/content/browser/frame_host/frame_tree.h
index 092e9cf2..2f47581 100644
--- a/content/browser/frame_host/frame_tree.h
+++ b/content/browser/frame_host/frame_tree.h
@@ -156,10 +156,6 @@
       int new_routing_id,
       service_manager::mojom::InterfaceProviderRequest
           interface_provider_request,
-      mojo::PendingReceiver<blink::mojom::DocumentInterfaceBroker>
-          document_interface_broker_content_receiver,
-      mojo::PendingReceiver<blink::mojom::DocumentInterfaceBroker>
-          document_interface_broker_blink_receiver,
       mojo::PendingReceiver<blink::mojom::BrowserInterfaceBroker>
           browser_interface_broker_receiver,
       blink::WebTreeScopeType scope,
diff --git a/content/browser/frame_host/frame_tree_node_blame_context_unittest.cc b/content/browser/frame_host/frame_tree_node_blame_context_unittest.cc
index a3a425d..666628c 100644
--- a/content/browser/frame_host/frame_tree_node_blame_context_unittest.cc
+++ b/content/browser/frame_host/frame_tree_node_blame_context_unittest.cc
@@ -100,8 +100,6 @@
       tree()->AddFrame(
           node, process_id(), child_id,
           TestRenderFrameHost::CreateStubInterfaceProviderRequest(),
-          TestRenderFrameHost::CreateStubDocumentInterfaceBrokerReceiver(),
-          TestRenderFrameHost::CreateStubDocumentInterfaceBrokerReceiver(),
           TestRenderFrameHost::CreateStubBrowserInterfaceBrokerReceiver(),
           blink::WebTreeScopeType::kDocument, std::string(),
           base::StringPrintf("uniqueName%d", child_id), false,
diff --git a/content/browser/frame_host/frame_tree_unittest.cc b/content/browser/frame_host/frame_tree_unittest.cc
index 1ffe334..071cc7d 100644
--- a/content/browser/frame_host/frame_tree_unittest.cc
+++ b/content/browser/frame_host/frame_tree_unittest.cc
@@ -57,11 +57,6 @@
   return TestRenderFrameHost::CreateStubInterfaceProviderRequest();
 }
 
-mojo::PendingReceiver<blink::mojom::DocumentInterfaceBroker>
-CreateStubDocumentInterfaceBrokerReceiver() {
-  return TestRenderFrameHost::CreateStubDocumentInterfaceBrokerReceiver();
-}
-
 mojo::PendingReceiver<blink::mojom::BrowserInterfaceBroker>
 CreateStubBrowserInterfaceBrokerReceiver() {
   return TestRenderFrameHost::CreateStubBrowserInterfaceBrokerReceiver();
@@ -176,48 +171,36 @@
   // Simulate attaching a series of frames to build the frame tree.
   frame_tree->AddFrame(
       root, process_id, 14, CreateStubInterfaceProviderRequest(),
-      CreateStubDocumentInterfaceBrokerReceiver(),
-      CreateStubDocumentInterfaceBrokerReceiver(),
       CreateStubBrowserInterfaceBrokerReceiver(),
       blink::WebTreeScopeType::kDocument, std::string(), "uniqueName0", false,
       base::UnguessableToken::Create(), blink::FramePolicy(),
       FrameOwnerProperties(), false, kOwnerType);
   frame_tree->AddFrame(
       root, process_id, 15, CreateStubInterfaceProviderRequest(),
-      CreateStubDocumentInterfaceBrokerReceiver(),
-      CreateStubDocumentInterfaceBrokerReceiver(),
       CreateStubBrowserInterfaceBrokerReceiver(),
       blink::WebTreeScopeType::kDocument, std::string(), "uniqueName1", false,
       base::UnguessableToken::Create(), blink::FramePolicy(),
       FrameOwnerProperties(), false, kOwnerType);
   frame_tree->AddFrame(
       root, process_id, 16, CreateStubInterfaceProviderRequest(),
-      CreateStubDocumentInterfaceBrokerReceiver(),
-      CreateStubDocumentInterfaceBrokerReceiver(),
       CreateStubBrowserInterfaceBrokerReceiver(),
       blink::WebTreeScopeType::kDocument, std::string(), "uniqueName2", false,
       base::UnguessableToken::Create(), blink::FramePolicy(),
       FrameOwnerProperties(), false, kOwnerType);
   frame_tree->AddFrame(
       root->child_at(0), process_id, 244, CreateStubInterfaceProviderRequest(),
-      CreateStubDocumentInterfaceBrokerReceiver(),
-      CreateStubDocumentInterfaceBrokerReceiver(),
       CreateStubBrowserInterfaceBrokerReceiver(),
       blink::WebTreeScopeType::kDocument, std::string(), "uniqueName3", false,
       base::UnguessableToken::Create(), blink::FramePolicy(),
       FrameOwnerProperties(), false, kOwnerType);
   frame_tree->AddFrame(
       root->child_at(1), process_id, 255, CreateStubInterfaceProviderRequest(),
-      CreateStubDocumentInterfaceBrokerReceiver(),
-      CreateStubDocumentInterfaceBrokerReceiver(),
       CreateStubBrowserInterfaceBrokerReceiver(),
       blink::WebTreeScopeType::kDocument, no_children_node, "uniqueName4",
       false, base::UnguessableToken::Create(), blink::FramePolicy(),
       FrameOwnerProperties(), false, kOwnerType);
   frame_tree->AddFrame(
       root->child_at(0), process_id, 245, CreateStubInterfaceProviderRequest(),
-      CreateStubDocumentInterfaceBrokerReceiver(),
-      CreateStubDocumentInterfaceBrokerReceiver(),
       CreateStubBrowserInterfaceBrokerReceiver(),
       blink::WebTreeScopeType::kDocument, std::string(), "uniqueName5", false,
       base::UnguessableToken::Create(), blink::FramePolicy(),
@@ -232,40 +215,30 @@
   FrameTreeNode* child_16 = root->child_at(2);
   frame_tree->AddFrame(
       child_16, process_id, 264, CreateStubInterfaceProviderRequest(),
-      CreateStubDocumentInterfaceBrokerReceiver(),
-      CreateStubDocumentInterfaceBrokerReceiver(),
       CreateStubBrowserInterfaceBrokerReceiver(),
       blink::WebTreeScopeType::kDocument, std::string(), "uniqueName6", false,
       base::UnguessableToken::Create(), blink::FramePolicy(),
       FrameOwnerProperties(), false, kOwnerType);
   frame_tree->AddFrame(
       child_16, process_id, 265, CreateStubInterfaceProviderRequest(),
-      CreateStubDocumentInterfaceBrokerReceiver(),
-      CreateStubDocumentInterfaceBrokerReceiver(),
       CreateStubBrowserInterfaceBrokerReceiver(),
       blink::WebTreeScopeType::kDocument, std::string(), "uniqueName7", false,
       base::UnguessableToken::Create(), blink::FramePolicy(),
       FrameOwnerProperties(), false, kOwnerType);
   frame_tree->AddFrame(
       child_16, process_id, 266, CreateStubInterfaceProviderRequest(),
-      CreateStubDocumentInterfaceBrokerReceiver(),
-      CreateStubDocumentInterfaceBrokerReceiver(),
       CreateStubBrowserInterfaceBrokerReceiver(),
       blink::WebTreeScopeType::kDocument, std::string(), "uniqueName8", false,
       base::UnguessableToken::Create(), blink::FramePolicy(),
       FrameOwnerProperties(), false, kOwnerType);
   frame_tree->AddFrame(
       child_16, process_id, 267, CreateStubInterfaceProviderRequest(),
-      CreateStubDocumentInterfaceBrokerReceiver(),
-      CreateStubDocumentInterfaceBrokerReceiver(),
       CreateStubBrowserInterfaceBrokerReceiver(),
       blink::WebTreeScopeType::kDocument, deep_subtree, "uniqueName9", false,
       base::UnguessableToken::Create(), blink::FramePolicy(),
       FrameOwnerProperties(), false, kOwnerType);
   frame_tree->AddFrame(
       child_16, process_id, 268, CreateStubInterfaceProviderRequest(),
-      CreateStubDocumentInterfaceBrokerReceiver(),
-      CreateStubDocumentInterfaceBrokerReceiver(),
       CreateStubBrowserInterfaceBrokerReceiver(),
       blink::WebTreeScopeType::kDocument, std::string(), "uniqueName10", false,
       base::UnguessableToken::Create(), blink::FramePolicy(),
@@ -274,16 +247,12 @@
   FrameTreeNode* child_267 = child_16->child_at(3);
   frame_tree->AddFrame(
       child_267, process_id, 365, CreateStubInterfaceProviderRequest(),
-      CreateStubDocumentInterfaceBrokerReceiver(),
-      CreateStubDocumentInterfaceBrokerReceiver(),
       CreateStubBrowserInterfaceBrokerReceiver(),
       blink::WebTreeScopeType::kDocument, std::string(), "uniqueName11", false,
       base::UnguessableToken::Create(), blink::FramePolicy(),
       FrameOwnerProperties(), false, kOwnerType);
   frame_tree->AddFrame(child_267->child_at(0), process_id, 455,
                        CreateStubInterfaceProviderRequest(),
-                       CreateStubDocumentInterfaceBrokerReceiver(),
-                       CreateStubDocumentInterfaceBrokerReceiver(),
                        CreateStubBrowserInterfaceBrokerReceiver(),
                        blink::WebTreeScopeType::kDocument, std::string(),
                        "uniqueName12", false, base::UnguessableToken::Create(),
@@ -291,8 +260,6 @@
                        kOwnerType);
   frame_tree->AddFrame(child_267->child_at(0)->child_at(0), process_id, 555,
                        CreateStubInterfaceProviderRequest(),
-                       CreateStubDocumentInterfaceBrokerReceiver(),
-                       CreateStubDocumentInterfaceBrokerReceiver(),
                        CreateStubBrowserInterfaceBrokerReceiver(),
                        blink::WebTreeScopeType::kDocument, std::string(),
                        "uniqueName13", false, base::UnguessableToken::Create(),
@@ -300,8 +267,6 @@
                        kOwnerType);
   frame_tree->AddFrame(child_267->child_at(0)->child_at(0)->child_at(0),
                        process_id, 655, CreateStubInterfaceProviderRequest(),
-                       CreateStubDocumentInterfaceBrokerReceiver(),
-                       CreateStubDocumentInterfaceBrokerReceiver(),
                        CreateStubBrowserInterfaceBrokerReceiver(),
                        blink::WebTreeScopeType::kDocument, std::string(),
                        "uniqueName14", false, base::UnguessableToken::Create(),
@@ -380,24 +345,18 @@
   constexpr auto kOwnerType = blink::FrameOwnerElementType::kIframe;
   main_test_rfh()->OnCreateChildFrame(
       22, CreateStubInterfaceProviderRequest(),
-      CreateStubDocumentInterfaceBrokerReceiver(),
-      CreateStubDocumentInterfaceBrokerReceiver(),
       CreateStubBrowserInterfaceBrokerReceiver(),
       blink::WebTreeScopeType::kDocument, "child0", "uniqueName0", false,
       base::UnguessableToken::Create(), blink::FramePolicy(),
       FrameOwnerProperties(), kOwnerType);
   main_test_rfh()->OnCreateChildFrame(
       23, CreateStubInterfaceProviderRequest(),
-      CreateStubDocumentInterfaceBrokerReceiver(),
-      CreateStubDocumentInterfaceBrokerReceiver(),
       CreateStubBrowserInterfaceBrokerReceiver(),
       blink::WebTreeScopeType::kDocument, "child1", "uniqueName1", false,
       base::UnguessableToken::Create(), blink::FramePolicy(),
       FrameOwnerProperties(), kOwnerType);
   main_test_rfh()->OnCreateChildFrame(
       24, CreateStubInterfaceProviderRequest(),
-      CreateStubDocumentInterfaceBrokerReceiver(),
-      CreateStubDocumentInterfaceBrokerReceiver(),
       CreateStubBrowserInterfaceBrokerReceiver(),
       blink::WebTreeScopeType::kDocument, std::string(), "uniqueName2", false,
       base::UnguessableToken::Create(), blink::FramePolicy(),
@@ -409,8 +368,6 @@
   // Add one grandchild frame.
   child1->current_frame_host()->OnCreateChildFrame(
       33, CreateStubInterfaceProviderRequest(),
-      CreateStubDocumentInterfaceBrokerReceiver(),
-      CreateStubDocumentInterfaceBrokerReceiver(),
       CreateStubBrowserInterfaceBrokerReceiver(),
       blink::WebTreeScopeType::kDocument, "grandchild", "uniqueName3", false,
       base::UnguessableToken::Create(), blink::FramePolicy(),
@@ -453,24 +410,18 @@
   FrameTreeNode* root = frame_tree->root();
   main_test_rfh()->OnCreateChildFrame(
       22, CreateStubInterfaceProviderRequest(),
-      CreateStubDocumentInterfaceBrokerReceiver(),
-      CreateStubDocumentInterfaceBrokerReceiver(),
       CreateStubBrowserInterfaceBrokerReceiver(),
       blink::WebTreeScopeType::kDocument, "child0", "uniqueName0", false,
       base::UnguessableToken::Create(), blink::FramePolicy(),
       FrameOwnerProperties(), kOwnerType);
   main_test_rfh()->OnCreateChildFrame(
       23, CreateStubInterfaceProviderRequest(),
-      CreateStubDocumentInterfaceBrokerReceiver(),
-      CreateStubDocumentInterfaceBrokerReceiver(),
       CreateStubBrowserInterfaceBrokerReceiver(),
       blink::WebTreeScopeType::kDocument, "child1", "uniqueName1", false,
       base::UnguessableToken::Create(), blink::FramePolicy(),
       FrameOwnerProperties(), kOwnerType);
   main_test_rfh()->OnCreateChildFrame(
       24, CreateStubInterfaceProviderRequest(),
-      CreateStubDocumentInterfaceBrokerReceiver(),
-      CreateStubDocumentInterfaceBrokerReceiver(),
       CreateStubBrowserInterfaceBrokerReceiver(),
       blink::WebTreeScopeType::kDocument, "child2", "uniqueName2", false,
       base::UnguessableToken::Create(), blink::FramePolicy(),
@@ -482,8 +433,6 @@
   // Add one grandchild frame.
   child1->current_frame_host()->OnCreateChildFrame(
       33, CreateStubInterfaceProviderRequest(),
-      CreateStubDocumentInterfaceBrokerReceiver(),
-      CreateStubDocumentInterfaceBrokerReceiver(),
       CreateStubBrowserInterfaceBrokerReceiver(),
       blink::WebTreeScopeType::kDocument, "grandchild", "uniqueName3", false,
       base::UnguessableToken::Create(), blink::FramePolicy(),
@@ -519,8 +468,6 @@
   // Simulate attaching a series of frames to build the frame tree.
   main_test_rfh()->OnCreateChildFrame(
       14, CreateStubInterfaceProviderRequest(),
-      CreateStubDocumentInterfaceBrokerReceiver(),
-      CreateStubDocumentInterfaceBrokerReceiver(),
       CreateStubBrowserInterfaceBrokerReceiver(),
       blink::WebTreeScopeType::kDocument, std::string(), "uniqueName0", false,
       base::UnguessableToken::Create(), blink::FramePolicy(),
@@ -531,8 +478,6 @@
       activity.GetLog());
   main_test_rfh()->OnCreateChildFrame(
       18, CreateStubInterfaceProviderRequest(),
-      CreateStubDocumentInterfaceBrokerReceiver(),
-      CreateStubDocumentInterfaceBrokerReceiver(),
       CreateStubBrowserInterfaceBrokerReceiver(),
       blink::WebTreeScopeType::kDocument, std::string(), "uniqueName1", false,
       base::UnguessableToken::Create(), blink::FramePolicy(),
@@ -557,8 +502,6 @@
   constexpr auto kOwnerType = blink::FrameOwnerElementType::kIframe;
   main_test_rfh()->OnCreateChildFrame(
       22, CreateStubInterfaceProviderRequest(),
-      CreateStubDocumentInterfaceBrokerReceiver(),
-      CreateStubDocumentInterfaceBrokerReceiver(),
       CreateStubBrowserInterfaceBrokerReceiver(),
       blink::WebTreeScopeType::kDocument, std::string(), "uniqueName0", false,
       base::UnguessableToken::Create(), blink::FramePolicy(),
@@ -569,8 +512,6 @@
       activity.GetLog());
   main_test_rfh()->OnCreateChildFrame(
       23, CreateStubInterfaceProviderRequest(),
-      CreateStubDocumentInterfaceBrokerReceiver(),
-      CreateStubDocumentInterfaceBrokerReceiver(),
       CreateStubBrowserInterfaceBrokerReceiver(),
       blink::WebTreeScopeType::kDocument, std::string(), "uniqueName1", false,
       base::UnguessableToken::Create(), blink::FramePolicy(),
@@ -603,8 +544,6 @@
   // Simulate attaching a frame from mismatched process id.
   ASSERT_FALSE(frame_tree->AddFrame(
       root, process_id + 1, 1, CreateStubInterfaceProviderRequest(),
-      CreateStubDocumentInterfaceBrokerReceiver(),
-      CreateStubDocumentInterfaceBrokerReceiver(),
       CreateStubBrowserInterfaceBrokerReceiver(),
       blink::WebTreeScopeType::kDocument, std::string(), "uniqueName0", false,
       base::UnguessableToken::Create(), blink::FramePolicy(),
@@ -623,16 +562,12 @@
   constexpr auto kOwnerType = blink::FrameOwnerElementType::kIframe;
   main_test_rfh()->OnCreateChildFrame(
       22, CreateStubInterfaceProviderRequest(),
-      CreateStubDocumentInterfaceBrokerReceiver(),
-      CreateStubDocumentInterfaceBrokerReceiver(),
       CreateStubBrowserInterfaceBrokerReceiver(),
       blink::WebTreeScopeType::kDocument, std::string(), "uniqueName0", false,
       base::UnguessableToken::Create(), blink::FramePolicy(),
       FrameOwnerProperties(), kOwnerType);
   main_test_rfh()->OnCreateChildFrame(
       23, CreateStubInterfaceProviderRequest(),
-      CreateStubDocumentInterfaceBrokerReceiver(),
-      CreateStubDocumentInterfaceBrokerReceiver(),
       CreateStubBrowserInterfaceBrokerReceiver(),
       blink::WebTreeScopeType::kDocument, std::string(), "uniqueName1", false,
       base::UnguessableToken::Create(), blink::FramePolicy(),
@@ -642,8 +577,6 @@
   RenderFrameHostImpl* child1_rfh = root->child_at(0)->current_frame_host();
   child1_rfh->OnCreateChildFrame(
       33, CreateStubInterfaceProviderRequest(),
-      CreateStubDocumentInterfaceBrokerReceiver(),
-      CreateStubDocumentInterfaceBrokerReceiver(),
       CreateStubBrowserInterfaceBrokerReceiver(),
       blink::WebTreeScopeType::kDocument, std::string(), "uniqueName2", false,
       base::UnguessableToken::Create(), blink::FramePolicy(),
diff --git a/content/browser/frame_host/navigation_controller_impl_unittest.cc b/content/browser/frame_host/navigation_controller_impl_unittest.cc
index 5b34d44..88441c5 100644
--- a/content/browser/frame_host/navigation_controller_impl_unittest.cc
+++ b/content/browser/frame_host/navigation_controller_impl_unittest.cc
@@ -1971,8 +1971,6 @@
   main_test_rfh()->OnCreateChildFrame(
       process()->GetNextRoutingID(),
       TestRenderFrameHost::CreateStubInterfaceProviderRequest(),
-      TestRenderFrameHost::CreateStubDocumentInterfaceBrokerReceiver(),
-      TestRenderFrameHost::CreateStubDocumentInterfaceBrokerReceiver(),
       TestRenderFrameHost::CreateStubBrowserInterfaceBrokerReceiver(),
       blink::WebTreeScopeType::kDocument, std::string(), unique_name0, false,
       base::UnguessableToken::Create(), blink::FramePolicy(),
@@ -2011,8 +2009,6 @@
   main_test_rfh()->OnCreateChildFrame(
       process()->GetNextRoutingID(),
       TestRenderFrameHost::CreateStubInterfaceProviderRequest(),
-      TestRenderFrameHost::CreateStubDocumentInterfaceBrokerReceiver(),
-      TestRenderFrameHost::CreateStubDocumentInterfaceBrokerReceiver(),
       TestRenderFrameHost::CreateStubBrowserInterfaceBrokerReceiver(),
       blink::WebTreeScopeType::kDocument, std::string(), unique_name1, false,
       base::UnguessableToken::Create(), blink::FramePolicy(),
@@ -2051,8 +2047,6 @@
   subframe->OnCreateChildFrame(
       process()->GetNextRoutingID(),
       TestRenderFrameHost::CreateStubInterfaceProviderRequest(),
-      TestRenderFrameHost::CreateStubDocumentInterfaceBrokerReceiver(),
-      TestRenderFrameHost::CreateStubDocumentInterfaceBrokerReceiver(),
       TestRenderFrameHost::CreateStubBrowserInterfaceBrokerReceiver(),
       blink::WebTreeScopeType::kDocument, std::string(), unique_name2, false,
       base::UnguessableToken::Create(), blink::FramePolicy(),
@@ -2109,8 +2103,6 @@
   main_test_rfh()->OnCreateChildFrame(
       process()->GetNextRoutingID(),
       TestRenderFrameHost::CreateStubInterfaceProviderRequest(),
-      TestRenderFrameHost::CreateStubDocumentInterfaceBrokerReceiver(),
-      TestRenderFrameHost::CreateStubDocumentInterfaceBrokerReceiver(),
       TestRenderFrameHost::CreateStubBrowserInterfaceBrokerReceiver(),
       blink::WebTreeScopeType::kDocument, std::string(), unique_name, false,
       base::UnguessableToken::Create(), blink::FramePolicy(),
@@ -3345,8 +3337,6 @@
   main_test_rfh()->OnCreateChildFrame(
       process()->GetNextRoutingID(),
       TestRenderFrameHost::CreateStubInterfaceProviderRequest(),
-      TestRenderFrameHost::CreateStubDocumentInterfaceBrokerReceiver(),
-      TestRenderFrameHost::CreateStubDocumentInterfaceBrokerReceiver(),
       TestRenderFrameHost::CreateStubBrowserInterfaceBrokerReceiver(),
       blink::WebTreeScopeType::kDocument, std::string(), unique_name, false,
       base::UnguessableToken::Create(), blink::FramePolicy(),
@@ -3514,8 +3504,6 @@
   main_test_rfh()->OnCreateChildFrame(
       process()->GetNextRoutingID(),
       TestRenderFrameHost::CreateStubInterfaceProviderRequest(),
-      TestRenderFrameHost::CreateStubDocumentInterfaceBrokerReceiver(),
-      TestRenderFrameHost::CreateStubDocumentInterfaceBrokerReceiver(),
       TestRenderFrameHost::CreateStubBrowserInterfaceBrokerReceiver(),
       blink::WebTreeScopeType::kDocument, std::string(), unique_name, false,
       base::UnguessableToken::Create(), blink::FramePolicy(),
@@ -4776,8 +4764,6 @@
   main_test_rfh()->OnCreateChildFrame(
       process()->GetNextRoutingID(),
       TestRenderFrameHost::CreateStubInterfaceProviderRequest(),
-      TestRenderFrameHost::CreateStubDocumentInterfaceBrokerReceiver(),
-      TestRenderFrameHost::CreateStubDocumentInterfaceBrokerReceiver(),
       TestRenderFrameHost::CreateStubBrowserInterfaceBrokerReceiver(),
       blink::WebTreeScopeType::kDocument, std::string(), unique_name, false,
       base::UnguessableToken::Create(), blink::FramePolicy(),
diff --git a/content/browser/frame_host/navigation_request.cc b/content/browser/frame_host/navigation_request.cc
index 2683dd8..8546749 100644
--- a/content/browser/frame_host/navigation_request.cc
+++ b/content/browser/frame_host/navigation_request.cc
@@ -1013,7 +1013,7 @@
           render_view_host->GetWebkitPreferences().javascript_enabled;
       AddNavigationRequestClientHintsHeaders(
           common_params_->url, &client_hints_headers, browser_context,
-          javascript_enabled, client_hints_delegate);
+          javascript_enabled, client_hints_delegate, frame_tree_node_);
       headers.MergeFrom(client_hints_headers);
     }
 
@@ -2235,7 +2235,7 @@
         render_view_host->GetWebkitPreferences().javascript_enabled;
     AddNavigationRequestClientHintsHeaders(
         common_params_->url, &client_hints_extra_headers, browser_context,
-        javascript_enabled, client_hints_delegate);
+        javascript_enabled, client_hints_delegate, frame_tree_node_);
     modified_headers.MergeFrom(client_hints_extra_headers);
   }
 
diff --git a/content/browser/frame_host/render_frame_host_delegate.cc b/content/browser/frame_host/render_frame_host_delegate.cc
index 7820476..80b13b70 100644
--- a/content/browser/frame_host/render_frame_host_delegate.cc
+++ b/content/browser/frame_host/render_frame_host_delegate.cc
@@ -170,6 +170,11 @@
   return nullptr;
 }
 
+media::MediaMetricsProvider::RecordAggregateWatchTimeCallback
+RenderFrameHostDelegate::GetRecordAggregateWatchTimeCallback() {
+  return base::NullCallback();
+}
+
 bool RenderFrameHostDelegate::IsFrameLowPriority(
     const RenderFrameHost* render_frame_host) {
   return false;
diff --git a/content/browser/frame_host/render_frame_host_delegate.h b/content/browser/frame_host/render_frame_host_delegate.h
index 36af172b..2af1308 100644
--- a/content/browser/frame_host/render_frame_host_delegate.h
+++ b/content/browser/frame_host/render_frame_host_delegate.h
@@ -23,6 +23,7 @@
 #include "content/public/common/javascript_dialog_type.h"
 #include "content/public/common/resource_load_info.mojom.h"
 #include "content/public/common/resource_type.h"
+#include "media/mojo/services/media_metrics_provider.h"
 #include "mojo/public/cpp/bindings/pending_receiver.h"
 #include "mojo/public/cpp/bindings/scoped_interface_endpoint_handle.h"
 #include "net/cert/cert_status_flags.h"
@@ -486,6 +487,9 @@
                                          const GURL& url,
                                          bool user_gesture) {}
 
+  virtual media::MediaMetricsProvider::RecordAggregateWatchTimeCallback
+  GetRecordAggregateWatchTimeCallback();
+
  protected:
   virtual ~RenderFrameHostDelegate() {}
 };
diff --git a/content/browser/frame_host/render_frame_host_impl.cc b/content/browser/frame_host/render_frame_host_impl.cc
index 321b7a23..311644f4 100644
--- a/content/browser/frame_host/render_frame_host_impl.cc
+++ b/content/browser/frame_host/render_frame_host_impl.cc
@@ -141,6 +141,7 @@
 #include "content/public/browser/download_manager.h"
 #include "content/public/browser/file_select_listener.h"
 #include "content/public/browser/global_routing_id.h"
+#include "content/public/browser/media_player_watch_time.h"
 #include "content/public/browser/network_service_instance.h"
 #include "content/public/browser/permission_type.h"
 #include "content/public/browser/render_process_host.h"
@@ -172,7 +173,6 @@
 #include "media/media_buildflags.h"
 #include "media/mojo/mojom/remoting.mojom.h"
 #include "media/mojo/services/media_interface_provider.h"
-#include "media/mojo/services/media_metrics_provider.h"
 #include "media/mojo/services/video_decode_perf_history.h"
 #include "mojo/public/cpp/bindings/interface_request.h"
 #include "mojo/public/cpp/bindings/message.h"
@@ -202,7 +202,6 @@
 #include "third_party/blink/public/mojom/appcache/appcache.mojom.h"
 #include "third_party/blink/public/mojom/bluetooth/web_bluetooth.mojom.h"
 #include "third_party/blink/public/mojom/choosers/file_chooser.mojom.h"
-#include "third_party/blink/public/mojom/frame/frame_host_test_interface.mojom.h"
 #include "third_party/blink/public/mojom/frame/fullscreen.mojom.h"
 #include "third_party/blink/public/mojom/loader/pause_subresource_loading_handle.mojom.h"
 #include "third_party/blink/public/mojom/loader/url_loader_factory_bundle.mojom.h"
@@ -1816,8 +1815,6 @@
   SetRenderFrameCreated(false);
   InvalidateMojoConnection();
   document_scoped_interface_provider_binding_.Close();
-  document_interface_broker_content_receiver_.reset();
-  document_interface_broker_blink_receiver_.reset();
   broker_receiver_.reset();
   SetLastCommittedUrl(GURL());
   bundled_exchanges_handle_.reset();
@@ -1992,14 +1989,6 @@
   service_manager::mojom::InterfaceProviderPtr interface_provider;
   BindInterfaceProviderRequest(mojo::MakeRequest(&interface_provider));
 
-  mojo::PendingRemote<blink::mojom::DocumentInterfaceBroker>
-      document_interface_broker_content;
-  mojo::PendingRemote<blink::mojom::DocumentInterfaceBroker>
-      document_interface_broker_blink;
-  BindDocumentInterfaceBrokerReceiver(
-      document_interface_broker_content.InitWithNewPipeAndPassReceiver(),
-      document_interface_broker_blink.InitWithNewPipeAndPassReceiver());
-
   mojo::PendingRemote<blink::mojom::BrowserInterfaceBroker>
       browser_interface_broker;
   BindBrowserInterfaceBrokerReceiver(
@@ -2008,8 +1997,6 @@
   mojom::CreateFrameParamsPtr params = mojom::CreateFrameParams::New();
   params->interface_bundle = mojom::DocumentScopedInterfaceBundle::New(
       interface_provider.PassInterface(),
-      std::move(document_interface_broker_content),
-      std::move(document_interface_broker_blink),
       std::move(browser_interface_broker));
 
   params->routing_id = routing_id_;
@@ -2211,10 +2198,6 @@
     int new_routing_id,
     service_manager::mojom::InterfaceProviderRequest
         new_interface_provider_provider_request,
-    mojo::PendingReceiver<blink::mojom::DocumentInterfaceBroker>
-        document_interface_broker_content_receiver,
-    mojo::PendingReceiver<blink::mojom::DocumentInterfaceBroker>
-        document_interface_broker_blink_receiver,
     mojo::PendingReceiver<blink::mojom::BrowserInterfaceBroker>
         browser_interface_broker_receiver,
     blink::WebTreeScopeType scope,
@@ -2228,8 +2211,6 @@
   // TODO(lukasza): Call ReceivedBadMessage when |frame_unique_name| is empty.
   DCHECK(!frame_unique_name.empty());
   DCHECK(new_interface_provider_provider_request.is_pending());
-  DCHECK(document_interface_broker_content_receiver.is_valid());
-  DCHECK(document_interface_broker_blink_receiver.is_valid());
   DCHECK(browser_interface_broker_receiver.is_valid());
   if (owner_type == blink::FrameOwnerElementType::kNone) {
     // Any child frame must have a HTMLFrameOwnerElement in its parent document
@@ -2247,15 +2228,11 @@
     return;
 
   // |new_routing_id|, |new_interface_provider_provider_request|,
-  // |document_interface_broker_content_receiver|,
-  // |document_interface_broker_blink_receiver|,
   // |browser_interface_broker_receiver| and |devtools_frame_token| were
   // generated on the browser's IO thread and not taken from the renderer
   // process.
   frame_tree_->AddFrame(frame_tree_node_, GetProcess()->GetID(), new_routing_id,
                         std::move(new_interface_provider_provider_request),
-                        std::move(document_interface_broker_content_receiver),
-                        std::move(document_interface_broker_blink_receiver),
                         std::move(browser_interface_broker_receiver), scope,
                         frame_name, frame_unique_name, is_created_by_script,
                         devtools_frame_token, frame_policy,
@@ -4236,23 +4213,6 @@
       std::make_unique<ActiveURLMessageFilter>(this));
 }
 
-void RenderFrameHostImpl::BindDocumentInterfaceBrokerReceiver(
-    mojo::PendingReceiver<blink::mojom::DocumentInterfaceBroker>
-        content_receiver,
-    mojo::PendingReceiver<blink::mojom::DocumentInterfaceBroker>
-        blink_receiver) {
-  DCHECK(!document_interface_broker_content_receiver_.is_bound());
-  DCHECK(content_receiver.is_valid());
-  document_interface_broker_content_receiver_.Bind(std::move(content_receiver));
-  document_interface_broker_content_receiver_.SetFilter(
-      std::make_unique<ActiveURLMessageFilter>(this));
-  DCHECK(!document_interface_broker_blink_receiver_.is_bound());
-  DCHECK(blink_receiver.is_valid());
-  document_interface_broker_blink_receiver_.Bind(std::move(blink_receiver));
-  document_interface_broker_blink_receiver_.SetFilter(
-      std::make_unique<ActiveURLMessageFilter>(this));
-}
-
 void RenderFrameHostImpl::BindBrowserInterfaceBrokerReceiver(
     mojo::PendingReceiver<blink::mojom::BrowserInterfaceBroker> receiver) {
   DCHECK(receiver.is_valid());
@@ -4430,15 +4390,6 @@
   main_frame->BindInterfaceProviderRequest(
       mojo::MakeRequest(&main_frame_interface_provider_info));
 
-  mojo::PendingRemote<blink::mojom::DocumentInterfaceBroker>
-      document_interface_broker_content;
-
-  mojo::PendingRemote<blink::mojom::DocumentInterfaceBroker>
-      document_interface_broker_blink;
-  main_frame->BindDocumentInterfaceBrokerReceiver(
-      document_interface_broker_content.InitWithNewPipeAndPassReceiver(),
-      document_interface_broker_blink.InitWithNewPipeAndPassReceiver());
-
   mojo::PendingRemote<blink::mojom::BrowserInterfaceBroker>
       browser_interface_broker;
   main_frame->BindBrowserInterfaceBrokerReceiver(
@@ -4459,8 +4410,6 @@
       main_frame->GetLocalRenderWidgetHost()->GetRoutingID(), visual_properties,
       mojom::DocumentScopedInterfaceBundle::New(
           std::move(main_frame_interface_provider_info),
-          std::move(document_interface_broker_content),
-          std::move(document_interface_broker_blink),
           std::move(browser_interface_broker)),
       cloned_namespace->id(), main_frame->GetDevToolsFrameToken());
 
@@ -4711,7 +4660,10 @@
                 ->GetBrowserContext()
                 ->GetLearningSession();
           },
-          weak_ptr_factory_.GetWeakPtr())));
+          weak_ptr_factory_.GetWeakPtr()),
+      base::BindRepeating(
+          &RenderFrameHostImpl::GetRecordAggregateWatchTimeCallback,
+          base::Unretained(this))));
 
   if (command_line->HasSwitch(cc::switches::kEnableGpuBenchmarking)) {
     registry_->AddInterface(base::BindRepeating(
@@ -4749,6 +4701,11 @@
       GetProcess()->GetID(), routing_id_, GetProcess()->GetStoragePartition()));
 }
 
+media::MediaMetricsProvider::RecordAggregateWatchTimeCallback
+RenderFrameHostImpl::GetRecordAggregateWatchTimeCallback() {
+  return delegate_->GetRecordAggregateWatchTimeCallback();
+}
+
 void RenderFrameHostImpl::ResetWaitingState() {
   DCHECK(is_active());
 
@@ -6649,22 +6606,6 @@
   }
 }
 
-// This is a test-only interface, not exposed in production.
-void RenderFrameHostImpl::GetFrameHostTestInterface(
-    mojo::PendingReceiver<blink::mojom::FrameHostTestInterface> receiver) {
-  class FrameHostTestInterfaceImpl
-      : public blink::mojom::FrameHostTestInterface {
-   public:
-    void Ping(const GURL& url, const std::string& event) override {}
-    void GetName(GetNameCallback callback) override {
-      std::move(callback).Run("RenderFrameHostImpl");
-    }
-  };
-
-  mojo::MakeSelfOwnedReceiver(std::make_unique<FrameHostTestInterfaceImpl>(),
-                              std::move(receiver));
-}
-
 void RenderFrameHostImpl::CreateAppCacheBackend(
     mojo::PendingReceiver<blink::mojom::AppCacheBackend> receiver) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
@@ -7487,13 +7428,7 @@
             std::move(interface_provider_request_of_previous_document));
     BindInterfaceProviderRequest(
         std::move(interface_params->interface_provider_request));
-
-    document_interface_broker_content_receiver_.reset();
-    document_interface_broker_blink_receiver_.reset();
     broker_receiver_.reset();
-    BindDocumentInterfaceBrokerReceiver(
-        std::move(interface_params->document_interface_broker_content_receiver),
-        std::move(interface_params->document_interface_broker_blink_receiver));
     BindBrowserInterfaceBrokerReceiver(
         std::move(interface_params->browser_interface_broker_receiver));
   } else {
@@ -7506,8 +7441,6 @@
     // possibly from a different security origin, will no longer be dispatched.
     if (frame_tree_node_->has_committed_real_load()) {
       document_scoped_interface_provider_binding_.Close();
-      document_interface_broker_content_receiver_.reset();
-      document_interface_broker_blink_receiver_.reset();
       broker_receiver_.reset();
       bad_message::ReceivedBadMessage(
           process, bad_message::RFH_INTERFACE_PROVIDER_MISSING);
diff --git a/content/browser/frame_host/render_frame_host_impl.h b/content/browser/frame_host/render_frame_host_impl.h
index ad8b66f..6665ae1f 100644
--- a/content/browser/frame_host/render_frame_host_impl.h
+++ b/content/browser/frame_host/render_frame_host_impl.h
@@ -59,6 +59,7 @@
 #include "content/public/common/previews_state.h"
 #include "content/public/common/transferrable_url_loader.mojom.h"
 #include "media/mojo/mojom/interface_factory.mojom.h"
+#include "media/mojo/services/media_metrics_provider.h"
 #include "mojo/public/cpp/bindings/associated_receiver.h"
 #include "mojo/public/cpp/bindings/associated_remote.h"
 #include "mojo/public/cpp/bindings/pending_associated_remote.h"
@@ -85,7 +86,6 @@
 #include "third_party/blink/public/mojom/commit_result/commit_result.mojom.h"
 #include "third_party/blink/public/mojom/contacts/contacts_manager.mojom.h"
 #include "third_party/blink/public/mojom/devtools/devtools_agent.mojom.h"
-#include "third_party/blink/public/mojom/frame/document_interface_broker.mojom.h"
 #include "third_party/blink/public/mojom/frame/find_in_page.mojom.h"
 #include "third_party/blink/public/mojom/frame/frame.mojom.h"
 #include "third_party/blink/public/mojom/frame/navigation_initiator.mojom.h"
@@ -204,7 +204,6 @@
       public RenderProcessHostObserver,
       public SiteInstanceImpl::Observer,
       public service_manager::mojom::InterfaceProvider,
-      public blink::mojom::DocumentInterfaceBroker,
       public blink::mojom::LocalFrameHost,
       public CSPContext,
       public ui::AXActionHandler {
@@ -417,12 +416,6 @@
   // interface that the RenderFrameHost corresponding to the child frame should
   // bind to expose services to the renderer process. The caller takes care of
   // sending down the client end of the pipe to the child RenderFrame to use.
-  // |document_interface_broker_content_handle| and
-  // |document_interface_broker_blink_handle| are the pipe handles bound by
-  // to request ends of DocumentInterfaceProviderInterface in content and blink
-  // parts of the child frame. RenderFrameHost should bind these handles to
-  // expose services to the renderer process. The caller takes care of sending
-  // down the client end of the pipe to the child RenderFrame to use.
   // |browser_interface_broker_receiver| is the receiver end of
   // BrowserInterfaceBroker interface in the child frame. RenderFrameHost should
   // bind this receiver to expose services to the renderer process. The caller
@@ -432,10 +425,6 @@
       int new_routing_id,
       service_manager::mojom::InterfaceProviderRequest
           interface_provider_request,
-      mojo::PendingReceiver<blink::mojom::DocumentInterfaceBroker>
-          document_interface_broker_content_receiver,
-      mojo::PendingReceiver<blink::mojom::DocumentInterfaceBroker>
-          document_interface_broker_blink_receiver,
       mojo::PendingReceiver<blink::mojom::BrowserInterfaceBroker>
           browser_interface_broker_receiver,
       blink::WebTreeScopeType scope,
@@ -549,6 +538,9 @@
 
   GlobalFrameRoutingId GetGlobalFrameRoutingId();
 
+  media::MediaMetricsProvider::RecordAggregateWatchTimeCallback
+  GetRecordAggregateWatchTimeCallback();
+
   // The unique ID of the latest NavigationEntry that this RenderFrameHost is
   // showing. This may change even when this frame hasn't committed a page,
   // such as for a new subframe navigation in a different frame.
@@ -886,16 +878,6 @@
       service_manager::mojom::InterfaceProviderRequest
           interface_provider_request);
 
-  // Binds content and blink receiver ends of the DocumentInterfaceProvider
-  // interface through which services provided by this RenderFrameHost are
-  // exposed to the corresponding RenderFrame. The caller is responsible for
-  // plumbing the client ends to the the renderer process.
-  void BindDocumentInterfaceBrokerReceiver(
-      mojo::PendingReceiver<blink::mojom::DocumentInterfaceBroker>
-          content_receiver,
-      mojo::PendingReceiver<blink::mojom::DocumentInterfaceBroker>
-          blink_receiver);
-
   // Binds the receiver end of the BrowserInterfaceBroker interface through
   // which services provided by this RenderFrameHost are exposed to the
   // corresponding RenderFrame. The caller is responsible for plumbing the
@@ -1671,11 +1653,6 @@
   void GetInterface(const std::string& interface_name,
                     mojo::ScopedMessagePipeHandle interface_pipe) override;
 
-  // blink::mojom::DocumentInterfaceBroker:
-  void GetFrameHostTestInterface(
-      mojo::PendingReceiver<blink::mojom::FrameHostTestInterface> receiver)
-      override;
-
   // Allows tests to disable the swapout event timer to simulate bugs that
   // happen before it fires (to avoid flakiness).
   void DisableSwapOutTimerForTesting();
@@ -2357,15 +2334,6 @@
   mojo::Binding<service_manager::mojom::InterfaceProvider>
       document_scoped_interface_provider_binding_;
 
-  // Receivers for the DocumentInterfaceBroker through which this
-  // RenderFrameHostImpl exposes document-scoped Mojo services to the currently
-  // active document in the corresponding RenderFrame. Because of the type
-  // difference between content and blink, two separate pipes are used.
-  mojo::Receiver<blink::mojom::DocumentInterfaceBroker>
-      document_interface_broker_content_receiver_{this};
-  mojo::Receiver<blink::mojom::DocumentInterfaceBroker>
-      document_interface_broker_blink_receiver_{this};
-
   // BrowserInterfaceBroker implementation through which this
   // RenderFrameHostImpl exposes document-scoped Mojo services to the currently
   // active document in the corresponding RenderFrame.
diff --git a/content/browser/frame_host/render_frame_host_manager_unittest.cc b/content/browser/frame_host/render_frame_host_manager_unittest.cc
index 951d859..7e46c7e 100644
--- a/content/browser/frame_host/render_frame_host_manager_unittest.cc
+++ b/content/browser/frame_host/render_frame_host_manager_unittest.cc
@@ -1797,8 +1797,6 @@
   contents()->GetMainFrame()->OnCreateChildFrame(
       contents()->GetMainFrame()->GetProcess()->GetNextRoutingID(),
       TestRenderFrameHost::CreateStubInterfaceProviderRequest(),
-      TestRenderFrameHost::CreateStubDocumentInterfaceBrokerReceiver(),
-      TestRenderFrameHost::CreateStubDocumentInterfaceBrokerReceiver(),
       TestRenderFrameHost::CreateStubBrowserInterfaceBrokerReceiver(),
       blink::WebTreeScopeType::kDocument, "frame_name", "uniqueName1", false,
       base::UnguessableToken::Create(), blink::FramePolicy(),
@@ -1806,8 +1804,6 @@
   contents()->GetMainFrame()->OnCreateChildFrame(
       contents()->GetMainFrame()->GetProcess()->GetNextRoutingID(),
       TestRenderFrameHost::CreateStubInterfaceProviderRequest(),
-      TestRenderFrameHost::CreateStubDocumentInterfaceBrokerReceiver(),
-      TestRenderFrameHost::CreateStubDocumentInterfaceBrokerReceiver(),
       TestRenderFrameHost::CreateStubBrowserInterfaceBrokerReceiver(),
       blink::WebTreeScopeType::kDocument, "frame_name", "uniqueName2", false,
       base::UnguessableToken::Create(), blink::FramePolicy(),
@@ -1950,8 +1946,6 @@
   contents1->GetMainFrame()->OnCreateChildFrame(
       contents1->GetMainFrame()->GetProcess()->GetNextRoutingID(),
       TestRenderFrameHost::CreateStubInterfaceProviderRequest(),
-      TestRenderFrameHost::CreateStubDocumentInterfaceBrokerReceiver(),
-      TestRenderFrameHost::CreateStubDocumentInterfaceBrokerReceiver(),
       TestRenderFrameHost::CreateStubBrowserInterfaceBrokerReceiver(),
       blink::WebTreeScopeType::kDocument, "frame_name", "uniqueName1", false,
       base::UnguessableToken::Create(), blink::FramePolicy(),
@@ -2003,8 +1997,6 @@
   main_rfh->OnCreateChildFrame(
       main_rfh->GetProcess()->GetNextRoutingID(),
       TestRenderFrameHost::CreateStubInterfaceProviderRequest(),
-      TestRenderFrameHost::CreateStubDocumentInterfaceBrokerReceiver(),
-      TestRenderFrameHost::CreateStubDocumentInterfaceBrokerReceiver(),
       TestRenderFrameHost::CreateStubBrowserInterfaceBrokerReceiver(),
       blink::WebTreeScopeType::kDocument, std::string(), "uniqueName1", false,
       base::UnguessableToken::Create(), blink::FramePolicy(),
@@ -2173,8 +2165,6 @@
   tree1->AddFrame(
       root1, process_id, 12,
       TestRenderFrameHost::CreateStubInterfaceProviderRequest(),
-      TestRenderFrameHost::CreateStubDocumentInterfaceBrokerReceiver(),
-      TestRenderFrameHost::CreateStubDocumentInterfaceBrokerReceiver(),
       TestRenderFrameHost::CreateStubBrowserInterfaceBrokerReceiver(),
       blink::WebTreeScopeType::kDocument, std::string(), "uniqueName0", false,
       base::UnguessableToken::Create(), blink::FramePolicy(),
@@ -2182,8 +2172,6 @@
   tree1->AddFrame(
       root1, process_id, 13,
       TestRenderFrameHost::CreateStubInterfaceProviderRequest(),
-      TestRenderFrameHost::CreateStubDocumentInterfaceBrokerReceiver(),
-      TestRenderFrameHost::CreateStubDocumentInterfaceBrokerReceiver(),
       TestRenderFrameHost::CreateStubBrowserInterfaceBrokerReceiver(),
       blink::WebTreeScopeType::kDocument, std::string(), "uniqueName1", false,
       base::UnguessableToken::Create(), blink::FramePolicy(),
@@ -2198,8 +2186,6 @@
   tree2->AddFrame(
       root2, process_id, 22,
       TestRenderFrameHost::CreateStubInterfaceProviderRequest(),
-      TestRenderFrameHost::CreateStubDocumentInterfaceBrokerReceiver(),
-      TestRenderFrameHost::CreateStubDocumentInterfaceBrokerReceiver(),
       TestRenderFrameHost::CreateStubBrowserInterfaceBrokerReceiver(),
       blink::WebTreeScopeType::kDocument, std::string(), "uniqueName2", false,
       base::UnguessableToken::Create(), blink::FramePolicy(),
@@ -2207,8 +2193,6 @@
   tree2->AddFrame(
       root2, process_id, 23,
       TestRenderFrameHost::CreateStubInterfaceProviderRequest(),
-      TestRenderFrameHost::CreateStubDocumentInterfaceBrokerReceiver(),
-      TestRenderFrameHost::CreateStubDocumentInterfaceBrokerReceiver(),
       TestRenderFrameHost::CreateStubBrowserInterfaceBrokerReceiver(),
       blink::WebTreeScopeType::kDocument, std::string(), "uniqueName3", false,
       base::UnguessableToken::Create(), blink::FramePolicy(),
@@ -2228,8 +2212,6 @@
   tree4->AddFrame(
       root4, process_id, 42,
       TestRenderFrameHost::CreateStubInterfaceProviderRequest(),
-      TestRenderFrameHost::CreateStubDocumentInterfaceBrokerReceiver(),
-      TestRenderFrameHost::CreateStubDocumentInterfaceBrokerReceiver(),
       TestRenderFrameHost::CreateStubBrowserInterfaceBrokerReceiver(),
       blink::WebTreeScopeType::kDocument, std::string(), "uniqueName4", false,
       base::UnguessableToken::Create(), blink::FramePolicy(),
@@ -2283,8 +2265,6 @@
   main_test_rfh()->OnCreateChildFrame(
       main_test_rfh()->GetProcess()->GetNextRoutingID(),
       TestRenderFrameHost::CreateStubInterfaceProviderRequest(),
-      TestRenderFrameHost::CreateStubDocumentInterfaceBrokerReceiver(),
-      TestRenderFrameHost::CreateStubDocumentInterfaceBrokerReceiver(),
       TestRenderFrameHost::CreateStubBrowserInterfaceBrokerReceiver(),
       blink::WebTreeScopeType::kDocument, "frame1", "uniqueName1", false,
       base::UnguessableToken::Create(), blink::FramePolicy(),
@@ -2292,8 +2272,6 @@
   main_test_rfh()->OnCreateChildFrame(
       main_test_rfh()->GetProcess()->GetNextRoutingID(),
       TestRenderFrameHost::CreateStubInterfaceProviderRequest(),
-      TestRenderFrameHost::CreateStubDocumentInterfaceBrokerReceiver(),
-      TestRenderFrameHost::CreateStubDocumentInterfaceBrokerReceiver(),
       TestRenderFrameHost::CreateStubBrowserInterfaceBrokerReceiver(),
       blink::WebTreeScopeType::kDocument, "frame2", "uniqueName2", false,
       base::UnguessableToken::Create(), blink::FramePolicy(),
@@ -2301,8 +2279,6 @@
   main_test_rfh()->OnCreateChildFrame(
       main_test_rfh()->GetProcess()->GetNextRoutingID(),
       TestRenderFrameHost::CreateStubInterfaceProviderRequest(),
-      TestRenderFrameHost::CreateStubDocumentInterfaceBrokerReceiver(),
-      TestRenderFrameHost::CreateStubDocumentInterfaceBrokerReceiver(),
       TestRenderFrameHost::CreateStubBrowserInterfaceBrokerReceiver(),
       blink::WebTreeScopeType::kDocument, "frame3", "uniqueName3", false,
       base::UnguessableToken::Create(), blink::FramePolicy(),
@@ -2401,8 +2377,6 @@
   main_test_rfh()->OnCreateChildFrame(
       main_test_rfh()->GetProcess()->GetNextRoutingID(),
       TestRenderFrameHost::CreateStubInterfaceProviderRequest(),
-      TestRenderFrameHost::CreateStubDocumentInterfaceBrokerReceiver(),
-      TestRenderFrameHost::CreateStubDocumentInterfaceBrokerReceiver(),
       TestRenderFrameHost::CreateStubBrowserInterfaceBrokerReceiver(),
       blink::WebTreeScopeType::kDocument, "frame1", "uniqueName1", false,
       base::UnguessableToken::Create(), blink::FramePolicy(),
@@ -2899,8 +2873,6 @@
   main_test_rfh()->OnCreateChildFrame(
       main_test_rfh()->GetProcess()->GetNextRoutingID(),
       TestRenderFrameHost::CreateStubInterfaceProviderRequest(),
-      TestRenderFrameHost::CreateStubDocumentInterfaceBrokerReceiver(),
-      TestRenderFrameHost::CreateStubDocumentInterfaceBrokerReceiver(),
       TestRenderFrameHost::CreateStubBrowserInterfaceBrokerReceiver(),
       blink::WebTreeScopeType::kDocument, "frame1", "uniqueName1", false,
       base::UnguessableToken::Create(), blink::FramePolicy(),
diff --git a/content/browser/frame_host/render_frame_message_filter.cc b/content/browser/frame_host/render_frame_message_filter.cc
index e4972dba..3ce08e3 100644
--- a/content/browser/frame_host/render_frame_message_filter.cc
+++ b/content/browser/frame_host/render_frame_message_filter.cc
@@ -81,8 +81,6 @@
     blink::FrameOwnerElementType owner_type,
     int new_routing_id,
     mojo::ScopedMessagePipeHandle interface_provider_request_handle,
-    mojo::ScopedMessagePipeHandle document_interface_broker_content_handle,
-    mojo::ScopedMessagePipeHandle document_interface_broker_blink_handle,
     mojo::ScopedMessagePipeHandle browser_interface_broker_handle) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
   RenderFrameHostImpl* render_frame_host =
@@ -94,10 +92,6 @@
         new_routing_id,
         service_manager::mojom::InterfaceProviderRequest(
             std::move(interface_provider_request_handle)),
-        mojo::PendingReceiver<blink::mojom::DocumentInterfaceBroker>(
-            std::move(document_interface_broker_content_handle)),
-        mojo::PendingReceiver<blink::mojom::DocumentInterfaceBroker>(
-            std::move(document_interface_broker_blink_handle)),
         mojo::PendingReceiver<blink::mojom::BrowserInterfaceBroker>(
             std::move(browser_interface_broker_handle)),
         scope, frame_name, frame_unique_name, is_created_by_script,
@@ -278,20 +272,6 @@
   params_reply->new_interface_provider =
       interface_provider.PassInterface().PassHandle().release();
 
-  mojo::PendingRemote<blink::mojom::DocumentInterfaceBroker>
-      document_interface_broker_content;
-  auto document_interface_broker_receiver_content =
-      document_interface_broker_content.InitWithNewPipeAndPassReceiver();
-  params_reply->document_interface_broker_content_handle =
-      document_interface_broker_content.PassPipe().release();
-
-  mojo::PendingRemote<blink::mojom::DocumentInterfaceBroker>
-      document_interface_broker_blink;
-  auto document_interface_broker_receiver_blink =
-      document_interface_broker_blink.InitWithNewPipeAndPassReceiver();
-  params_reply->document_interface_broker_blink_handle =
-      document_interface_broker_blink.PassPipe().release();
-
   mojo::PendingRemote<blink::mojom::BrowserInterfaceBroker>
       browser_interface_broker;
   auto browser_interface_broker_receiver =
@@ -310,8 +290,6 @@
           params.frame_policy, params.frame_owner_properties,
           params.frame_owner_element_type, params_reply->child_routing_id,
           interface_provider_request.PassMessagePipe(),
-          document_interface_broker_receiver_content.PassPipe(),
-          document_interface_broker_receiver_blink.PassPipe(),
           browser_interface_broker_receiver.PassPipe()));
 }
 
diff --git a/content/browser/frame_host/render_frame_proxy_host.cc b/content/browser/frame_host/render_frame_proxy_host.cc
index 1554efac..abd2343c 100644
--- a/content/browser/frame_host/render_frame_proxy_host.cc
+++ b/content/browser/frame_host/render_frame_proxy_host.cc
@@ -305,6 +305,11 @@
       touch_action);
 }
 
+void RenderFrameProxyHost::VisibilityChanged(
+    blink::mojom::FrameVisibility visibility) {
+  cross_process_frame_connector_->OnVisibilityChanged(visibility);
+}
+
 void RenderFrameProxyHost::UpdateOpener() {
   // Another frame in this proxy's SiteInstance may reach the new opener by
   // first reaching this proxy and then referencing its window.opener.  Ensure
diff --git a/content/browser/frame_host/render_frame_proxy_host.h b/content/browser/frame_host/render_frame_proxy_host.h
index 7c15e52..2ca6efc6 100644
--- a/content/browser/frame_host/render_frame_proxy_host.h
+++ b/content/browser/frame_host/render_frame_proxy_host.h
@@ -147,6 +147,7 @@
 
   // blink::mojom::RemoteFrameHost
   void SetInheritedEffectiveTouchAction(cc::TouchAction touch_action) override;
+  void VisibilityChanged(blink::mojom::FrameVisibility visibility) override;
 
  private:
   // IPC Message handlers.
diff --git a/content/browser/navigation_browsertest.cc b/content/browser/navigation_browsertest.cc
index 8d87f50..d822691d 100644
--- a/content/browser/navigation_browsertest.cc
+++ b/content/browser/navigation_browsertest.cc
@@ -100,16 +100,6 @@
     return intercepted_requests_;
   }
 
-  std::vector<mojo::PendingReceiver<blink::mojom::DocumentInterfaceBroker>>&
-  intercepted_broker_content_receivers() {
-    return intercepted_broker_content_receivers_;
-  }
-
-  std::vector<mojo::PendingReceiver<blink::mojom::DocumentInterfaceBroker>>&
-  intercepted_broker_blink_receivers() {
-    return intercepted_broker_blink_receivers_;
-  }
-
  protected:
   bool WillProcessDidCommitNavigation(
       RenderFrameHost* render_frame_host,
@@ -123,16 +113,6 @@
         *interface_params
             ? std::move((*interface_params)->interface_provider_request)
             : nullptr);
-    intercepted_broker_content_receivers_.push_back(
-        *interface_params
-            ? std::move((*interface_params)
-                            ->document_interface_broker_content_receiver)
-            : mojo::NullReceiver());
-    intercepted_broker_blink_receivers_.push_back(
-        *interface_params
-            ? std::move(
-                  (*interface_params)->document_interface_broker_blink_receiver)
-            : mojo::NullReceiver());
     if (loop_)
       loop_->Quit();
     // Do not send the message to the RenderFrameHostImpl.
@@ -146,10 +126,6 @@
       intercepted_messages_;
   std::vector<::service_manager::mojom::InterfaceProviderRequest>
       intercepted_requests_;
-  std::vector<mojo::PendingReceiver<blink::mojom::DocumentInterfaceBroker>>
-      intercepted_broker_content_receivers_;
-  std::vector<mojo::PendingReceiver<blink::mojom::DocumentInterfaceBroker>>
-      intercepted_broker_blink_receivers_;
   std::unique_ptr<base::RunLoop> loop_;
 };
 
diff --git a/content/browser/network_service_browsertest.cc b/content/browser/network_service_browsertest.cc
index 12d6c9c..dddc660 100644
--- a/content/browser/network_service_browsertest.cc
+++ b/content/browser/network_service_browsertest.cc
@@ -164,6 +164,11 @@
     std::unique_ptr<network::ResourceRequest> request =
         std::make_unique<network::ResourceRequest>();
     request->url = url;
+    url::Origin origin = url::Origin::Create(url);
+    request->trusted_params = network::ResourceRequest::TrustedParams();
+    request->trusted_params->network_isolation_key =
+        net::NetworkIsolationKey(origin, origin);
+
     SimpleURLLoaderTestHelper simple_loader_helper;
     std::unique_ptr<network::SimpleURLLoader> simple_loader =
         network::SimpleURLLoader::Create(std::move(request),
@@ -256,6 +261,7 @@
       network::mojom::URLLoaderFactoryParams::New();
   params->process_id = network::mojom::kBrowserProcessId;
   params->is_corb_enabled = false;
+  params->is_trusted = true;
   mojo::Remote<network::mojom::URLLoaderFactory> loader_factory;
   network_context->CreateURLLoaderFactory(
       loader_factory.BindNewPipeAndPassReceiver(), std::move(params));
diff --git a/content/browser/portal/portal.cc b/content/browser/portal/portal.cc
index bbc4261..edd87424 100644
--- a/content/browser/portal/portal.cc
+++ b/content/browser/portal/portal.cc
@@ -148,10 +148,6 @@
       owner_render_frame_host_->GetProcess()->GetID(),
       owner_render_frame_host_->GetProcess()->GetNextRoutingID(),
       std::move(interface_provider_request),
-      mojo::PendingRemote<blink::mojom::DocumentInterfaceBroker>()
-          .InitWithNewPipeAndPassReceiver(),
-      mojo::PendingRemote<blink::mojom::DocumentInterfaceBroker>()
-          .InitWithNewPipeAndPassReceiver(),
       mojo::PendingRemote<blink::mojom::BrowserInterfaceBroker>()
           .InitWithNewPipeAndPassReceiver(),
       blink::WebTreeScopeType::kDocument, "", "", true,
diff --git a/content/browser/renderer_host/accessibility_tree_linkage_win_browsertest.cc b/content/browser/renderer_host/accessibility_tree_linkage_win_browsertest.cc
index aec4470..06230f2 100644
--- a/content/browser/renderer_host/accessibility_tree_linkage_win_browsertest.cc
+++ b/content/browser/renderer_host/accessibility_tree_linkage_win_browsertest.cc
@@ -103,15 +103,8 @@
     EXPECT_EQ(accessibility_native_view_accessible,
               dummy_ax_platform_node_->GetNativeViewAccessible());
   } else {
-    if (GetParam().is_uia_enabled) {
-      EXPECT_EQ(accessibility_native_view_accessible,
-                ui::AXFragmentRootWin::GetForAcceleratedWidget(
-                    GetView()->AccessibilityGetAcceleratedWidget())
-                    ->GetNativeViewAccessible());
-    } else {
-      EXPECT_EQ(accessibility_native_view_accessible,
-                GetLegacyRenderWidgetHostHWND()->window_accessible());
-    }
+    EXPECT_EQ(accessibility_native_view_accessible,
+              GetLegacyRenderWidgetHostHWND()->window_accessible());
   }
 }
 
diff --git a/content/browser/renderer_host/legacy_render_widget_host_win.cc b/content/browser/renderer_host/legacy_render_widget_host_win.cc
index 14709196..6b4f5fd 100644
--- a/content/browser/renderer_host/legacy_render_widget_host_win.cc
+++ b/content/browser/renderer_host/legacy_render_widget_host_win.cc
@@ -14,6 +14,7 @@
 #include "content/browser/accessibility/browser_accessibility_manager_win.h"
 #include "content/browser/accessibility/browser_accessibility_state_impl.h"
 #include "content/browser/accessibility/browser_accessibility_win.h"
+#include "content/browser/accessibility/one_shot_accessibility_tree_search.h"
 #include "content/browser/renderer_host/direct_manipulation_helper_win.h"
 #include "content/browser/renderer_host/render_widget_host_impl.h"
 #include "content/browser/renderer_host/render_widget_host_view_aura.h"
@@ -151,8 +152,7 @@
     // element B, then ask element B for its fragment root, without having sent
     // WM_GETOBJECT to element B's window. So we create the fragment root now to
     // ensure it's ready if asked for.
-    ax_fragment_root_ =
-        std::make_unique<ui::AXFragmentRootWin>(hwnd(), this, true);
+    ax_fragment_root_ = std::make_unique<ui::AXFragmentRootWin>(hwnd(), this);
     hr = S_OK;
   }
 
@@ -550,7 +550,41 @@
   if (!manager || !manager->GetRoot())
     return nullptr;
 
-  return manager->GetRoot()->GetNativeViewAccessible();
+  BrowserAccessibility* root_node = manager->GetRoot();
+
+  // A datetime popup will have a second window with its own kRootWebArea.
+  // However, the BrowserAccessibilityManager is shared with the main window,
+  // and the popup window's kRootWebArea will be inserted as a sibling of the
+  // popup button. When this is called on a popup, we must return the popup
+  // window's kRootWebArea instead of the root document's kRootWebArea. This
+  // will ensure that we're not placing duplicate document roots in the
+  // accessibility tree.
+  if (host_->GetWidgetType() == WidgetType::kPopup) {
+    OneShotAccessibilityTreeSearch tree_search(root_node);
+    tree_search.SetStartNode(root_node);
+    tree_search.SetDirection(OneShotAccessibilityTreeSearch::FORWARDS);
+    tree_search.SetImmediateDescendantsOnly(false);
+    tree_search.SetCanWrapToLastElement(false);
+    tree_search.AddPredicate(AccessibilityPopupButtonPredicate);
+
+    size_t matches = tree_search.CountMatches();
+    for (size_t i = 0; i < matches; ++i) {
+      BrowserAccessibility* match = tree_search.GetMatchAtIndex(i);
+      DCHECK(match);
+
+      // The web root should be the next sibling of the popup node, however it
+      // is not created instantly, so sometimes the popup window exists before
+      // the popup's kRootWebArea has been added to the tree. In this case we
+      // will fall back to the main document's root.
+      BrowserAccessibility* popup_web_root = match->PlatformGetNextSibling();
+      if (popup_web_root &&
+          popup_web_root->GetRole() == ax::mojom::Role::kRootWebArea) {
+        return popup_web_root->GetNativeViewAccessible();
+      }
+    }
+  }
+
+  return root_node->GetNativeViewAccessible();
 }
 
 }  // namespace content
diff --git a/content/browser/renderer_host/render_process_host_impl.cc b/content/browser/renderer_host/render_process_host_impl.cc
index 9ad27a3..f49e51c6 100644
--- a/content/browser/renderer_host/render_process_host_impl.cc
+++ b/content/browser/renderer_host/render_process_host_impl.cc
@@ -1501,7 +1501,7 @@
 
   // This instance of PushMessagingManager is only used from clients
   // bound to service workers (i.e. PushProvider), since frame-bound
-  // clients will rely on DocumentInterfaceBroker instead. Therefore,
+  // clients will rely on BrowserInterfaceBroker instead. Therefore,
   // pass an invalid frame ID here.
   //
   // Constructing the manager must occur after RegisterHost(), since
diff --git a/content/browser/renderer_host/render_process_host_unittest.cc b/content/browser/renderer_host/render_process_host_unittest.cc
index 6e4d0c4..05b0d08 100644
--- a/content/browser/renderer_host/render_process_host_unittest.cc
+++ b/content/browser/renderer_host/render_process_host_unittest.cc
@@ -134,8 +134,6 @@
   main_test_rfh()->OnCreateChildFrame(
       process()->GetNextRoutingID(),
       TestRenderFrameHost::CreateStubInterfaceProviderRequest(),
-      TestRenderFrameHost::CreateStubDocumentInterfaceBrokerReceiver(),
-      TestRenderFrameHost::CreateStubDocumentInterfaceBrokerReceiver(),
       TestRenderFrameHost::CreateStubBrowserInterfaceBrokerReceiver(),
       blink::WebTreeScopeType::kDocument, std::string(), unique_name, false,
       base::UnguessableToken::Create(), blink::FramePolicy(),
diff --git a/content/browser/renderer_host/render_view_host_impl.cc b/content/browser/renderer_host/render_view_host_impl.cc
index e7528c8..b34aa688 100644
--- a/content/browser/renderer_host/render_view_host_impl.cc
+++ b/content/browser/renderer_host/render_view_host_impl.cc
@@ -349,11 +349,6 @@
         mojom::DocumentScopedInterfaceBundle::New();
     main_rfh->BindInterfaceProviderRequest(mojo::MakeRequest(
         &params->main_frame_interface_bundle->interface_provider));
-    main_rfh->BindDocumentInterfaceBrokerReceiver(
-        params->main_frame_interface_bundle->document_interface_broker_content
-            .InitWithNewPipeAndPassReceiver(),
-        params->main_frame_interface_bundle->document_interface_broker_blink
-            .InitWithNewPipeAndPassReceiver());
     main_rfh->BindBrowserInterfaceBrokerReceiver(
         params->main_frame_interface_bundle->browser_interface_broker
             .InitWithNewPipeAndPassReceiver());
diff --git a/content/browser/renderer_host/render_widget_host_view_aura.cc b/content/browser/renderer_host/render_widget_host_view_aura.cc
index 1e4ffbe..57f2c8f5 100644
--- a/content/browser/renderer_host/render_widget_host_view_aura.cc
+++ b/content/browser/renderer_host/render_widget_host_view_aura.cc
@@ -859,6 +859,13 @@
 
 gfx::NativeViewAccessible
 RenderWidgetHostViewAura::GetParentNativeViewAccessible() {
+  // If a popup_parent_host_view_ exists, that means we are in a popup (such as
+  // datetime) and our accessible parent window is popup_parent_host_view_
+  if (popup_parent_host_view_) {
+    DCHECK_EQ(widget_type_, WidgetType::kPopup);
+    return popup_parent_host_view_->GetParentNativeViewAccessible();
+  }
+
   if (window_->parent()) {
     return window_->parent()->GetProperty(
         aura::client::kParentNativeViewAccessibleKey);
@@ -1148,15 +1155,7 @@
 RenderWidgetHostViewAura::AccessibilityGetNativeViewAccessible() {
 #if defined(OS_WIN)
   if (legacy_render_widget_host_HWND_) {
-    if (switches::IsExperimentalAccessibilityPlatformUIAEnabled()) {
-      ui::AXFragmentRootWin* fragment_root =
-          ui::AXFragmentRootWin::GetForAcceleratedWidget(
-              legacy_render_widget_host_HWND_->hwnd());
-      if (fragment_root)
-        return fragment_root->GetNativeViewAccessible();
-    } else {
-      return legacy_render_widget_host_HWND_->window_accessible();
-    }
+    return legacy_render_widget_host_HWND_->window_accessible();
   }
 #endif
 
diff --git a/content/browser/security_exploit_browsertest.cc b/content/browser/security_exploit_browsertest.cc
index 8bc61d4..d303c1b5 100644
--- a/content/browser/security_exploit_browsertest.cc
+++ b/content/browser/security_exploit_browsertest.cc
@@ -749,7 +749,7 @@
 // rule, see: RenderFrameHostImplBrowserTest.
 // InterfaceProviderRequestIsOptionalForFirstCommit.
 // TODO(crbug.com/718652): when all clients are converted to use
-// DocumentInterfaceBroker, InterfaceProviderRequest-related code will be
+// BrowserInterfaceBroker, InterfaceProviderRequest-related code will be
 // removed.
 IN_PROC_BROWSER_TEST_F(SecurityExploitBrowserTest,
                        MissingInterfaceProviderOnNonSameDocumentCommit) {
@@ -883,10 +883,6 @@
           std::move(params),
           mojom::DidCommitProvisionalLoadInterfaceParams::New(
               mojo::MakeRequest(&isolated_interface_provider),
-              mojo::PendingRemote<blink::mojom::DocumentInterfaceBroker>()
-                  .InitWithNewPipeAndPassReceiver(),
-              mojo::PendingRemote<blink::mojom::DocumentInterfaceBroker>()
-                  .InitWithNewPipeAndPassReceiver(),
               mojo::PendingRemote<blink::mojom::BrowserInterfaceBroker>()
                   .InitWithNewPipeAndPassReceiver()));
 
diff --git a/content/browser/site_per_process_browsertest.cc b/content/browser/site_per_process_browsertest.cc
index 26f26d2..17147204 100644
--- a/content/browser/site_per_process_browsertest.cc
+++ b/content/browser/site_per_process_browsertest.cc
@@ -6200,10 +6200,6 @@
     params->routing_id = frame_routing_id;
     params->interface_bundle = mojom::DocumentScopedInterfaceBundle::New();
     mojo::MakeRequest(&params->interface_bundle->interface_provider);
-    ignore_result(params->interface_bundle->document_interface_broker_content
-                      .InitWithNewPipeAndPassReceiver());
-    ignore_result(params->interface_bundle->document_interface_broker_blink
-                      .InitWithNewPipeAndPassReceiver());
     ignore_result(params->interface_bundle->browser_interface_broker
                       .InitWithNewPipeAndPassReceiver());
     params->previous_routing_id = previous_routing_id;
@@ -6270,10 +6266,6 @@
     params->routing_id = frame_routing_id;
     params->interface_bundle = mojom::DocumentScopedInterfaceBundle::New();
     mojo::MakeRequest(&params->interface_bundle->interface_provider);
-    ignore_result(params->interface_bundle->document_interface_broker_content
-                      .InitWithNewPipeAndPassReceiver());
-    ignore_result(params->interface_bundle->document_interface_broker_blink
-                      .InitWithNewPipeAndPassReceiver());
     ignore_result(params->interface_bundle->browser_interface_broker
                       .InitWithNewPipeAndPassReceiver());
     params->previous_routing_id = IPC::mojom::kRoutingIdNone;
@@ -14435,10 +14427,6 @@
           std::move(params),
           mojom::DidCommitProvisionalLoadInterfaceParams::New(
               mojo::MakeRequest(&interface_provider),
-              mojo::PendingRemote<blink::mojom::DocumentInterfaceBroker>()
-                  .InitWithNewPipeAndPassReceiver(),
-              mojo::PendingRemote<blink::mojom::DocumentInterfaceBroker>()
-                  .InitWithNewPipeAndPassReceiver(),
               mojo::PendingRemote<blink::mojom::BrowserInterfaceBroker>()
                   .InitWithNewPipeAndPassReceiver()));
 
diff --git a/content/browser/ssl/ssl_client_auth_handler.cc b/content/browser/ssl/ssl_client_auth_handler.cc
index 3fd2d00..73428440 100644
--- a/content/browser/ssl/ssl_client_auth_handler.cc
+++ b/content/browser/ssl/ssl_client_auth_handler.cc
@@ -21,20 +21,16 @@
 
 namespace content {
 
-namespace {
-
-class ClientCertificateDelegateImpl : public ClientCertificateDelegate {
+class SSLClientAuthHandler::ClientCertificateDelegateImpl
+    : public ClientCertificateDelegate {
  public:
   explicit ClientCertificateDelegateImpl(
-      const base::WeakPtr<SSLClientAuthHandler>& handler)
-      : handler_(handler), continue_called_(false) {}
+      base::WeakPtr<SSLClientAuthHandler> handler)
+      : handler_(std::move(handler)) {}
 
   ~ClientCertificateDelegateImpl() override {
-    if (!continue_called_) {
-      base::PostTask(
-          FROM_HERE, {BrowserThread::IO},
-          base::BindOnce(&SSLClientAuthHandler::CancelCertificateSelection,
-                         handler_));
+    if (!continue_called_ && handler_) {
+      handler_->delegate_->CancelCertificateSelection();
     }
   }
 
@@ -43,67 +39,24 @@
                                scoped_refptr<net::SSLPrivateKey> key) override {
     DCHECK(!continue_called_);
     continue_called_ = true;
-    base::PostTask(
-        FROM_HERE, {BrowserThread::IO},
-        base::BindOnce(&SSLClientAuthHandler::ContinueWithCertificate, handler_,
-                       std::move(cert), std::move(key)));
+    if (handler_) {
+      handler_->delegate_->ContinueWithCertificate(std::move(cert),
+                                                   std::move(key));
+    }
   }
 
  private:
   base::WeakPtr<SSLClientAuthHandler> handler_;
-  bool continue_called_;
+  bool continue_called_ = false;
 
   DISALLOW_COPY_AND_ASSIGN(ClientCertificateDelegateImpl);
 };
 
-// This function is used to pass the UI cancellation callback from the UI thread
-// to the SSLClientAuthHandler |handler| on the IO thread. If |handler| has
-// already expired, the UI elements are considered orphaned, so we post the
-// cancellation callback to the UI thread immediately.
-void TrySetCancellationCallback(
-    const base::WeakPtr<SSLClientAuthHandler>& handler,
-    base::OnceClosure callback) {
-  DCHECK_CURRENTLY_ON(BrowserThread::IO);
-  if (handler) {
-    handler->SetCancellationCallback(std::move(callback));
-  } else if (callback) {
-    base::PostTask(FROM_HERE, {BrowserThread::UI}, std::move(callback));
-  }
-}
-
-void SelectCertificateOnUIThread(
-    const WebContents::Getter& wc_getter,
-    net::SSLCertRequestInfo* cert_request_info,
-    net::ClientCertIdentityList client_certs,
-    const base::WeakPtr<SSLClientAuthHandler>& handler) {
-  DCHECK_CURRENTLY_ON(BrowserThread::UI);
-
-  std::unique_ptr<ClientCertificateDelegate> delegate(
-      new ClientCertificateDelegateImpl(handler));
-
-  WebContents* web_contents = wc_getter.Run();
-  if (!web_contents)
-    return;
-
-  base::OnceClosure cancellation_callback =
-      GetContentClient()->browser()->SelectClientCertificate(
-          web_contents, cert_request_info, std::move(client_certs),
-          std::move(delegate));
-
-  // Attempt to pass the callback to |handler|. If |handler| has already been
-  // destroyed, TrySetCancellationCallback will just invoke the callback. In
-  // contrast, simply posting SetCancellationCallback to the IO thread would
-  // result in |cancellation_callback| never being called if |handler| had
-  // already been destroyed when the task ran.
-  base::PostTask(FROM_HERE, {BrowserThread::IO},
-                 base::BindOnce(&TrySetCancellationCallback, handler,
-                                std::move(cancellation_callback)));
-}
-
-}  // namespace
-
 // A reference-counted core to allow the ClientCertStore and SSLCertRequestInfo
 // to outlive SSLClientAuthHandler if needbe.
+//
+// TODO(davidben): Fix ClientCertStore's lifetime contract. See
+// https://crbug.com/1011579.
 class SSLClientAuthHandler::Core : public base::RefCountedThreadSafe<Core> {
  public:
   Core(const base::WeakPtr<SSLClientAuthHandler>& handler,
@@ -136,8 +89,11 @@
 
   // Called when |client_cert_store_| is done retrieving the cert list.
   void DidGetClientCerts(net::ClientCertIdentityList client_certs) {
-    if (handler_)
-      handler_->DidGetClientCerts(std::move(client_certs));
+    // Run this on a PostTask to avoid reentrancy problems.
+    base::PostTask(
+        FROM_HERE, {BrowserThread::UI},
+        base::BindOnce(&SSLClientAuthHandler::DidGetClientCerts,
+                       std::move(handler_), std::move(client_certs)));
   }
 
   base::WeakPtr<SSLClientAuthHandler> handler_;
@@ -150,10 +106,10 @@
     WebContents::Getter web_contents_getter,
     net::SSLCertRequestInfo* cert_request_info,
     Delegate* delegate)
-    : web_contents_getter_(web_contents_getter),
+    : web_contents_getter_(std::move(web_contents_getter)),
       cert_request_info_(cert_request_info),
       delegate_(delegate) {
-  DCHECK_CURRENTLY_ON(BrowserThread::IO);
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
   core_ = new Core(weak_factory_.GetWeakPtr(), std::move(client_cert_store),
                    cert_request_info_.get());
@@ -161,67 +117,49 @@
 
 SSLClientAuthHandler::~SSLClientAuthHandler() {
   if (cancellation_callback_) {
-    base::PostTask(FROM_HERE, {BrowserThread::UI},
-                   std::move(cancellation_callback_));
+    std::move(cancellation_callback_).Run();
   }
 }
 
 void SSLClientAuthHandler::SelectCertificate() {
-  DCHECK_CURRENTLY_ON(BrowserThread::IO);
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
   // |core_| will call DidGetClientCerts when done.
   core_->GetClientCerts();
 }
 
-void SSLClientAuthHandler::SetCancellationCallback(base::OnceClosure callback) {
-  DCHECK_CURRENTLY_ON(BrowserThread::IO);
-  cancellation_callback_ = std::move(callback);
-}
-
-// static
-void SSLClientAuthHandler::ContinueWithCertificate(
-    const base::WeakPtr<SSLClientAuthHandler>& handler,
-    scoped_refptr<net::X509Certificate> cert,
-    scoped_refptr<net::SSLPrivateKey> key) {
-  if (handler)
-    handler->delegate_->ContinueWithCertificate(std::move(cert),
-                                                std::move(key));
-}
-
-// static
-void SSLClientAuthHandler::CancelCertificateSelection(
-    const base::WeakPtr<SSLClientAuthHandler>& handler) {
-  if (handler)
-    handler->delegate_->CancelCertificateSelection();
-}
-
 void SSLClientAuthHandler::DidGetClientCerts(
     net::ClientCertIdentityList client_certs) {
-  DCHECK_CURRENTLY_ON(BrowserThread::IO);
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+
+  WebContents* web_contents = web_contents_getter_.Run();
+  if (!web_contents) {
+    delegate_->CancelCertificateSelection();
+    return;
+  }
 
   // Note that if |client_cert_store_| is NULL, we intentionally fall through to
-  // SelectCertificateOnUIThread. This is for platforms where the client cert
+  // SelectClientCertificate(). This is for platforms where the client cert
   // matching is not performed by Chrome. Those platforms handle the cert
   // matching before showing the dialog.
   if (core_->has_client_cert_store() && client_certs.empty()) {
     // No need to query the user if there are no certs to choose from.
-    //
-    // TODO(davidben): The WebContents-less check on the UI thread should come
-    // before checking ClientCertStore; ClientCertStore itself should probably
-    // be handled by the embedder (https://crbug.com/394131), especially since
-    // this doesn't work on Android (https://crbug.com/345641).
-    base::PostTask(
-        FROM_HERE, {BrowserThread::IO},
-        base::BindOnce(&SSLClientAuthHandler::ContinueWithCertificate,
-                       weak_factory_.GetWeakPtr(), nullptr, nullptr));
+    delegate_->ContinueWithCertificate(nullptr, nullptr);
     return;
   }
 
-  base::PostTask(
-      FROM_HERE, {BrowserThread::UI},
-      base::BindOnce(&SelectCertificateOnUIThread, web_contents_getter_,
-                     base::RetainedRef(cert_request_info_),
-                     std::move(client_certs), weak_factory_.GetWeakPtr()));
+  // SelectClientCertificate() may call back into |delegate_| synchronously and
+  // destroy this object, so guard the cancellation callback logic by a WeakPtr.
+  base::WeakPtr<SSLClientAuthHandler> weak_self = weak_factory_.GetWeakPtr();
+  base::OnceClosure cancellation_callback =
+      GetContentClient()->browser()->SelectClientCertificate(
+          web_contents, cert_request_info_.get(), std::move(client_certs),
+          std::make_unique<ClientCertificateDelegateImpl>(weak_self));
+  if (weak_self) {
+    cancellation_callback_ = std::move(cancellation_callback);
+  } else if (!cancellation_callback.is_null()) {
+    std::move(cancellation_callback).Run();
+  }
 }
 
 }  // namespace content
diff --git a/content/browser/ssl/ssl_client_auth_handler.h b/content/browser/ssl/ssl_client_auth_handler.h
index d98dca43..732d764 100644
--- a/content/browser/ssl/ssl_client_auth_handler.h
+++ b/content/browser/ssl/ssl_client_auth_handler.h
@@ -26,7 +26,7 @@
 namespace content {
 
 // This class handles the approval and selection of a certificate for SSL client
-// authentication by the user. Should only be used on the IO thread. If the
+// authentication by the user. Should only be used on the UI thread. If the
 // SSLClientAuthHandler is destroyed before the certificate is selected, the
 // selection is canceled and the delegate never called.
 class SSLClientAuthHandler {
@@ -63,24 +63,8 @@
   // Selects a certificate and resumes the URL request with that certificate.
   void SelectCertificate();
 
-  // Sets a UI-thread callback that can be used to close the UI associated with
-  // this certificate selection. The callback is run when SSLClientAuthHandler
-  // is destroyed.
-  void SetCancellationCallback(base::OnceClosure callback);
-
-  // Called to continue the request associated with |handler| using |cert|. This
-  // is static to avoid deleting |handler| while it is on the stack.
-  static void ContinueWithCertificate(
-      const base::WeakPtr<SSLClientAuthHandler>& handler,
-      scoped_refptr<net::X509Certificate> cert,
-      scoped_refptr<net::SSLPrivateKey> key);
-
-  // Called to abort the request associated with |handler|. This is static to
-  // avoid deleting |handler| while it is on the stack.
-  static void CancelCertificateSelection(
-      const base::WeakPtr<SSLClientAuthHandler>& handler);
-
  private:
+  class ClientCertificateDelegateImpl;
   class Core;
 
   // Called when |core_| is done retrieving the cert list.
diff --git a/content/browser/storage_partition_impl.cc b/content/browser/storage_partition_impl.cc
index 535c077d..3f15dcfb 100644
--- a/content/browser/storage_partition_impl.cc
+++ b/content/browser/storage_partition_impl.cc
@@ -719,7 +719,7 @@
   return frame_tree_node && frame_tree_node->IsMainFrame();
 }
 
-// This class lives on the IO thread. It is self-owned and will delete itself
+// This class lives on the UI thread. It is self-owned and will delete itself
 // after any of the SSLClientAuthHandler::Delegate methods are invoked (or when
 // a mojo connection error occurs).
 class SSLClientAuthDelegate : public SSLClientAuthHandler::Delegate {
@@ -727,30 +727,30 @@
   SSLClientAuthDelegate(
       mojo::PendingRemote<network::mojom::ClientCertificateResponder>
           client_cert_responder_remote,
-      content::ResourceContext* resource_context,
+      content::BrowserContext* browser_context,
       WebContents::Getter web_contents_getter,
       const scoped_refptr<net::SSLCertRequestInfo>& cert_info)
       : client_cert_responder_(std::move(client_cert_responder_remote)),
         ssl_client_auth_handler_(std::make_unique<SSLClientAuthHandler>(
             GetContentClient()->browser()->CreateClientCertStore(
-                resource_context),
+                browser_context),
             std::move(web_contents_getter),
             std::move(cert_info.get()),
             this)) {
-    DCHECK_CURRENTLY_ON(BrowserThread::IO);
+    DCHECK_CURRENTLY_ON(BrowserThread::UI);
     DCHECK(client_cert_responder_);
-    ssl_client_auth_handler_->SelectCertificate();
     client_cert_responder_.set_disconnect_handler(base::BindOnce(
         &SSLClientAuthDelegate::DeleteSelf, base::Unretained(this)));
+    ssl_client_auth_handler_->SelectCertificate();
   }
 
-  ~SSLClientAuthDelegate() override { DCHECK_CURRENTLY_ON(BrowserThread::IO); }
+  ~SSLClientAuthDelegate() override { DCHECK_CURRENTLY_ON(BrowserThread::UI); }
 
   void DeleteSelf() { delete this; }
 
   // SSLClientAuthHandler::Delegate:
   void CancelCertificateSelection() override {
-    DCHECK_CURRENTLY_ON(BrowserThread::IO);
+    DCHECK_CURRENTLY_ON(BrowserThread::UI);
     client_cert_responder_->CancelRequest();
     DeleteSelf();
   }
@@ -759,7 +759,7 @@
   void ContinueWithCertificate(
       scoped_refptr<net::X509Certificate> cert,
       scoped_refptr<net::SSLPrivateKey> private_key) override {
-    DCHECK_CURRENTLY_ON(BrowserThread::IO);
+    DCHECK_CURRENTLY_ON(BrowserThread::UI);
     DCHECK((cert && private_key) || (!cert && !private_key));
 
     if (cert && private_key) {
@@ -785,18 +785,6 @@
   std::unique_ptr<SSLClientAuthHandler> ssl_client_auth_handler_;
 };
 
-void CreateSSLClientAuthDelegateOnIO(
-    mojo::PendingRemote<network::mojom::ClientCertificateResponder>
-        client_cert_responder_remote,
-    content::ResourceContext* resource_context,
-    WebContents::Getter web_contents_getter,
-    scoped_refptr<net::SSLCertRequestInfo> cert_info) {
-  DCHECK_CURRENTLY_ON(BrowserThread::IO);
-  new SSLClientAuthDelegate(std::move(client_cert_responder_remote),
-                            resource_context, std::move(web_contents_getter),
-                            cert_info);  // deletes self
-}
-
 void OnCertificateRequestedContinuation(
     uint32_t process_id,
     uint32_t routing_id,
@@ -818,12 +806,10 @@
     return;
   }
 
-  base::PostTask(
-      FROM_HERE, {BrowserThread::IO},
-      base::BindOnce(&CreateSSLClientAuthDelegateOnIO,
-                     std::move(client_cert_responder_remote),
-                     web_contents->GetBrowserContext()->GetResourceContext(),
-                     std::move(web_contents_getter), cert_info));
+  new SSLClientAuthDelegate(std::move(client_cert_responder_remote),
+                            web_contents->GetBrowserContext(),
+                            std::move(web_contents_getter),
+                            cert_info);  // deletes self
 }
 
 class SSLErrorDelegate : public SSLErrorHandler::Delegate {
diff --git a/content/browser/web_contents/web_contents_impl.cc b/content/browser/web_contents/web_contents_impl.cc
index 9a9b78eb..1c63b46a 100644
--- a/content/browser/web_contents/web_contents_impl.cc
+++ b/content/browser/web_contents/web_contents_impl.cc
@@ -3279,7 +3279,7 @@
     std::vector<AccessibilityTreeFormatter::PropertyFilter> property_filters) {
   auto* ax_mgr = GetOrCreateRootBrowserAccessibilityManager();
   DCHECK(ax_mgr);
-  return AccessibilityTreeFormatter::DumpAccessibilityTreeFromManager(
+  return AccessibilityTreeFormatterBase::DumpAccessibilityTreeFromManager(
       ax_mgr, internal, property_filters);
 }
 
@@ -7241,6 +7241,23 @@
     observer.MediaWatchTimeChanged(watch_time);
 }
 
+media::MediaMetricsProvider::RecordAggregateWatchTimeCallback
+WebContentsImpl::GetRecordAggregateWatchTimeCallback() {
+  return base::BindRepeating(
+      [](base::WeakPtr<RenderFrameHostDelegate> delegate,
+         GURL last_committed_url, base::TimeDelta total_watch_time,
+         base::TimeDelta time_stamp) {
+        content::MediaPlayerWatchTime watch_time(last_committed_url,
+                                                 last_committed_url.GetOrigin(),
+                                                 total_watch_time, time_stamp);
+
+        // Save the watch time if the delegate is still alive.
+        if (delegate)
+          delegate->MediaWatchTimeChanged(watch_time);
+      },
+      weak_factory_.GetWeakPtr(), GetMainFrameLastCommittedURL());
+}
+
 RenderFrameHostImpl* WebContentsImpl::GetMainFrameForInnerDelegate(
     FrameTreeNode* frame_tree_node) {
   if (auto* web_contents = node_.GetInnerWebContentsInFrame(frame_tree_node))
diff --git a/content/browser/web_contents/web_contents_impl.h b/content/browser/web_contents/web_contents_impl.h
index d53596abe..059506cd 100644
--- a/content/browser/web_contents/web_contents_impl.h
+++ b/content/browser/web_contents/web_contents_impl.h
@@ -82,11 +82,11 @@
 
 namespace base {
 class FilePath;
-}
+}  // namespace base
 
 namespace service_manager {
 class InterfaceProvider;
-}
+}  // namespace service_manager
 
 namespace content {
 enum class PictureInPictureResult;
@@ -126,7 +126,7 @@
 
 namespace mojom {
 class CreateNewWindowParams;
-}
+}  // namespace mojom
 
 #if defined(OS_ANDROID)
 class WebContentsAndroid;
@@ -645,6 +645,8 @@
                                    int context_id) override;
   void MediaWatchTimeChanged(
       const content::MediaPlayerWatchTime& watch_time) override;
+  media::MediaMetricsProvider::RecordAggregateWatchTimeCallback
+  GetRecordAggregateWatchTimeCallback() override;
   RenderFrameHostImpl* GetMainFrameForInnerDelegate(
       FrameTreeNode* frame_tree_node) override;
   bool IsFrameLowPriority(const RenderFrameHost* render_frame_host) override;
diff --git a/content/common/content_security_policy/content_security_policy.cc b/content/common/content_security_policy/content_security_policy.cc
index b196b24..5a3c72b 100644
--- a/content/common/content_security_policy/content_security_policy.cc
+++ b/content/common/content_security_policy/content_security_policy.cc
@@ -17,6 +17,7 @@
     case CSPDirective::FormAction:
     case CSPDirective::UpgradeInsecureRequests:
     case CSPDirective::NavigateTo:
+    case CSPDirective::FrameAncestors:
       return CSPDirective::Unknown;
 
     case CSPDirective::FrameSrc:
diff --git a/content/common/content_security_policy/csp_directive.cc b/content/common/content_security_policy/csp_directive.cc
index 51a6cb2..b639421 100644
--- a/content/common/content_security_policy/csp_directive.cc
+++ b/content/common/content_security_policy/csp_directive.cc
@@ -32,6 +32,8 @@
       return "upgrade-insecure-requests";
     case NavigateTo:
       return "navigate-to";
+    case FrameAncestors:
+      return "frame-ancestors";
     case Unknown:
       return "";
   }
@@ -53,6 +55,8 @@
     return CSPDirective::UpgradeInsecureRequests;
   if (name == "navigate-to")
     return CSPDirective::NavigateTo;
+  if (name == "frame-ancestors")
+    return CSPDirective::FrameAncestors;
   return CSPDirective::Unknown;
 }
 
diff --git a/content/common/content_security_policy/csp_directive.h b/content/common/content_security_policy/csp_directive.h
index 289e5fba..e4805573 100644
--- a/content/common/content_security_policy/csp_directive.h
+++ b/content/common/content_security_policy/csp_directive.h
@@ -28,6 +28,7 @@
     FormAction,
     UpgradeInsecureRequests,
     NavigateTo,
+    FrameAncestors,
 
     Unknown,
     NameLast = Unknown,
diff --git a/content/common/document_scoped_interface_bundle.mojom b/content/common/document_scoped_interface_bundle.mojom
index 63f816b9..9c52764 100644
--- a/content/common/document_scoped_interface_bundle.mojom
+++ b/content/common/document_scoped_interface_bundle.mojom
@@ -5,7 +5,6 @@
 module content.mojom;
 
 import "services/service_manager/public/mojom/interface_provider.mojom";
-import "third_party/blink/public/mojom/frame/document_interface_broker.mojom";
 import "third_party/blink/public/mojom/browser_interface_broker.mojom";
 
 struct DocumentScopedInterfaceBundle {
@@ -13,17 +12,10 @@
   // services exposed by its RenderFrameHost.
   service_manager.mojom.InterfaceProvider interface_provider;
 
-  // The DocumentInterfaceBroker through which the RenderFrame can access
-  // interfaces exposed by its RenderFrameHost
-  pending_remote<blink.mojom.DocumentInterfaceBroker>
-      document_interface_broker_content;
-  pending_remote<blink.mojom.DocumentInterfaceBroker>
-      document_interface_broker_blink;
-
   // The BrowserInterfaceBroker through which the RenderFrame can access
-  // interfaces exposed by its RenderFrameHost
-  // It will eventually replace interface_provider and
-  // document_interface_broker* above and become the only way to get
-  // document-scoped interfaces from the browser (see crbug.com/985120)
+  // interfaces exposed by its RenderFrameHost.
+  // It will eventually replace interface_provider above and become the
+  // only way to get document-scoped interfaces from the browser
+  // (see crbug.com/985120).
   pending_remote<blink.mojom.BrowserInterfaceBroker> browser_interface_broker;
 };
diff --git a/content/common/frame.mojom b/content/common/frame.mojom
index a4699d3..e062802 100644
--- a/content/common/frame.mojom
+++ b/content/common/frame.mojom
@@ -377,7 +377,7 @@
   // Sent by the renderer when a navigation commits in the frame.
 
   // If |interface_params| is non-empty, the FrameHost implementation
-  // must unbind the old InterfaceProvider and DocumentInterfaceBroker
+  // must unbind the old InterfaceProvider and BrowserInterfaceBroker
   // connections, and drop any interface requests pending on them.
   // Then it should bind the appropriate requests and start servicing
   // GetInterface messages coming in on these new connections
@@ -387,9 +387,8 @@
   // is set for cross-document navigations. This prevents origin confusion by
   // ensuring that interface requests racing with navigation commit will be
   // either ignored, or serviced correctly in the security context of the
-  // document they originated from (based on which InterfaceProvider connection
-  // the GetInterface messages arrive on or DocumentInterfaceBroker
-  // connection the Get<interface> messages arrive on).
+  // document they originated from (based on which InterfaceProvider or
+  // BrowserInterfaceBroker connection the GetInterface messages arrive on).
   DidCommitProvisionalLoad(
       DidCommitProvisionalLoadParams params,
       DidCommitProvisionalLoadInterfaceParams? interface_params);
@@ -467,10 +466,6 @@
   // navigation.
   FrameSizeChanged(gfx.mojom.Size size);
 
-  // Notifies the browser that the current frame has changed its visibility
-  // status.
-  VisibilityChanged(blink.mojom.FrameVisibility visibility);
-
   // Updates information to determine whether a user gesture should carryover to
   // future navigations. This is needed so navigations within a certain
   // timeframe of a request initiated by a gesture will be treated as if they
diff --git a/content/common/frame_messages.h b/content/common/frame_messages.h
index 9c06143..6ef92aa 100644
--- a/content/common/frame_messages.h
+++ b/content/common/frame_messages.h
@@ -568,10 +568,6 @@
 IPC_STRUCT_BEGIN(FrameHostMsg_CreateChildFrame_Params_Reply)
   IPC_STRUCT_MEMBER(int32_t, child_routing_id)
   IPC_STRUCT_MEMBER(mojo::MessagePipeHandle, new_interface_provider)
-  IPC_STRUCT_MEMBER(mojo::MessagePipeHandle,
-                    document_interface_broker_content_handle)
-  IPC_STRUCT_MEMBER(mojo::MessagePipeHandle,
-                    document_interface_broker_blink_handle)
   IPC_STRUCT_MEMBER(mojo::MessagePipeHandle, browser_interface_broker_handle)
   IPC_STRUCT_MEMBER(base::UnguessableToken, devtools_frame_token)
 IPC_STRUCT_END()
@@ -1201,10 +1197,6 @@
 IPC_MESSAGE_ROUTED1(FrameHostMsg_UpdateViewportIntersection,
                     blink::ViewportIntersectionState /* intersection_state */)
 
-// Informs the child that the frame has changed visibility.
-IPC_MESSAGE_ROUTED1(FrameHostMsg_VisibilityChanged,
-                    blink::mojom::FrameVisibility /* visibility */)
-
 // Sent by a RenderFrameProxy to the browser signaling that the renderer
 // has determined the DOM subtree it represents is inert and should no
 // longer process input events. Also see WidgetMsg_SetIsInert.
diff --git a/content/common/frame_messages.mojom b/content/common/frame_messages.mojom
index 0ad8ece..9607369 100644
--- a/content/common/frame_messages.mojom
+++ b/content/common/frame_messages.mojom
@@ -6,17 +6,12 @@
 
 import "services/service_manager/public/mojom/interface_provider.mojom";
 import "third_party/blink/public/mojom/browser_interface_broker.mojom";
-import "third_party/blink/public/mojom/frame/document_interface_broker.mojom";
 
 [Native]
 struct DidCommitProvisionalLoadParams;
 
 struct DidCommitProvisionalLoadInterfaceParams {
   service_manager.mojom.InterfaceProvider& interface_provider_request;
-  pending_receiver<blink.mojom.DocumentInterfaceBroker>
-      document_interface_broker_content_receiver;
-  pending_receiver<blink.mojom.DocumentInterfaceBroker>
-      document_interface_broker_blink_receiver;
   pending_receiver<blink.mojom.BrowserInterfaceBroker>
       browser_interface_broker_receiver;
 };
diff --git a/content/public/android/java/src/org/chromium/content/app/ContentChildProcessServiceDelegate.java b/content/public/android/java/src/org/chromium/content/app/ContentChildProcessServiceDelegate.java
index c353bfb..422f2c7 100644
--- a/content/public/android/java/src/org/chromium/content/app/ContentChildProcessServiceDelegate.java
+++ b/content/public/android/java/src/org/chromium/content/app/ContentChildProcessServiceDelegate.java
@@ -20,7 +20,6 @@
 import org.chromium.base.annotations.MainDex;
 import org.chromium.base.annotations.NativeMethods;
 import org.chromium.base.library_loader.LibraryLoader;
-import org.chromium.base.library_loader.LibraryLoaderConfig;
 import org.chromium.base.library_loader.Linker;
 import org.chromium.base.library_loader.ProcessInitException;
 import org.chromium.base.memory.MemoryPressureUma;
@@ -83,7 +82,7 @@
         mCpuFeatures = connectionBundle.getLong(ContentChildProcessConstants.EXTRA_CPU_FEATURES);
         assert mCpuCount > 0;
 
-        if (LibraryLoaderConfig.useChromiumLinker()
+        if (LibraryLoader.getInstance().useChromiumLinker()
                 && !LibraryLoader.getInstance().isLoadedByZygote()) {
             Bundle sharedRelros = connectionBundle.getBundle(Linker.EXTRA_LINKER_SHARED_RELROS);
             if (sharedRelros != null) getLinker().provideSharedRelros(sharedRelros);
@@ -109,7 +108,7 @@
 
         Linker linker = null;
         boolean requestedSharedRelro = false;
-        if (LibraryLoaderConfig.useChromiumLinker()) {
+        if (LibraryLoader.getInstance().useChromiumLinker()) {
             assert mLinkerParams != null;
             linker = getLinker();
             if (mLinkerParams.mWaitForSharedRelro) {
@@ -167,7 +166,7 @@
 
     // Return a Linker instance. If testing, the Linker needs special setup.
     private Linker getLinker() {
-        if (LibraryLoaderConfig.areTestsEnabled()) {
+        if (LibraryLoader.getInstance().areTestsEnabled()) {
             // For testing, set the Linker implementation and the test runner
             // class name to match those used by the parent.
             assert mLinkerParams != null;
diff --git a/content/public/android/java/src/org/chromium/content/browser/ChildProcessLauncherHelperImpl.java b/content/public/android/java/src/org/chromium/content/browser/ChildProcessLauncherHelperImpl.java
index 96537fb..d851e89 100644
--- a/content/public/android/java/src/org/chromium/content/browser/ChildProcessLauncherHelperImpl.java
+++ b/content/public/android/java/src/org/chromium/content/browser/ChildProcessLauncherHelperImpl.java
@@ -24,7 +24,7 @@
 import org.chromium.base.annotations.CalledByNative;
 import org.chromium.base.annotations.JNINamespace;
 import org.chromium.base.annotations.NativeMethods;
-import org.chromium.base.library_loader.LibraryLoaderConfig;
+import org.chromium.base.library_loader.LibraryLoader;
 import org.chromium.base.library_loader.Linker;
 import org.chromium.base.process_launcher.ChildConnectionAllocator;
 import org.chromium.base.process_launcher.ChildProcessConnection;
@@ -129,7 +129,7 @@
                             ContentChildProcessConstants.EXTRA_CPU_COUNT, CpuFeatures.getCount());
                     connectionBundle.putLong(
                             ContentChildProcessConstants.EXTRA_CPU_FEATURES, CpuFeatures.getMask());
-                    if (LibraryLoaderConfig.useChromiumLinker()) {
+                    if (LibraryLoader.getInstance().useChromiumLinker()) {
                         connectionBundle.putBundle(Linker.EXTRA_LINKER_SHARED_RELROS,
                                 Linker.getInstance().getSharedRelros());
                     }
@@ -598,7 +598,7 @@
     private static void initLinker() {
         assert LauncherThread.runningOnLauncherThread();
         if (sLinkerInitialized) return;
-        if (LibraryLoaderConfig.useChromiumLinker()) {
+        if (LibraryLoader.getInstance().useChromiumLinker()) {
             sLinkerLoadAddress = Linker.getInstance().getBaseLoadAddress();
             if (sLinkerLoadAddress == 0) {
                 Log.i(TAG, "Shared RELRO support disabled!");
@@ -616,7 +616,7 @@
 
         // Always wait for the shared RELROs in service processes.
         final boolean waitForSharedRelros = true;
-        if (LibraryLoaderConfig.areTestsEnabled()) {
+        if (LibraryLoader.getInstance().areTestsEnabled()) {
             Linker linker = Linker.getInstance();
             return new ChromiumLinkerParams(sLinkerLoadAddress, waitForSharedRelros,
                     linker.getTestRunnerClassNameForTesting(),
diff --git a/content/public/browser/accessibility_tree_formatter.h b/content/public/browser/accessibility_tree_formatter.h
index 56a90d0..55c3c9f2 100644
--- a/content/public/browser/accessibility_tree_formatter.h
+++ b/content/public/browser/accessibility_tree_formatter.h
@@ -7,6 +7,8 @@
 
 #include <stdint.h>
 
+#include <memory>
+#include <string>
 #include <vector>
 
 #include "base/files/file_path.h"
@@ -26,7 +28,6 @@
 namespace content {
 
 class BrowserAccessibility;
-class BrowserAccessibilityManager;
 
 class AccessibilityTestExpectationsLocator {
  public:
@@ -108,30 +109,6 @@
   static bool MatchesNodeFilters(const std::vector<NodeFilter>& node_filters,
                                  const base::DictionaryValue& dict);
 
-  // Populates the given DictionaryValue with the accessibility tree.
-  // The dictionary contains a key/value pair for each attribute of the node,
-  // plus a "children" attribute containing a list of all child nodes.
-  // {
-  //   "AXName": "node",  /* actual attributes will vary by platform */
-  //   "position": {  /* some attributes may be dictionaries */
-  //     "x": 0,
-  //     "y": 0
-  //   },
-  //   /* ... more attributes of |node| */
-  //   "children": [ {  /* list of children created recursively */
-  //     "AXName": "child node 1",
-  //     /* ... more attributes */
-  //     "children": [ ]
-  //   }, {
-  //     "AXName": "child name 2",
-  //     /* ... more attributes */
-  //     "children": [ ]
-  //   } ]
-  // }
-  // Build an accessibility tree for the current Chrome app.
-  virtual std::unique_ptr<base::DictionaryValue> BuildAccessibilityTree(
-      BrowserAccessibility* root) = 0;
-
   // Build an accessibility tree for any process with a window.
   virtual std::unique_ptr<base::DictionaryValue>
   BuildAccessibilityTreeForProcess(base::ProcessId pid) = 0;
@@ -156,11 +133,6 @@
   virtual void FormatAccessibilityTree(const base::DictionaryValue& tree_node,
                                        base::string16* contents) = 0;
 
-  static base::string16 DumpAccessibilityTreeFromManager(
-      BrowserAccessibilityManager* ax_mgr,
-      bool internal,
-      std::vector<PropertyFilter> property_filters);
-
   // Set regular expression filters that apply to each property of every node
   // before it's output.
   virtual void SetPropertyFilters(
@@ -196,4 +168,4 @@
 
 }  // namespace content
 
-#endif  // CONTENT_BROWSER_ACCESSIBILITY_ACCESSIBILITY_TREE_FORMATTER_H_
+#endif  // CONTENT_PUBLIC_BROWSER_ACCESSIBILITY_TREE_FORMATTER_H_
diff --git a/content/public/browser/client_hints_controller_delegate.h b/content/public/browser/client_hints_controller_delegate.h
index a87757e..2b3da2b 100644
--- a/content/public/browser/client_hints_controller_delegate.h
+++ b/content/public/browser/client_hints_controller_delegate.h
@@ -9,8 +9,13 @@
 #include <string>
 
 #include "base/optional.h"
+#include "base/time/time.h"
 #include "content/common/content_export.h"
 #include "content/public/browser/browser_context.h"
+#include "content/public/common/client_hints.mojom.h"
+#include "mojo/public/cpp/bindings/pending_receiver.h"
+#include "third_party/blink/public/platform/web_client_hints_type.h"
+#include "url/origin.h"
 
 class GURL;
 
@@ -26,10 +31,9 @@
 
 namespace content {
 
-class CONTENT_EXPORT ClientHintsControllerDelegate {
+class CONTENT_EXPORT ClientHintsControllerDelegate
+    : public client_hints::mojom::ClientHints {
  public:
-  virtual ~ClientHintsControllerDelegate() = default;
-
   virtual network::NetworkQualityTracker* GetNetworkQualityTracker() = 0;
 
   // Get which client hints opt-ins were persisted on current origin.
@@ -42,6 +46,17 @@
   virtual std::string GetAcceptLanguageString() = 0;
 
   virtual blink::UserAgentMetadata GetUserAgentMetadata() = 0;
+
+  virtual void Bind(
+      mojo::PendingReceiver<client_hints::mojom::ClientHints> receiver) {}
+
+  // mojom::ClientHints implementation.
+  void PersistClientHints(
+      const url::Origin& primary_origin,
+      const std::vector<blink::mojom::WebClientHintsType>& client_hints,
+      base::TimeDelta expiration_duration) override = 0;
+
+  virtual void ResetForTesting() {}
 };
 
 }  // namespace content
diff --git a/content/public/browser/content_browser_client.cc b/content/public/browser/content_browser_client.cc
index 8257c40..b50b87e4 100644
--- a/content/public/browser/content_browser_client.cc
+++ b/content/public/browser/content_browser_client.cc
@@ -880,7 +880,7 @@
 }
 
 std::unique_ptr<net::ClientCertStore>
-ContentBrowserClient::CreateClientCertStore(ResourceContext* resource_context) {
+ContentBrowserClient::CreateClientCertStore(BrowserContext* browser_context) {
   return nullptr;
 }
 
diff --git a/content/public/browser/content_browser_client.h b/content/public/browser/content_browser_client.h
index f0147c2..3ff6710 100644
--- a/content/public/browser/content_browser_client.h
+++ b/content/public/browser/content_browser_client.h
@@ -1502,9 +1502,9 @@
   GetWebAuthenticationRequestDelegate(RenderFrameHost* render_frame_host,
                                       const std::string& relying_party_id);
 
-  // Get platform ClientCertStore. May return nullptr.
+  // Get platform ClientCertStore. May return nullptr. Called on the UI thread.
   virtual std::unique_ptr<net::ClientCertStore> CreateClientCertStore(
-      ResourceContext* resource_context);
+      BrowserContext* browser_context);
 
   // Creates a LoginDelegate that asks the user for a username and password.
   // |web_contents| should not be null when CreateLoginDelegate is called.
diff --git a/content/public/common/BUILD.gn b/content/public/common/BUILD.gn
index fd22f18..6323821a 100644
--- a/content/public/common/BUILD.gn
+++ b/content/public/common/BUILD.gn
@@ -360,6 +360,7 @@
   }
 
   public_deps = [
+    ":client_hints_mojom",
     ":resource_type_bindings",
     "//mojo/public/mojom/base:base",
     "//services/network/public/mojom",
@@ -376,6 +377,18 @@
   export_header = "content/common/content_export.h"
 }
 
+mojom("client_hints_mojom") {
+  sources = [
+    "client_hints.mojom",
+  ]
+
+  public_deps = [
+    "//mojo/public/mojom/base",
+    "//third_party/blink/public/mojom:web_client_hints_types_mojo_bindings",
+    "//url/mojom:url_mojom_origin",
+  ]
+}
+
 mojom("service_names") {
   sources = [
     "service_names.mojom",
diff --git a/chrome/common/client_hints.mojom b/content/public/common/client_hints.mojom
similarity index 71%
rename from chrome/common/client_hints.mojom
rename to content/public/common/client_hints.mojom
index 81efb357..6765069 100644
--- a/chrome/common/client_hints.mojom
+++ b/content/public/common/client_hints.mojom
@@ -5,7 +5,8 @@
 module client_hints.mojom;
 
 import "mojo/public/mojom/base/time.mojom";
-import "third_party/blink/public/mojom/web_client_hints/web_client_hints_types.mojom";
+import
+"third_party/blink/public/mojom/web_client_hints/web_client_hints_types.mojom";
 import "url/mojom/origin.mojom";
 
 // Sent from renderer to browser process when the lifetime of the client hints
@@ -13,6 +14,9 @@
 // that |primary_origin| has requested for. The client hints should be sent
 // for a duration of |expiration_duration|.
 interface ClientHints {
+  // Provide information to the browser process about Client Hints opt-in.
+  // Inputs are the origin setting the opt-in, an array of the hints that were
+  // set, and the duration for which the opt-in would be valid.
   PersistClientHints(url.mojom.Origin primary_origin,
                      array<blink.mojom.WebClientHintsType> client_hints,
                      mojo_base.mojom.TimeDelta expiration_duration);
diff --git a/content/public/common/common_param_traits_macros.h b/content/public/common/common_param_traits_macros.h
index e22745ce..91f0329 100644
--- a/content/public/common/common_param_traits_macros.h
+++ b/content/public/common/common_param_traits_macros.h
@@ -234,6 +234,7 @@
   IPC_STRUCT_TRAITS_MEMBER(scroll_top_left_interop_enabled)
   IPC_STRUCT_TRAITS_MEMBER(disable_features_depending_on_viz)
   IPC_STRUCT_TRAITS_MEMBER(disable_accelerated_small_canvases)
+  IPC_STRUCT_TRAITS_MEMBER(reenable_web_components_v0)
 #endif  // defined(OS_ANDROID)
   IPC_STRUCT_TRAITS_MEMBER(force_dark_mode_enabled)
   IPC_STRUCT_TRAITS_MEMBER(default_minimum_page_scale_factor)
diff --git a/content/public/common/content_features.cc b/content/public/common/content_features.cc
index 98ff77d5..c9e56e0 100644
--- a/content/public/common/content_features.cc
+++ b/content/public/common/content_features.cc
@@ -749,6 +749,10 @@
 const base::Feature kTrustedDOMTypes{"TrustedDOMTypes",
                                      base::FEATURE_DISABLED_BY_DEFAULT};
 
+// Controls whether Client Hints are guarded by FeaturePolicy.
+const base::Feature kFeaturePolicyForClientHints{
+    "FeaturePolicyForClientHints", base::FEATURE_DISABLED_BY_DEFAULT};
+
 // Use ThreadPriority::DISPLAY for browser UI and IO threads.
 #if defined(OS_ANDROID) || defined(OS_CHROMEOS)
 const base::Feature kBrowserUseDisplayThreadPriority{
diff --git a/content/public/common/content_features.h b/content/public/common/content_features.h
index 756c1a76..13256b8a 100644
--- a/content/public/common/content_features.h
+++ b/content/public/common/content_features.h
@@ -157,6 +157,7 @@
 CONTENT_EXPORT extern const base::Feature kScriptStreamingOnPreload;
 CONTENT_EXPORT extern const base::Feature kTrustedDOMTypes;
 CONTENT_EXPORT extern const base::Feature kBrowserUseDisplayThreadPriority;
+CONTENT_EXPORT extern const base::Feature kFeaturePolicyForClientHints;
 
 #if defined(OS_ANDROID)
 CONTENT_EXPORT extern const base::Feature kAndroidAutofillAccessibility;
diff --git a/content/public/common/web_preferences.cc b/content/public/common/web_preferences.cc
index 94a53d2..55b8100 100644
--- a/content/public/common/web_preferences.cc
+++ b/content/public/common/web_preferences.cc
@@ -207,6 +207,7 @@
       scroll_top_left_interop_enabled(true),
       disable_features_depending_on_viz(false),
       disable_accelerated_small_canvases(false),
+      reenable_web_components_v0(false),
 #endif  // defined(OS_ANDROID)
 #if defined(OS_ANDROID)
       default_minimum_page_scale_factor(0.25f),
diff --git a/content/public/common/web_preferences.h b/content/public/common/web_preferences.h
index 6b0631e..58ff02d 100644
--- a/content/public/common/web_preferences.h
+++ b/content/public/common/web_preferences.h
@@ -280,6 +280,9 @@
   bool disable_features_depending_on_viz;
   // Don't accelerate small canvases to avoid crashes TODO(crbug.com/1004304)
   bool disable_accelerated_small_canvases;
+  // Re-enable Web Components v0 on Webview, temporarily. This should get
+  // removed when crbug.com/1021631 gets fixed.
+  bool reenable_web_components_v0;
 #endif  // defined(OS_ANDROID)
 
   // Enable forcibly modifying content rendering to result in a light on dark
diff --git a/content/public/renderer/render_frame.h b/content/public/renderer/render_frame.h
index 4c36a22..d60699b 100644
--- a/content/public/renderer/render_frame.h
+++ b/content/public/renderer/render_frame.h
@@ -22,7 +22,6 @@
 #include "services/service_manager/public/cpp/binder_registry.h"
 #include "third_party/blink/public/common/navigation/triggering_event_info.h"
 #include "third_party/blink/public/mojom/devtools/console_message.mojom.h"
-#include "third_party/blink/public/mojom/frame/document_interface_broker.mojom.h"
 #include "third_party/blink/public/platform/task_type.h"
 #include "third_party/blink/public/web/web_navigation_policy.h"
 #include "ui/accessibility/ax_mode.h"
@@ -173,11 +172,6 @@
   // interfaces exposed to it by the application running in this frame.
   virtual service_manager::InterfaceProvider* GetRemoteInterfaces() = 0;
 
-  // Returns the DocumentInterfaceBroker that this process can use to bind
-  // interfaces exposed to it by the application running in this frame.
-  virtual blink::mojom::DocumentInterfaceBroker*
-  GetDocumentInterfaceBroker() = 0;
-
   // Returns the BrowserInterfaceBrokerProxy that this process can use to bind
   // interfaces exposed to it by the application running in this frame.
   virtual blink::BrowserInterfaceBrokerProxy* GetBrowserInterfaceBroker() = 0;
diff --git a/content/public/test/mock_render_thread.cc b/content/public/test/mock_render_thread.cc
index 491681e..46ddf4d9 100644
--- a/content/public/test/mock_render_thread.cc
+++ b/content/public/test/mock_render_thread.cc
@@ -277,18 +277,6 @@
   return interface_provider_request;
 }
 
-mojo::PendingReceiver<blink::mojom::DocumentInterfaceBroker>
-MockRenderThread::TakeInitialDocumentInterfaceBrokerReceiverForFrame(
-    int32_t routing_id) {
-  auto it =
-      frame_routing_id_to_initial_document_broker_receivers_.find(routing_id);
-  if (it == frame_routing_id_to_initial_document_broker_receivers_.end())
-    return mojo::NullReceiver();
-  auto document_broker_receiver = std::move(it->second);
-  frame_routing_id_to_initial_document_broker_receivers_.erase(it);
-  return document_broker_receiver;
-}
-
 mojo::PendingReceiver<blink::mojom::BrowserInterfaceBroker>
 MockRenderThread::TakeInitialBrowserInterfaceBrokerReceiverForFrame(
     int32_t routing_id) {
@@ -323,21 +311,6 @@
   params_reply->new_interface_provider =
       interface_provider.PassInterface().PassHandle().release();
 
-  mojo::PendingRemote<blink::mojom::DocumentInterfaceBroker>
-      document_interface_broker;
-  frame_routing_id_to_initial_document_broker_receivers_.emplace(
-      params_reply->child_routing_id,
-      document_interface_broker.InitWithNewPipeAndPassReceiver());
-  params_reply->document_interface_broker_content_handle =
-      document_interface_broker.PassPipe().release();
-
-  mojo::PendingRemote<blink::mojom::DocumentInterfaceBroker>
-      document_interface_broker_blink;
-  ignore_result(
-      document_interface_broker_blink.InitWithNewPipeAndPassReceiver());
-  params_reply->document_interface_broker_blink_handle =
-      document_interface_broker_blink.PassPipe().release();
-
   mojo::PendingRemote<blink::mojom::BrowserInterfaceBroker>
       browser_interface_broker;
   frame_routing_id_to_initial_browser_broker_receivers_.emplace(
@@ -391,25 +364,11 @@
       reply->main_frame_route_id,
       mojo::MakeRequest(
           &reply->main_frame_interface_bundle->interface_provider));
-
-  mojo::PendingRemote<blink::mojom::DocumentInterfaceBroker>
-      document_interface_broker;
-  frame_routing_id_to_initial_document_broker_receivers_.emplace(
-      reply->main_frame_route_id,
-      document_interface_broker.InitWithNewPipeAndPassReceiver());
-
   mojo::PendingRemote<blink::mojom::BrowserInterfaceBroker>
       browser_interface_broker;
   frame_routing_id_to_initial_browser_broker_receivers_.emplace(
       reply->main_frame_route_id,
       browser_interface_broker.InitWithNewPipeAndPassReceiver());
-  reply->main_frame_interface_bundle->document_interface_broker_content =
-      std::move(document_interface_broker);
-
-  ignore_result(document_interface_broker.InitWithNewPipeAndPassReceiver());
-  reply->main_frame_interface_bundle->document_interface_broker_blink =
-      std::move(document_interface_broker);
-
   reply->main_frame_interface_bundle->browser_interface_broker =
       std::move(browser_interface_broker);
 
diff --git a/content/public/test/mock_render_thread.h b/content/public/test/mock_render_thread.h
index d9af68f..38ef2fa 100644
--- a/content/public/test/mock_render_thread.h
+++ b/content/public/test/mock_render_thread.h
@@ -20,7 +20,6 @@
 #include "mojo/public/cpp/bindings/pending_receiver.h"
 #include "services/service_manager/public/mojom/interface_provider.mojom.h"
 #include "third_party/blink/public/mojom/browser_interface_broker.mojom.h"
-#include "third_party/blink/public/mojom/frame/document_interface_broker.mojom.h"
 
 struct FrameHostMsg_CreateChildFrame_Params;
 struct FrameHostMsg_CreateChildFrame_Params_Reply;
@@ -120,13 +119,6 @@
   service_manager::mojom::InterfaceProviderRequest
   TakeInitialInterfaceProviderRequestForFrame(int32_t routing_id);
 
-  // Returns the receiver end of the DocumentInterfaceBroker interface whose
-  // client end was passed in to construct RenderFrame with |routing_id|; if
-  // any. The client end will be used by the RenderFrame to service interface
-  // requests originating from the initial empty document.
-  mojo::PendingReceiver<blink::mojom::DocumentInterfaceBroker>
-  TakeInitialDocumentInterfaceBrokerReceiverForFrame(int32_t routing_id);
-
   // Returns the receiver end of the BrowserInterfaceBroker interface whose
   // client end was passed in to construct RenderFrame with |routing_id|; if
   // any. The client end will be used by the RenderFrame to service interface
@@ -165,10 +157,6 @@
   std::map<int32_t, service_manager::mojom::InterfaceProviderRequest>
       frame_routing_id_to_initial_interface_provider_requests_;
 
-  std::map<int32_t,
-           mojo::PendingReceiver<blink::mojom::DocumentInterfaceBroker>>
-      frame_routing_id_to_initial_document_broker_receivers_;
-
   std::map<int32_t, mojo::PendingReceiver<blink::mojom::BrowserInterfaceBroker>>
       frame_routing_id_to_initial_browser_broker_receivers_;
 
diff --git a/content/public/test/render_view_test.cc b/content/public/test/render_view_test.cc
index 7635ba48..374f186 100644
--- a/content/public/test/render_view_test.cc
+++ b/content/public/test/render_view_test.cc
@@ -428,14 +428,6 @@
       mojo::MakeRequest(
           &view_params->main_frame_interface_bundle->interface_provider));
 
-  mojo::PendingRemote<blink::mojom::DocumentInterfaceBroker>
-      document_interface_broker;
-  ignore_result(document_interface_broker.InitWithNewPipeAndPassReceiver());
-  view_params->main_frame_interface_bundle->document_interface_broker_content =
-      std::move(document_interface_broker);
-  ignore_result(document_interface_broker.InitWithNewPipeAndPassReceiver());
-  view_params->main_frame_interface_bundle->document_interface_broker_blink =
-      std::move(document_interface_broker);
   mojo::PendingRemote<blink::mojom::BrowserInterfaceBroker>
       browser_interface_broker;
   // Ignoring the returned PendingReceiver because it is not bound to anything
diff --git a/content/renderer/render_frame_impl.cc b/content/renderer/render_frame_impl.cc
index 2327b098..4d83bfa 100644
--- a/content/renderer/render_frame_impl.cc
+++ b/content/renderer/render_frame_impl.cc
@@ -1359,14 +1359,11 @@
     RenderViewImpl* render_view,
     int32_t routing_id,
     service_manager::mojom::InterfaceProviderPtr interface_provider,
-    mojo::Remote<blink::mojom::DocumentInterfaceBroker>
-        document_interface_broker_content,
     mojo::PendingRemote<blink::mojom::BrowserInterfaceBroker>
         browser_interface_broker,
     const base::UnguessableToken& devtools_frame_token) {
   DCHECK(routing_id != MSG_ROUTING_NONE);
   CreateParams params(render_view, routing_id, std::move(interface_provider),
-                      std::move(document_interface_broker_content),
                       std::move(browser_interface_broker),
                       devtools_frame_token);
 
@@ -1406,26 +1403,16 @@
   service_manager::mojom::InterfaceProviderPtr main_frame_interface_provider(
       std::move(params->main_frame_interface_bundle->interface_provider));
 
-  mojo::Remote<blink::mojom::DocumentInterfaceBroker>
-      document_interface_broker_content(
-          std::move(params->main_frame_interface_bundle
-                        ->document_interface_broker_content));
   RenderFrameImpl* render_frame = RenderFrameImpl::Create(
       render_view, params->main_frame_routing_id,
       std::move(main_frame_interface_provider),
-      std::move(document_interface_broker_content),
       std::move(params->main_frame_interface_bundle->browser_interface_broker),
       params->devtools_main_frame_token);
   render_frame->InitializeBlameContext(nullptr);
 
-  mojo::PendingRemote<blink::mojom::DocumentInterfaceBroker>
-      document_interface_broker_blink(
-          std::move(params->main_frame_interface_bundle
-                        ->document_interface_broker_blink));
   WebLocalFrame* web_frame = WebLocalFrame::CreateMainFrame(
       render_view->webview(), render_frame,
-      render_frame->blink_interface_registry_.get(),
-      document_interface_broker_blink.PassPipe(), opener,
+      render_frame->blink_interface_registry_.get(), opener,
       // This conversion is a little sad, as this often comes from a
       // WebString...
       WebString::FromUTF8(params->replicated_frame_state.name),
@@ -1474,10 +1461,6 @@
 void RenderFrameImpl::CreateFrame(
     int routing_id,
     service_manager::mojom::InterfaceProviderPtr interface_provider,
-    mojo::PendingRemote<blink::mojom::DocumentInterfaceBroker>
-        document_interface_broker_content,
-    mojo::PendingRemote<blink::mojom::DocumentInterfaceBroker>
-        document_interface_broker_blink,
     mojo::PendingRemote<blink::mojom::BrowserInterfaceBroker>
         browser_interface_broker,
     int previous_routing_id,
@@ -1521,8 +1504,6 @@
     // Create the RenderFrame and WebLocalFrame, linking the two.
     render_frame = RenderFrameImpl::Create(
         parent_proxy->render_view(), routing_id, std::move(interface_provider),
-        mojo::Remote<blink::mojom::DocumentInterfaceBroker>(
-            std::move(document_interface_broker_content)),
         std::move(browser_interface_broker), devtools_frame_token);
     render_frame->InitializeBlameContext(FromRoutingID(parent_routing_id));
     render_frame->unique_name_helper_.set_propagated_name(
@@ -1531,7 +1512,7 @@
         replicated_state.scope, WebString::FromUTF8(replicated_state.name),
         replicated_state.frame_policy, render_frame,
         render_frame->blink_interface_registry_.get(),
-        document_interface_broker_blink.PassPipe(), previous_sibling_web_frame,
+        previous_sibling_web_frame,
         ConvertFrameOwnerPropertiesToWebFrameOwnerProperties(
             frame_owner_properties),
         replicated_state.frame_owner_element_type,
@@ -1560,16 +1541,13 @@
     render_view = proxy->render_view();
     render_frame = RenderFrameImpl::Create(
         render_view, routing_id, std::move(interface_provider),
-        mojo::Remote<blink::mojom::DocumentInterfaceBroker>(
-            std::move(document_interface_broker_content)),
         std::move(browser_interface_broker), devtools_frame_token);
     render_frame->InitializeBlameContext(nullptr);
     render_frame->previous_routing_id_ = previous_routing_id;
     proxy->set_provisional_frame_routing_id(routing_id);
     web_frame = blink::WebLocalFrame::CreateProvisional(
         render_frame, render_frame->blink_interface_registry_.get(),
-        document_interface_broker_blink.PassPipe(), proxy->web_frame(),
-        replicated_state.frame_policy);
+        proxy->web_frame(), replicated_state.frame_policy);
     // The new |web_frame| is a main frame iff the proxy's frame was.
     DCHECK_EQ(proxy_is_main_frame, !web_frame->Parent());
   }
@@ -1804,16 +1782,12 @@
     RenderViewImpl* render_view,
     int32_t routing_id,
     service_manager::mojom::InterfaceProviderPtr interface_provider,
-    mojo::Remote<blink::mojom::DocumentInterfaceBroker>
-        document_interface_broker_content,
     mojo::PendingRemote<blink::mojom::BrowserInterfaceBroker>
         browser_interface_broker,
     const base::UnguessableToken& devtools_frame_token)
     : render_view(render_view),
       routing_id(routing_id),
       interface_provider(std::move(interface_provider)),
-      document_interface_broker_content(
-          std::move(document_interface_broker_content)),
       browser_interface_broker(std::move(browser_interface_broker)),
       devtools_frame_token(devtools_frame_token) {}
 RenderFrameImpl::CreateParams::~CreateParams() = default;
@@ -1861,12 +1835,6 @@
   blink_interface_registry_.reset(new BlinkInterfaceRegistryImpl(
       registry_.GetWeakPtr(), associated_interfaces_.GetWeakPtr()));
 
-  // The DocumentInterfaceBroker to access Mojo services exposed by the RFHI
-  // must be provided at construction time. See: https://crbug.com/718652/.
-  CHECK(params.document_interface_broker_content.is_bound());
-  document_interface_broker_ =
-      std::move(params.document_interface_broker_content);
-
   CHECK(params.browser_interface_broker.is_valid());
   browser_interface_broker_proxy_.Bind(
       std::move(params.browser_interface_broker));
@@ -2770,11 +2738,6 @@
   render_view_->UpdateBrowserControlsState(constraints, current, animate);
 }
 
-void RenderFrameImpl::VisibilityChanged(
-    blink::mojom::FrameVisibility visibility) {
-  GetFrameHost()->VisibilityChanged(visibility);
-}
-
 #if defined(OS_ANDROID)
 void RenderFrameImpl::ExtractSmartClipData(
     const gfx::Rect& rect,
@@ -3062,18 +3025,6 @@
   return &remote_interfaces_;
 }
 
-blink::mojom::DocumentInterfaceBroker*
-RenderFrameImpl::GetDocumentInterfaceBroker() {
-  DCHECK(document_interface_broker_.is_bound());
-  return document_interface_broker_.get();
-}
-
-void RenderFrameImpl::SetDocumentInterfaceBrokerForTesting(
-    mojo::PendingRemote<blink::mojom::DocumentInterfaceBroker> test_broker) {
-  document_interface_broker_.reset();
-  document_interface_broker_.Bind(std::move(test_broker));
-}
-
 blink::AssociatedInterfaceRegistry*
 RenderFrameImpl::GetAssociatedInterfaceRegistry() {
   return &associated_interfaces_;
@@ -4180,28 +4131,8 @@
           0u),
       GetTaskRunner(blink::TaskType::kInternalIPC));
 
-  DCHECK(params_reply.document_interface_broker_content_handle.is_valid());
-  DCHECK(params_reply.document_interface_broker_blink_handle.is_valid());
   DCHECK(params_reply.browser_interface_broker_handle.is_valid());
 
-  mojo::Remote<blink::mojom::DocumentInterfaceBroker>
-      document_interface_broker_content;
-  document_interface_broker_content.Bind(
-      mojo::PendingRemote<blink::mojom::DocumentInterfaceBroker>(
-          mojo::ScopedMessagePipeHandle(
-              params_reply.document_interface_broker_content_handle),
-          blink::mojom::DocumentInterfaceBroker::Version_),
-      GetTaskRunner(blink::TaskType::kInternalIPC));
-
-  mojo::Remote<blink::mojom::DocumentInterfaceBroker>
-      document_interface_broker_blink;
-  document_interface_broker_blink.Bind(
-      mojo::PendingRemote<blink::mojom::DocumentInterfaceBroker>(
-          mojo::ScopedMessagePipeHandle(
-              params_reply.document_interface_broker_blink_handle),
-          blink::mojom::DocumentInterfaceBroker::Version_),
-      GetTaskRunner(blink::TaskType::kInternalIPC));
-
   // This method is always called by local frames, never remote frames.
 
   // Tracing analysis uses this to find main frames when this value is
@@ -4213,7 +4144,6 @@
   RenderFrameImpl* child_render_frame = RenderFrameImpl::Create(
       render_view_, params_reply.child_routing_id,
       std::move(child_interface_provider),
-      std::move(document_interface_broker_content),
       mojo::PendingRemote<blink::mojom::BrowserInterfaceBroker>(
           mojo::ScopedMessagePipeHandle(
               params_reply.browser_interface_broker_handle),
@@ -4226,8 +4156,7 @@
   child_render_frame->InitializeBlameContext(this);
   blink::WebLocalFrame* web_frame = parent->CreateLocalChild(
       scope, child_render_frame,
-      child_render_frame->blink_interface_registry_.get(),
-      document_interface_broker_blink.Unbind().PassPipe());
+      child_render_frame->blink_interface_registry_.get());
 
   child_render_frame->in_frame_tree_ = true;
   child_render_frame->Initialize();
@@ -4533,7 +4462,6 @@
 void RenderFrameImpl::DidCommitProvisionalLoad(
     const blink::WebHistoryItem& item,
     blink::WebHistoryCommitType commit_type,
-    mojo::ScopedMessagePipeHandle document_interface_broker_blink_handle,
     bool should_reset_browser_interface_broker) {
   TRACE_EVENT2("navigation,rail", "RenderFrameImpl::didCommitProvisionalLoad",
                "id", routing_id_,
@@ -4570,8 +4498,6 @@
 
   service_manager::mojom::InterfaceProviderRequest
       remote_interface_provider_request;
-  mojo::PendingReceiver<blink::mojom::DocumentInterfaceBroker>
-      document_interface_broker_receiver;
   mojo::PendingReceiver<blink::mojom::BrowserInterfaceBroker>
       browser_interface_broker_receiver;
 
@@ -4593,22 +4519,6 @@
     remote_interfaces_.Close();
     remote_interfaces_.Bind(std::move(interfaces_provider));
 
-    // If we're navigating to a new document, bind |document_interface_broker_|
-    // to a new message pipe. The receiver end of the new
-    // DocumentInterfaceBroker interface will be sent over as part of
-    // DidCommitProvisionalLoad. After the RFHI receives the commit
-    // confirmation, it will immediately close the old message pipe to avoid
-    // Get<interface> calls racing with navigation commit, and bind the receiver
-    // end of the message pipe created here. Must initialize
-    // |document_interface_broker_| with a new working pipe *before* observers
-    // receive DidCommitProvisionalLoad, so they can already receive remote
-    // interfaces. The interface receivers will be serviced once the
-    // DocumentInterfaceBroker interface receiver is bound by the
-    // RenderFrameHostImpl.
-    document_interface_broker_.reset();
-    document_interface_broker_receiver =
-        document_interface_broker_.BindNewPipeAndPassReceiver();
-
     // If we're navigating to a new document, bind
     // |browser_interface_broker_proxy_| to a new browser interface broker. The
     // request end of the new BrowserInterfaceBroker interface will be sent over
@@ -4663,9 +4573,6 @@
       should_reset_browser_interface_broker
           ? mojom::DidCommitProvisionalLoadInterfaceParams::New(
                 std::move(remote_interface_provider_request),
-                std::move(document_interface_broker_receiver),
-                mojo::PendingReceiver<blink::mojom::DocumentInterfaceBroker>(
-                    std::move(document_interface_broker_blink_handle)),
                 std::move(browser_interface_broker_receiver))
           : nullptr);
 
diff --git a/content/renderer/render_frame_impl.h b/content/renderer/render_frame_impl.h
index 77744ae5..07d02f7 100644
--- a/content/renderer/render_frame_impl.h
+++ b/content/renderer/render_frame_impl.h
@@ -219,10 +219,6 @@
   static void CreateFrame(
       int routing_id,
       service_manager::mojom::InterfaceProviderPtr interface_provider,
-      mojo::PendingRemote<blink::mojom::DocumentInterfaceBroker>
-          document_interface_broker_content,
-      mojo::PendingRemote<blink::mojom::DocumentInterfaceBroker>
-          document_interface_broker_blink,
       mojo::PendingRemote<blink::mojom::BrowserInterfaceBroker>
           browser_interface_broker,
       int previous_routing_id,
@@ -248,8 +244,6 @@
         RenderViewImpl* render_view,
         int32_t routing_id,
         service_manager::mojom::InterfaceProviderPtr interface_provider,
-        mojo::Remote<blink::mojom::DocumentInterfaceBroker>
-            document_interface_broker_content,
         mojo::PendingRemote<blink::mojom::BrowserInterfaceBroker>
             browser_interface_broker,
         const base::UnguessableToken& devtools_frame_token);
@@ -261,8 +255,6 @@
     RenderViewImpl* render_view;
     int32_t routing_id;
     service_manager::mojom::InterfaceProviderPtr interface_provider;
-    mojo::Remote<blink::mojom::DocumentInterfaceBroker>
-        document_interface_broker_content;
     mojo::PendingRemote<blink::mojom::BrowserInterfaceBroker>
         browser_interface_broker;
     base::UnguessableToken devtools_frame_token;
@@ -464,7 +456,6 @@
       const std::string& interface_name,
       mojo::ScopedMessagePipeHandle interface_pipe) override;
   service_manager::InterfaceProvider* GetRemoteInterfaces() override;
-  blink::mojom::DocumentInterfaceBroker* GetDocumentInterfaceBroker() override;
   blink::AssociatedInterfaceRegistry* GetAssociatedInterfaceRegistry() override;
   blink::AssociatedInterfaceProvider* GetRemoteAssociatedInterfaces() override;
 #if BUILDFLAG(ENABLE_PLUGINS)
@@ -729,7 +720,6 @@
   void DidCommitProvisionalLoad(
       const blink::WebHistoryItem& item,
       blink::WebHistoryCommitType commit_type,
-      mojo::ScopedMessagePipeHandle document_interface_broker_blink_handle,
       bool should_reset_browser_interface_broker) override;
   void DidCreateNewDocument() override;
   void DidClearWindowObject() override;
@@ -832,7 +822,6 @@
   void BubbleLogicalScrollInParentFrame(
       blink::WebScrollDirection direction,
       ui::input_types::ScrollGranularity granularity) override;
-  void VisibilityChanged(blink::mojom::FrameVisibility visibility) override;
   blink::BrowserInterfaceBrokerProxy* GetBrowserInterfaceBroker() override;
 
   // WebFrameSerializerClient implementation:
@@ -973,10 +962,6 @@
 
   void TransferUserActivationFrom(blink::WebLocalFrame* source_frame) override;
 
-  // Used in tests to override DocumentInterfaceBroker's methods
-  void SetDocumentInterfaceBrokerForTesting(
-      mojo::PendingRemote<blink::mojom::DocumentInterfaceBroker> test_broker);
-
   // Used in tests to install a fake WebURLLoaderFactory via
   // RenderViewTest::CreateFakeWebURLLoaderFactory().
   void SetWebURLLoaderFactoryOverrideForTest(
@@ -1060,15 +1045,13 @@
   // Creates a new RenderFrame. |render_view| is the RenderView object that this
   // frame belongs to, |interface_provider| is the RenderFrameHost's
   // InterfaceProvider through which services are exposed to the RenderFrame,
-  // and |document_interface_broker_content| is the RenderFrameHost's
-  // DocumentInterfaceBroker through which services are exposed to the
+  // and |browser_interface_broker| is the RenderFrameHost's
+  // BrowserInterfaceBroker through which services are exposed to the
   // RenderFrame.
   static RenderFrameImpl* Create(
       RenderViewImpl* render_view,
       int32_t routing_id,
       service_manager::mojom::InterfaceProviderPtr interface_provider,
-      mojo::Remote<blink::mojom::DocumentInterfaceBroker>
-          document_interface_broker_content,
       mojo::PendingRemote<blink::mojom::BrowserInterfaceBroker>
           browser_interface_broker,
       const base::UnguessableToken& devtools_frame_token);
@@ -1552,8 +1535,6 @@
   service_manager::InterfaceProvider remote_interfaces_;
   std::unique_ptr<BlinkInterfaceRegistryImpl> blink_interface_registry_;
 
-  mojo::Remote<blink::mojom::DocumentInterfaceBroker>
-      document_interface_broker_;
   blink::BrowserInterfaceBrokerProxy browser_interface_broker_proxy_;
 
   service_manager::BindSourceInfo local_info_;
diff --git a/content/renderer/render_frame_impl_browsertest.cc b/content/renderer/render_frame_impl_browsertest.cc
index 1822a3c4..89bef6f1 100644
--- a/content/renderer/render_frame_impl_browsertest.cc
+++ b/content/renderer/render_frame_impl_browsertest.cc
@@ -38,7 +38,6 @@
 #include "content/renderer/render_view_impl.h"
 #include "content/test/fake_compositor_dependencies.h"
 #include "content/test/frame_host_test_interface.mojom.h"
-#include "content/test/test_document_interface_broker.h"
 #include "content/test/test_render_frame.h"
 #include "mojo/public/cpp/bindings/pending_receiver.h"
 #include "mojo/public/cpp/bindings/pending_remote.h"
@@ -48,7 +47,6 @@
 #include "services/service_manager/public/mojom/interface_provider.mojom.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
-#include "third_party/blink/public/mojom/frame/frame_host_test_interface.mojom.h"
 #include "third_party/blink/public/platform/web_runtime_features.h"
 #include "third_party/blink/public/platform/web_string.h"
 #include "third_party/blink/public/platform/web_url.h"
@@ -73,9 +71,6 @@
 const char kParentFrameHTML[] = "Parent frame <iframe name='frame'></iframe>";
 
 const char kAutoplayTestOrigin[] = "https://www.google.com";
-
-constexpr char kGetNameTestResponse[] = "TestName";
-
 }  // namespace
 
 // RenderFrameImplTest creates a RenderFrameImpl that is a child of the
@@ -113,16 +108,6 @@
     service_manager::mojom::InterfaceProviderPtr stub_interface_provider;
     mojo::MakeRequest(&stub_interface_provider);
 
-    mojo::PendingRemote<blink::mojom::DocumentInterfaceBroker>
-        stub_document_interface_broker_content;
-    ignore_result(stub_document_interface_broker_content
-                      .InitWithNewPipeAndPassReceiver());
-
-    mojo::PendingRemote<blink::mojom::DocumentInterfaceBroker>
-        stub_document_interface_broker_blink;
-    ignore_result(
-        stub_document_interface_broker_blink.InitWithNewPipeAndPassReceiver());
-
     mojo::PendingRemote<blink::mojom::BrowserInterfaceBroker>
         stub_browser_interface_broker;
     ignore_result(
@@ -130,8 +115,6 @@
 
     RenderFrameImpl::CreateFrame(
         kSubframeRouteId, std::move(stub_interface_provider),
-        std::move(stub_document_interface_broker_content),
-        std::move(stub_document_interface_broker_blink),
         std::move(stub_browser_interface_broker), MSG_ROUTING_NONE,
         MSG_ROUTING_NONE, kFrameProxyRouteId, MSG_ROUTING_NONE,
         base::UnguessableToken::Create(), frame_replication_state,
@@ -267,11 +250,6 @@
 TEST_F(RenderFrameImplTest, LocalChildFrameWasShown) {
   service_manager::mojom::InterfaceProviderPtr stub_interface_provider;
   mojo::MakeRequest(&stub_interface_provider);
-
-  mojo::Remote<blink::mojom::DocumentInterfaceBroker>
-      stub_document_interface_broker;
-  ignore_result(stub_document_interface_broker.BindNewPipeAndPassReceiver());
-
   mojo::PendingRemote<blink::mojom::BrowserInterfaceBroker>
       stub_browser_interface_broker;
   ignore_result(stub_browser_interface_broker.InitWithNewPipeAndPassReceiver());
@@ -281,15 +259,13 @@
   RenderFrameImpl* grandchild =
       RenderFrameImpl::Create(frame()->render_view(), kEmbeddedSubframeRouteId,
                               std::move(stub_interface_provider),
-                              std::move(stub_document_interface_broker),
                               std::move(stub_browser_interface_broker),
                               base::UnguessableToken::Create());
   blink::WebLocalFrame* parent_web_frame = frame()->GetWebFrame();
 
   parent_web_frame->CreateLocalChild(
       blink::WebTreeScopeType::kDocument, grandchild,
-      grandchild->blink_interface_registry_.get(),
-      stub_document_interface_broker.BindNewPipeAndPassReceiver().PassPipe());
+      grandchild->blink_interface_registry_.get());
   grandchild->in_frame_tree_ = true;
   grandchild->Initialize();
 
@@ -521,74 +497,6 @@
   }
 };
 
-// TODO(crbug.com/718652): this is a blink version of the FrameHostTestInterface
-// implementation. The non-blink one will be removed when all clients are
-// converted to use DocumentInterfaceBroker.
-class BlinkFrameHostTestInterfaceImpl
-    : public blink::mojom::FrameHostTestInterface {
- public:
-  BlinkFrameHostTestInterfaceImpl() {}
-  ~BlinkFrameHostTestInterfaceImpl() override {}
-
-  void BindAndFlush(
-      mojo::PendingReceiver<blink::mojom::FrameHostTestInterface> receiver) {
-    receiver_.Bind(std::move(receiver));
-    receiver_.WaitForIncomingCall();
-  }
-
-  const base::Optional<SourceAnnotation>& ping_source() const {
-    return ping_source_;
-  }
-
- protected:
-  // blink::mojom::FrameHostTestInterface
-  void Ping(const GURL& url, const std::string& event) override {
-    ping_source_ = SourceAnnotation{url, event};
-  }
-  void GetName(GetNameCallback callback) override {
-    std::move(callback).Run(kGetNameTestResponse);
-  }
-
- private:
-  mojo::Receiver<blink::mojom::FrameHostTestInterface> receiver_{this};
-  base::Optional<SourceAnnotation> ping_source_;
-
-  DISALLOW_COPY_AND_ASSIGN(BlinkFrameHostTestInterfaceImpl);
-};
-
-class FrameHostTestDocumentInterfaceBroker
-    : public TestDocumentInterfaceBroker {
- public:
-  FrameHostTestDocumentInterfaceBroker(
-      blink::mojom::DocumentInterfaceBroker* document_interface_broker,
-      mojo::PendingReceiver<blink::mojom::DocumentInterfaceBroker> receiver)
-      : TestDocumentInterfaceBroker(document_interface_broker,
-                                    std::move(receiver)) {}
-
-  void GetFrameHostTestInterface(
-      mojo::PendingReceiver<blink::mojom::FrameHostTestInterface> receiver)
-      override {
-    BlinkFrameHostTestInterfaceImpl impl;
-    impl.BindAndFlush(std::move(receiver));
-  }
-};
-
-TEST_F(RenderFrameImplTest, TestDocumentInterfaceBrokerOverride) {
-  mojo::PendingRemote<blink::mojom::DocumentInterfaceBroker> doc;
-  FrameHostTestDocumentInterfaceBroker frame_interface_broker(
-      frame()->GetDocumentInterfaceBroker(),
-      doc.InitWithNewPipeAndPassReceiver());
-  frame()->SetDocumentInterfaceBrokerForTesting(std::move(doc));
-
-  mojo::Remote<blink::mojom::FrameHostTestInterface> frame_test;
-  frame()->GetDocumentInterfaceBroker()->GetFrameHostTestInterface(
-      frame_test.BindNewPipeAndPassReceiver());
-  frame_test->GetName(base::BindOnce([](const std::string& result) {
-    EXPECT_EQ(result, kGetNameTestResponse);
-  }));
-  frame_interface_broker.Flush();
-}
-
 // RenderFrameRemoteInterfacesTest ------------------------------------
 
 namespace {
@@ -650,34 +558,6 @@
   DISALLOW_COPY_AND_ASSIGN(TestSimpleInterfaceProviderImpl);
 };
 
-class TestSimpleDocumentInterfaceBrokerImpl
-    : public blink::mojom::DocumentInterfaceBroker {
- public:
-  using BinderCallback = base::RepeatingCallback<void(
-      mojo::PendingReceiver<blink::mojom::FrameHostTestInterface>)>;
-  explicit TestSimpleDocumentInterfaceBrokerImpl(BinderCallback binder_callback)
-      : binder_callback_(binder_callback) {}
-  void BindAndFlush(
-      mojo::PendingReceiver<blink::mojom::DocumentInterfaceBroker> receiver) {
-    ASSERT_FALSE(receiver_.is_bound());
-    receiver_.Bind(std::move(receiver));
-    receiver_.FlushForTesting();
-  }
-
- private:
-  // blink::mojom::DocumentInterfaceBroker
-  void GetFrameHostTestInterface(
-      mojo::PendingReceiver<blink::mojom::FrameHostTestInterface> receiver)
-      override {
-    binder_callback_.Run(std::move(receiver));
-  }
-
-  mojo::Receiver<blink::mojom::DocumentInterfaceBroker> receiver_{this};
-  BinderCallback binder_callback_;
-
-  DISALLOW_COPY_AND_ASSIGN(TestSimpleDocumentInterfaceBrokerImpl);
-};
-
 class TestSimpleBrowserInterfaceBrokerImpl
     : public blink::mojom::BrowserInterfaceBroker {
  public:
@@ -759,16 +639,6 @@
         !document.IsNull() ? GURL(document.Url()) : GURL(kNoDocumentMarkerURL),
         event);
 
-    mojo::Remote<blink::mojom::FrameHostTestInterface> blink_remote;
-    blink::mojom::DocumentInterfaceBroker* document_interface_broker =
-        render_frame()->GetDocumentInterfaceBroker();
-    DCHECK(document_interface_broker);
-    document_interface_broker->GetFrameHostTestInterface(
-        blink_remote.BindNewPipeAndPassReceiver());
-    blink_remote->Ping(
-        !document.IsNull() ? GURL(document.Url()) : GURL(kNoDocumentMarkerURL),
-        event);
-
     remote.reset();
     render_frame()->GetBrowserInterfaceBroker()->GetInterface(
         remote.BindNewPipeAndPassReceiver());
@@ -891,9 +761,6 @@
     interface_request_for_first_document_ =
         frame_->TakeLastInterfaceProviderRequest();
 
-    document_interface_broker_receiver_for_first_document_ =
-        frame_->TakeLastDocumentInterfaceBrokerReceiver();
-
     browser_interface_broker_receiver_for_first_document_ =
         frame_->TakeLastBrowserInterfaceBrokerReceiver();
   }
@@ -903,12 +770,6 @@
     return std::move(interface_request_for_initial_empty_document_);
   }
 
-  mojo::PendingReceiver<blink::mojom::DocumentInterfaceBroker>
-  document_interface_broker_receiver_for_initial_empty_document() {
-    return std::move(
-        document_interface_broker_receiver_for_initial_empty_document_);
-  }
-
   mojo::PendingReceiver<blink::mojom::BrowserInterfaceBroker>
   browser_interface_broker_receiver_for_initial_empty_document() {
     return std::move(
@@ -920,11 +781,6 @@
     return std::move(interface_request_for_first_document_);
   }
 
-  mojo::PendingReceiver<blink::mojom::DocumentInterfaceBroker>
-  document_interface_broker_receiver_for_first_document() {
-    return std::move(document_interface_broker_receiver_for_first_document_);
-  }
-
   mojo::PendingReceiver<blink::mojom::BrowserInterfaceBroker>
   browser_interface_broker_receiver_for_first_document() {
     return std::move(browser_interface_broker_receiver_for_first_document_);
@@ -950,8 +806,6 @@
 
     interface_request_for_initial_empty_document_ =
         frame->TakeLastInterfaceProviderRequest();
-    document_interface_broker_receiver_for_initial_empty_document_ =
-        frame->TakeLastDocumentInterfaceBrokerReceiver();
     browser_interface_broker_receiver_for_initial_empty_document_ =
         frame_->TakeLastBrowserInterfaceBrokerReceiver();
     EXPECT_TRUE(frame->current_history_item().IsNull());
@@ -970,11 +824,6 @@
   service_manager::mojom::InterfaceProviderRequest
       interface_request_for_first_document_;
 
-  mojo::PendingReceiver<blink::mojom::DocumentInterfaceBroker>
-      document_interface_broker_receiver_for_initial_empty_document_;
-  mojo::PendingReceiver<blink::mojom::DocumentInterfaceBroker>
-      document_interface_broker_receiver_for_first_document_;
-
   mojo::PendingReceiver<blink::mojom::BrowserInterfaceBroker>
       browser_interface_broker_receiver_for_initial_empty_document_;
   mojo::PendingReceiver<blink::mojom::BrowserInterfaceBroker>
@@ -989,8 +838,6 @@
 // FrameHostTestInterface requests.
 void ExpectPendingInterfaceRequestsFromSources(
     service_manager::mojom::InterfaceProviderRequest interface_provider_request,
-    mojo::PendingReceiver<blink::mojom::DocumentInterfaceBroker>
-        document_interface_broker_receiver,
     mojo::PendingReceiver<blink::mojom::BrowserInterfaceBroker>
         browser_interface_broker_receiver,
     std::vector<SourceAnnotation> expected_sources) {
@@ -1010,21 +857,6 @@
   provider.BindAndFlush(std::move(interface_provider_request));
   EXPECT_THAT(sources, ::testing::ElementsAreArray(expected_sources));
 
-  std::vector<SourceAnnotation> document_interface_broker_sources;
-  ASSERT_TRUE(document_interface_broker_receiver.is_valid());
-  TestSimpleDocumentInterfaceBrokerImpl broker(base::BindLambdaForTesting(
-      [&document_interface_broker_sources](
-          mojo::PendingReceiver<blink::mojom::FrameHostTestInterface>
-              receiver) {
-        BlinkFrameHostTestInterfaceImpl impl;
-        impl.BindAndFlush(std::move(receiver));
-        ASSERT_TRUE(impl.ping_source().has_value());
-        document_interface_broker_sources.push_back(impl.ping_source().value());
-      }));
-  broker.BindAndFlush(std::move(document_interface_broker_receiver));
-  EXPECT_THAT(document_interface_broker_sources,
-              ::testing::ElementsAreArray(expected_sources));
-
   std::vector<SourceAnnotation> browser_interface_broker_sources;
   ASSERT_TRUE(browser_interface_broker_receiver.is_valid());
   TestSimpleBrowserInterfaceBrokerImpl browser_broker(
@@ -1088,7 +920,7 @@
 // Expect that |remote_interfaces_| is bound before the first committed load in
 // a child frame, and then re-bound on the first commit.
 // TODO(crbug.com/718652): when all clients are converted to use
-// DocumentInterfaceBroker, InterfaceProviderRequest-related code will be
+// BrowserInterfaceBroker, InterfaceProviderRequest-related code will be
 // removed.
 TEST_F(RenderFrameRemoteInterfacesTest, ChildFrameAtFirstCommittedLoad) {
   ScopedNewFrameInterfaceProviderExerciser child_frame_exerciser(
@@ -1107,8 +939,6 @@
   ExpectPendingInterfaceRequestsFromSources(
       child_frame_exerciser.interface_request_for_initial_empty_document(),
       child_frame_exerciser
-          .document_interface_broker_receiver_for_initial_empty_document(),
-      child_frame_exerciser
           .browser_interface_broker_receiver_for_initial_empty_document(),
       {{GURL(kNoDocumentMarkerURL), kFrameEventDidCreateNewFrame},
        {initial_empty_url, kFrameEventDidCreateNewDocument},
@@ -1121,8 +951,6 @@
   ExpectPendingInterfaceRequestsFromSources(
       child_frame_exerciser.interface_request_for_first_document(),
       child_frame_exerciser
-          .document_interface_broker_receiver_for_first_document(),
-      child_frame_exerciser
           .browser_interface_broker_receiver_for_first_document(),
       {{child_frame_url, kFrameEventDidCommitProvisionalLoad},
        {child_frame_url, kFrameEventDidCreateDocumentElement}});
@@ -1131,7 +959,7 @@
 // Expect that |remote_interfaces_| is bound before the first committed load in
 // the main frame of an opened window, and then re-bound on the first commit.
 // TODO(crbug.com/718652): when all clients are converted to use
-// DocumentInterfaceBroker, InterfaceProviderRequest-related code will be
+// BrowserInterfaceBroker, InterfaceProviderRequest-related code will be
 // removed.
 TEST_F(RenderFrameRemoteInterfacesTest,
        MainFrameOfOpenedWindowAtFirstCommittedLoad) {
@@ -1163,8 +991,6 @@
   ExpectPendingInterfaceRequestsFromSources(
       main_frame_exerciser.interface_request_for_initial_empty_document(),
       main_frame_exerciser
-          .document_interface_broker_receiver_for_initial_empty_document(),
-      main_frame_exerciser
           .browser_interface_broker_receiver_for_initial_empty_document(),
       {{initial_empty_url, kFrameEventDidCreateNewFrame},
        {new_window_url, kFrameEventReadyToCommitNavigation},
@@ -1172,8 +998,6 @@
   ExpectPendingInterfaceRequestsFromSources(
       main_frame_exerciser.interface_request_for_first_document(),
       main_frame_exerciser
-          .document_interface_broker_receiver_for_first_document(),
-      main_frame_exerciser
           .browser_interface_broker_receiver_for_first_document(),
       {{new_window_url, kFrameEventDidCommitProvisionalLoad},
        {new_window_url, kFrameEventDidCreateDocumentElement}});
@@ -1202,7 +1026,7 @@
 // their own DocumentLoader in blink and model them as a real navigation, we
 // should add a test case here.
 // TODO(crbug.com/718652): when all clients are converted to use
-// DocumentInterfaceBroker, InterfaceProviderRequest-related code will be
+// BrowserInterfaceBroker, InterfaceProviderRequest-related code will be
 // removed.
 TEST_F(RenderFrameRemoteInterfacesTest,
        ChildFrameReusingWindowOfInitialDocument) {
@@ -1229,8 +1053,6 @@
     ExpectPendingInterfaceRequestsFromSources(
         child_frame_exerciser.interface_request_for_initial_empty_document(),
         child_frame_exerciser
-            .document_interface_broker_receiver_for_initial_empty_document(),
-        child_frame_exerciser
             .browser_interface_broker_receiver_for_initial_empty_document(),
         {{GURL(kNoDocumentMarkerURL), kFrameEventDidCreateNewFrame},
          {initial_empty_url, kFrameEventDidCreateNewDocument},
@@ -1242,10 +1064,6 @@
 
     auto request = child_frame_exerciser.interface_request_for_first_document();
     ASSERT_FALSE(request.is_pending());
-    auto document_interface_broker_receiver =
-        child_frame_exerciser
-            .document_interface_broker_receiver_for_first_document();
-    ASSERT_FALSE(document_interface_broker_receiver.is_valid());
     auto browser_interface_broker_receiver =
         child_frame_exerciser
             .browser_interface_broker_receiver_for_first_document();
@@ -1256,7 +1074,7 @@
 // Expect that |remote_interfaces_| is bound to a new pipe on cross-document
 // navigations.
 // TODO(crbug.com/718652): when all clients are converted to use
-// DocumentInterfaceBroker, InterfaceProviderRequest-related code will be
+// BrowserInterfaceBroker, InterfaceProviderRequest-related code will be
 // removed.
 TEST_F(RenderFrameRemoteInterfacesTest, ReplacedOnNonSameDocumentNavigation) {
   LoadHTMLWithUrlOverride("", kTestFirstURL);
@@ -1264,9 +1082,6 @@
   auto interface_provider_request_for_first_document =
       GetMainRenderFrame()->TakeLastInterfaceProviderRequest();
 
-  auto document_interface_broker_receiver_for_first_document =
-      GetMainRenderFrame()->TakeLastDocumentInterfaceBrokerReceiver();
-
   auto browser_interface_broker_receiver_for_first_document =
       GetMainRenderFrame()->TakeLastBrowserInterfaceBrokerReceiver();
 
@@ -1278,31 +1093,24 @@
   auto interface_provider_request_for_second_document =
       GetMainRenderFrame()->TakeLastInterfaceProviderRequest();
 
-  auto document_interface_broker_request_for_second_document =
-      GetMainRenderFrame()->TakeLastDocumentInterfaceBrokerReceiver();
-
   auto browser_interface_broker_receiver_for_second_document =
       GetMainRenderFrame()->TakeLastBrowserInterfaceBrokerReceiver();
 
   ASSERT_TRUE(interface_provider_request_for_first_document.is_pending());
-  ASSERT_TRUE(document_interface_broker_receiver_for_first_document.is_valid());
   ASSERT_TRUE(browser_interface_broker_receiver_for_first_document.is_valid());
 
   ExpectPendingInterfaceRequestsFromSources(
       std::move(interface_provider_request_for_first_document),
-      std::move(document_interface_broker_receiver_for_first_document),
       std::move(browser_interface_broker_receiver_for_first_document),
       {{GURL(kTestFirstURL), kFrameEventAfterCommit},
        {GURL(kTestSecondURL), kFrameEventReadyToCommitNavigation},
        {GURL(kTestSecondURL), kFrameEventDidCreateNewDocument}});
 
   ASSERT_TRUE(interface_provider_request_for_second_document.is_pending());
-  ASSERT_TRUE(document_interface_broker_request_for_second_document.is_valid());
   ASSERT_TRUE(browser_interface_broker_receiver_for_second_document.is_valid());
 
   ExpectPendingInterfaceRequestsFromSources(
       std::move(interface_provider_request_for_second_document),
-      std::move(document_interface_broker_request_for_second_document),
       std::move(browser_interface_broker_receiver_for_second_document),
       {{GURL(kTestSecondURL), kFrameEventDidCommitProvisionalLoad},
        {GURL(kTestSecondURL), kFrameEventDidCreateDocumentElement}});
@@ -1312,7 +1120,7 @@
 // navigations, i.e. the existing InterfaceProvider connection is continued to
 // be used.
 // TODO(crbug.com/718652): when all clients are converted to use
-// DocumentInterfaceBroker, InterfaceProviderRequest-related code will be
+// BrowserInterfaceBroker, InterfaceProviderRequest-related code will be
 // removed.
 TEST_F(RenderFrameRemoteInterfacesTest, ReusedOnSameDocumentNavigation) {
   LoadHTMLWithUrlOverride("", kTestFirstURL);
@@ -1320,9 +1128,6 @@
   auto interface_provider_request =
       GetMainRenderFrame()->TakeLastInterfaceProviderRequest();
 
-  auto document_interface_broker =
-      GetMainRenderFrame()->TakeLastDocumentInterfaceBrokerReceiver();
-
   auto browser_interface_broker_receiver =
       GetMainRenderFrame()->TakeLastBrowserInterfaceBrokerReceiver();
 
@@ -1332,17 +1137,11 @@
   EXPECT_FALSE(
       GetMainRenderFrame()->TakeLastInterfaceProviderRequest().is_pending());
 
-  EXPECT_FALSE(GetMainRenderFrame()
-                   ->TakeLastDocumentInterfaceBrokerReceiver()
-                   .is_valid());
-
   ASSERT_TRUE(interface_provider_request.is_pending());
-  ASSERT_TRUE(document_interface_broker.is_valid());
   ASSERT_TRUE(browser_interface_broker_receiver.is_valid());
 
   ExpectPendingInterfaceRequestsFromSources(
       std::move(interface_provider_request),
-      std::move(document_interface_broker),
       std::move(browser_interface_broker_receiver),
       {{GURL(kTestFirstURL), kFrameEventDidCommitSameDocumentLoad}});
 }
diff --git a/content/renderer/render_frame_proxy.cc b/content/renderer/render_frame_proxy.cc
index 52f2369..cf47b2e 100644
--- a/content/renderer/render_frame_proxy.cc
+++ b/content/renderer/render_frame_proxy.cc
@@ -837,11 +837,6 @@
        intersection_state.occlusion_state}));
 }
 
-void RenderFrameProxy::VisibilityChanged(
-    blink::mojom::FrameVisibility visibility) {
-  Send(new FrameHostMsg_VisibilityChanged(routing_id_, visibility));
-}
-
 void RenderFrameProxy::SetIsInert(bool inert) {
   Send(new FrameHostMsg_SetIsInert(routing_id_, inert));
 }
diff --git a/content/renderer/render_frame_proxy.h b/content/renderer/render_frame_proxy.h
index d257487..dfacdfe 100644
--- a/content/renderer/render_frame_proxy.h
+++ b/content/renderer/render_frame_proxy.h
@@ -192,7 +192,6 @@
                          const blink::WebRect& screen_space_rect) override;
   void UpdateRemoteViewportIntersection(
       const blink::ViewportIntersectionState& intersection_state) override;
-  void VisibilityChanged(blink::mojom::FrameVisibility visibility) override;
   void SetIsInert(bool) override;
   void UpdateRenderThrottlingStatus(bool is_throttled,
                                     bool subtree_throttled) override;
diff --git a/content/renderer/render_thread_impl.cc b/content/renderer/render_thread_impl.cc
index 6bffd8b..a2a157c 100644
--- a/content/renderer/render_thread_impl.cc
+++ b/content/renderer/render_thread_impl.cc
@@ -2045,19 +2045,11 @@
   CompositorDependencies* compositor_deps = this;
   service_manager::mojom::InterfaceProviderPtr interface_provider(
       std::move(params->interface_bundle->interface_provider));
-  mojo::PendingRemote<blink::mojom::DocumentInterfaceBroker>
-      document_interface_broker_content(std::move(
-          params->interface_bundle->document_interface_broker_content));
-  mojo::PendingRemote<blink::mojom::DocumentInterfaceBroker>
-      document_interface_broker_blink(
-          std::move(params->interface_bundle->document_interface_broker_blink));
   mojo::PendingRemote<blink::mojom::BrowserInterfaceBroker>
       browser_interface_broker(
           std::move(params->interface_bundle->browser_interface_broker));
   RenderFrameImpl::CreateFrame(
       params->routing_id, std::move(interface_provider),
-      std::move(document_interface_broker_content),
-      std::move(document_interface_broker_blink),
       std::move(browser_interface_broker), params->previous_routing_id,
       params->opener_routing_id, params->parent_routing_id,
       params->previous_sibling_routing_id, params->devtools_frame_token,
diff --git a/content/renderer/render_view_browsertest.cc b/content/renderer/render_view_browsertest.cc
index b8c9f30..2e4920b0 100644
--- a/content/renderer/render_view_browsertest.cc
+++ b/content/renderer/render_view_browsertest.cc
@@ -1104,14 +1104,6 @@
   int routing_id = kProxyRoutingId + 1;
   service_manager::mojom::InterfaceProviderPtr stub_interface_provider;
   mojo::MakeRequest(&stub_interface_provider);
-  mojo::PendingRemote<blink::mojom::DocumentInterfaceBroker>
-      stub_document_interface_broker_content;
-  ignore_result(
-      stub_document_interface_broker_content.InitWithNewPipeAndPassReceiver());
-  mojo::PendingRemote<blink::mojom::DocumentInterfaceBroker>
-      stub_document_interface_broker_blink;
-  ignore_result(
-      stub_document_interface_broker_blink.InitWithNewPipeAndPassReceiver());
   mojo::PendingRemote<blink::mojom::BrowserInterfaceBroker>
       stub_browser_interface_broker;
   ignore_result(stub_browser_interface_broker.InitWithNewPipeAndPassReceiver());
@@ -1123,8 +1115,6 @@
   widget_params.visual_properties = test_visual_properties;
   RenderFrameImpl::CreateFrame(
       routing_id, std::move(stub_interface_provider),
-      std::move(stub_document_interface_broker_content),
-      std::move(stub_document_interface_broker_blink),
       std::move(stub_browser_interface_broker), kProxyRoutingId,
       MSG_ROUTING_NONE, MSG_ROUTING_NONE, MSG_ROUTING_NONE,
       base::UnguessableToken::Create(), replication_state, nullptr,
@@ -1182,22 +1172,12 @@
   int routing_id = kProxyRoutingId + 1;
   service_manager::mojom::InterfaceProviderPtr stub_interface_provider;
   mojo::MakeRequest(&stub_interface_provider);
-  mojo::PendingRemote<blink::mojom::DocumentInterfaceBroker>
-      stub_document_interface_broker_content;
-  ignore_result(
-      stub_document_interface_broker_content.InitWithNewPipeAndPassReceiver());
-  mojo::PendingRemote<blink::mojom::DocumentInterfaceBroker>
-      stub_document_interface_broker_blink;
-  ignore_result(
-      stub_document_interface_broker_blink.InitWithNewPipeAndPassReceiver());
   mojo::PendingRemote<blink::mojom::BrowserInterfaceBroker>
       stub_browser_interface_broker;
   ignore_result(stub_browser_interface_broker.InitWithNewPipeAndPassReceiver());
 
   RenderFrameImpl::CreateFrame(
       routing_id, std::move(stub_interface_provider),
-      std::move(stub_document_interface_broker_content),
-      std::move(stub_document_interface_broker_blink),
       std::move(stub_browser_interface_broker), kProxyRoutingId,
       MSG_ROUTING_NONE, frame()->GetRoutingID(), MSG_ROUTING_NONE,
       base::UnguessableToken::Create(), replication_state, nullptr,
diff --git a/content/renderer/render_view_impl.cc b/content/renderer/render_view_impl.cc
index 1fc7e19..84c8bb9 100644
--- a/content/renderer/render_view_impl.cc
+++ b/content/renderer/render_view_impl.cc
@@ -860,6 +860,11 @@
       !prefs.disable_features_depending_on_viz);
   WebRuntimeFeatures::EnableAcceleratedSmallCanvases(
       !prefs.disable_accelerated_small_canvases);
+  if (prefs.reenable_web_components_v0) {
+    WebRuntimeFeatures::EnableShadowDOMV0(true);
+    WebRuntimeFeatures::EnableCustomElementsV0(true);
+    WebRuntimeFeatures::EnableHTMLImports(true);
+  }
 #endif  // defined(OS_ANDROID)
 
   settings->SetForceDarkModeEnabled(prefs.force_dark_mode_enabled);
@@ -1394,10 +1399,6 @@
   view_params
       ->main_frame_interface_bundle = mojom::DocumentScopedInterfaceBundle::New(
       std::move(reply->main_frame_interface_bundle->interface_provider),
-      std::move(reply->main_frame_interface_bundle
-                    ->document_interface_broker_content),
-      std::move(
-          reply->main_frame_interface_bundle->document_interface_broker_blink),
       std::move(reply->main_frame_interface_bundle->browser_interface_broker));
   view_params->main_frame_widget_routing_id = reply->main_frame_widget_route_id;
   view_params->session_storage_namespace_id =
diff --git a/content/shell/BUILD.gn b/content/shell/BUILD.gn
index d8e7bb65..048213f 100644
--- a/content/shell/BUILD.gn
+++ b/content/shell/BUILD.gn
@@ -163,6 +163,8 @@
     "browser/web_test/fake_bluetooth_scanning_prompt.h",
     "browser/web_test/leak_detector.cc",
     "browser/web_test/leak_detector.h",
+    "browser/web_test/mock_client_hints_controller_delegate.cc",
+    "browser/web_test/mock_client_hints_controller_delegate.h",
     "browser/web_test/mojo_web_test_helper.cc",
     "browser/web_test/mojo_web_test_helper.h",
     "browser/web_test/secondary_test_window_observer.cc",
@@ -274,6 +276,7 @@
     "//services/network:network_service",
   ]
   deps = [
+    ":client_hints_util",
     ":resources",
     ":web_test_messages",
     ":web_test_switches",
@@ -483,6 +486,20 @@
   }
 }
 
+static_library("client_hints_util") {
+  testonly = true
+
+  sources = [
+    "utility/mock_client_hints_utils.cc",
+    "utility/mock_client_hints_utils.h",
+  ]
+
+  deps = [
+    "//content/public/common",
+    "//third_party/blink/public:blink",
+  ]
+}
+
 grit("content_shell_resources_grit") {
   testonly = true
 
diff --git a/content/shell/OWNERS b/content/shell/OWNERS
index eb6d043..e498d1e 100644
--- a/content/shell/OWNERS
+++ b/content/shell/OWNERS
@@ -2,3 +2,4 @@
 peter@chromium.org
 
 # COMPONENT: Test
+
diff --git a/content/shell/browser/shell_browser_context.cc b/content/shell/browser/shell_browser_context.cc
index 46cb647..8faba962 100644
--- a/content/shell/browser/shell_browser_context.cc
+++ b/content/shell/browser/shell_browser_context.cc
@@ -78,8 +78,8 @@
   // outstanding request while URLRequestContext's destructor ensures that there
   // are no more outstanding requests.
   if (resource_context_) {
-    BrowserThread::DeleteSoon(
-      BrowserThread::IO, FROM_HERE, resource_context_.release());
+    base::DeleteSoon(FROM_HERE, {BrowserThread::IO},
+                     resource_context_.release());
   }
   ShutdownStoragePartitions();
 }
diff --git a/content/shell/browser/shell_browser_context.h b/content/shell/browser/shell_browser_context.h
index ffe16965..d199c22 100644
--- a/content/shell/browser/shell_browser_context.h
+++ b/content/shell/browser/shell_browser_context.h
@@ -21,6 +21,7 @@
 
 class BackgroundSyncController;
 class ContentIndexProvider;
+class ClientHintsControllerDelegate;
 class DownloadManagerDelegate;
 class PermissionControllerDelegate;
 class ShellDownloadManagerDelegate;
@@ -56,11 +57,11 @@
   StorageNotificationService* GetStorageNotificationService() override;
   SSLHostStateDelegate* GetSSLHostStateDelegate() override;
   PermissionControllerDelegate* GetPermissionControllerDelegate() override;
-  ClientHintsControllerDelegate* GetClientHintsControllerDelegate() override;
   BackgroundFetchDelegate* GetBackgroundFetchDelegate() override;
   BackgroundSyncController* GetBackgroundSyncController() override;
   BrowsingDataRemoverDelegate* GetBrowsingDataRemoverDelegate() override;
   ContentIndexProvider* GetContentIndexProvider() override;
+  ClientHintsControllerDelegate* GetClientHintsControllerDelegate() override;
 
  protected:
   // Contains URLRequestContextGetter required for resource loading.
diff --git a/content/shell/browser/shell_content_browser_client.cc b/content/shell/browser/shell_content_browser_client.cc
index 6c454813..d2624e4b 100644
--- a/content/shell/browser/shell_content_browser_client.cc
+++ b/content/shell/browser/shell_content_browser_client.cc
@@ -212,6 +212,10 @@
   return BuildUserAgentFromProduct(product);
 }
 
+std::string GetShellLanguage() {
+  return "en-us,en";
+}
+
 blink::UserAgentMetadata GetShellUserAgentMetadata() {
   blink::UserAgentMetadata metadata;
 
@@ -363,7 +367,7 @@
 }
 
 std::string ShellContentBrowserClient::GetAcceptLangs(BrowserContext* context) {
-  return "en-us,en";
+  return GetShellLanguage();
 }
 
 std::string ShellContentBrowserClient::GetDefaultDownloadName() {
@@ -499,7 +503,7 @@
       network::mojom::NetworkContextParams::New();
   UpdateCorsExemptHeader(context_params.get());
   context_params->user_agent = GetUserAgent();
-  context_params->accept_language = "en-us,en";
+  context_params->accept_language = GetAcceptLangs(context);
 
 #if BUILDFLAG(ENABLE_REPORTING)
   if (base::CommandLine::ForCurrentProcess()->HasSwitch(
diff --git a/content/shell/browser/shell_content_browser_client.h b/content/shell/browser/shell_content_browser_client.h
index da8f30f..fd4138f 100644
--- a/content/shell/browser/shell_content_browser_client.h
+++ b/content/shell/browser/shell_content_browser_client.h
@@ -23,6 +23,7 @@
 class ShellBrowserMainParts;
 
 std::string GetShellUserAgent();
+std::string GetShellLanguage();
 blink::UserAgentMetadata GetShellUserAgentMetadata();
 
 class ShellContentBrowserClient : public ContentBrowserClient {
diff --git a/content/shell/browser/web_test/blink_test_controller.cc b/content/shell/browser/web_test/blink_test_controller.cc
index 24581d54..3f9ffd0a 100644
--- a/content/shell/browser/web_test/blink_test_controller.cc
+++ b/content/shell/browser/web_test/blink_test_controller.cc
@@ -62,6 +62,7 @@
 #include "content/shell/browser/shell_devtools_frontend.h"
 #include "content/shell/browser/web_test/devtools_protocol_test_bindings.h"
 #include "content/shell/browser/web_test/fake_bluetooth_chooser.h"
+#include "content/shell/browser/web_test/mock_client_hints_controller_delegate.h"
 #include "content/shell/browser/web_test/test_info_extractor.h"
 #include "content/shell/browser/web_test/web_test_bluetooth_chooser_factory.h"
 #include "content/shell/browser/web_test/web_test_content_browser_client.h"
@@ -391,6 +392,9 @@
 
   ShellBrowserContext* browser_context =
       ShellContentBrowserClient::Get()->browser_context();
+
+  browser_context->GetClientHintsControllerDelegate()->ResetForTesting();
+
   initial_size_ = Shell::GetShellDefaultSize();
   if (!main_window_) {
     main_window_ = content::Shell::CreateNewWindow(
diff --git a/content/shell/browser/web_test/mock_client_hints_controller_delegate.cc b/content/shell/browser/web_test/mock_client_hints_controller_delegate.cc
new file mode 100644
index 0000000..0ef2fd9
--- /dev/null
+++ b/content/shell/browser/web_test/mock_client_hints_controller_delegate.cc
@@ -0,0 +1,64 @@
+// 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 "content/shell/browser/web_test/mock_client_hints_controller_delegate.h"
+#include "content/public/common/origin_util.h"
+#include "content/shell/browser/shell_content_browser_client.h"
+#include "mojo/public/cpp/bindings/self_owned_receiver.h"
+#include "third_party/blink/public/common/user_agent/user_agent_metadata.h"
+#include "url/gurl.h"
+
+namespace content {
+
+MockClientHintsControllerDelegate::MockClientHintsControllerDelegate() {}
+MockClientHintsControllerDelegate::~MockClientHintsControllerDelegate() {}
+
+void MockClientHintsControllerDelegate::Bind(
+    mojo::PendingReceiver<client_hints::mojom::ClientHints> receiver) {
+  receivers_.Add(this, std::move(receiver));
+}
+
+network::NetworkQualityTracker*
+MockClientHintsControllerDelegate::GetNetworkQualityTracker() {
+  return nullptr;
+}
+
+bool MockClientHintsControllerDelegate::IsJavaScriptAllowed(const GURL& url) {
+  return true;
+}
+
+std::string MockClientHintsControllerDelegate::GetAcceptLanguageString() {
+  return content::GetShellLanguage();
+}
+
+blink::UserAgentMetadata
+MockClientHintsControllerDelegate::GetUserAgentMetadata() {
+  return content::GetShellUserAgentMetadata();
+}
+
+void MockClientHintsControllerDelegate::PersistClientHints(
+    const url::Origin& primary_origin,
+    const std::vector<::blink::mojom::WebClientHintsType>& client_hints,
+    base::TimeDelta expiration_duration) {
+  blink::WebEnabledClientHints web_client_hints;
+  for (const auto& type : client_hints) {
+    web_client_hints.SetIsEnabled(type, true);
+  }
+
+  PersistClientHintsHelper(primary_origin.GetURL(), web_client_hints,
+                           expiration_duration, &client_hints_map_);
+}
+
+// Get which client hints opt-ins were persisted on current origin.
+void MockClientHintsControllerDelegate::GetAllowedClientHintsFromSource(
+    const GURL& url,
+    blink::WebEnabledClientHints* client_hints) {
+  GetAllowedClientHintsFromSourceHelper(url, client_hints_map_, client_hints);
+}
+
+void MockClientHintsControllerDelegate::ResetForTesting() {
+  client_hints_map_.clear();
+}
+
+}  // end namespace content
diff --git a/content/shell/browser/web_test/mock_client_hints_controller_delegate.h b/content/shell/browser/web_test/mock_client_hints_controller_delegate.h
new file mode 100644
index 0000000..015bf4ae
--- /dev/null
+++ b/content/shell/browser/web_test/mock_client_hints_controller_delegate.h
@@ -0,0 +1,52 @@
+// 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 CONTENT_SHELL_BROWSER_WEB_TEST_MOCK_CLIENT_HINTS_CONTROLLER_DELEGATE_H_
+#define CONTENT_SHELL_BROWSER_WEB_TEST_MOCK_CLIENT_HINTS_CONTROLLER_DELEGATE_H_
+
+#include "content/public/browser/client_hints_controller_delegate.h"
+#include "content/shell/utility/mock_client_hints_utils.h"
+#include "mojo/public/cpp/bindings/associated_receiver_set.h"
+#include "services/network/public/cpp/network_quality_tracker.h"
+#include "third_party/blink/public/platform/web_client_hints_type.h"
+#include "url/origin.h"
+
+namespace content {
+
+class MockClientHintsControllerDelegate
+    : public content::ClientHintsControllerDelegate {
+ public:
+  MockClientHintsControllerDelegate();
+  ~MockClientHintsControllerDelegate() override;
+  network::NetworkQualityTracker* GetNetworkQualityTracker() override;
+
+  // Get which client hints opt-ins were persisted on current origin.
+  void GetAllowedClientHintsFromSource(
+      const GURL& url,
+      blink::WebEnabledClientHints* client_hints) override;
+
+  bool IsJavaScriptAllowed(const GURL& url) override;
+
+  std::string GetAcceptLanguageString() override;
+
+  blink::UserAgentMetadata GetUserAgentMetadata() override;
+  // mojom::ClientHints implementation.
+  void PersistClientHints(
+      const url::Origin& primary_origin,
+      const std::vector<::blink::mojom::WebClientHintsType>& client_hints,
+      base::TimeDelta expiration_duration) override;
+
+  void Bind(mojo::PendingReceiver<client_hints::mojom::ClientHints> receiver)
+      override;
+
+  void ResetForTesting() override;
+
+ private:
+  ClientHintsContainer client_hints_map_;
+  mojo::ReceiverSet<client_hints::mojom::ClientHints> receivers_;
+
+  DISALLOW_COPY_AND_ASSIGN(MockClientHintsControllerDelegate);
+};
+}  // end namespace content
+#endif  // CONTENT_SHELL_BROWSER_WEB_TEST_MOCK_CLIENT_HINTS_CONTROLLER_DELEGATE_H_
diff --git a/content/shell/browser/web_test/web_test_browser_context.cc b/content/shell/browser/web_test/web_test_browser_context.cc
index 4b3f4d5..bc4635f 100644
--- a/content/shell/browser/web_test/web_test_browser_context.cc
+++ b/content/shell/browser/web_test/web_test_browser_context.cc
@@ -16,6 +16,7 @@
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/push_messaging_service.h"
 #include "content/public/browser/resource_context.h"
+#include "content/shell/browser/web_test/mock_client_hints_controller_delegate.h"
 #include "content/shell/browser/web_test/web_test_background_fetch_delegate.h"
 #include "content/shell/browser/web_test/web_test_content_index_provider.h"
 #include "content/shell/browser/web_test/web_test_download_manager_delegate.h"
@@ -98,4 +99,13 @@
   return content_index_provider_.get();
 }
 
+ClientHintsControllerDelegate*
+WebTestBrowserContext::GetClientHintsControllerDelegate() {
+  if (!client_hints_controller_delegate_) {
+    client_hints_controller_delegate_ =
+        std::make_unique<content::MockClientHintsControllerDelegate>();
+  }
+  return client_hints_controller_delegate_.get();
+}
+
 }  // namespace content
diff --git a/content/shell/browser/web_test/web_test_browser_context.h b/content/shell/browser/web_test/web_test_browser_context.h
index 054c2360..af55eb6 100644
--- a/content/shell/browser/web_test/web_test_browser_context.h
+++ b/content/shell/browser/web_test/web_test_browser_context.h
@@ -36,6 +36,7 @@
   BackgroundFetchDelegate* GetBackgroundFetchDelegate() override;
   BackgroundSyncController* GetBackgroundSyncController() override;
   ContentIndexProvider* GetContentIndexProvider() override;
+  ClientHintsControllerDelegate* GetClientHintsControllerDelegate() override;
 
   WebTestPermissionManager* GetWebTestPermissionManager();
 
@@ -46,6 +47,8 @@
   std::unique_ptr<BackgroundSyncController> background_sync_controller_;
   std::unique_ptr<device::ScopedGeolocationOverrider> geolocation_overrider_;
   std::unique_ptr<ContentIndexProvider> content_index_provider_;
+  std::unique_ptr<ClientHintsControllerDelegate>
+      client_hints_controller_delegate_;
 
   DISALLOW_COPY_AND_ASSIGN(WebTestBrowserContext);
 };
diff --git a/content/shell/browser/web_test/web_test_content_browser_client.cc b/content/shell/browser/web_test/web_test_content_browser_client.cc
index 87667cb..b02eb3c 100644
--- a/content/shell/browser/web_test/web_test_content_browser_client.cc
+++ b/content/shell/browser/web_test/web_test_content_browser_client.cc
@@ -18,6 +18,7 @@
 #include "content/public/browser/browser_context.h"
 #include "content/public/browser/browser_task_traits.h"
 #include "content/public/browser/browser_thread.h"
+#include "content/public/browser/client_hints_controller_delegate.h"
 #include "content/public/browser/login_delegate.h"
 #include "content/public/browser/overlay_window.h"
 #include "content/public/browser/render_process_host.h"
@@ -163,6 +164,12 @@
           &WebTestContentBrowserClient::BindClipboardHostForRequest,
           base::Unretained(this)),
       ui_task_runner);
+
+  registry->AddInterface(
+      base::BindRepeating(
+          &WebTestContentBrowserClient::BindClientHintsControllerDelegate,
+          base::Unretained(this)),
+      ui_task_runner);
 }
 
 void WebTestContentBrowserClient::BindClipboardHostForRequest(
@@ -179,6 +186,14 @@
   mock_clipboard_host_->Bind(std::move(receiver));
 }
 
+void WebTestContentBrowserClient::BindClientHintsControllerDelegate(
+    mojo::PendingReceiver<client_hints::mojom::ClientHints> receiver) {
+  ClientHintsControllerDelegate* delegate =
+      browser_context()->GetClientHintsControllerDelegate();
+  DCHECK(delegate);
+  delegate->Bind(std::move(receiver));
+}
+
 void WebTestContentBrowserClient::OverrideWebkitPrefs(
     RenderViewHost* render_view_host,
     WebPreferences* prefs) {
diff --git a/content/shell/browser/web_test/web_test_content_browser_client.h b/content/shell/browser/web_test/web_test_content_browser_client.h
index 987f222..3b30376 100644
--- a/content/shell/browser/web_test/web_test_content_browser_client.h
+++ b/content/shell/browser/web_test/web_test_content_browser_client.h
@@ -7,6 +7,7 @@
 
 #include <memory>
 
+#include "content/public/common/client_hints.mojom.h"
 #include "content/shell/browser/shell_content_browser_client.h"
 #include "content/shell/common/web_test/fake_bluetooth_chooser.mojom.h"
 #include "mojo/public/cpp/bindings/pending_receiver.h"
@@ -101,6 +102,9 @@
   void BindClipboardHost(
       mojo::PendingReceiver<blink::mojom::ClipboardHost> receiver);
 
+  void BindClientHintsControllerDelegate(
+      mojo::PendingReceiver<client_hints::mojom::ClientHints> receiver);
+
   std::unique_ptr<MockPlatformNotificationService>
       mock_platform_notification_service_;
   bool block_popups_ = false;
diff --git a/content/shell/test_runner/BUILD.gn b/content/shell/test_runner/BUILD.gn
index 7f0f821a..fcce08a 100644
--- a/content/shell/test_runner/BUILD.gn
+++ b/content/shell/test_runner/BUILD.gn
@@ -92,9 +92,11 @@
     "//cc/paint",
     "//components/viz/common",
     "//content/public/common",
+    "//content/public/common:client_hints_mojom",
     "//content/public/common:service_names",
     "//content/public/renderer",
     "//content/renderer:for_content_tests",
+    "//content/shell:client_hints_util",
     "//content/shell:web_test_switches",
     "//content/shell:web_test_utils",
     "//content/test:test_runner_support",
diff --git a/content/shell/test_runner/mock_content_settings_client.cc b/content/shell/test_runner/mock_content_settings_client.cc
index fb332e9..e2e38084 100644
--- a/content/shell/test_runner/mock_content_settings_client.cc
+++ b/content/shell/test_runner/mock_content_settings_client.cc
@@ -8,13 +8,21 @@
 #include "content/shell/test_runner/test_common.h"
 #include "content/shell/test_runner/web_test_delegate.h"
 #include "content/shell/test_runner/web_test_runtime_flags.h"
+#include "mojo/public/cpp/bindings/remote.h"
+#include "third_party/blink/public/common/thread_safe_browser_interface_broker_proxy.h"
+#include "third_party/blink/public/platform/platform.h"
 #include "third_party/blink/public/platform/web_url.h"
 
 namespace test_runner {
 
 MockContentSettingsClient::MockContentSettingsClient(
     WebTestRuntimeFlags* web_test_runtime_flags)
-    : delegate_(nullptr), flags_(web_test_runtime_flags) {}
+    : delegate_(nullptr), flags_(web_test_runtime_flags) {
+  mojo::PendingRemote<client_hints::mojom::ClientHints> host_observer;
+  blink::Platform::Current()->GetBrowserInterfaceBrokerProxy()->GetInterface(
+      host_observer.InitWithNewPipeAndPassReceiver());
+  remote_.Bind(std::move(host_observer));
+}
 
 MockContentSettingsClient::~MockContentSettingsClient() {}
 
@@ -65,32 +73,55 @@
   delegate_ = delegate;
 }
 
+namespace {
+
+void ConvertWebEnabledClientHintsToWebClientHintsTypeVector(
+    const blink::WebEnabledClientHints& enabled_client_hints,
+    const int max_length,
+    std::vector<blink::mojom::WebClientHintsType>* client_hints) {
+  DCHECK(client_hints);
+  for (int type = 0; type < max_length; ++type) {
+    blink::mojom::WebClientHintsType client_hints_type =
+        static_cast<blink::mojom::WebClientHintsType>(type);
+    if (enabled_client_hints.IsEnabled(client_hints_type)) {
+      client_hints->push_back(client_hints_type);
+    }
+  }
+}
+
+void PersistClientHintsInEmbedder(
+    const blink::WebEnabledClientHints& enabled_client_hints,
+    base::TimeDelta duration,
+    const blink::WebURL& url,
+    const mojo::Remote<client_hints::mojom::ClientHints>& remote) {
+  const int max_length =
+      static_cast<int>(blink::mojom::WebClientHintsType::kMaxValue) + 1;
+  std::vector<blink::mojom::WebClientHintsType> client_hints(max_length);
+  const url::Origin origin = url::Origin::Create(url);
+
+  ConvertWebEnabledClientHintsToWebClientHintsTypeVector(
+      enabled_client_hints, max_length, &client_hints);
+
+  remote->PersistClientHints(origin, client_hints, duration);
+}
+}  // namespace
+
 void MockContentSettingsClient::PersistClientHints(
     const blink::WebEnabledClientHints& enabled_client_hints,
     base::TimeDelta duration,
     const blink::WebURL& url) {
-  const url::Origin origin = url::Origin::Create(url);
-  if (!content::IsOriginSecure(url))
+  if (!PersistClientHintsHelper(url, enabled_client_hints, duration,
+                                &client_hints_map_)) {
     return;
-  if (duration <= base::TimeDelta())
-    return;
-  ClientHintsPersistencyData data;
-  data.expiration = base::Time::Now() + duration;
-  data.client_hints = enabled_client_hints;
-  client_hints_map_[origin] = data;
+  }
+
+  PersistClientHintsInEmbedder(enabled_client_hints, duration, url, remote_);
 }
 
 void MockContentSettingsClient::GetAllowedClientHintsFromSource(
     const blink::WebURL& url,
     blink::WebEnabledClientHints* client_hints) const {
-  const url::Origin origin = url::Origin::Create(url);
-  const auto& it = client_hints_map_.find(origin);
-
-  DCHECK(client_hints);
-  if (it != client_hints_map_.end() &&
-      it->second.expiration >= base::Time::Now()) {
-    *client_hints = it->second.client_hints;
-  }
+  GetAllowedClientHintsFromSourceHelper(url, client_hints_map_, client_hints);
 }
 
 void MockContentSettingsClient::ResetClientHintsPersistencyData() {
diff --git a/content/shell/test_runner/mock_content_settings_client.h b/content/shell/test_runner/mock_content_settings_client.h
index 0e8823d..034a2da 100644
--- a/content/shell/test_runner/mock_content_settings_client.h
+++ b/content/shell/test_runner/mock_content_settings_client.h
@@ -9,6 +9,9 @@
 
 #include "base/macros.h"
 #include "base/time/time.h"
+#include "content/public/common/client_hints.mojom.h"
+#include "content/shell/utility/mock_client_hints_utils.h"
+#include "mojo/public/cpp/bindings/remote.h"
 #include "third_party/blink/public/platform/web_client_hints_type.h"
 #include "third_party/blink/public/platform/web_content_settings_client.h"
 #include "url/origin.h"
@@ -52,13 +55,9 @@
   WebTestDelegate* delegate_;
 
   WebTestRuntimeFlags* flags_;
+  mojo::Remote<client_hints::mojom::ClientHints> remote_;
 
-  struct ClientHintsPersistencyData {
-    blink::WebEnabledClientHints client_hints;
-    base::Time expiration;
-  };
-
-  std::map<const url::Origin, ClientHintsPersistencyData> client_hints_map_;
+  content::ClientHintsContainer client_hints_map_;
 
   DISALLOW_COPY_AND_ASSIGN(MockContentSettingsClient);
 };
diff --git a/content/shell/utility/mock_client_hints_utils.cc b/content/shell/utility/mock_client_hints_utils.cc
new file mode 100644
index 0000000..75f80b6
--- /dev/null
+++ b/content/shell/utility/mock_client_hints_utils.cc
@@ -0,0 +1,38 @@
+// 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 "content/shell/utility/mock_client_hints_utils.h"
+
+namespace content {
+
+bool PersistClientHintsHelper(const GURL& url,
+                              const blink::WebEnabledClientHints& client_hints,
+                              base::TimeDelta expiration_duration,
+                              ClientHintsContainer* container) {
+  DCHECK(container);
+  if (!content::IsOriginSecure(url) ||
+      expiration_duration <= base::TimeDelta()) {
+    return false;
+  }
+  ClientHintsPersistencyData data;
+  data.expiration = base::Time::Now() + expiration_duration;
+  data.client_hints = client_hints;
+  const url::Origin origin = url::Origin::Create(url);
+  (*container)[origin] = data;
+  return true;
+}
+
+void GetAllowedClientHintsFromSourceHelper(
+    const GURL& url,
+    const ClientHintsContainer& container,
+    blink::WebEnabledClientHints* client_hints) {
+  const url::Origin origin = url::Origin::Create(url);
+  const auto& it = container.find(origin);
+  DCHECK(client_hints);
+  if (it != container.end() && it->second.expiration >= base::Time::Now()) {
+    *client_hints = it->second.client_hints;
+  }
+}
+
+}  // namespace content
diff --git a/content/shell/utility/mock_client_hints_utils.h b/content/shell/utility/mock_client_hints_utils.h
new file mode 100644
index 0000000..8eff05d
--- /dev/null
+++ b/content/shell/utility/mock_client_hints_utils.h
@@ -0,0 +1,35 @@
+// 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 CONTENT_SHELL_UTILITY_MOCK_CLIENT_HINTS_UTILS_H_
+#define CONTENT_SHELL_UTILITY_MOCK_CLIENT_HINTS_UTILS_H_
+
+#include "content/public/common/origin_util.h"
+#include "third_party/blink/public/platform/web_client_hints_type.h"
+#include "third_party/blink/public/platform/web_url.h"
+
+namespace content {
+
+struct ClientHintsPersistencyData {
+  blink::WebEnabledClientHints client_hints;
+  base::Time expiration;
+};
+
+using ClientHintsContainer =
+    std::map<const url::Origin, ClientHintsPersistencyData>;
+
+bool PersistClientHintsHelper(
+    const GURL& url,
+    const blink::WebEnabledClientHints& enabled_client_hints,
+    base::TimeDelta expiration_duration,
+    ClientHintsContainer* container);
+
+void GetAllowedClientHintsFromSourceHelper(
+    const GURL& url,
+    const ClientHintsContainer& container,
+    blink::WebEnabledClientHints* client_hints);
+
+}  // namespace content
+
+#endif  // CONTENT_SHELL_UTILITY_MOCK_CLIENT_HINTS_UTILS_H_
diff --git a/content/test/BUILD.gn b/content/test/BUILD.gn
index 2270e1b..f496c51 100644
--- a/content/test/BUILD.gn
+++ b/content/test/BUILD.gn
@@ -300,8 +300,6 @@
     "test_content_browser_client.h",
     "test_content_client.cc",
     "test_content_client.h",
-    "test_document_interface_broker.cc",
-    "test_document_interface_broker.h",
     "test_mojo_proxy_resolver_factory.cc",
     "test_mojo_proxy_resolver_factory.h",
     "test_navigation_url_loader.cc",
@@ -370,6 +368,7 @@
     "//content/public/browser",
     "//content/public/child",
     "//content/public/common",
+    "//content/public/common:client_hints_mojom",
     "//content/public/common:service_names",
     "//content/public/renderer",
     "//content/public/utility",
diff --git a/content/test/data/accessibility/html/input-date-with-popup-open-multiple-expected-android.txt b/content/test/data/accessibility/html/input-date-with-popup-open-multiple-expected-android.txt
new file mode 100644
index 0000000..4bb3794
--- /dev/null
+++ b/content/test/data/accessibility/html/input-date-with-popup-open-multiple-expected-android.txt
@@ -0,0 +1 @@
+#<skip - date and time controls drop their children, including the popup button, on Android>
diff --git a/content/test/data/accessibility/html/input-date-with-popup-open-multiple-expected-auralinux.txt b/content/test/data/accessibility/html/input-date-with-popup-open-multiple-expected-auralinux.txt
new file mode 100644
index 0000000..a122a04
--- /dev/null
+++ b/content/test/data/accessibility/html/input-date-with-popup-open-multiple-expected-auralinux.txt
@@ -0,0 +1,69 @@
+[document web]
+++[section]
+++++[dateeditor]
+++++++[section]
+++++++++[section]
+++++++++++[spin button] name='Month' current=9.000000 minimum=1.000000 maximum=12.000000
+++++++++++++[static] name='09'
+++++++++++[static] name='/'
+++++++++++[spin button] name='Day' current=1.000000 minimum=1.000000 maximum=31.000000
+++++++++++++[static] name='01'
+++++++++++[static] name='/'
+++++++++++[spin button] name='Year' current=2008.000000 minimum=1.000000 maximum=275760.000000
+++++++++++++[static] name='2008'
+++++++[push button] name='Show date picker'
+++++[dateeditor]
+++++++[section]
+++++++++[section]
+++++++++++[spin button] name='Month' current=9.000000 minimum=1.000000 maximum=12.000000
+++++++++++++[static] name='09'
+++++++++++[static] name='/'
+++++++++++[spin button] name='Day' current=1.000000 minimum=1.000000 maximum=31.000000
+++++++++++++[static] name='01'
+++++++++++[static] name='/'
+++++++++++[spin button] name='Year' current=2008.000000 minimum=1.000000 maximum=275760.000000
+++++++++++++[static] name='2008'
+++++++[push button] name='Show date picker'
+++++[dateeditor] name='Third date picker' controller-for
+++++++[section]
+++++++++[section]
+++++++++++[spin button] name='Month Third date picker' current=9.000000 minimum=1.000000 maximum=12.000000
+++++++++++++[static] name='09'
+++++++++++[static] name='/'
+++++++++++[spin button] name='Day Third date picker' current=1.000000 minimum=1.000000 maximum=31.000000
+++++++++++++[static] name='01'
+++++++++++[static] name='/'
+++++++++++[spin button] name='Year Third date picker' current=2008.000000 minimum=1.000000 maximum=275760.000000
+++++++++++++[static] name='2008'
+++++++[push button] name='Show date picker'
+++++++[document web] controlled-by
+++++++++[section]
+++++++++++[section]
+++++++++++++[section]
+++++++++++++++[push button] name='Show month selection panel'
+++++++++++++++++[static] name='September 2008'
+++++++++++++++++[document frame]
+++++++++++++[push button] name='Show previous month'
+++++++++++++++[document frame]
+++++++++++++[push button] name='Today'
+++++++++++++++[section]
+++++++++++++[push button] name='Show next month'
+++++++++++++++[document frame]
+++++++++++++[table] cols=0 headers=(NONE); rows=0 headers=(NONE); caption=false; spans=(all: 1x1)
+++++++++++++++[section]
+++++++++++++++++[section]
+++++++++++++++++++[static] name='Sun'
+++++++++++++++++[section]
+++++++++++++++++++[static] name='Mon'
+++++++++++++++++[section]
+++++++++++++++++++[static] name='Tue'
+++++++++++++++++[section]
+++++++++++++++++++[static] name='Wed'
+++++++++++++++++[section]
+++++++++++++++++++[static] name='Thu'
+++++++++++++++++[section]
+++++++++++++++++++[static] name='Fri'
+++++++++++++++++[section]
+++++++++++++++++++[static] name='Sat'
+++++++++++++++[section]
+++++++++++++++++[section]
diff --git a/content/test/data/accessibility/html/input-date-with-popup-open-multiple-expected-blink.txt b/content/test/data/accessibility/html/input-date-with-popup-open-multiple-expected-blink.txt
new file mode 100644
index 0000000..1f42785
--- /dev/null
+++ b/content/test/data/accessibility/html/input-date-with-popup-open-multiple-expected-blink.txt
@@ -0,0 +1,86 @@
+rootWebArea
+++genericContainer
+++++date inputType='date' value='2008-09-01'
+++++++genericContainer
+++++++++genericContainer
+++++++++++spinButton name='Month' value='09' valueForRange=9.00 minValueForRange=1.00 maxValueForRange=12.00
+++++++++++++staticText name='09'
+++++++++++++++inlineTextBox name='09'
+++++++++++staticText name='/'
+++++++++++++inlineTextBox name='/'
+++++++++++spinButton name='Day' value='01' valueForRange=1.00 minValueForRange=1.00 maxValueForRange=31.00
+++++++++++++staticText name='01'
+++++++++++++++inlineTextBox name='01'
+++++++++++staticText name='/'
+++++++++++++inlineTextBox name='/'
+++++++++++spinButton name='Year' value='2008' valueForRange=2008.00 minValueForRange=1.00 maxValueForRange=275760.00
+++++++++++++staticText name='2008'
+++++++++++++++inlineTextBox name='2008'
+++++++popUpButton name='Show date picker'
+++++date inputType='date' value='2008-09-01'
+++++++genericContainer
+++++++++genericContainer
+++++++++++spinButton name='Month' value='09' valueForRange=9.00 minValueForRange=1.00 maxValueForRange=12.00
+++++++++++++staticText name='09'
+++++++++++++++inlineTextBox name='09'
+++++++++++staticText name='/'
+++++++++++++inlineTextBox name='/'
+++++++++++spinButton name='Day' value='01' valueForRange=1.00 minValueForRange=1.00 maxValueForRange=31.00
+++++++++++++staticText name='01'
+++++++++++++++inlineTextBox name='01'
+++++++++++staticText name='/'
+++++++++++++inlineTextBox name='/'
+++++++++++spinButton name='Year' value='2008' valueForRange=2008.00 minValueForRange=1.00 maxValueForRange=275760.00
+++++++++++++staticText name='2008'
+++++++++++++++inlineTextBox name='2008'
+++++++popUpButton name='Show date picker'
+++++date inputType='date' name='Third date picker' value='2008-09-01' controlsIds=rootWebArea
+++++++genericContainer
+++++++++genericContainer
+++++++++++spinButton name='Month Third date picker' value='09' valueForRange=9.00 minValueForRange=1.00 maxValueForRange=12.00
+++++++++++++staticText name='09'
+++++++++++++++inlineTextBox name='09'
+++++++++++staticText name='/'
+++++++++++++inlineTextBox name='/'
+++++++++++spinButton name='Day Third date picker' value='01' valueForRange=1.00 minValueForRange=1.00 maxValueForRange=31.00
+++++++++++++staticText name='01'
+++++++++++++++inlineTextBox name='01'
+++++++++++staticText name='/'
+++++++++++++inlineTextBox name='/'
+++++++++++spinButton name='Year Third date picker' value='2008' valueForRange=2008.00 minValueForRange=1.00 maxValueForRange=275760.00
+++++++++++++staticText name='2008'
+++++++++++++++inlineTextBox name='2008'
+++++++popUpButton name='Show date picker'
+++++++rootWebArea
+++++++++genericContainer ignored
+++++++++++genericContainer
+++++++++++++genericContainer
+++++++++++++++genericContainer ignored
+++++++++++++++++genericContainer
+++++++++++++++++++button name='Show month selection panel'
+++++++++++++++++++++staticText
+++++++++++++++++++++svgRoot
+++++++++++++++++button name='Show previous month'
+++++++++++++++++++svgRoot
+++++++++++++++++button name='Today'
+++++++++++++++++++genericContainer
+++++++++++++++++button name='Show next month'
+++++++++++++++++++svgRoot
+++++++++++++++grid
+++++++++++++++++genericContainer
+++++++++++++++++++genericContainer
+++++++++++++++++++++staticText name='Sun'
+++++++++++++++++++genericContainer
+++++++++++++++++++++staticText name='Mon'
+++++++++++++++++++genericContainer
+++++++++++++++++++++staticText name='Tue'
+++++++++++++++++++genericContainer
+++++++++++++++++++++staticText name='Wed'
+++++++++++++++++++genericContainer
+++++++++++++++++++++staticText name='Thu'
+++++++++++++++++++genericContainer
+++++++++++++++++++++staticText name='Fri'
+++++++++++++++++++genericContainer
+++++++++++++++++++++staticText name='Sat'
+++++++++++++++++genericContainer
+++++++++++++++++++genericContainer
diff --git a/content/test/data/accessibility/html/input-date-with-popup-open-multiple-expected-mac.txt b/content/test/data/accessibility/html/input-date-with-popup-open-multiple-expected-mac.txt
new file mode 100644
index 0000000..146e097
--- /dev/null
+++ b/content/test/data/accessibility/html/input-date-with-popup-open-multiple-expected-mac.txt
@@ -0,0 +1,70 @@
+AXWebArea AXRoleDescription='HTML content'
+++AXGroup AXRoleDescription='group'
+++++AXDateField AXRoleDescription='date field' AXValue='2008-09-01'
+++++++AXGroup AXRoleDescription='group'
+++++++++AXGroup AXRoleDescription='group'
+++++++++++AXIncrementor AXRoleDescription='stepper' AXValue='9' AXDescription='Month'
+++++++++++++AXStaticText AXRoleDescription='text' AXValue='09'
+++++++++++AXStaticText AXRoleDescription='text' AXValue='/'
+++++++++++AXIncrementor AXRoleDescription='stepper' AXValue='1' AXDescription='Day'
+++++++++++++AXStaticText AXRoleDescription='text' AXValue='01'
+++++++++++AXStaticText AXRoleDescription='text' AXValue='/'
+++++++++++AXIncrementor AXRoleDescription='stepper' AXValue='2008' AXDescription='Year'
+++++++++++++AXStaticText AXRoleDescription='text' AXValue='2008'
+++++++AXPopUpButton AXRoleDescription='pop up button' AXDescription='Show date picker'
+++++AXDateField AXRoleDescription='date field' AXValue='2008-09-01'
+++++++AXGroup AXRoleDescription='group'
+++++++++AXGroup AXRoleDescription='group'
+++++++++++AXIncrementor AXRoleDescription='stepper' AXValue='9' AXDescription='Month'
+++++++++++++AXStaticText AXRoleDescription='text' AXValue='09'
+++++++++++AXStaticText AXRoleDescription='text' AXValue='/'
+++++++++++AXIncrementor AXRoleDescription='stepper' AXValue='1' AXDescription='Day'
+++++++++++++AXStaticText AXRoleDescription='text' AXValue='01'
+++++++++++AXStaticText AXRoleDescription='text' AXValue='/'
+++++++++++AXIncrementor AXRoleDescription='stepper' AXValue='2008' AXDescription='Year'
+++++++++++++AXStaticText AXRoleDescription='text' AXValue='2008'
+++++++AXPopUpButton AXRoleDescription='pop up button' AXDescription='Show date picker'
+++++AXDateField AXRoleDescription='date field' AXValue='2008-09-01' AXDescription='Third date picker'
+++++++AXGroup AXRoleDescription='group'
+++++++++AXGroup AXRoleDescription='group'
+++++++++++AXIncrementor AXRoleDescription='stepper' AXValue='9' AXDescription='Month Third date picker'
+++++++++++++AXStaticText AXRoleDescription='text' AXValue='09'
+++++++++++AXStaticText AXRoleDescription='text' AXValue='/'
+++++++++++AXIncrementor AXRoleDescription='stepper' AXValue='1' AXDescription='Day Third date picker'
+++++++++++++AXStaticText AXRoleDescription='text' AXValue='01'
+++++++++++AXStaticText AXRoleDescription='text' AXValue='/'
+++++++++++AXIncrementor AXRoleDescription='stepper' AXValue='2008' AXDescription='Year Third date picker'
+++++++++++++AXStaticText AXRoleDescription='text' AXValue='2008'
+++++++AXPopUpButton AXRoleDescription='pop up button' AXDescription='Show date picker'
+++++++AXWebArea AXRoleDescription='HTML content'
+++++++++AXGroup AXRoleDescription='group'
+++++++++++AXGroup AXRoleDescription='group'
+++++++++++++AXGroup AXRoleDescription='group'
+++++++++++++++AXButton AXRoleDescription='button' AXDescription='Show month selection panel'
+++++++++++++++++AXStaticText AXRoleDescription='text' AXValue='September 2008'
+++++++++++++++++AXGroup AXRoleDescription='group'
+++++++++++++AXButton AXRoleDescription='button' AXDescription='Show previous month'
+++++++++++++++AXGroup AXRoleDescription='group'
+++++++++++++AXButton AXRoleDescription='button' AXDescription='Today'
+++++++++++++++AXGroup AXRoleDescription='group'
+++++++++++++AXButton AXRoleDescription='button' AXDescription='Show next month'
+++++++++++++++AXGroup AXRoleDescription='group'
+++++++++++++AXTable AXRoleDescription='table' AXDescription='Sun Mon Tue Wed Thu Fri Sat'
+++++++++++++++AXGroup AXRoleDescription='group'
+++++++++++++++++AXGroup AXRoleDescription='group'
+++++++++++++++++++AXStaticText AXRoleDescription='text' AXValue='Sun'
+++++++++++++++++AXGroup AXRoleDescription='group'
+++++++++++++++++++AXStaticText AXRoleDescription='text' AXValue='Mon'
+++++++++++++++++AXGroup AXRoleDescription='group'
+++++++++++++++++++AXStaticText AXRoleDescription='text' AXValue='Tue'
+++++++++++++++++AXGroup AXRoleDescription='group'
+++++++++++++++++++AXStaticText AXRoleDescription='text' AXValue='Wed'
+++++++++++++++++AXGroup AXRoleDescription='group'
+++++++++++++++++++AXStaticText AXRoleDescription='text' AXValue='Thu'
+++++++++++++++++AXGroup AXRoleDescription='group'
+++++++++++++++++++AXStaticText AXRoleDescription='text' AXValue='Fri'
+++++++++++++++++AXGroup AXRoleDescription='group'
+++++++++++++++++++AXStaticText AXRoleDescription='text' AXValue='Sat'
+++++++++++++++AXGroup AXRoleDescription='group'
+++++++++++++++++AXGroup AXRoleDescription='group'
+++++++++++++++AXGroup AXRoleDescription='group'
diff --git a/content/test/data/accessibility/html/input-date-with-popup-open-multiple-expected-uia-win.txt b/content/test/data/accessibility/html/input-date-with-popup-open-multiple-expected-uia-win.txt
new file mode 100644
index 0000000..d07f024
--- /dev/null
+++ b/content/test/data/accessibility/html/input-date-with-popup-open-multiple-expected-uia-win.txt
@@ -0,0 +1,69 @@
+document
+++group
+++++textbox LocalizedControlType='date picker'
+++++++group
+++++++++group
+++++++++++spinbutton Name='Month' RangeValue.IsReadOnly=false RangeValue.LargeChange=0.00 RangeValue.SmallChange=0.00 RangeValue.Maximum=12.00 RangeValue.Minimum=1.00 RangeValue.Value=9.00 Value.Value='09'
+++++++++++++description Name='09'
+++++++++++description Name='/'
+++++++++++spinbutton Name='Day' RangeValue.IsReadOnly=false RangeValue.LargeChange=0.00 RangeValue.SmallChange=0.00 RangeValue.Maximum=31.00 RangeValue.Minimum=1.00 RangeValue.Value=1.00 Value.Value='01'
+++++++++++++description Name='01'
+++++++++++description Name='/'
+++++++++++spinbutton Name='Year' RangeValue.IsReadOnly=false RangeValue.LargeChange=0.00 RangeValue.SmallChange=0.00 RangeValue.Maximum=275760.00 RangeValue.Minimum=1.00 RangeValue.Value=2008.00 Value.Value='2008'
+++++++++++++description Name='2008'
+++++++menu Name='Show date picker' ExpandCollapse.ExpandCollapseState='Collapsed'
+++++textbox LocalizedControlType='date picker'
+++++++group
+++++++++group
+++++++++++spinbutton Name='Month' RangeValue.IsReadOnly=false RangeValue.LargeChange=0.00 RangeValue.SmallChange=0.00 RangeValue.Maximum=12.00 RangeValue.Minimum=1.00 RangeValue.Value=9.00 Value.Value='09'
+++++++++++++description Name='09'
+++++++++++description Name='/'
+++++++++++spinbutton Name='Day' RangeValue.IsReadOnly=false RangeValue.LargeChange=0.00 RangeValue.SmallChange=0.00 RangeValue.Maximum=31.00 RangeValue.Minimum=1.00 RangeValue.Value=1.00 Value.Value='01'
+++++++++++++description Name='01'
+++++++++++description Name='/'
+++++++++++spinbutton Name='Year' RangeValue.IsReadOnly=false RangeValue.LargeChange=0.00 RangeValue.SmallChange=0.00 RangeValue.Maximum=275760.00 RangeValue.Minimum=1.00 RangeValue.Value=2008.00 Value.Value='2008'
+++++++++++++description Name='2008'
+++++++menu Name='Show date picker' ExpandCollapse.ExpandCollapseState='Collapsed'
+++++textbox LocalizedControlType='date picker' Name='Third date picker' ControllerFor='{document}'
+++++++group
+++++++++group
+++++++++++spinbutton Name='Month Third date picker' RangeValue.IsReadOnly=false RangeValue.LargeChange=0.00 RangeValue.SmallChange=0.00 RangeValue.Maximum=12.00 RangeValue.Minimum=1.00 RangeValue.Value=9.00 Value.Value='09'
+++++++++++++description Name='09'
+++++++++++description Name='/'
+++++++++++spinbutton Name='Day Third date picker' RangeValue.IsReadOnly=false RangeValue.LargeChange=0.00 RangeValue.SmallChange=0.00 RangeValue.Maximum=31.00 RangeValue.Minimum=1.00 RangeValue.Value=1.00 Value.Value='01'
+++++++++++++description Name='01'
+++++++++++description Name='/'
+++++++++++spinbutton Name='Year Third date picker' RangeValue.IsReadOnly=false RangeValue.LargeChange=0.00 RangeValue.SmallChange=0.00 RangeValue.Maximum=275760.00 RangeValue.Minimum=1.00 RangeValue.Value=2008.00 Value.Value='2008'
+++++++++++++description Name='2008'
+++++++menu Name='Show date picker' ExpandCollapse.ExpandCollapseState='Collapsed'
+++++++document
+++++++++group
+++++++++++group
+++++++++++++group
+++++++++++++++button Name='Show month selection panel'
+++++++++++++++++img
+++++++++++++button Name='Show previous month'
+++++++++++++++img
+++++++++++++button Name='Today'
+++++++++++++++group
+++++++++++++button Name='Show next month'
+++++++++++++++img
+++++++++++++grid Grid.ColumnCount=0 Grid.RowCount=0 Selection.CanSelectMultiple=false Selection.IsSelectionRequired=false Table.RowOrColumnMajor='RowMajor'
+++++++++++++++group
+++++++++++++++++group
+++++++++++++++++++description Name='Sun'
+++++++++++++++++group
+++++++++++++++++++description Name='Mon'
+++++++++++++++++group
+++++++++++++++++++description Name='Tue'
+++++++++++++++++group
+++++++++++++++++++description Name='Wed'
+++++++++++++++++group
+++++++++++++++++++description Name='Thu'
+++++++++++++++++group
+++++++++++++++++++description Name='Fri'
+++++++++++++++++group
+++++++++++++++++++description Name='Sat'
+++++++++++++++group
+++++++++++++++++group
+
diff --git a/content/test/data/accessibility/html/input-date-with-popup-open-multiple-expected-win.txt b/content/test/data/accessibility/html/input-date-with-popup-open-multiple-expected-win.txt
new file mode 100644
index 0000000..8ec92ba
--- /dev/null
+++ b/content/test/data/accessibility/html/input-date-with-popup-open-multiple-expected-win.txt
@@ -0,0 +1,69 @@
+ROLE_SYSTEM_DOCUMENT READONLY FOCUSABLE
+++IA2_ROLE_SECTION
+++++IA2_ROLE_DATE_EDITOR value='2008-09-01' FOCUSABLE
+++++++IA2_ROLE_SECTION
+++++++++IA2_ROLE_SECTION
+++++++++++ROLE_SYSTEM_SPINBUTTON name='Month' value='09' FOCUSABLE
+++++++++++++ROLE_SYSTEM_STATICTEXT name='09'
+++++++++++ROLE_SYSTEM_STATICTEXT name='/'
+++++++++++ROLE_SYSTEM_SPINBUTTON name='Day' value='01' FOCUSABLE
+++++++++++++ROLE_SYSTEM_STATICTEXT name='01'
+++++++++++ROLE_SYSTEM_STATICTEXT name='/'
+++++++++++ROLE_SYSTEM_SPINBUTTON name='Year' value='2008' FOCUSABLE
+++++++++++++ROLE_SYSTEM_STATICTEXT name='2008'
+++++++ROLE_SYSTEM_BUTTONMENU name='Show date picker' FOCUSABLE HASPOPUP
+++++IA2_ROLE_DATE_EDITOR value='2008-09-01' FOCUSABLE
+++++++IA2_ROLE_SECTION
+++++++++IA2_ROLE_SECTION
+++++++++++ROLE_SYSTEM_SPINBUTTON name='Month' value='09' FOCUSABLE
+++++++++++++ROLE_SYSTEM_STATICTEXT name='09'
+++++++++++ROLE_SYSTEM_STATICTEXT name='/'
+++++++++++ROLE_SYSTEM_SPINBUTTON name='Day' value='01' FOCUSABLE
+++++++++++++ROLE_SYSTEM_STATICTEXT name='01'
+++++++++++ROLE_SYSTEM_STATICTEXT name='/'
+++++++++++ROLE_SYSTEM_SPINBUTTON name='Year' value='2008' FOCUSABLE
+++++++++++++ROLE_SYSTEM_STATICTEXT name='2008'
+++++++ROLE_SYSTEM_BUTTONMENU name='Show date picker' FOCUSABLE HASPOPUP
+++++IA2_ROLE_DATE_EDITOR name='Third date picker' value='2008-09-01' FOCUSABLE
+++++++IA2_ROLE_SECTION
+++++++++IA2_ROLE_SECTION
+++++++++++ROLE_SYSTEM_SPINBUTTON name='Month Third date picker' value='09' FOCUSABLE
+++++++++++++ROLE_SYSTEM_STATICTEXT name='09'
+++++++++++ROLE_SYSTEM_STATICTEXT name='/'
+++++++++++ROLE_SYSTEM_SPINBUTTON name='Day Third date picker' value='01' FOCUSABLE
+++++++++++++ROLE_SYSTEM_STATICTEXT name='01'
+++++++++++ROLE_SYSTEM_STATICTEXT name='/'
+++++++++++ROLE_SYSTEM_SPINBUTTON name='Year Third date picker' value='2008' FOCUSABLE
+++++++++++++ROLE_SYSTEM_STATICTEXT name='2008'
+++++++ROLE_SYSTEM_BUTTONMENU name='Show date picker' FOCUSABLE HASPOPUP
+++++++ROLE_SYSTEM_DOCUMENT READONLY FOCUSABLE
+++++++++IA2_ROLE_SECTION
+++++++++++IA2_ROLE_SECTION
+++++++++++++IA2_ROLE_SECTION
+++++++++++++++ROLE_SYSTEM_PUSHBUTTON name='Show month selection panel' FOCUSABLE
+++++++++++++++++ROLE_SYSTEM_STATICTEXT
+++++++++++++++++ROLE_SYSTEM_GRAPHIC
+++++++++++++ROLE_SYSTEM_PUSHBUTTON name='Show previous month' FOCUSABLE
+++++++++++++++ROLE_SYSTEM_GRAPHIC
+++++++++++++ROLE_SYSTEM_PUSHBUTTON name='Today' FOCUSABLE
+++++++++++++++IA2_ROLE_SECTION
+++++++++++++ROLE_SYSTEM_PUSHBUTTON name='Show next month' FOCUSABLE
+++++++++++++++ROLE_SYSTEM_GRAPHIC
+++++++++++++ROLE_SYSTEM_TABLE FOCUSABLE
+++++++++++++++IA2_ROLE_SECTION
+++++++++++++++++IA2_ROLE_SECTION
+++++++++++++++++++ROLE_SYSTEM_STATICTEXT name='Sun'
+++++++++++++++++IA2_ROLE_SECTION
+++++++++++++++++++ROLE_SYSTEM_STATICTEXT name='Mon'
+++++++++++++++++IA2_ROLE_SECTION
+++++++++++++++++++ROLE_SYSTEM_STATICTEXT name='Tue'
+++++++++++++++++IA2_ROLE_SECTION
+++++++++++++++++++ROLE_SYSTEM_STATICTEXT name='Wed'
+++++++++++++++++IA2_ROLE_SECTION
+++++++++++++++++++ROLE_SYSTEM_STATICTEXT name='Thu'
+++++++++++++++++IA2_ROLE_SECTION
+++++++++++++++++++ROLE_SYSTEM_STATICTEXT name='Fri'
+++++++++++++++++IA2_ROLE_SECTION
+++++++++++++++++++ROLE_SYSTEM_STATICTEXT name='Sat'
+++++++++++++++IA2_ROLE_SECTION
+++++++++++++++++IA2_ROLE_SECTION
diff --git a/content/test/data/accessibility/html/input-date-with-popup-open-multiple.html b/content/test/data/accessibility/html/input-date-with-popup-open-multiple.html
new file mode 100644
index 0000000..4cdd93b9
--- /dev/null
+++ b/content/test/data/accessibility/html/input-date-with-popup-open-multiple.html
@@ -0,0 +1,24 @@
+<!--
+@BLINK-ALLOW:inputType=*
+@MAC-ALLOW:AXRole*
+@UIA-WIN-ALLOW:ControllerFor=*
+@UIA-WIN-ALLOW:LocalizedControlType='date picker'
+@DEFAULT-ACTION-ON:Show date picker,Third date picker
+@WAIT-FOR:Today
+
+Windows 7 formats dates differently than Windows 10 (September, 2008 in Windows
+7 vs September 2008 in Windows 10), so skip the name attribute in this case
+so this test can pass on both platforms.
+
+@BLINK-DENY:name='September*
+@WIN-DENY:name='September*
+@UIA-WIN-DENY:Name='September*
+-->
+<!DOCTYPE html>
+<html>
+<body>
+  <input type="date" value="2008-09-01">
+  <input type="date" value="2008-09-01">
+  <input type="date" value="2008-09-01" aria-label="Third date picker">
+</body>
+</html>
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 88c0c63..97fe0a6 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
@@ -712,6 +712,7 @@
 crbug.com/906735 [ android qualcomm ] conformance2/textures/video/tex-2d-rgba8ui-rgba_integer-unsigned_byte.html [ Failure ]
 crbug.com/981216 [ android qualcomm-adreno-(tm)-540 ] deqp/functional/gles3/fbocolorbuffer/tex2d_01.html [ RetryOnFailure ]
 crbug.com/949321 [ android qualcomm ] deqp/functional/gles3/framebufferblit/default_framebuffer_02.html [ RetryOnFailure ]
+crbug.com/1022410 [ android qualcomm ] conformance2/transform_feedback/switching-objects.html [ RetryOnFailure ]
 
 # This test is failing on Android Pixel 2 and 3 (Qualcomm)
 # Seems to be an OpenGL ES bug.
diff --git a/content/test/gpu/gpu_tests/test_expectations/webgl_conformance_expectations.txt b/content/test/gpu/gpu_tests/test_expectations/webgl_conformance_expectations.txt
index 660ec39..17c0e39 100644
--- a/content/test/gpu/gpu_tests/test_expectations/webgl_conformance_expectations.txt
+++ b/content/test/gpu/gpu_tests/test_expectations/webgl_conformance_expectations.txt
@@ -282,6 +282,9 @@
 crbug.com/1017162 [ win nvidia vulkan passthrough ] conformance/ogles/GL/sqrt/sqrt_001_to_006.html [ Failure ]
 crbug.com/1017162 [ win nvidia vulkan passthrough ] conformance/ogles/GL/step/step_001_to_006.html [ Failure ]
 crbug.com/1017162 [ win nvidia vulkan passthrough ] conformance/ogles/GL/struct/struct_001_to_008.html [ Failure ]
+crbug.com/1017162 [ win nvidia vulkan passthrough ] conformance/ogles/GL/struct/struct_009_to_016.html [ Failure ]
+crbug.com/1017162 [ win nvidia vulkan passthrough ] conformance/ogles/GL/struct/struct_017_to_024.html [ Failure ]
+crbug.com/1017162 [ win nvidia vulkan passthrough ] conformance/ogles/GL/struct/struct_025_to_032.html [ Failure ]
 
 # Vulkan / Win / AMD / Passthrough command decoder
 crbug.com/angleproject/2898 [ win amd vulkan passthrough ] conformance/extensions/oes-texture-float-with-canvas.html [ Failure ]
diff --git a/content/test/navigation_simulator_impl.cc b/content/test/navigation_simulator_impl.cc
index f4f32d5..86becd6 100644
--- a/content/test/navigation_simulator_impl.cc
+++ b/content/test/navigation_simulator_impl.cc
@@ -344,13 +344,6 @@
 
   service_manager::mojom::InterfaceProviderPtr stub_interface_provider;
   interface_provider_request_ = mojo::MakeRequest(&stub_interface_provider);
-
-  document_interface_broker_content_receiver_ =
-      mojo::PendingRemote<blink::mojom::DocumentInterfaceBroker>()
-          .InitWithNewPipeAndPassReceiver();
-  document_interface_broker_blink_receiver_ =
-      mojo::PendingRemote<blink::mojom::DocumentInterfaceBroker>()
-          .InitWithNewPipeAndPassReceiver();
   browser_interface_broker_receiver_ =
       mojo::PendingRemote<blink::mojom::BrowserInterfaceBroker>()
           .InitWithNewPipeAndPassReceiver();
@@ -597,8 +590,6 @@
 
   if (same_document_) {
     interface_provider_request_ = nullptr;
-    document_interface_broker_content_receiver_.reset();
-    document_interface_broker_blink_receiver_.reset();
     browser_interface_broker_receiver_.reset();
   }
 
@@ -617,8 +608,6 @@
       false /* same_document */, false /* failed_navigation */);
   render_frame_host_->SimulateCommitProcessed(
       request_, std::move(params), std::move(interface_provider_request_),
-      std::move(document_interface_broker_content_receiver_),
-      std::move(document_interface_broker_blink_receiver_),
       std::move(browser_interface_broker_receiver_), same_document_);
 
   // Simulate the UnloadACK in the old RenderFrameHost if it was swapped out at
@@ -764,8 +753,6 @@
       false /* same_document */, true /* failed_navigation */);
   render_frame_host_->SimulateCommitProcessed(
       request_, std::move(params), std::move(interface_provider_request_),
-      std::move(document_interface_broker_content_receiver_),
-      std::move(document_interface_broker_blink_receiver_),
       std::move(browser_interface_broker_receiver_), false /* same_document */);
 
   // Simulate the UnloadACK in the old RenderFrameHost if it was swapped out at
@@ -798,14 +785,10 @@
       true /* same_document */, false /* failed_navigation */);
 
   interface_provider_request_ = nullptr;
-  document_interface_broker_content_receiver_.reset();
-  document_interface_broker_blink_receiver_.reset();
   browser_interface_broker_receiver_.reset();
 
   render_frame_host_->SimulateCommitProcessed(
       request_, std::move(params), nullptr /* interface_provider_request_ */,
-      mojo::NullReceiver() /* document_interface_broker_content_receiver */,
-      mojo::NullReceiver() /* document_interface_broker_blink_receiver */,
       mojo::NullReceiver() /* browser_interface_broker_receiver */,
       true /* same_document */);
 
diff --git a/content/test/navigation_simulator_impl.h b/content/test/navigation_simulator_impl.h
index 72a3603..e7b5a8c 100644
--- a/content/test/navigation_simulator_impl.h
+++ b/content/test/navigation_simulator_impl.h
@@ -22,7 +22,6 @@
 #include "net/base/host_port_pair.h"
 #include "net/base/ip_endpoint.h"
 #include "services/service_manager/public/cpp/interface_provider.h"
-#include "third_party/blink/public/mojom/frame/document_interface_broker.mojom.h"
 #include "third_party/blink/public/mojom/referrer.mojom.h"
 #include "url/gurl.h"
 
@@ -284,10 +283,6 @@
   int session_history_offset_ = 0;
   bool has_user_gesture_ = true;
   service_manager::mojom::InterfaceProviderRequest interface_provider_request_;
-  mojo::PendingReceiver<blink::mojom::DocumentInterfaceBroker>
-      document_interface_broker_content_receiver_;
-  mojo::PendingReceiver<blink::mojom::DocumentInterfaceBroker>
-      document_interface_broker_blink_receiver_;
   mojo::PendingReceiver<blink::mojom::BrowserInterfaceBroker>
       browser_interface_broker_receiver_;
   std::string contents_mime_type_;
diff --git a/content/test/test_document_interface_broker.cc b/content/test/test_document_interface_broker.cc
deleted file mode 100644
index 564bcfd..0000000
--- a/content/test/test_document_interface_broker.cc
+++ /dev/null
@@ -1,26 +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 "content/test/test_document_interface_broker.h"
-
-namespace content {
-
-TestDocumentInterfaceBroker::TestDocumentInterfaceBroker(
-    blink::mojom::DocumentInterfaceBroker* document_interface_broker,
-    mojo::PendingReceiver<blink::mojom::DocumentInterfaceBroker> receiver)
-    : real_broker_(document_interface_broker),
-      receiver_(this, std::move(receiver)) {}
-
-TestDocumentInterfaceBroker::~TestDocumentInterfaceBroker() {}
-
-blink::mojom::DocumentInterfaceBroker*
-TestDocumentInterfaceBroker::GetForwardingInterface() {
-  return real_broker_;
-}
-
-void TestDocumentInterfaceBroker::Flush() {
-  receiver_.FlushForTesting();
-}
-
-}  // namespace content
\ No newline at end of file
diff --git a/content/test/test_document_interface_broker.h b/content/test/test_document_interface_broker.h
deleted file mode 100644
index e78c3296..0000000
--- a/content/test/test_document_interface_broker.h
+++ /dev/null
@@ -1,36 +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 CONTENT_TEST_TEST_DOCUMENT_INTERFACE_BROKER_H_
-#define CONTENT_TEST_TEST_DOCUMENT_INTERFACE_BROKER_H_
-
-#include <utility>
-
-#include "mojo/public/cpp/bindings/pending_receiver.h"
-#include "mojo/public/cpp/bindings/receiver.h"
-#include "third_party/blink/public/mojom/frame/document_interface_broker.mojom-test-utils.h"
-
-namespace content {
-// This class can be subclassed to override specific methods of RenderFrame's
-// DocumentInterfaceBroker in tests. The rest of the calls will be forwarded to
-// the implementation passed to the constructor (typically returned by
-// RenderFrame::GetDocumentInterfaceBroker()).
-class TestDocumentInterfaceBroker
-    : public blink::mojom::DocumentInterfaceBrokerInterceptorForTesting {
- public:
-  TestDocumentInterfaceBroker(
-      blink::mojom::DocumentInterfaceBroker* document_interface_broker,
-      mojo::PendingReceiver<blink::mojom::DocumentInterfaceBroker> receiver);
-  ~TestDocumentInterfaceBroker() override;
-  blink::mojom::DocumentInterfaceBroker* GetForwardingInterface() override;
-  void Flush();
-
- private:
-  blink::mojom::DocumentInterfaceBroker* real_broker_;
-  mojo::Receiver<DocumentInterfaceBroker> receiver_;
-};
-
-}  // namespace content
-
-#endif  // CONTENT_TEST_TEST_DOCUMENT_INTERFACE_BROKER_H_
\ No newline at end of file
diff --git a/content/test/test_render_frame.cc b/content/test/test_render_frame.cc
index 19475ef..c4d7a49 100644
--- a/content/test/test_render_frame.cc
+++ b/content/test/test_render_frame.cc
@@ -49,11 +49,6 @@
     return std::move(last_interface_provider_request_);
   }
 
-  mojo::PendingReceiver<blink::mojom::DocumentInterfaceBroker>
-  TakeLastDocumentInterfaceBrokerReceiver() {
-    return std::move(last_document_interface_broker_receiver_);
-  }
-
   mojo::PendingReceiver<blink::mojom::BrowserInterfaceBroker>
   TakeLastBrowserInterfaceBrokerReceiver() {
     return std::move(last_browser_interface_broker_receiver_);
@@ -74,17 +69,6 @@
     last_interface_provider_request_ = std::move(interface_provider_request);
   }
 
-  // Holds on to the receiver end of the DocumentInterfaceBroker interface whose
-  // client end is bound to the corresponding RenderFrame's
-  // |document_interface_broker_| to facilitate retrieving the most recent
-  // |document_interface_broker_receiver| in tests.
-  void PassLastDocumentInterfaceBrokerReceiver(
-      mojo::PendingReceiver<blink::mojom::DocumentInterfaceBroker>
-          document_interface_broker_receiver) {
-    last_document_interface_broker_receiver_ =
-        std::move(document_interface_broker_receiver);
-  }
-
   // Holds on to the request end of the BrowserInterfaceBroker interface whose
   // client end is bound to the corresponding RenderFrame's
   // |browser_interface_broker_proxy_| to facilitate retrieving the most recent
@@ -104,10 +88,6 @@
     if (interface_params) {
       last_interface_provider_request_ =
           std::move(interface_params->interface_provider_request);
-      last_document_interface_broker_receiver_ =
-          mojo::PendingReceiver<blink::mojom::DocumentInterfaceBroker>(
-              std::move(interface_params
-                            ->document_interface_broker_content_receiver));
       last_browser_interface_broker_receiver_ =
           std::move(interface_params->browser_interface_broker_receiver);
     }
@@ -195,8 +175,6 @@
 
   void FrameSizeChanged(const gfx::Size& frame_size) override {}
 
-  void VisibilityChanged(blink::mojom::FrameVisibility visibility) override {}
-
   void UpdateActiveSchedulerTrackedFeatures(uint64_t features_mask) override {}
 
   void DidAddMessageToConsole(blink::mojom::ConsoleMessageLevel log_level,
@@ -224,8 +202,6 @@
       last_commit_params_;
   service_manager::mojom::InterfaceProviderRequest
       last_interface_provider_request_;
-  mojo::PendingReceiver<blink::mojom::DocumentInterfaceBroker>
-      last_document_interface_broker_receiver_;
   mojo::PendingReceiver<blink::mojom::BrowserInterfaceBroker>
       last_browser_interface_broker_receiver_;
 
@@ -249,9 +225,6 @@
   mock_frame_host_->PassLastInterfaceProviderRequest(
       mock_render_thread->TakeInitialInterfaceProviderRequestForFrame(
           params.routing_id));
-  mock_frame_host_->PassLastDocumentInterfaceBrokerReceiver(
-      mock_render_thread->TakeInitialDocumentInterfaceBrokerReceiverForFrame(
-          params.routing_id));
   mock_frame_host_->PassLastBrowserInterfaceBrokerReceiver(
       mock_render_thread->TakeInitialBrowserInterfaceBrokerReceiverForFrame(
           params.routing_id));
@@ -401,11 +374,6 @@
   return mock_frame_host_->TakeLastInterfaceProviderRequest();
 }
 
-mojo::PendingReceiver<blink::mojom::DocumentInterfaceBroker>
-TestRenderFrame::TakeLastDocumentInterfaceBrokerReceiver() {
-  return mock_frame_host_->TakeLastDocumentInterfaceBrokerReceiver();
-}
-
 mojo::PendingReceiver<blink::mojom::BrowserInterfaceBroker>
 TestRenderFrame::TakeLastBrowserInterfaceBrokerReceiver() {
   return mock_frame_host_->TakeLastBrowserInterfaceBrokerReceiver();
diff --git a/content/test/test_render_frame.h b/content/test/test_render_frame.h
index 9853bce..c429df2b 100644
--- a/content/test/test_render_frame.h
+++ b/content/test/test_render_frame.h
@@ -78,9 +78,6 @@
   service_manager::mojom::InterfaceProviderRequest
   TakeLastInterfaceProviderRequest();
 
-  mojo::PendingReceiver<blink::mojom::DocumentInterfaceBroker>
-  TakeLastDocumentInterfaceBrokerReceiver();
-
   mojo::PendingReceiver<blink::mojom::BrowserInterfaceBroker>
   TakeLastBrowserInterfaceBrokerReceiver();
 
diff --git a/content/test/test_render_frame_host.cc b/content/test/test_render_frame_host.cc
index c973a7c..0e9ebb01 100644
--- a/content/test/test_render_frame_host.cc
+++ b/content/test/test_render_frame_host.cc
@@ -135,8 +135,6 @@
   std::string frame_unique_name = base::GenerateGUID();
   OnCreateChildFrame(
       GetProcess()->GetNextRoutingID(), CreateStubInterfaceProviderRequest(),
-      CreateStubDocumentInterfaceBrokerReceiver(),
-      CreateStubDocumentInterfaceBrokerReceiver(),
       CreateStubBrowserInterfaceBrokerReceiver(),
       blink::WebTreeScopeType::kDocument, frame_name, frame_unique_name, false,
       base::UnguessableToken::Create(), blink::FramePolicy(),
@@ -434,10 +432,6 @@
     NavigationRequest* navigation_request,
     std::unique_ptr<FrameHostMsg_DidCommitProvisionalLoad_Params> params,
     service_manager::mojom::InterfaceProviderRequest interface_provider_request,
-    mojo::PendingReceiver<blink::mojom::DocumentInterfaceBroker>
-        document_interface_broker_content_receiver,
-    mojo::PendingReceiver<blink::mojom::DocumentInterfaceBroker>
-        document_interface_broker_blink_receiver,
     mojo::PendingReceiver<blink::mojom::BrowserInterfaceBroker>
         browser_interface_broker_receiver,
     bool same_document) {
@@ -455,8 +449,6 @@
             .Run(std::move(params),
                  mojom::DidCommitProvisionalLoadInterfaceParams::New(
                      std::move(interface_provider_request),
-                     std::move(document_interface_broker_content_receiver),
-                     std::move(document_interface_broker_blink_receiver),
                      std::move(browser_interface_broker_receiver)));
         did_commit = true;
       }
@@ -468,8 +460,6 @@
             .Run(std::move(params),
                  mojom::DidCommitProvisionalLoadInterfaceParams::New(
                      std::move(interface_provider_request),
-                     std::move(document_interface_broker_content_receiver),
-                     std::move(document_interface_broker_blink_receiver),
                      std::move(browser_interface_broker_receiver)));
         did_commit = true;
       }
@@ -481,8 +471,6 @@
         params.get(),
         mojom::DidCommitProvisionalLoadInterfaceParams::New(
             std::move(interface_provider_request),
-            std::move(document_interface_broker_content_receiver),
-            std::move(document_interface_broker_blink_receiver),
             std::move(browser_interface_broker_receiver)),
         same_document);
   }
@@ -605,21 +593,11 @@
   service_manager::mojom::InterfaceProviderPtr interface_provider;
   service_manager::mojom::InterfaceProviderRequest interface_provider_request;
 
-  mojo::PendingReceiver<blink::mojom::DocumentInterfaceBroker>
-      document_interface_broker_content_receiver;
-  mojo::PendingReceiver<blink::mojom::DocumentInterfaceBroker>
-      document_interface_broker_blink_receiver;
   mojo::PendingReceiver<blink::mojom::BrowserInterfaceBroker>
       browser_interface_broker_receiver;
 
   if (!is_same_document) {
     interface_provider_request = mojo::MakeRequest(&interface_provider);
-    document_interface_broker_content_receiver =
-        mojo::PendingRemote<blink::mojom::DocumentInterfaceBroker>()
-            .InitWithNewPipeAndPassReceiver();
-    document_interface_broker_blink_receiver =
-        mojo::PendingRemote<blink::mojom::DocumentInterfaceBroker>()
-            .InitWithNewPipeAndPassReceiver();
     browser_interface_broker_receiver =
         mojo::PendingRemote<blink::mojom::BrowserInterfaceBroker>()
             .InitWithNewPipeAndPassReceiver();
@@ -627,8 +605,6 @@
 
   auto interface_params = mojom::DidCommitProvisionalLoadInterfaceParams::New(
       std::move(interface_provider_request),
-      std::move(document_interface_broker_content_receiver),
-      std::move(document_interface_broker_blink_receiver),
       std::move(browser_interface_broker_receiver));
   return interface_params;
 }
@@ -646,13 +622,6 @@
 }
 
 // static
-mojo::PendingReceiver<blink::mojom::DocumentInterfaceBroker>
-TestRenderFrameHost::CreateStubDocumentInterfaceBrokerReceiver() {
-  return mojo::PendingRemote<::blink::mojom::DocumentInterfaceBroker>()
-      .InitWithNewPipeAndPassReceiver();
-}
-
-// static
 mojo::PendingReceiver<blink::mojom::BrowserInterfaceBroker>
 TestRenderFrameHost::CreateStubBrowserInterfaceBrokerReceiver() {
   return mojo::PendingRemote<blink::mojom::BrowserInterfaceBroker>()
diff --git a/content/test/test_render_frame_host.h b/content/test/test_render_frame_host.h
index c1b6c436..beab678 100644
--- a/content/test/test_render_frame_host.h
+++ b/content/test/test_render_frame_host.h
@@ -155,10 +155,6 @@
       std::unique_ptr<FrameHostMsg_DidCommitProvisionalLoad_Params> params,
       service_manager::mojom::InterfaceProviderRequest
           interface_provider_request,
-      mojo::PendingReceiver<blink::mojom::DocumentInterfaceBroker>
-          document_interface_broker_content_receiver,
-      mojo::PendingReceiver<blink::mojom::DocumentInterfaceBroker>
-          document_interface_broker_blink_receiver,
       mojo::PendingReceiver<blink::mojom::BrowserInterfaceBroker>
           browser_interface_broker_receiver,
       bool same_document);
@@ -182,11 +178,6 @@
   static service_manager::mojom::InterfaceProviderRequest
   CreateStubInterfaceProviderRequest();
 
-  // Returns a pending PendingReceiver<DocumentInterfaceBroker> that is safe to
-  // bind to an implementation, but will never receive any interface requests.
-  static mojo::PendingReceiver<blink::mojom::DocumentInterfaceBroker>
-  CreateStubDocumentInterfaceBrokerReceiver();
-
   // Returns a PendingReceiver<BrowserInterfaceBroker> that is safe to bind to
   // an implementation, but will never receive any interface requests.
   static mojo::PendingReceiver<blink::mojom::BrowserInterfaceBroker>
diff --git a/device/bluetooth/bluetooth_device.cc b/device/bluetooth/bluetooth_device.cc
index 399a627..71689ba 100644
--- a/device/bluetooth/bluetooth_device.cc
+++ b/device/bluetooth/bluetooth_device.cc
@@ -4,6 +4,7 @@
 
 #include "device/bluetooth/bluetooth_device.h"
 
+#include <array>
 #include <iterator>
 #include <memory>
 #include <string>
@@ -11,7 +12,9 @@
 
 #include "base/memory/ptr_util.h"
 #include "base/stl_util.h"
+#include "base/strings/string_number_conversions.h"
 #include "base/strings/string_util.h"
+#include "base/strings/stringprintf.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/time/time.h"
 #include "base/values.h"
@@ -377,40 +380,54 @@
 }
 
 // static
-std::string BluetoothDevice::CanonicalizeAddress(const std::string& address) {
-  std::string canonicalized = address;
-  if (address.size() == 12) {
-    // Might be an address in the format "1A2B3C4D5E6F". Add separators.
-    for (size_t i = 2; i < canonicalized.size(); i += 3) {
-      canonicalized.insert(i, ":");
-    }
-  }
+std::string BluetoothDevice::CanonicalizeAddress(base::StringPiece address) {
+  std::array<uint8_t, 6> bytes;
 
-  // Verify that the length matches the canonical format "1A:2B:3C:4D:5E:6F".
-  const size_t kCanonicalAddressLength = 17;
-  if (canonicalized.size() != kCanonicalAddressLength)
+  if (!ParseAddress(address, bytes))
     return std::string();
 
-  const char separator = canonicalized[2];
-  for (size_t i = 0; i < canonicalized.size(); ++i) {
-    bool is_separator = (i + 1) % 3 == 0;
-    if (is_separator) {
-      // All separators in the input |address| should be consistent.
-      if (canonicalized[i] != separator)
-        return std::string();
+  std::string canonicalized;
+  canonicalized.reserve(17);
 
-      canonicalized[i] = ':';
-    } else {
-      if (!base::IsHexDigit(canonicalized[i]))
-        return std::string();
-
-      canonicalized[i] = base::ToUpperASCII(canonicalized[i]);
-    }
+  for (size_t i = 0; i < bytes.size(); ++i) {
+    if (i != 0)
+      canonicalized.push_back(':');
+    base::StringAppendF(&canonicalized, "%02X", bytes[i]);
   }
 
   return canonicalized;
 }
 
+bool BluetoothDevice::ParseAddress(base::StringPiece input,
+                                   base::span<uint8_t> output) {
+  if (output.size() != 6)
+    return false;
+
+  // Try parsing addresses that lack separators, like "1A2B3C4D5E6F".
+  if (input.size() == 12)
+    return base::HexStringToSpan(input, output);
+
+  // Try parsing MAC address with separators like: "00:11:22:33:44:55" or
+  // "00-11-22-33-44-55". Separator can be either '-' or ':', but must use the
+  // same style throughout.
+  if (input.size() == 17) {
+    const char separator = input[2];
+    if (separator != '-' && separator != ':')
+      return false;
+    return (input[2] == separator) && (input[5] == separator) &&
+           (input[8] == separator) && (input[11] == separator) &&
+           (input[14] == separator) &&
+           base::HexStringToSpan(input.substr(0, 2), output.subspan<0, 1>()) &&
+           base::HexStringToSpan(input.substr(3, 2), output.subspan<1, 1>()) &&
+           base::HexStringToSpan(input.substr(6, 2), output.subspan<2, 1>()) &&
+           base::HexStringToSpan(input.substr(9, 2), output.subspan<3, 1>()) &&
+           base::HexStringToSpan(input.substr(12, 2), output.subspan<4, 1>()) &&
+           base::HexStringToSpan(input.substr(15, 2), output.subspan<5, 1>());
+  }
+
+  return false;
+}
+
 std::string BluetoothDevice::GetIdentifier() const { return GetAddress(); }
 
 void BluetoothDevice::UpdateAdvertisementData(
diff --git a/device/bluetooth/bluetooth_device.h b/device/bluetooth/bluetooth_device.h
index dbe3b2f..6f8b73af 100644
--- a/device/bluetooth/bluetooth_device.h
+++ b/device/bluetooth/bluetooth_device.h
@@ -17,11 +17,13 @@
 
 #include "base/callback.h"
 #include "base/containers/flat_set.h"
+#include "base/containers/span.h"
 #include "base/gtest_prod_util.h"
 #include "base/macros.h"
 #include "base/memory/ref_counted.h"
 #include "base/optional.h"
 #include "base/strings/string16.h"
+#include "base/strings/string_piece_forward.h"
 #include "base/time/time.h"
 #include "build/build_config.h"
 #include "device/bluetooth/bluetooth_common.h"
@@ -536,7 +538,15 @@
   // Returns the |address| in the canonical format: XX:XX:XX:XX:XX:XX, where
   // each 'X' is a hex digit.  If the input |address| is invalid, returns an
   // empty string.
-  static std::string CanonicalizeAddress(const std::string& address);
+  static std::string CanonicalizeAddress(base::StringPiece address);
+
+  // Parses a Bluetooth address to an output buffer. The output buffer must be
+  // exactly 6 bytes in size. The address can be formatted in one of three ways:
+  //
+  //   1A:2B:3C:4D:5E:6F
+  //   1A-2B-3C-4D-5E-6F
+  //   1A2B3C4D5E6F
+  static bool ParseAddress(base::StringPiece input, base::span<uint8_t> output);
 
   // Update the last time this device was seen.
   void UpdateTimestamp();
diff --git a/device/bluetooth/bluetooth_device_unittest.cc b/device/bluetooth/bluetooth_device_unittest.cc
index 529eb2f..1a98b75 100644
--- a/device/bluetooth/bluetooth_device_unittest.cc
+++ b/device/bluetooth/bluetooth_device_unittest.cc
@@ -68,31 +68,41 @@
     SCOPED_TRACE(std::string("Input format: '") + kValidFormats[i] + "'");
     EXPECT_EQ("1A:2B:3C:4D:5E:6F",
               BluetoothDevice::CanonicalizeAddress(kValidFormats[i]));
+
+    std::array<uint8_t, 6> parsed;
+    EXPECT_TRUE(BluetoothDevice::ParseAddress(kValidFormats[i], parsed));
+    EXPECT_EQ("\x1a\x2b\x3c\x4d\x5e\x6f",
+              std::string(parsed.begin(), parsed.end()));
   }
 }
 
 TEST(BluetoothDeviceTest, CanonicalizeAddressFormat_RejectsInvalidFormats) {
-  const char* const kValidFormats[] = {
-    // Empty string.
-    "",
-    // Too short.
-    "1A:2B:3C:4D:5E",
-    // Too long.
-    "1A:2B:3C:4D:5E:6F:70",
-    // Missing a separator.
-    "1A:2B:3C:4D:5E6F",
-    // Mixed separators.
-    "1A:2B-3C:4D-5E:6F",
-    // Invalid characters.
-    "1A:2B-3C:4D-5E:6X",
-    // Separators in the wrong place.
-    "1:A2:B3:C4:D5:E6F",
+  const char* const kInvalidFormats[] = {
+      // Empty string.
+      "",
+      // Too short.
+      "1A:2B:3C:4D:5E",
+      // Too long.
+      "1A:2B:3C:4D:5E:6F:70",
+      // Missing a separator.
+      "1A:2B:3C:4D:5E6F",
+      // Mixed separators.
+      "1A:2B-3C:4D-5E:6F",
+      // Invalid hex (6X)
+      "1A:2B:3C:4D:5E:6X",
+      // Separators in the wrong place.
+      "1:A2:B3:C4:D5:E6F",
+      // Wrong separator
+      "1A|2B|3C|4D|5E|6F",
   };
 
-  for (size_t i = 0; i < base::size(kValidFormats); ++i) {
-    SCOPED_TRACE(std::string("Input format: '") + kValidFormats[i] + "'");
+  for (size_t i = 0; i < base::size(kInvalidFormats); ++i) {
+    SCOPED_TRACE(std::string("Input format: '") + kInvalidFormats[i] + "'");
     EXPECT_EQ(std::string(),
-              BluetoothDevice::CanonicalizeAddress(kValidFormats[i]));
+              BluetoothDevice::CanonicalizeAddress(kInvalidFormats[i]));
+
+    std::array<uint8_t, 6> parsed;
+    EXPECT_FALSE(BluetoothDevice::ParseAddress(kInvalidFormats[i], parsed));
   }
 }
 
diff --git a/device/bluetooth/bluetooth_socket_mac.mm b/device/bluetooth/bluetooth_socket_mac.mm
index 747800b..9c3f4fa 100644
--- a/device/bluetooth/bluetooth_socket_mac.mm
+++ b/device/bluetooth/bluetooth_socket_mac.mm
@@ -231,23 +231,8 @@
 
 // Converts |uuid| to a IOBluetoothSDPUUID instance.
 IOBluetoothSDPUUID* GetIOBluetoothSDPUUID(const BluetoothUUID& uuid) {
-  // The canonical UUID format is XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX.
-  const std::string uuid_str = uuid.canonical_value();
-  DCHECK_EQ(uuid_str.size(), 36U);
-  DCHECK_EQ(uuid_str[8], '-');
-  DCHECK_EQ(uuid_str[13], '-');
-  DCHECK_EQ(uuid_str[18], '-');
-  DCHECK_EQ(uuid_str[23], '-');
-  std::string numbers_only = uuid_str;
-  numbers_only.erase(23, 1);
-  numbers_only.erase(18, 1);
-  numbers_only.erase(13, 1);
-  numbers_only.erase(8, 1);
-  std::vector<uint8_t> uuid_bytes_vector;
-  base::HexStringToBytes(numbers_only, &uuid_bytes_vector);
-  DCHECK_EQ(uuid_bytes_vector.size(), 16U);
-
-  return [IOBluetoothSDPUUID uuidWithBytes:&uuid_bytes_vector.front()
+  std::vector<uint8_t> uuid_bytes_vector = uuid.GetBytes();
+  return [IOBluetoothSDPUUID uuidWithBytes:uuid_bytes_vector.data()
                                     length:uuid_bytes_vector.size()];
 }
 
diff --git a/device/bluetooth/cast/bluetooth_utils.cc b/device/bluetooth/cast/bluetooth_utils.cc
index 8ed347a..cc5a86a 100644
--- a/device/bluetooth/cast/bluetooth_utils.cc
+++ b/device/bluetooth/cast/bluetooth_utils.cc
@@ -4,6 +4,7 @@
 
 #include "device/bluetooth/cast/bluetooth_utils.h"
 
+#include "base/strings/string_piece.h"
 #include "chromecast/device/bluetooth/bluetooth_util.h"
 #include "device/bluetooth/bluetooth_device.h"
 
@@ -25,4 +26,4 @@
   return UuidToBluetoothUUID(uuid).canonical_value();
 }
 
-}  // namespace device
\ No newline at end of file
+}  // namespace device
diff --git a/device/bluetooth/public/cpp/bluetooth_uuid.cc b/device/bluetooth/public/cpp/bluetooth_uuid.cc
index c1afb39..fa3d33a00 100644
--- a/device/bluetooth/public/cpp/bluetooth_uuid.cc
+++ b/device/bluetooth/public/cpp/bluetooth_uuid.cc
@@ -7,6 +7,7 @@
 #include <stddef.h>
 
 #include "base/logging.h"
+#include "base/strings/string_number_conversions.h"
 #include "base/strings/string_piece.h"
 #include "base/strings/string_util.h"
 #include "base/strings/utf_string_conversions.h"
@@ -109,6 +110,33 @@
   return format_ != kFormatInvalid;
 }
 
+std::vector<uint8_t> BluetoothUUID::GetBytes() const {
+  if (!IsValid())
+    return std::vector<uint8_t>();
+
+  base::StringPiece input(canonical_value());
+
+  std::vector<uint8_t> bytes(16);
+  base::span<uint8_t> out(bytes);
+
+  //           0         1         2         3
+  //           012345678901234567890123456789012345
+  // Example: "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
+  //           12345678-1234-5678-9abc-def123456789
+  bool success =
+      (input.size() == 36) && (input[8] == '-') && (input[13] == '-') &&
+      (input[18] == '-') && (input[23] == '-') &&
+      base::HexStringToSpan(input.substr(0, 8), out.subspan<0, 4>()) &&
+      base::HexStringToSpan(input.substr(9, 4), out.subspan<4, 2>()) &&
+      base::HexStringToSpan(input.substr(14, 4), out.subspan<6, 2>()) &&
+      base::HexStringToSpan(input.substr(19, 4), out.subspan<8, 2>()) &&
+      base::HexStringToSpan(input.substr(24, 12), out.subspan<10, 6>());
+
+  DCHECK(success);
+
+  return bytes;
+}
+
 bool BluetoothUUID::operator<(const BluetoothUUID& uuid) const {
   return canonical_value_ < uuid.canonical_value_;
 }
diff --git a/device/bluetooth/public/cpp/bluetooth_uuid.h b/device/bluetooth/public/cpp/bluetooth_uuid.h
index 2650490..e3d5b44 100644
--- a/device/bluetooth/public/cpp/bluetooth_uuid.h
+++ b/device/bluetooth/public/cpp/bluetooth_uuid.h
@@ -82,6 +82,10 @@
   // where x is a lowercase hex digit.
   const std::string& canonical_value() const { return canonical_value_; }
 
+  // Returns the bytes of the canonical 128-bit UUID, or empty vector if
+  // invalid.
+  std::vector<uint8_t> GetBytes() const;
+
   // Permit sufficient comparison to allow a UUID to be used as a key in a
   // std::map.
   bool operator<(const BluetoothUUID& uuid) const;
diff --git a/device/bluetooth/public/cpp/bluetooth_uuid_unittest.cc b/device/bluetooth/public/cpp/bluetooth_uuid_unittest.cc
index d52d6c2..523f65e 100644
--- a/device/bluetooth/public/cpp/bluetooth_uuid_unittest.cc
+++ b/device/bluetooth/public/cpp/bluetooth_uuid_unittest.cc
@@ -9,6 +9,7 @@
 #include "base/stl_util.h"
 #include "base/strings/string_piece.h"
 #include "build/build_config.h"
+#include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace device {
@@ -27,12 +28,20 @@
   EXPECT_TRUE(uuid0.IsValid());
   EXPECT_EQ(BluetoothUUID::kFormat128Bit, uuid0.format());
   EXPECT_EQ(uuid0.value(), uuid0.canonical_value());
+  EXPECT_THAT(
+      uuid0.GetBytes(),
+      ::testing::ElementsAre(0x12, 0x34, 0x56, 0x78, 0x12, 0x34, 0x56, 0x78,
+                             0x9a, 0xbc, 0xde, 0xf1, 0x23, 0x45, 0x67, 0x89));
 
   // Valid 128-bit UUID.
   BluetoothUUID uuid1(kValid128Bit1);
   EXPECT_TRUE(uuid1.IsValid());
   EXPECT_EQ(BluetoothUUID::kFormat128Bit, uuid1.format());
   EXPECT_EQ(uuid1.value(), uuid1.canonical_value());
+  EXPECT_THAT(
+      uuid1.GetBytes(),
+      ::testing::ElementsAre(0x00, 0x00, 0x11, 0x01, 0x00, 0x00, 0x10, 0x00,
+                             0x80, 0x00, 0x00, 0x80, 0x5f, 0x9b, 0x34, 0xfb));
 
   EXPECT_NE(uuid0, uuid1);
 
@@ -42,6 +51,7 @@
   EXPECT_EQ(BluetoothUUID::kFormatInvalid, uuid2.format());
   EXPECT_TRUE(uuid2.value().empty());
   EXPECT_TRUE(uuid2.canonical_value().empty());
+  EXPECT_THAT(uuid2.GetBytes(), ::testing::ElementsAre());
 
   // Invalid 128-bit UUID.
   BluetoothUUID uuid3(kInvalid36Char1);
@@ -49,6 +59,7 @@
   EXPECT_EQ(BluetoothUUID::kFormatInvalid, uuid3.format());
   EXPECT_TRUE(uuid3.value().empty());
   EXPECT_TRUE(uuid3.canonical_value().empty());
+  EXPECT_THAT(uuid3.GetBytes(), ::testing::ElementsAre());
 
   // Invalid 16-bit UUID.
   BluetoothUUID uuid4(kInvalid4Char);
@@ -56,6 +67,7 @@
   EXPECT_EQ(BluetoothUUID::kFormatInvalid, uuid4.format());
   EXPECT_TRUE(uuid4.value().empty());
   EXPECT_TRUE(uuid4.canonical_value().empty());
+  EXPECT_THAT(uuid4.GetBytes(), ::testing::ElementsAre());
 
   // Valid 16-bit UUID.
   BluetoothUUID uuid5(kValid16Bit);
@@ -64,6 +76,10 @@
   EXPECT_NE(uuid5.value(), uuid5.canonical_value());
   EXPECT_EQ("1101", uuid5.value());
   EXPECT_EQ(kValid128Bit1, uuid5.canonical_value());
+  EXPECT_THAT(
+      uuid5.GetBytes(),
+      ::testing::ElementsAre(0x00, 0x00, 0x11, 0x01, 0x00, 0x00, 0x10, 0x00,
+                             0x80, 0x00, 0x00, 0x80, 0x5f, 0x9b, 0x34, 0xfb));
 
   // Valid 32-bit UUID.
   BluetoothUUID uuid6(kValid32Bit);
@@ -72,6 +88,10 @@
   EXPECT_NE(uuid6.value(), uuid6.canonical_value());
   EXPECT_EQ("00001101", uuid6.value());
   EXPECT_EQ(kValid128Bit1, uuid6.canonical_value());
+  EXPECT_THAT(
+      uuid6.GetBytes(),
+      ::testing::ElementsAre(0x00, 0x00, 0x11, 0x01, 0x00, 0x00, 0x10, 0x00,
+                             0x80, 0x00, 0x00, 0x80, 0x5f, 0x9b, 0x34, 0xfb));
 
   // uuid5, uuid6, and uuid1 are equivalent.
   EXPECT_EQ(uuid5, uuid6);
diff --git a/device/bluetooth/test/fake_bluetooth_le_advertisement_winrt.cc b/device/bluetooth/test/fake_bluetooth_le_advertisement_winrt.cc
index cd2f68c..4734b57 100644
--- a/device/bluetooth/test/fake_bluetooth_le_advertisement_winrt.cc
+++ b/device/bluetooth/test/fake_bluetooth_le_advertisement_winrt.cc
@@ -10,9 +10,7 @@
 #include <utility>
 #include <vector>
 
-#include "base/strings/string_number_conversions.h"
 #include "base/strings/string_piece.h"
-#include "base/strings/string_split.h"
 #include "base/win/reference.h"
 #include "base/win/scoped_hstring.h"
 #include "base/win/vector.h"
@@ -92,24 +90,12 @@
 
 namespace {
 
-constexpr size_t kNumBytesCanonicalUuid = 16;
-
 std::vector<ComPtr<IBluetoothLEAdvertisementDataSection>> ToDataSections(
     const BluetoothDevice::ServiceDataMap& service_data) {
   std::vector<ComPtr<IBluetoothLEAdvertisementDataSection>> data_sections;
   data_sections.reserve(service_data.size());
   for (const auto& pair : service_data) {
-    std::vector<uint8_t> data;
-    data.reserve(kNumBytesCanonicalUuid + pair.second.size());
-
-    // HexDecode the UUID while ignoring dashes.
-    for (const auto& token :
-         base::SplitStringPiece(pair.first.canonical_value(), "-",
-                                base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL)) {
-      std::vector<uint8_t> tmp;
-      base::HexStringToBytes(token, &tmp);
-      data.insert(data.end(), tmp.begin(), tmp.end());
-    }
+    std::vector<uint8_t> data = pair.first.GetBytes();
 
     // Reverse the data as UUIDs are specified in little endian order in the
     // advertisement payload.
diff --git a/device/fido/cable/fido_cable_discovery.cc b/device/fido/cable/fido_cable_discovery.cc
index dbfc0060..aa13090 100644
--- a/device/fido/cable/fido_cable_discovery.cc
+++ b/device/fido/cable/fido_cable_discovery.cc
@@ -632,29 +632,11 @@
 
   const auto service_uuids = device->GetUUIDs();
   for (const auto& uuid : service_uuids) {
-    // |uuid_hex| is a hex string with the format:
-    // xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
-    const std::string& uuid_hex = uuid.canonical_value();
-    DCHECK_EQ(32u + 4u, uuid_hex.size());
-
-    // Copy substrings of |uuid_hex| to drop the hyphens.
-    std::string hex;
-    hex.reserve(32);
-    hex.append(uuid_hex, 0, 8);
-    hex.append(uuid_hex, 9, 4);
-    hex.append(uuid_hex, 14, 4);
-    hex.append(uuid_hex, 19, 4);
-    hex.append(uuid_hex, 24, 12);
-    DCHECK_EQ(32u, hex.size());
-
-    std::vector<uint8_t> uuid_binary;
-    const bool ok = base::HexStringToBytes(hex, &uuid_binary);
-    DCHECK(ok);
-
+    std::vector<uint8_t> uuid_binary = uuid.GetBytes();
     CableEidArray authenticator_eid;
     DCHECK_EQ(authenticator_eid.size(), uuid_binary.size());
     memcpy(authenticator_eid.data(), uuid_binary.data(),
-           authenticator_eid.size());
+           std::min(uuid_binary.size(), authenticator_eid.size()));
 
     ret.emplace_back(std::move(authenticator_eid));
   }
diff --git a/docs/servicification.md b/docs/servicification.md
index d24dcd3..8e9735e 100644
--- a/docs/servicification.md
+++ b/docs/servicification.md
@@ -300,12 +300,6 @@
 The current way to set up that sort of thing looks like
 [this](https://cs.chromium.org/chromium/src/third_party/blink/web_tests/battery-status/resources/mock-battery-monitor.js?rcl=be6e0001855f7f1cfc26205d0ff5a2b5b324fcbd&l=19).
 
-*** aside
-**NOTE:** The above approach to mocking in JS no longer applies when using
-the new recommended `DocumentInterfaceBroker` approach to exposing interfaces
-to documents. New JS mocking support is in development for this.
-***
-
 #### Feature Impls That Depend on Blink Headers
 In the course of servicifying a feature that has Blink as a client, you might
 encounter cases where the feature implementation has dependencies on Blink
diff --git a/docs/speed/benchmark/benchmark_ownership.md b/docs/speed/benchmark/benchmark_ownership.md
index 5297041..bb9343d 100644
--- a/docs/speed/benchmark/benchmark_ownership.md
+++ b/docs/speed/benchmark/benchmark_ownership.md
@@ -34,7 +34,7 @@
 
 ### C++ Perf Benchmarks
 1. Open [`src/tools/perf/core/perf_data_generator.py`](https://cs.chromium.org/chromium/src/tools/perf/core/perf_data_generator.py).
-1. Find the BenchmarkMetadata for the benchmark. It will be in a dictionary named `NON_TELEMETRY_BENCHMARKS` or `NON_WATERFALL_BENCHMARKS`.
+1. Find the BenchmarkMetadata for the benchmark. It will be in a dictionary named `GTEST_BENCHMARKS`.
 1. Update the email (first field of `BenchmarkMetadata`).
 1. Run `tools/perf/generate_perf_data` to update `tools/perf/benchmark.csv`.
 1. Upload `perf_data_generator.py` and `benchmark.csv` to a CL for review. Please add any previous owners to the review.
diff --git a/docs/speed/binary_size/optimization_advice.md b/docs/speed/binary_size/optimization_advice.md
index cd9e11c..db7857a 100644
--- a/docs/speed/binary_size/optimization_advice.md
+++ b/docs/speed/binary_size/optimization_advice.md
@@ -36,17 +36,25 @@
 ## Optimizing Images
 
  * Would a vector image work?
+   * Images that can be described by a series of paths should generally be
+     stored as vectors.
+     * The one exception is if the image will be used pre-Lollipop in a
+       notification or application icon.
    * For images used in native code: [VectorIcon](https://chromium.googlesource.com/chromium/src/+/HEAD/components/vector_icons/README.md).
-   * For Android drawables: [VectorDrawable](https://codereview.chromium.org/2857893003/).
+   * For Android drawables: [VectorDrawable](https://developer.android.com/guide/topics/graphics/vector-drawable-resources).
+     * Convert from `.svg` online using https://inloop.github.io/svg2android/.
      * Optimize vector drawables with [avocado](https://bugs.chromium.org/p/chromium/issues/detail?id=982302).
+     * (Googlers): Find most icons as .svg at [go/icons](https://goto.google.com/icons).
  * Would **lossy** compression make sense (often true for large images)?
    * If so, [use lossy webp](https://codereview.chromium.org/2615243002/).
    * And omit some densities (e.g. add only an xxhdpi version).
- * Would **near-lossless** compression make sense?
-   * This can often reduce size by >50% without a perceptible difference.
-   * [Use pngquant](https://pngquant.org) to try this out (use one of the GUI
-     tools to compare before/after).
- * Are the **lossless** images fully optimized?
+ * For lossless `.png` images, see how few unique colors you can use without a
+   noticeable difference.
+   * This can often reduce an already optimized .png by 33%-50%.
+   * [Use pngquant](https://pngquant.org) to try this out.
+     * Requires trial and error for each number of unique colors.
+     * Use one of the GUI tools linked from the website to do this easily.
+ * Finally - Ensure .png files are fully optimized.
    * Use [tools/resources/optimize-png-files.sh](https://cs.chromium.org/chromium/src/tools/resources/optimize-png-files.sh).
    * There is some [Googler-specific guidance](https://goto.google.com/clank/engineering/best-practices/adding-image-assets) as well.
 
diff --git a/extensions/browser/extension_function_histogram_value.h b/extensions/browser/extension_function_histogram_value.h
index a3a714f..6eee8b7 100644
--- a/extensions/browser/extension_function_histogram_value.h
+++ b/extensions/browser/extension_function_histogram_value.h
@@ -1465,6 +1465,7 @@
   AUTOTESTPRIVATE_ACTIVATEDESKATINDEX = 1402,
   AUTOTESTPRIVATE_REMOVEACTIVEDESK = 1403,
   TERMINALPRIVATE_GETCROSHSETTINGS = 1404,
+  AUTOTESTPRIVATE_ENABLEASSISTANTANDWAITFORREADY = 1405,
   // Last entry: Add new entries above, then run:
   // python tools/metrics/histograms/update_extension_histograms.py
   ENUM_BOUNDARY
diff --git a/extensions/renderer/DEPS b/extensions/renderer/DEPS
index 1e9c6096..ad6a8442 100644
--- a/extensions/renderer/DEPS
+++ b/extensions/renderer/DEPS
@@ -9,7 +9,6 @@
   "+third_party/cld_3",
 
   "+third_party/blink/public/mojom/devtools/console_message.mojom.h",
-  "+third_party/blink/public/mojom/frame/document_interface_broker.mojom.h",
   "+third_party/blink/public/mojom/service_worker/service_worker_registration.mojom.h",
   "+third_party/blink/public/platform",
   "+third_party/blink/public/strings/grit/blink_strings.h",
diff --git a/extensions/renderer/scoped_web_frame.cc b/extensions/renderer/scoped_web_frame.cc
index 18bcd84..1531ca6 100644
--- a/extensions/renderer/scoped_web_frame.cc
+++ b/extensions/renderer/scoped_web_frame.cc
@@ -5,31 +5,21 @@
 #include "extensions/renderer/scoped_web_frame.h"
 
 #include "mojo/public/cpp/bindings/pending_remote.h"
-#include "third_party/blink/public/mojom/frame/document_interface_broker.mojom.h"
 #include "third_party/blink/public/web/web_heap.h"
 #include "third_party/blink/public/web/web_view.h"
 #include "third_party/blink/public/web/web_widget.h"
 
 namespace extensions {
 
-// returns a valid handle that can be passed to WebLocalFrame constructor
-mojo::ScopedMessagePipeHandle CreateStubDocumentInterfaceBrokerHandle() {
-  return mojo::PendingRemote<blink::mojom::DocumentInterfaceBroker>()
-      .InitWithNewPipeAndPassReceiver()
-      .PassPipe();
-}
-
 ScopedWebFrame::ScopedWebFrame()
     : view_(blink::WebView::Create(/*client=*/nullptr,
                                    /*is_hidden=*/false,
                                    /*compositing_enabled=*/false,
                                    /*opener=*/nullptr)),
-      frame_(blink::WebLocalFrame::CreateMainFrame(
-          view_,
-          &frame_client_,
-          nullptr,
-          CreateStubDocumentInterfaceBrokerHandle(),
-          nullptr)) {}
+      frame_(blink::WebLocalFrame::CreateMainFrame(view_,
+                                                   &frame_client_,
+                                                   nullptr,
+                                                   nullptr)) {}
 
 ScopedWebFrame::~ScopedWebFrame() {
   view_->Close();
diff --git a/gpu/ipc/in_process_command_buffer.cc b/gpu/ipc/in_process_command_buffer.cc
index 62d10e2..b3e8d236 100644
--- a/gpu/ipc/in_process_command_buffer.cc
+++ b/gpu/ipc/in_process_command_buffer.cc
@@ -1773,6 +1773,11 @@
 }
 
 base::TimeDelta InProcessCommandBuffer::GetGpuBlockedTimeSinceLastSwap() {
+  // Some examples and tests create InProcessCommandBuffer without
+  // GpuChannelManagerDelegate.
+  if (!gpu_channel_manager_delegate_)
+    return base::TimeDelta::Min();
+
   return gpu_channel_manager_delegate_->GetGpuScheduler()
       ->TakeTotalBlockingTime();
 }
diff --git a/ios/chrome/app/application_delegate/BUILD.gn b/ios/chrome/app/application_delegate/BUILD.gn
index 3cb6f0c..5b2e830 100644
--- a/ios/chrome/app/application_delegate/BUILD.gn
+++ b/ios/chrome/app/application_delegate/BUILD.gn
@@ -145,7 +145,6 @@
     "//ios/public/provider/chrome/browser/user_feedback",
     "//ios/web",
     "//net",
-    "//third_party/google_toolbox_for_mac",
     "//ui/base",
     "//url",
   ]
diff --git a/ios/chrome/app/application_delegate/DEPS b/ios/chrome/app/application_delegate/DEPS
deleted file mode 100644
index fd7dad12..0000000
--- a/ios/chrome/app/application_delegate/DEPS
+++ /dev/null
@@ -1,5 +0,0 @@
-specific_include_rules = {
-  "metrics_mediator\.mm": [
-    "+third_party/google_toolbox_for_mac/src/Foundation/GTMTimeUtils.h",
-  ],
-}
diff --git a/ios/chrome/app/application_delegate/metrics_mediator.mm b/ios/chrome/app/application_delegate/metrics_mediator.mm
index 98a1a74..53c34fa 100644
--- a/ios/chrome/app/application_delegate/metrics_mediator.mm
+++ b/ios/chrome/app/application_delegate/metrics_mediator.mm
@@ -4,6 +4,8 @@
 
 #import "ios/chrome/app/application_delegate/metrics_mediator.h"
 
+#include <sys/sysctl.h>
+
 #include "base/bind.h"
 #include "base/metrics/histogram_macros.h"
 #include "base/metrics/user_metrics_action.h"
@@ -35,12 +37,6 @@
 #import "ios/web/public/web_state.h"
 #include "url/gurl.h"
 
-// Make sure symbols for GTMTimeUtils are decorated as C otherwise the linker
-// looks for CPP decorated symbols and fails.
-extern "C" {
-#include "third_party/google_toolbox_for_mac/src/Foundation/GTMTimeUtils.h"
-}
-
 #if !defined(__has_feature) || !__has_feature(objc_arc)
 #error "This file requires ARC support."
 #endif
@@ -48,6 +44,22 @@
 namespace {
 // The amount of time (in seconds) to wait for the user to start a new task.
 const NSTimeInterval kFirstUserActionTimeout = 30.0;
+
+// Returns time delta since app launch as retrieved from kernel info about
+// the current process.
+base::TimeDelta TimeDeltaSinceAppLaunchFromProcess() {
+  struct kinfo_proc info;
+  size_t length = sizeof(struct kinfo_proc);
+  int mib[4] = {CTL_KERN, KERN_PROC, KERN_PROC_PID, (int)getpid()};
+  const int kr = sysctl(mib, base::size(mib), &info, &length, nullptr, 0);
+  DCHECK_EQ(KERN_SUCCESS, kr);
+
+  const struct timeval time = info.kp_proc.p_starttime;
+  const NSTimeInterval time_since_1970 =
+      time.tv_sec + (time.tv_usec / (double)USEC_PER_SEC);
+  NSDate* date = [NSDate dateWithTimeIntervalSince1970:time_since_1970];
+  return base::TimeDelta::FromSecondsD(-date.timeIntervalSinceNow);
+}
 }  // namespace
 
 namespace metrics_mediator {
@@ -105,11 +117,11 @@
   if (![startupInformation isColdStart])
     return;
 
-  base::TimeDelta startDuration =
+  const base::TimeDelta startDuration =
       base::TimeTicks::Now() - [startupInformation appLaunchTime];
 
-  base::TimeDelta startDurationFromProcess =
-      base::TimeDelta::FromSecondsD(-GTMAppLaunchDate().timeIntervalSinceNow);
+  const base::TimeDelta startDurationFromProcess =
+      TimeDeltaSinceAppLaunchFromProcess();
 
   UMA_HISTOGRAM_TIMES("Startup.ColdStartFromProcessCreationTime",
                       startDurationFromProcess);
diff --git a/ios/chrome/browser/ui/settings/autofill/autofill_profile_settings_egtest.mm b/ios/chrome/browser/ui/settings/autofill/autofill_profile_settings_egtest.mm
index ced8e4de..9a227b8 100644
--- a/ios/chrome/browser/ui/settings/autofill/autofill_profile_settings_egtest.mm
+++ b/ios/chrome/browser/ui/settings/autofill/autofill_profile_settings_egtest.mm
@@ -45,6 +45,7 @@
     {IDS_IOS_AUTOFILL_CITY, @"Elysium"},
     {IDS_IOS_AUTOFILL_STATE, @"CA"},
     {IDS_IOS_AUTOFILL_ZIP, @"91111"},
+    {IDS_IOS_AUTOFILL_COUNTRY, @"United States"},
     {IDS_IOS_AUTOFILL_PHONE, @"16502111111"},
     {IDS_IOS_AUTOFILL_EMAIL, @"johndoe@hades.com"}};
 
@@ -136,8 +137,7 @@
 }
 
 // Test that the page for viewing Autofill profile details is as expected.
-// TODO(crbug.com/922117): Reenable test.
-- (void)FLAKY_testAutofillProfileViewPage {
+- (void)testAutofillProfileViewPage {
   [AutofillAppInterface saveExampleProfile];
   [self openEditProfile:kProfileLabel];
 
@@ -147,13 +147,18 @@
         stringWithFormat:@"%@, %@",
                          l10n_util::GetNSString(expectation.display_string_id),
                          expectation.expected_result]);
+    BOOL mustBePresent = YES;
+    if (expectation.display_string_id == IDS_IOS_AUTOFILL_COMPANY_NAME &&
+        ![ChromeEarlGrey isAutofillCompanyNameEnabled]) {
+      mustBePresent = NO;
+    }
     [[[EarlGrey
         selectElementWithMatcher:grey_allOf(elementMatcher,
                                             grey_sufficientlyVisible(), nil)]
            usingSearchAction:grey_scrollInDirection(kGREYDirectionDown, 150)
         onElementWithMatcher:grey_accessibilityID(
                                  kAutofillProfileEditTableViewId)]
-        assertWithMatcher:grey_notNil()];
+        assertWithMatcher:mustBePresent ? grey_notNil() : grey_nil()];
   }
 
   // Go back to the list view page.
diff --git a/ios/chrome/browser/ui/tab_grid/tab_grid_view_controller.mm b/ios/chrome/browser/ui/tab_grid/tab_grid_view_controller.mm
index 06b185d..6994cab 100644
--- a/ios/chrome/browser/ui/tab_grid/tab_grid_view_controller.mm
+++ b/ios/chrome/browser/ui/tab_grid/tab_grid_view_controller.mm
@@ -500,7 +500,7 @@
 // to match. If |animated| is YES, the scroll view change may animate; if it is
 // NO, it will never animate.
 - (void)scrollToPage:(TabGridPage)targetPage animated:(BOOL)animated {
-  // This method should never early return if |currentPage| == |_currentPage|;
+  // This method should never early return if |targetPage| == |_currentPage|;
   // the ivar may have been set before the scroll view could be updated. Calling
   // this method should always update the scroll view's offset if possible.
 
@@ -509,21 +509,23 @@
     self.currentPage = targetPage;
     return;
   }
+
   CGFloat pageWidth = self.scrollView.frame.size.width;
   NSUInteger pageIndex = GetPageIndexFromPage(targetPage);
-  CGPoint offset = CGPointMake(pageIndex * pageWidth, 0);
+  CGPoint targetOffset = CGPointMake(pageIndex * pageWidth, 0);
+
   // If the view is visible and |animated| is YES, animate the change.
   // Otherwise don't.
   if (self.view.window == nil || !animated) {
-    [self.scrollView setContentOffset:offset animated:NO];
+    [self.scrollView setContentOffset:targetOffset animated:NO];
     self.currentPage = targetPage;
   } else {
     // Only set |scrollViewAnimatingContentOffset| to YES if there's an actual
     // change in the contentOffset, as |-scrollViewDidEndScrollingAnimation:| is
     // never called if the animation does not occur.
-    if (!CGPointEqualToPoint(self.scrollView.contentOffset, offset)) {
+    if (!CGPointEqualToPoint(self.scrollView.contentOffset, targetOffset)) {
       self.scrollViewAnimatingContentOffset = YES;
-      [self.scrollView setContentOffset:offset animated:YES];
+      [self.scrollView setContentOffset:targetOffset animated:YES];
       // |self.currentPage| is set in scrollViewDidEndScrollingAnimation:
     } else {
       self.currentPage = targetPage;
@@ -1046,12 +1048,12 @@
 
 // Sets both the current page and page control's selected page to |page|.
 // Animation is used if |animated| is YES.
-- (void)setCurrentPageAndPageControlSelectedPage:(TabGridPage)page
-                                        animated:(BOOL)animated {
+- (void)setCurrentPageAndPageControl:(TabGridPage)page animated:(BOOL)animated {
   if (self.topToolbar.pageControl.selectedPage != page)
     [self.topToolbar.pageControl setSelectedPage:page animated:animated];
-  if (self.currentPage != page)
+  if (self.currentPage != page) {
     [self scrollToPage:page animated:animated];
+  }
 }
 
 #pragma mark - GridViewControllerDelegate
@@ -1120,20 +1122,26 @@
       if (self.viewLoaded && self.view.window) {
         // Visibly scroll to the regular tabs panel after a slight delay when
         // the user is already in the tab switcher.
+        // Per crbug.com/980844, if the user has VoiceOver enabled, don't delay
+        // and just animate immediately; delaying the scrolling will cause
+        // VoiceOver to focus the text on the Incognito page.
         __weak TabGridViewController* weakSelf = self;
-        base::PostDelayedTask(
-            FROM_HERE, {web::WebThread::UI}, base::BindOnce(^{
-              [weakSelf setCurrentPageAndPageControlSelectedPage:
-                            TabGridPageRegularTabs
-                                                        animated:YES];
-            }),
-            base::TimeDelta::FromMilliseconds(
-                kTabGridScrollAnimationDelayInMilliseconds));
+        auto scrollToRegularTabs = ^{
+          [weakSelf setCurrentPageAndPageControl:TabGridPageRegularTabs
+                                        animated:YES];
+        };
+        if (UIAccessibilityIsVoiceOverRunning()) {
+          scrollToRegularTabs();
+        } else {
+          base::TimeDelta delay = base::TimeDelta::FromMilliseconds(
+              kTabGridScrollAnimationDelayInMilliseconds);
+          base::PostDelayedTask(FROM_HERE, {web::WebThread::UI},
+                                base::BindOnce(scrollToRegularTabs), delay);
+        }
       } else {
-        // Directly show the regular tabs in tab switcher without animation if
+        // Directly show the regular tab page without animation if
         // the user was not already in tab switcher.
-        [self setCurrentPageAndPageControlSelectedPage:TabGridPageRegularTabs
-                                              animated:NO];
+        [self setCurrentPageAndPageControl:TabGridPageRegularTabs animated:NO];
       }
     }
   }
diff --git a/ios/chrome/test/earl_grey/BUILD.gn b/ios/chrome/test/earl_grey/BUILD.gn
index 3c845d2..0d3c33c 100644
--- a/ios/chrome/test/earl_grey/BUILD.gn
+++ b/ios/chrome/test/earl_grey/BUILD.gn
@@ -34,7 +34,9 @@
     "//ios/chrome/browser/ntp_tiles:eg_tests",
     "//ios/chrome/browser/passwords:eg_tests",
     "//ios/chrome/browser/prerender:eg_tests",
+    "//ios/chrome/browser/ui/autofill:eg_tests",
     "//ios/chrome/browser/ui/autofill/manual_fill:eg_tests",
+    "//ios/chrome/browser/ui/content_suggestions:eg_tests",
     "//ios/chrome/browser/ui/payments:eg_tests",
   ]
 }
@@ -73,9 +75,7 @@
   deps = [
     "//ios/chrome/browser/ui/activity_services:eg_tests",
     "//ios/chrome/browser/ui/alert_coordinator:eg_tests",
-    "//ios/chrome/browser/ui/autofill:eg_tests",
     "//ios/chrome/browser/ui/browser_view:eg_tests",
-    "//ios/chrome/browser/ui/content_suggestions:eg_tests",
     "//ios/chrome/browser/ui/dialogs:eg_tests",
     "//ios/chrome/browser/ui/download:eg_tests",
     "//ios/chrome/browser/ui/find_bar:eg_tests",
diff --git a/ios/chrome/test/earl_grey/DEPS b/ios/chrome/test/earl_grey/DEPS
index 371d256..e063335e 100644
--- a/ios/chrome/test/earl_grey/DEPS
+++ b/ios/chrome/test/earl_grey/DEPS
@@ -6,6 +6,7 @@
   ],
   # To compile base::Feature under EG2
   "chrome_earl_grey_app_interface\.mm":[
+   "+components/autofill/core/common/autofill_features.h",
    "+components/payments/core/features.h",
    "+components/ukm/ios/features.h",
    "+services/metrics/public/cpp/ukm_recorder.h",
diff --git a/ios/chrome/test/earl_grey/chrome_earl_grey.h b/ios/chrome/test/earl_grey/chrome_earl_grey.h
index aa1e2c7..a50e2329 100644
--- a/ios/chrome/test/earl_grey/chrome_earl_grey.h
+++ b/ios/chrome/test/earl_grey/chrome_earl_grey.h
@@ -429,6 +429,9 @@
 // Returns YES if CreditCardScanner feature is enabled.
 - (BOOL)isCreditCardScannerEnabled WARN_UNUSED_RESULT;
 
+// Returns YES if AutofillEnableCompanyName feature is enabled.
+- (BOOL)isAutofillCompanyNameEnabled WARN_UNUSED_RESULT;
+
 // Returns YES if custom WebKit frameworks were properly loaded, rather than
 // system frameworks. Always returns YES if the app was not requested to run
 // with custom WebKit frameworks.
diff --git a/ios/chrome/test/earl_grey/chrome_earl_grey.mm b/ios/chrome/test/earl_grey/chrome_earl_grey.mm
index 4c1dd6c..8a2e3e3 100644
--- a/ios/chrome/test/earl_grey/chrome_earl_grey.mm
+++ b/ios/chrome/test/earl_grey/chrome_earl_grey.mm
@@ -740,6 +740,10 @@
   return [ChromeEarlGreyAppInterface isCreditCardScannerEnabled];
 }
 
+- (BOOL)isAutofillCompanyNameEnabled {
+  return [ChromeEarlGreyAppInterface isAutofillCompanyNameEnabled];
+}
+
 - (BOOL)isCustomWebKitLoadedIfRequested {
   return [ChromeEarlGreyAppInterface isCustomWebKitLoadedIfRequested];
 }
diff --git a/ios/chrome/test/earl_grey/chrome_earl_grey_app_interface.h b/ios/chrome/test/earl_grey/chrome_earl_grey_app_interface.h
index f2905a5..505dd4e 100644
--- a/ios/chrome/test/earl_grey/chrome_earl_grey_app_interface.h
+++ b/ios/chrome/test/earl_grey/chrome_earl_grey_app_interface.h
@@ -338,6 +338,9 @@
 // Returns YES if CreditCardScanner feature is enabled.
 + (BOOL)isCreditCardScannerEnabled WARN_UNUSED_RESULT;
 
+// Returns YES if AutofillEnableCompanyName feature is enabled.
++ (BOOL)isAutofillCompanyNameEnabled WARN_UNUSED_RESULT;
+
 // Returns YES if custom WebKit frameworks were properly loaded, rather than
 // system frameworks. Always returns YES if the app was not requested to run
 // with custom WebKit frameworks.
diff --git a/ios/chrome/test/earl_grey/chrome_earl_grey_app_interface.mm b/ios/chrome/test/earl_grey/chrome_earl_grey_app_interface.mm
index b9941d4..3b449ed5 100644
--- a/ios/chrome/test/earl_grey/chrome_earl_grey_app_interface.mm
+++ b/ios/chrome/test/earl_grey/chrome_earl_grey_app_interface.mm
@@ -7,6 +7,7 @@
 
 #include "base/strings/sys_string_conversions.h"
 #include "components/autofill/core/browser/personal_data_manager.h"
+#include "components/autofill/core/common/autofill_features.h"
 #include "components/content_settings/core/browser/host_content_settings_map.h"
 #import "components/payments/core/features.h"
 #import "components/ukm/ios/features.h"
@@ -584,6 +585,11 @@
   return base::FeatureList::IsEnabled(kCreditCardScanner);
 }
 
++ (BOOL)isAutofillCompanyNameEnabled {
+  return base::FeatureList::IsEnabled(
+      autofill::features::kAutofillEnableCompanyName);
+}
+
 + (BOOL)isCustomWebKitLoadedIfRequested {
   return IsCustomWebKitLoadedIfRequested();
 }
diff --git a/ios/chrome/test/earl_grey2/BUILD.gn b/ios/chrome/test/earl_grey2/BUILD.gn
index e679e643..b0fc51a 100644
--- a/ios/chrome/test/earl_grey2/BUILD.gn
+++ b/ios/chrome/test/earl_grey2/BUILD.gn
@@ -41,6 +41,10 @@
     "//ios/chrome/browser/ntp_tiles:eg2_tests",
     "//ios/chrome/browser/passwords:eg2_tests",
     "//ios/chrome/browser/prerender:eg2_tests",
+    "//ios/chrome/browser/ui/autofill:eg2_tests",
+    "//ios/chrome/browser/ui/autofill/manual_fill:eg2_tests",
+    "//ios/chrome/browser/ui/content_suggestions:eg2_tests",
+    "//ios/chrome/browser/ui/integration_tests:eg2_tests",
   ]
 }
 
@@ -66,14 +70,10 @@
 
   deps = [
     "//ios/chrome/browser/ui/activity_services:eg2_tests",
-    "//ios/chrome/browser/ui/autofill:eg2_tests",
-    "//ios/chrome/browser/ui/autofill/manual_fill:eg2_tests",
-    "//ios/chrome/browser/ui/content_suggestions:eg2_tests",
     "//ios/chrome/browser/ui/dialogs:eg2_tests",
     "//ios/chrome/browser/ui/download:eg2_tests",
     "//ios/chrome/browser/ui/first_run:eg2_tests",
     "//ios/chrome/browser/ui/fullscreen:eg2_tests",
-    "//ios/chrome/browser/ui/integration_tests:eg2_tests",
     "//ios/chrome/browser/ui/keyboard:eg2_tests",
     "//ios/chrome/browser/ui/ntp:eg2_tests",
     "//ios/chrome/browser/ui/omnibox/popup:eg2_tests",
diff --git a/ios/testing/BUILD.gn b/ios/testing/BUILD.gn
index 1e4d6a824..acc55d5 100644
--- a/ios/testing/BUILD.gn
+++ b/ios/testing/BUILD.gn
@@ -67,7 +67,6 @@
   ]
 
   public_deps = [
-    "//third_party/google_toolbox_for_mac",
     "//third_party/ocmock",
   ]
 
diff --git a/ios/testing/DEPS b/ios/testing/DEPS
index 49528e5..27b11ce 100644
--- a/ios/testing/DEPS
+++ b/ios/testing/DEPS
@@ -3,9 +3,3 @@
   "+net",
   "+third_party/ocmock",
 ]
-
-specific_include_rules = {
-  "ocmock_complex_type_helper\.h": [
-    "+third_party/google_toolbox_for_mac/src/Foundation/GTMLightweightProxy.h",
-  ],
-}
diff --git a/ios/testing/ocmock_complex_type_helper.h b/ios/testing/ocmock_complex_type_helper.h
index ad8d83d..3dc7a4c8 100644
--- a/ios/testing/ocmock_complex_type_helper.h
+++ b/ios/testing/ocmock_complex_type_helper.h
@@ -5,20 +5,16 @@
 #ifndef IOS_TESTING_OCMOCK_COMPLEX_TYPE_HELPER_H_
 #define IOS_TESTING_OCMOCK_COMPLEX_TYPE_HELPER_H_
 
-#import "third_party/google_toolbox_for_mac/src/Foundation/GTMLightweightProxy.h"
 #import "third_party/ocmock/OCMock/OCMock.h"
 
 // OCMock cannot check scalar arguments to method as it requires NSObjects to
 // work its magic. This class tries to alleviate this restriction in a crude
 // way. For an example of use, see the associated unittest class.
-@interface OCMockComplexTypeHelper : GTMLightweightProxy
-// As opposed to its parent class the OCMockComplexTypeHelper retains its
-// represented object.
-- (instancetype)initWithRepresentedObject:(id)object;
+@interface OCMockComplexTypeHelper : NSProxy
+// Init OCMockComplexTypeHelper with a represented object (retained).
+- (instancetype)initWithRepresentedObject:(OCMockObject*)object;
 // Registers a block to be called when a selector is called.
 - (void)onSelector:(SEL)selector callBlockExpectation:(id)block;
-// Unregisters the block associated to the selector.
-- (void)removeBlockExpectationOnSelector:(SEL)selector;
 // Returns the block for the given selector. Intended for use by subclasses.
 - (id)blockForSelector:(SEL)selector;
 @end
diff --git a/ios/testing/ocmock_complex_type_helper.mm b/ios/testing/ocmock_complex_type_helper.mm
index 677d300..28f9d663 100644
--- a/ios/testing/ocmock_complex_type_helper.mm
+++ b/ios/testing/ocmock_complex_type_helper.mm
@@ -12,35 +12,26 @@
 #endif
 
 @implementation OCMockComplexTypeHelper {
-  // Same as the superclass -representedObject, but retained.
+  // The represented object.
   OCMockObject* _object;
-  // All the blocks registered by selector.
-  NSMutableDictionary* _blocks;
+
+  // Dictionary holding blocks registered by selector.
+  NSMutableDictionary<NSString*, id>* _blocks;
 }
 
-#pragma mark - public methods.
+#pragma mark - Public methods.
 
-- (instancetype)initWithRepresentedObject:(id)object {
-  if ((self = [super initWithRepresentedObject:object]))
-    _object = object;
+- (instancetype)initWithRepresentedObject:(OCMockObject*)object {
+  DCHECK(object);
+  _object = object;
+  _blocks = [[NSMutableDictionary alloc] init];
   return self;
 }
 
 - (void)onSelector:(SEL)selector callBlockExpectation:(id)block {
-  if (!_blocks)
-    _blocks = [[NSMutableDictionary alloc] init];
-
   NSString* key = NSStringFromSelector(selector);
   DCHECK(![_blocks objectForKey:key]) << "Only one expectation per signature";
-  id value = [block copy];
-  [_blocks setObject:value forKey:key];
-}
-
-- (void)removeBlockExpectationOnSelector:(SEL)selector {
-  NSString* key = NSStringFromSelector(selector);
-  DCHECK([_blocks objectForKey:key])
-      << "No expectation for selector " << base::SysNSStringToUTF8(key);
-  [_blocks removeObjectForKey:key];
+  [_blocks setObject:block forKey:key];
 }
 
 - (id)blockForSelector:(SEL)selector {
@@ -54,29 +45,50 @@
 #pragma mark - OCMockObject forwarding.
 
 // OCMockObject -respondsToSelector responds NO for the OCMock object specific
-// methods. This confuses the GTMLightweightProxy class. In order to forward
+// methods. This confuses the NSProxy architecture. In order to forward
 // those properly the simplest approach is to forward them explicitely.
 - (id)stub {
   return [_object stub];
 }
+
 - (id)expect {
   return [_object expect];
 }
+
 - (id)reject {
   return [_object reject];
 }
+
 - (void)verify {
   [_object verify];
 }
+
 - (void)setExpectationOrderMatters:(BOOL)flag {
   [_object setExpectationOrderMatters:flag];
 }
 
-#pragma mark - Internal methods.
+#pragma mark - NSProxy implementation.
+
+- (void)forwardInvocation:(NSInvocation*)invocation {
+  SEL selector = [invocation selector];
+  if ([_object respondsToSelector:selector])
+    [invocation invokeWithTarget:_object];
+}
+
+- (NSMethodSignature*)methodSignatureForSelector:(SEL)selector {
+  return [_object methodSignatureForSelector:selector];
+}
+
+- (void)doesNotRecognizeSelector:(SEL)selector {
+  [(id)_object doesNotRecognizeSelector:selector];
+}
 
 - (BOOL)respondsToSelector:(SEL)selector {
   DCHECK(![_blocks objectForKey:NSStringFromSelector(selector)]);
-  return [super respondsToSelector:selector];
+  if (selector == @selector(initWithRepresentedObject:))
+    return YES;
+
+  return [_object respondsToSelector:selector];
 }
 
 @end
diff --git a/media/blink/watch_time_component_unittest.cc b/media/blink/watch_time_component_unittest.cc
index 3c627dfc..6646239c200 100644
--- a/media/blink/watch_time_component_unittest.cc
+++ b/media/blink/watch_time_component_unittest.cc
@@ -28,6 +28,7 @@
   MOCK_METHOD2(UpdateUnderflowDuration, void(int32_t, base::TimeDelta));
   MOCK_METHOD1(UpdateSecondaryProperties,
                void(mojom::SecondaryPlaybackPropertiesPtr));
+  MOCK_METHOD1(OnCurrentTimestampChanged, void(base::TimeDelta));
 };
 
 class WatchTimeComponentTest : public testing::Test {
diff --git a/media/blink/watch_time_reporter.cc b/media/blink/watch_time_reporter.cc
index 5186410..6a32465f 100644
--- a/media/blink/watch_time_reporter.cc
+++ b/media/blink/watch_time_reporter.cc
@@ -530,6 +530,9 @@
     display_type_component_->RecordWatchTime(current_timestamp);
   if (controls_component_)
     controls_component_->RecordWatchTime(current_timestamp);
+
+  // Update the last timestamp with the current timestamp.
+  recorder_->OnCurrentTimestampChanged(current_timestamp);
 }
 
 void WatchTimeReporter::UpdateWatchTime() {
diff --git a/media/blink/watch_time_reporter_unittest.cc b/media/blink/watch_time_reporter_unittest.cc
index bd2ba1a..0a12e04 100644
--- a/media/blink/watch_time_reporter_unittest.cc
+++ b/media/blink/watch_time_reporter_unittest.cc
@@ -229,6 +229,10 @@
                                         video_frames_dropped);
     }
 
+    void OnCurrentTimestampChanged(base::TimeDelta duration) override {
+      parent_->OnCurrentTimestampChanged(duration);
+    }
+
    private:
     WatchTimeReporterTest* parent_;
 
@@ -623,6 +627,7 @@
   MOCK_METHOD1(OnSetAutoplayInitiated, void(bool));
   MOCK_METHOD1(OnDurationChanged, void(base::TimeDelta));
   MOCK_METHOD2(OnUpdateVideoDecodeStats, void(uint32_t, uint32_t));
+  MOCK_METHOD1(OnCurrentTimestampChanged, void(base::TimeDelta));
 
   const bool has_video_;
   const bool has_audio_;
diff --git a/media/blink/webmediaplayer_impl_unittest.cc b/media/blink/webmediaplayer_impl_unittest.cc
index 9f16754..1fd9a16 100644
--- a/media/blink/webmediaplayer_impl_unittest.cc
+++ b/media/blink/webmediaplayer_impl_unittest.cc
@@ -51,7 +51,6 @@
 #include "mojo/public/cpp/bindings/strong_binding.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
-#include "third_party/blink/public/mojom/frame/document_interface_broker.mojom.h"
 #include "third_party/blink/public/platform/media/webmediaplayer_delegate.h"
 #include "third_party/blink/public/platform/web_fullscreen_video_status.h"
 #include "third_party/blink/public/platform/web_media_player.h"
@@ -102,13 +101,6 @@
                                   std::string(new_rate_string));
 }
 
-// returns a valid handle that can be passed to WebLocalFrame constructor
-mojo::ScopedMessagePipeHandle CreateStubDocumentInterfaceBrokerHandle() {
-  return mojo::PendingRemote<blink::mojom::DocumentInterfaceBroker>()
-      .InitWithNewPipeAndPassReceiver()
-      .PassPipe();
-}
-
 class MockWebMediaPlayerClient : public blink::WebMediaPlayerClient {
  public:
   MockWebMediaPlayerClient() = default;
@@ -314,12 +306,11 @@
                                          /*is_hidden=*/false,
                                          /*compositing_enabled=*/false,
                                          nullptr)),
-        web_local_frame_(blink::WebLocalFrame::CreateMainFrame(
-            web_view_,
-            &web_frame_client_,
-            nullptr,
-            CreateStubDocumentInterfaceBrokerHandle(),
-            nullptr)),
+        web_local_frame_(
+            blink::WebLocalFrame::CreateMainFrame(web_view_,
+                                                  &web_frame_client_,
+                                                  nullptr,
+                                                  nullptr)),
         context_provider_(viz::TestContextProvider::Create()),
         audio_parameters_(TestAudioParameters::Normal()) {
     media_thread_.StartAndWaitForTesting();
@@ -368,6 +359,9 @@
         base::BindRepeating([]() { return learning::FeatureValue(0); }),
         VideoDecodePerfHistory::SaveCallback(),
         MediaMetricsProvider::GetLearningSessionCallback(),
+        base::BindRepeating(
+            &WebMediaPlayerImplTest::GetRecordAggregateWatchTimeCallback,
+            base::Unretained(this)),
         provider.BindNewPipeAndPassReceiver());
 
     // Initialize provider since none of the tests below actually go through the
@@ -451,6 +445,11 @@
     wmpi_->OnDurationChange();
   }
 
+  MediaMetricsProvider::RecordAggregateWatchTimeCallback
+  GetRecordAggregateWatchTimeCallback() {
+    return base::NullCallback();
+  }
+
   base::TimeDelta GetCurrentTimeInternal() {
     return wmpi_->GetCurrentTimeInternal();
   }
diff --git a/media/mojo/mojom/watch_time_recorder.mojom b/media/mojo/mojom/watch_time_recorder.mojom
index 52311c2..8374b82 100644
--- a/media/mojo/mojom/watch_time_recorder.mojom
+++ b/media/mojo/mojom/watch_time_recorder.mojom
@@ -93,4 +93,7 @@
   UpdateUnderflowCount(int32 total_count);
   UpdateUnderflowDuration(int32 total_completed_count,
                           mojo_base.mojom.TimeDelta total_duration);
+
+  // Updates the last known media timestamp for the video.
+  OnCurrentTimestampChanged(mojo_base.mojom.TimeDelta last_timestamp);
 };
diff --git a/media/mojo/services/media_metrics_provider.cc b/media/mojo/services/media_metrics_provider.cc
index a1c9601..e5511c8 100644
--- a/media/mojo/services/media_metrics_provider.cc
+++ b/media/mojo/services/media_metrics_provider.cc
@@ -39,13 +39,15 @@
     ukm::SourceId source_id,
     learning::FeatureValue origin,
     VideoDecodePerfHistory::SaveCallback save_cb,
-    GetLearningSessionCallback learning_session_cb)
+    GetLearningSessionCallback learning_session_cb,
+    RecordAggregateWatchTimeCallback record_playback_cb)
     : player_id_(g_player_id++),
       is_top_frame_(is_top_frame == FrameStatus::kTopFrame),
       source_id_(source_id),
       origin_(origin),
       save_cb_(std::move(save_cb)),
       learning_session_cb_(learning_session_cb),
+      record_playback_cb_(std::move(record_playback_cb)),
       uma_info_(is_incognito == BrowsingMode::kIncognito) {}
 
 MediaMetricsProvider::~MediaMetricsProvider() {
@@ -167,11 +169,13 @@
     GetOriginCallback get_origin_cb,
     VideoDecodePerfHistory::SaveCallback save_cb,
     GetLearningSessionCallback learning_session_cb,
+    GetRecordAggregateWatchTimeCallback get_record_playback_cb,
     mojo::PendingReceiver<mojom::MediaMetricsProvider> receiver) {
   mojo::MakeSelfOwnedReceiver(
       std::make_unique<MediaMetricsProvider>(
           is_incognito, is_top_frame, get_source_id_cb.Run(),
-          get_origin_cb.Run(), std::move(save_cb), learning_session_cb),
+          get_origin_cb.Run(), std::move(save_cb), learning_session_cb,
+          std::move(get_record_playback_cb).Run()),
       std::move(receiver));
 }
 
@@ -262,7 +266,8 @@
 
   mojo::MakeSelfOwnedReceiver(
       std::make_unique<WatchTimeRecorder>(std::move(properties), source_id_,
-                                          is_top_frame_, player_id_),
+                                          is_top_frame_, player_id_,
+                                          record_playback_cb_),
       std::move(receiver));
 }
 
diff --git a/media/mojo/services/media_metrics_provider.h b/media/mojo/services/media_metrics_provider.h
index d928aba..b231e37 100644
--- a/media/mojo/services/media_metrics_provider.h
+++ b/media/mojo/services/media_metrics_provider.h
@@ -18,6 +18,7 @@
 #include "media/mojo/services/video_decode_perf_history.h"
 #include "mojo/public/cpp/bindings/pending_receiver.h"
 #include "services/metrics/public/cpp/ukm_source_id.h"
+#include "url/gurl.h"
 
 namespace media {
 
@@ -34,17 +35,28 @@
   using GetLearningSessionCallback =
       base::RepeatingCallback<learning::LearningSession*()>;
 
+  using RecordAggregateWatchTimeCallback =
+      base::RepeatingCallback<void(base::TimeDelta total_watch_time,
+                                   base::TimeDelta time_stamp)>;
+
+  using GetRecordAggregateWatchTimeCallback =
+      base::RepeatingCallback<RecordAggregateWatchTimeCallback(void)>;
+
   MediaMetricsProvider(BrowsingMode is_incognito,
                        FrameStatus is_top_frame,
                        ukm::SourceId source_id,
                        learning::FeatureValue origin,
                        VideoDecodePerfHistory::SaveCallback save_cb,
-                       GetLearningSessionCallback learning_session_cb);
+                       GetLearningSessionCallback learning_session_cb,
+                       RecordAggregateWatchTimeCallback record_playback_cb);
   ~MediaMetricsProvider() override;
 
   // Callback for retrieving a ukm::SourceId.
   using GetSourceIdCallback = base::RepeatingCallback<ukm::SourceId(void)>;
 
+  using GetLastCommittedURLCallback =
+      base::RepeatingCallback<const GURL&(void)>;
+
   // TODO(liberato): This should be from a FeatureProvider, but the way in which
   // we attach LearningHelper more or less precludes it.  Per-frame task
   // controllers would make this easy, but we bypass that here.
@@ -65,6 +77,7 @@
       GetOriginCallback get_origin_cb,
       VideoDecodePerfHistory::SaveCallback save_cb,
       GetLearningSessionCallback learning_session_cb,
+      GetRecordAggregateWatchTimeCallback get_record_playback_cb,
       mojo::PendingReceiver<mojom::MediaMetricsProvider> receiver);
 
  private:
@@ -126,6 +139,7 @@
 
   const VideoDecodePerfHistory::SaveCallback save_cb_;
   const GetLearningSessionCallback learning_session_cb_;
+  const RecordAggregateWatchTimeCallback record_playback_cb_;
 
   // UMA pipeline packaged data
   PipelineInfo uma_info_;
diff --git a/media/mojo/services/media_metrics_provider_unittest.cc b/media/mojo/services/media_metrics_provider_unittest.cc
index 35e6f4d9..d68ae39 100644
--- a/media/mojo/services/media_metrics_provider_unittest.cc
+++ b/media/mojo/services/media_metrics_provider_unittest.cc
@@ -49,12 +49,20 @@
         base::BindRepeating([]() { return learning::FeatureValue(0); }),
         VideoDecodePerfHistory::SaveCallback(),
         MediaMetricsProvider::GetLearningSessionCallback(),
+        base::BindRepeating(
+            &MediaMetricsProviderTest::GetRecordAggregateWatchTimeCallback,
+            base::Unretained(this)),
         provider_.BindNewPipeAndPassReceiver());
     provider_->Initialize(is_mse, scheme);
   }
 
   ukm::SourceId GetSourceId() { return source_id_; }
 
+  MediaMetricsProvider::RecordAggregateWatchTimeCallback
+  GetRecordAggregateWatchTimeCallback() {
+    return base::NullCallback();
+  }
+
   void ResetMetricRecorders() {
     // Ensure cleared global before attempting to create a new TestUkmReporter.
     test_recorder_.reset();
diff --git a/media/mojo/services/watch_time_recorder.cc b/media/mojo/services/watch_time_recorder.cc
index bbe298e..e6624be 100644
--- a/media/mojo/services/watch_time_recorder.cc
+++ b/media/mojo/services/watch_time_recorder.cc
@@ -7,6 +7,7 @@
 #include <algorithm>
 #include <cmath>
 
+#include "base/callback.h"
 #include "base/hash/hash.h"
 #include "base/metrics/histogram_functions.h"
 #include "base/strings/string_piece.h"
@@ -139,10 +140,12 @@
 
 WatchTimeRecorder::WatchTimeUkmRecord::~WatchTimeUkmRecord() = default;
 
-WatchTimeRecorder::WatchTimeRecorder(mojom::PlaybackPropertiesPtr properties,
-                                     ukm::SourceId source_id,
-                                     bool is_top_frame,
-                                     uint64_t player_id)
+WatchTimeRecorder::WatchTimeRecorder(
+    mojom::PlaybackPropertiesPtr properties,
+    ukm::SourceId source_id,
+    bool is_top_frame,
+    uint64_t player_id,
+    RecordAggregateWatchTimeCallback record_playback_cb)
     : properties_(std::move(properties)),
       source_id_(source_id),
       is_top_frame_(is_top_frame),
@@ -162,7 +165,8 @@
             kRebuffersCountAudioVideoMse, kDiscardedWatchTimeAudioVideoMse},
            {WatchTimeKey::kAudioVideoEme,
             kMeanTimeBetweenRebuffersAudioVideoEme,
-            kRebuffersCountAudioVideoEme, kDiscardedWatchTimeAudioVideoEme}}) {}
+            kRebuffersCountAudioVideoEme, kDiscardedWatchTimeAudioVideoEme}}),
+      record_playback_cb_(std::move(record_playback_cb)) {}
 
 WatchTimeRecorder::~WatchTimeRecorder() {
   FinalizeWatchTime({});
@@ -375,6 +379,11 @@
   underflow_duration_ = total_duration;
 }
 
+void WatchTimeRecorder::OnCurrentTimestampChanged(
+    base::TimeDelta current_timestamp) {
+  last_timestamp_ = current_timestamp;
+}
+
 void WatchTimeRecorder::RecordUkmPlaybackData() {
   // UKM may be unavailable in content_shell or other non-chrome/ builds; it
   // may also be unavailable if browser shutdown has started; so this may be a
@@ -399,6 +408,7 @@
     }
   }
 
+  base::TimeDelta total_watch_time;
   for (auto& ukm_record : ukm_records_) {
     ukm::builders::Media_BasicPlayback builder(source_id_);
 
@@ -423,6 +433,7 @@
         // Only one of these keys should be present.
         DCHECK(!recorded_all_metric);
         recorded_all_metric = true;
+        total_watch_time += kv.second;
 
         builder.SetWatchTime(kv.second.InMilliseconds());
         if (ukm_record.total_underflow_count) {
@@ -520,6 +531,11 @@
     builder.Record(ukm_recorder);
   }
 
+  // Only save the playback if the video contains both video and audio.
+  if (properties_->has_video && properties_->has_audio &&
+      total_watch_time > base::TimeDelta())
+    std::move(record_playback_cb_).Run(total_watch_time, last_timestamp_);
+
   ukm_records_.clear();
 }
 
diff --git a/media/mojo/services/watch_time_recorder.h b/media/mojo/services/watch_time_recorder.h
index 3776ad38..5e5fd15 100644
--- a/media/mojo/services/watch_time_recorder.h
+++ b/media/mojo/services/watch_time_recorder.h
@@ -17,16 +17,22 @@
 #include "media/mojo/mojom/watch_time_recorder.mojom.h"
 #include "media/mojo/services/media_mojo_export.h"
 #include "services/metrics/public/cpp/ukm_source_id.h"
+#include "url/gurl.h"
 
 namespace media {
 
 // See mojom::WatchTimeRecorder for documentation.
 class MEDIA_MOJO_EXPORT WatchTimeRecorder : public mojom::WatchTimeRecorder {
  public:
+  using RecordAggregateWatchTimeCallback =
+      base::OnceCallback<void(base::TimeDelta total_watch_time,
+                              base::TimeDelta time_stamp)>;
+
   WatchTimeRecorder(mojom::PlaybackPropertiesPtr properties,
                     ukm::SourceId source_id,
                     bool is_top_frame,
-                    uint64_t player_id);
+                    uint64_t player_id,
+                    RecordAggregateWatchTimeCallback record_playback_cb);
   ~WatchTimeRecorder() override;
 
   // mojom::WatchTimeRecorder implementation:
@@ -43,6 +49,7 @@
   void UpdateUnderflowCount(int32_t total_count) override;
   void UpdateUnderflowDuration(int32_t total_completed_count,
                                base::TimeDelta total_duration) override;
+  void OnCurrentTimestampChanged(base::TimeDelta current_timestamp) override;
 
  private:
   // Records a UKM event based on |aggregate_watch_time_info_|; only recorded
@@ -115,8 +122,9 @@
 
   PipelineStatus pipeline_status_ = PIPELINE_OK;
   base::TimeDelta duration_ = kNoTimestamp;
-
+  base::TimeDelta last_timestamp_ = kNoTimestamp;
   base::Optional<bool> autoplay_initiated_;
+  RecordAggregateWatchTimeCallback record_playback_cb_;
 
   DISALLOW_COPY_AND_ASSIGN(WatchTimeRecorder);
 };
diff --git a/media/mojo/services/watch_time_recorder_unittest.cc b/media/mojo/services/watch_time_recorder_unittest.cc
index 6886d8b..0fcdea77 100644
--- a/media/mojo/services/watch_time_recorder_unittest.cc
+++ b/media/mojo/services/watch_time_recorder_unittest.cc
@@ -31,6 +31,10 @@
 
 using UkmEntry = ukm::builders::Media_BasicPlayback;
 
+namespace content {
+class RenderFrameHostDelegate;
+}  // namespace content
+
 namespace media {
 
 constexpr char kTestOrigin[] = "https://test.google.com/";
@@ -68,6 +72,9 @@
             []() { return learning::FeatureValue(0); }) /* origin callback */,
         VideoDecodePerfHistory::SaveCallback(),
         MediaMetricsProvider::GetLearningSessionCallback(),
+        base::BindRepeating(
+            &WatchTimeRecorderTest::GetRecordAggregateWatchTimeCallback,
+            base::Unretained(this)),
         provider_.BindNewPipeAndPassReceiver());
   }
 
@@ -169,6 +176,17 @@
 
   ukm::SourceId GetSourceId() { return source_id_; }
 
+  MediaMetricsProvider::RecordAggregateWatchTimeCallback
+  GetRecordAggregateWatchTimeCallback() {
+    return base::BindRepeating(
+        [](base::WeakPtr<content::RenderFrameHostDelegate> delegate,
+           GURL last_committed_url, base::TimeDelta total_watch_time,
+           base::TimeDelta time_stamp) {
+          // Do nothing as this mock callback will never be called.
+        },
+        nullptr, GURL());
+  }
+
   MOCK_METHOD0(GetCurrentMediaTime, base::TimeDelta());
 
  protected:
diff --git a/mojo/public/cpp/bindings/tests/versioning_apptest.cc b/mojo/public/cpp/bindings/tests/versioning_apptest.cc
deleted file mode 100644
index 95a89c0..0000000
--- a/mojo/public/cpp/bindings/tests/versioning_apptest.cc
+++ /dev/null
@@ -1,123 +0,0 @@
-// Copyright 2015 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include <stddef.h>
-#include <stdint.h>
-
-#include "base/macros.h"
-#include "mojo/public/interfaces/bindings/tests/versioning_test_client.mojom.h"
-#include "services/service_manager/public/cpp/application_test_base.h"
-#include "services/service_manager/public/cpp/connector.h"
-
-namespace mojo {
-namespace test {
-namespace versioning {
-
-class VersioningApplicationTest : public ApplicationTestBase {
- public:
-  VersioningApplicationTest() : ApplicationTestBase() {}
-  ~VersioningApplicationTest() override {}
-
- protected:
-  // ApplicationTestBase overrides.
-  void SetUp() override {
-    ApplicationTestBase::SetUp();
-
-    connector()->BindInterface("versioning_test_service", &database_);
-  }
-
-  HumanResourceDatabasePtr database_;
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(VersioningApplicationTest);
-};
-
-TEST_F(VersioningApplicationTest, Struct) {
-  // The service side uses a newer version of Employee defintion.
-  // The returned struct should be truncated.
-  EmployeePtr employee(Employee::New());
-  employee->employee_id = 1;
-  employee->name = "Homer Simpson";
-  employee->department = DEPARTMENT_DEV;
-
-  database_->QueryEmployee(1, true,
-                           [&employee](EmployeePtr returned_employee,
-                                       Array<uint8_t> returned_finger_print) {
-                             EXPECT_TRUE(employee->Equals(*returned_employee));
-                             EXPECT_FALSE(returned_finger_print.is_null());
-                           });
-  database_.WaitForIncomingResponse();
-
-  // Passing a struct of older version to the service side works.
-  EmployeePtr new_employee(Employee::New());
-  new_employee->employee_id = 2;
-  new_employee->name = "Marge Simpson";
-  new_employee->department = DEPARTMENT_SALES;
-
-  database_->AddEmployee(new_employee.Clone(),
-                         [](bool success) { EXPECT_TRUE(success); });
-  database_.WaitForIncomingResponse();
-
-  database_->QueryEmployee(
-      2, false, [&new_employee](EmployeePtr returned_employee,
-                                Array<uint8_t> returned_finger_print) {
-        EXPECT_TRUE(new_employee->Equals(*returned_employee));
-        EXPECT_TRUE(returned_finger_print.is_null());
-      });
-  database_.WaitForIncomingResponse();
-}
-
-TEST_F(VersioningApplicationTest, QueryVersion) {
-  EXPECT_EQ(0u, database_.version());
-  database_.QueryVersion([](uint32_t version) { EXPECT_EQ(1u, version); });
-  database_.WaitForIncomingResponse();
-  EXPECT_EQ(1u, database_.version());
-}
-
-TEST_F(VersioningApplicationTest, RequireVersion) {
-  EXPECT_EQ(0u, database_.version());
-
-  database_.RequireVersion(1);
-  EXPECT_EQ(1u, database_.version());
-  database_->QueryEmployee(3, false,
-                           [](EmployeePtr returned_employee,
-                              Array<uint8_t> returned_finger_print) {});
-  database_.WaitForIncomingResponse();
-  EXPECT_FALSE(database_.encountered_error());
-
-  // Requiring a version higher than what the service side implements will close
-  // the pipe.
-  database_.RequireVersion(3);
-  EXPECT_EQ(3u, database_.version());
-  database_->QueryEmployee(1, false,
-                           [](EmployeePtr returned_employee,
-                              Array<uint8_t> returned_finger_print) {});
-  database_.WaitForIncomingResponse();
-  EXPECT_TRUE(database_.encountered_error());
-}
-
-TEST_F(VersioningApplicationTest, CallNonexistentMethod) {
-  EXPECT_EQ(0u, database_.version());
-
-  Array<uint8_t> new_finger_print(128);
-  for (size_t i = 0; i < 128; ++i)
-    new_finger_print[i] = i + 13;
-
-  // Although the client side doesn't know whether the service side supports
-  // version 1, calling a version 1 method succeeds as long as the service side
-  // supports version 1.
-  database_->AttachFingerPrint(1, new_finger_print.Clone(),
-                               [](bool success) { EXPECT_TRUE(success); });
-  database_.WaitForIncomingResponse();
-
-  // Calling a version 2 method (which the service side doesn't support) closes
-  // the pipe.
-  database_->ListEmployeeIds([](Array<uint64_t> ids) { EXPECT_TRUE(false); });
-  database_.WaitForIncomingResponse();
-  EXPECT_TRUE(database_.encountered_error());
-}
-
-}  // namespace versioning
-}  // namespace examples
-}  // namespace mojo
diff --git a/mojo/public/cpp/bindings/tests/versioning_test_service.cc b/mojo/public/cpp/bindings/tests/versioning_test_service.cc
deleted file mode 100644
index b60e88a..0000000
--- a/mojo/public/cpp/bindings/tests/versioning_test_service.cc
+++ /dev/null
@@ -1,131 +0,0 @@
-// Copyright 2015 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include <stdint.h>
-
-#include <map>
-#include <utility>
-
-#include "base/bind.h"
-#include "base/macros.h"
-#include "mojo/public/cpp/bindings/strong_binding.h"
-#include "mojo/public/interfaces/bindings/tests/versioning_test_service.mojom.h"
-#include "services/service_manager/public/c/main.h"
-#include "services/service_manager/public/cpp/binder_registry.h"
-#include "services/service_manager/public/cpp/service.h"
-#include "services/service_manager/public/cpp/service_runner.h"
-
-namespace mojo {
-namespace test {
-namespace versioning {
-
-struct EmployeeInfo {
- public:
-  EmployeeInfo() {}
-
-  EmployeePtr employee;
-  Array<uint8_t> finger_print;
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(EmployeeInfo);
-};
-
-class HumanResourceDatabaseImpl : public HumanResourceDatabase {
- public:
-  explicit HumanResourceDatabaseImpl(
-      InterfaceRequest<HumanResourceDatabase> request)
-      : strong_binding_(this, std::move(request)) {
-    // Pretend that there is already some data in the system.
-    EmployeeInfo* info = new EmployeeInfo();
-    employees_[1] = info;
-    info->employee = Employee::New();
-    info->employee->employee_id = 1;
-    info->employee->name = "Homer Simpson";
-    info->employee->department = DEPARTMENT_DEV;
-    info->employee->birthday = Date::New();
-    info->employee->birthday->year = 1955;
-    info->employee->birthday->month = 5;
-    info->employee->birthday->day = 12;
-    info->finger_print.resize(1024);
-    for (uint32_t i = 0; i < 1024; ++i)
-      info->finger_print[i] = i;
-  }
-
-  ~HumanResourceDatabaseImpl() override {
-    for (auto iter = employees_.begin(); iter != employees_.end(); ++iter)
-      delete iter->second;
-  }
-
-  void AddEmployee(EmployeePtr employee,
-                   const AddEmployeeCallback& callback) override {
-    uint64_t id = employee->employee_id;
-    if (employees_.find(id) == employees_.end())
-      employees_[id] = new EmployeeInfo();
-    employees_[id]->employee = std::move(employee);
-    callback.Run(true);
-  }
-
-  void QueryEmployee(uint64_t id,
-                     bool retrieve_finger_print,
-                     const QueryEmployeeCallback& callback) override {
-    if (employees_.find(id) == employees_.end()) {
-      callback.Run(nullptr, Array<uint8_t>());
-      return;
-    }
-    callback.Run(employees_[id]->employee.Clone(),
-                 retrieve_finger_print ? employees_[id]->finger_print.Clone()
-                                       : Array<uint8_t>());
-  }
-
-  void AttachFingerPrint(uint64_t id,
-                         Array<uint8_t> finger_print,
-                         const AttachFingerPrintCallback& callback) override {
-    if (employees_.find(id) == employees_.end()) {
-      callback.Run(false);
-      return;
-    }
-    employees_[id]->finger_print = std::move(finger_print);
-    callback.Run(true);
-  }
-
- private:
-  std::map<uint64_t, EmployeeInfo*> employees_;
-
-  StrongBinding<HumanResourceDatabase> strong_binding_;
-};
-
-class HumanResourceSystemServer : public service_manager::Service {
- public:
-  HumanResourceSystemServer() {
-    registry_.AddInterface<HumanResourceDatabase>(
-        base::Bind(&HumanResourceSystemServer::Create, base::Unretained(this)));
-  }
-
-  // service_manager::Service implementation.
-  void OnBindInterface(const service_manager::BindSourceInfo& source_info,
-                       const std::string& interface_name,
-                       mojo::ScopedMessagePipeHandle interface_pipe) override {
-    registry_.BindInterface(interface_name, std::move(interface_pipe));
-  }
-
-  void Create(HumanResourceDatabaseRequest request) {
-    // It will be deleted automatically when the underlying pipe encounters a
-    // connection error.
-    new HumanResourceDatabaseImpl(std::move(request));
-  }
-
- private:
-  service_manager::BinderRegistry registry_;
-};
-
-}  // namespace versioning
-}  // namespace test
-}  // namespace mojo
-
-MojoResult ServiceMain(MojoHandle request) {
-  mojo::ServiceRunner runner(
-      new mojo::test::versioning::HumanResourceSystemServer());
-
-  return runner.Run(request);
-}
diff --git a/mojo/public/interfaces/bindings/tests/BUILD.gn b/mojo/public/interfaces/bindings/tests/BUILD.gn
index b79cf2f..e30fbae 100644
--- a/mojo/public/interfaces/bindings/tests/BUILD.gn
+++ b/mojo/public/interfaces/bindings/tests/BUILD.gn
@@ -463,20 +463,6 @@
   scramble_message_ids = false
 }
 
-mojom("versioning_test_service_interfaces") {
-  testonly = true
-  sources = [
-    "versioning_test_service.mojom",
-  ]
-}
-
-mojom("versioning_test_client_interfaces") {
-  testonly = true
-  sources = [
-    "versioning_test_client.mojom",
-  ]
-}
-
 mojom("test_wtf_types") {
   testonly = true
 
diff --git a/mojo/public/interfaces/bindings/tests/versioning_test_client.mojom b/mojo/public/interfaces/bindings/tests/versioning_test_client.mojom
deleted file mode 100644
index f0136db..0000000
--- a/mojo/public/interfaces/bindings/tests/versioning_test_client.mojom
+++ /dev/null
@@ -1,34 +0,0 @@
-// Copyright 2015 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-module mojo.test.versioning;
-
-// versioning_test_service.mojom and versioning_test_client.mojom contain
-// different versions of Mojom definitions for a fictitious human resource
-// management system. They are used to test the versioning mechanism.
-
-enum Department {
-  SALES,
-  DEV
-};
-
-struct Employee {
-  uint64 employee_id;
-  string name;
-  Department department;
-};
-
-interface HumanResourceDatabase {
-  AddEmployee(Employee employee) => (bool success);
-
-  QueryEmployee(uint64 id, [MinVersion=1] bool retrieve_finger_print)
-      => (Employee? employee, [MinVersion=1] array<uint8>? finger_print);
-
-  [MinVersion=1]
-  AttachFingerPrint(uint64 id, array<uint8> finger_print)
-      => (bool success);
-
-  [MinVersion=2]
-  ListEmployeeIds() => (array<uint64>? ids);
-};
diff --git a/mojo/public/interfaces/bindings/tests/versioning_test_service.mojom b/mojo/public/interfaces/bindings/tests/versioning_test_service.mojom
deleted file mode 100644
index 59b3832..0000000
--- a/mojo/public/interfaces/bindings/tests/versioning_test_service.mojom
+++ /dev/null
@@ -1,38 +0,0 @@
-// Copyright 2015 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-module mojo.test.versioning;
-
-// versioning_test_service.mojom and versioning_test_client.mojom contain
-// different versions of Mojom definitions for a fictitious human resource
-// management system. They are used to test the versioning mechanism.
-
-enum Department {
-  SALES,
-  DEV
-};
-
-struct Date {
-  uint16 year;
-  uint8 month;
-  uint8 day;
-};
-
-struct Employee {
-  uint64 employee_id;
-  string name;
-  Department department;
-  [MinVersion=1] Date? birthday;
-};
-
-interface HumanResourceDatabase {
-  AddEmployee(Employee employee) => (bool success);
-
-  QueryEmployee(uint64 id, [MinVersion=1] bool retrieve_finger_print)
-      => (Employee? employee, [MinVersion=1] array<uint8>? finger_print);
-
-  [MinVersion=1]
-  AttachFingerPrint(uint64 id, array<uint8> finger_print)
-      => (bool success);
-};
diff --git a/services/device/bluetooth/bluetooth_system.cc b/services/device/bluetooth/bluetooth_system.cc
index ccc3e2bc..5febdf4 100644
--- a/services/device/bluetooth/bluetooth_system.cc
+++ b/services/device/bluetooth/bluetooth_system.cc
@@ -13,9 +13,9 @@
 
 #include "base/bind.h"
 #include "base/optional.h"
-#include "base/strings/string_number_conversions.h"
 #include "base/strings/string_util.h"
 #include "dbus/object_path.h"
+#include "device/bluetooth/bluetooth_device.h"
 #include "device/bluetooth/dbus/bluetooth_adapter_client.h"
 #include "device/bluetooth/dbus/bluetooth_device_client.h"
 #include "device/bluetooth/dbus/bluez_dbus_manager.h"
@@ -23,42 +23,6 @@
 
 namespace device {
 
-namespace {
-
-base::Optional<std::array<uint8_t, 6>> ParseAddress(
-    const std::string& address_str) {
-  // First check the str has the expected format.
-  static constexpr size_t kCanonicalAddressLength = 17;
-  if (address_str.size() != kCanonicalAddressLength)
-    return base::nullopt;
-
-  for (size_t i = 0; i < address_str.size(); ++i) {
-    const char character = address_str[i];
-
-    bool is_separator = (i + 1) % 3 == 0;
-    bool valid_address_character =
-        is_separator ? (character == ':') : base::IsHexDigit(character);
-
-    if (!valid_address_character)
-      return base::nullopt;
-  }
-
-  // Remove the separator and then parse the result.
-  std::string numbers_only;
-  base::RemoveChars(address_str, ":", &numbers_only);
-
-  std::vector<uint8_t> address_vector;
-  bool success = base::HexStringToBytes(numbers_only, &address_vector);
-  DCHECK(success);
-
-  std::array<uint8_t, 6> address_array;
-  std::copy_n(address_vector.begin(), 6, address_array.begin());
-
-  return address_array;
-}
-
-}  // namespace
-
 void BluetoothSystem::Create(
     mojo::PendingReceiver<mojom::BluetoothSystem> receiver,
     mojo::PendingRemote<mojom::BluetoothSystemClient> client) {
@@ -244,9 +208,9 @@
   std::vector<mojom::BluetoothDeviceInfoPtr> devices;
   for (const auto& device_path : device_paths) {
     auto* properties = GetBluetoothDeviceClient()->GetProperties(device_path);
-    base::Optional<std::array<uint8_t, 6>> parsed_address =
-        ParseAddress(properties->address.value());
-    if (!parsed_address) {
+    std::array<uint8_t, 6> parsed_address;
+    if (!BluetoothDevice::ParseAddress(properties->address.value(),
+                                       parsed_address)) {
       LOG(WARNING) << "Failed to parse device address '"
                    << properties->address.value() << "' for "
                    << device_path.value();
@@ -254,7 +218,7 @@
     }
 
     auto device_info = mojom::BluetoothDeviceInfo::New();
-    device_info->address = std::move(parsed_address.value());
+    device_info->address = std::move(parsed_address);
     device_info->name = properties->name.is_valid()
                             ? base::make_optional(properties->name.value())
                             : base::nullopt;
diff --git a/services/device/bluetooth/bluetooth_system_unittest.cc b/services/device/bluetooth/bluetooth_system_unittest.cc
index f1aab71..bf393e7 100644
--- a/services/device/bluetooth/bluetooth_system_unittest.cc
+++ b/services/device/bluetooth/bluetooth_system_unittest.cc
@@ -1925,7 +1925,7 @@
       // Invalid addresses
       {"1", "00:11"},                 // Too short
       {"2", "00:11:22:AA:BB:CC:DD"},  // Too long
-      {"3", "00-11-22-AA-BB-CC"},     // Invalid separator
+      {"3", "00|11|22|AA|BB|CC"},     // Invalid separator
       {"4", "00:11:22:XX:BB:CC"},     // Invalid character
       // Valid addresses
       {"5", "00:11:22:aa:bb:cc"},  // Lowercase
diff --git a/services/network/content_security_policy_fuzzer.cc b/services/network/content_security_policy_fuzzer.cc
index c837de1..ab828e3 100644
--- a/services/network/content_security_policy_fuzzer.cc
+++ b/services/network/content_security_policy_fuzzer.cc
@@ -6,12 +6,27 @@
 
 #include <string>
 
+#include "base/at_exit.h"
+#include "base/i18n/icu_util.h"
 #include "net/http/http_response_headers.h"
 #include "testing/libfuzzer/libfuzzer_exports.h"
 
+namespace {
+
+// This is a workaround for https://crbug.com/778929.
+struct IcuEnvironment {
+  IcuEnvironment() { CHECK(base::i18n::InitializeICU()); }
+  // used by ICU integration.
+  base::AtExitManager at_exit_manager;
+};
+
+}  // namespace
+
 namespace network {
 
 int LLVMFuzzerInitialize(int* argc, char*** argv) {
+  static IcuEnvironment env;
+
   return 0;
 }
 
diff --git a/skia/BUILD.gn b/skia/BUILD.gn
index 99da57ad..dd36d2a1 100644
--- a/skia/BUILD.gn
+++ b/skia/BUILD.gn
@@ -190,7 +190,11 @@
                   # assembly code contains flow control(jmp or jcc) statements.
 
       "/wd4800",  # forcing value to bool 'true/false'(assigning int to bool).
+      "/wd5041",  # out-of-line definition for constexpr static data member is not needed and is deprecated in C++17
     ]
+    cflags_cc = [ "/std:c++17" ]
+  } else {
+    cflags_cc = [ "-std=c++17" ]
   }
 }
 
diff --git a/testing/test.gni b/testing/test.gni
index 9fdf9c71..6b469076 100644
--- a/testing/test.gni
+++ b/testing/test.gni
@@ -93,7 +93,7 @@
         "android_manifest",
         "android_manifest_dep",
         "enable_multidex",
-        "locale_config_java_packages",
+        "product_config_java_packages",
         "min_sdk_version",
         "proguard_configs",
         "proguard_enabled",
diff --git a/testing/variations/fieldtrial_testing_config.json b/testing/variations/fieldtrial_testing_config.json
index a59b79fd..9485104c 100644
--- a/testing/variations/fieldtrial_testing_config.json
+++ b/testing/variations/fieldtrial_testing_config.json
@@ -3249,6 +3249,28 @@
             ]
         }
     ],
+    "LookalikeUrlNavigationSuggestionsUIV2": [
+        {
+            "platforms": [
+                "linux",
+                "mac",
+                "windows",
+                "android",
+                "chromeos"
+            ],
+            "experiments": [
+                {
+                    "name": "Enabled",
+                    "params": {
+                        "topsites": "true"
+                    },
+                    "enable_features": [
+                        "LookalikeUrlNavigationSuggestionsUI"
+                    ]
+                }
+            ]
+        }
+    ],
     "LowPriorityIframes2": [
         {
             "platforms": [
diff --git a/third_party/android_deps/libs/javax_annotation_jsr250_api/README.chromium b/third_party/android_deps/libs/javax_annotation_jsr250_api/README.chromium
index cf7dc61..7e8296f 100644
--- a/third_party/android_deps/libs/javax_annotation_jsr250_api/README.chromium
+++ b/third_party/android_deps/libs/javax_annotation_jsr250_api/README.chromium
@@ -3,8 +3,8 @@
 URL: http://jcp.org/aboutJava/communityprocess/final/jsr250/index.html
 Version: 1.0
 License: CDDLv1.0
-License File: LICENSE
-Security Critical: yes
+License File: NOT_SHIPPED
+Security Critical: no
 
 Description:
 JSR-250 Reference Implementation by Glassfish
diff --git a/third_party/blink/public/mojom/BUILD.gn b/third_party/blink/public/mojom/BUILD.gn
index 9b5692d2..47d71cb 100644
--- a/third_party/blink/public/mojom/BUILD.gn
+++ b/third_party/blink/public/mojom/BUILD.gn
@@ -52,10 +52,8 @@
     "fetch/fetch_api_response.mojom",
     "file/file_utilities.mojom",
     "filesystem/file_system.mojom",
-    "frame/document_interface_broker.mojom",
     "frame/find_in_page.mojom",
     "frame/frame.mojom",
-    "frame/frame_host_test_interface.mojom",
     "frame/fullscreen.mojom",
     "frame/lifecycle.mojom",
     "frame/navigation_initiator.mojom",
diff --git a/third_party/blink/public/mojom/frame/document_interface_broker.mojom b/third_party/blink/public/mojom/frame/document_interface_broker.mojom
deleted file mode 100644
index c036ff4..0000000
--- a/third_party/blink/public/mojom/frame/document_interface_broker.mojom
+++ /dev/null
@@ -1,18 +0,0 @@
-// Copyright 2018 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-module blink.mojom;
-
-import "third_party/blink/public/mojom/frame/frame_host_test_interface.mojom";
-
-// An interface through which the renderer may request document-scoped
-// interfaces from the browser.
-interface DocumentInterfaceBroker {
-  // Binds the blink.mojom.FrameHostTestInterface pending receiver to its
-  // remote implementation in the browser process, to allow using this test
-  // interface to exercise requesting document-scoped interfaces from the
-  // RenderFrameHost through the DocumentInterfaceBroker interface.
-  GetFrameHostTestInterface(
-      pending_receiver<blink.mojom.FrameHostTestInterface> receiver);
-};
diff --git a/third_party/blink/public/mojom/frame/frame.mojom b/third_party/blink/public/mojom/frame/frame.mojom
index 016269b..da5d918d 100644
--- a/third_party/blink/public/mojom/frame/frame.mojom
+++ b/third_party/blink/public/mojom/frame/frame.mojom
@@ -68,6 +68,12 @@
   // Evicts the page from the back/forward cache due to e.g., JavaScript
   // execution.
   EvictFromBackForwardCache();
+
+  // Notifies the browser that the associated frame has changed its visibility
+  // status. Visibility status changes occur when the frame moves in/out
+  // of the viewport, or the need for a layout object changes, e.g. if the
+  // frame owner assigns a display: none style.
+  VisibilityChanged(blink.mojom.FrameVisibility visibility);
 };
 
 // Implemented in Blink, this interface defines frame-specific methods that will
@@ -117,6 +123,12 @@
   // ancestor of the associated RemoteFrame and should be propogated to
   // the associated LocalFrame in the other render process.
   SetInheritedEffectiveTouchAction(cc.mojom.TouchAction touch_action);
+
+  // Notifies the browser that the associated frame has changed its visibility
+  // status. Visibility status changes occur when the frame moves in/out
+  // of the viewport, or the need for a layout object changes, e.g. if the
+  // frame owner assigns a display: none style.
+  VisibilityChanged(blink.mojom.FrameVisibility visibility);
 };
 
 // Implemented in Blink, this interface defines frame-specific methods that will
diff --git a/third_party/blink/public/mojom/frame/frame_host_test_interface.mojom b/third_party/blink/public/mojom/frame/frame_host_test_interface.mojom
deleted file mode 100644
index fdfcf38..0000000
--- a/third_party/blink/public/mojom/frame/frame_host_test_interface.mojom
+++ /dev/null
@@ -1,26 +0,0 @@
-// Copyright 2018 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-module blink.mojom;
-
-import "url/mojom/url.mojom";
-
-// TODO(crbug.com/718652) This is a copy of
-// content/test/frame_host_test_interface.mojom to be used in parallel while
-// InterfaceProvider->DocumentInterfaceBroker conversion is taking place.
-
-// Test interface used in RenderFrame and RenderFrameHost tests to exercise
-// requesting document-scoped interfaces from the RenderFrameHost through
-// the DocumentInterfaceBroker interface.
-//
-// The `Ping` method is invoked by clients immediately after making the
-// FrameHostTestInterfaceRequest, so as to annotate where the request
-// originates from. This allows verification that the request was delivered /
-// not delivered to a certain DocumentInterfaceBroker implementation.
-interface FrameHostTestInterface {
-  Ping(url.mojom.Url source_url, string source_event);
-  // Used in tests to distinguish between the different implementations
-  // and verify that interface requests are routed to the proper override.
-  GetName() => (string name);
-};
diff --git a/third_party/blink/public/web/web_local_frame.h b/third_party/blink/public/web/web_local_frame.h
index 1357eb3..ebf2ca9 100644
--- a/third_party/blink/public/web/web_local_frame.h
+++ b/third_party/blink/public/web/web_local_frame.h
@@ -84,7 +84,6 @@
       WebView*,
       WebLocalFrameClient*,
       blink::InterfaceRegistry*,
-      mojo::ScopedMessagePipeHandle,
       WebFrame* opener = nullptr,
       const WebString& name = WebString(),
       WebSandboxFlags = WebSandboxFlags::kNone,
@@ -112,7 +111,6 @@
   BLINK_EXPORT static WebLocalFrame* CreateProvisional(
       WebLocalFrameClient*,
       blink::InterfaceRegistry*,
-      mojo::ScopedMessagePipeHandle,
       WebFrame* previous_web_frame,
       const FramePolicy&);
 
@@ -121,8 +119,7 @@
   // it's no longer needed.
   virtual WebLocalFrame* CreateLocalChild(WebTreeScopeType,
                                           WebLocalFrameClient*,
-                                          blink::InterfaceRegistry*,
-                                          mojo::ScopedMessagePipeHandle) = 0;
+                                          blink::InterfaceRegistry*) = 0;
 
   // Returns the WebFrame associated with the current V8 context. This
   // function can return 0 if the context is associated with a Document that
diff --git a/third_party/blink/public/web/web_local_frame_client.h b/third_party/blink/public/web/web_local_frame_client.h
index f4566aa..6794708d 100644
--- a/third_party/blink/public/web/web_local_frame_client.h
+++ b/third_party/blink/public/web/web_local_frame_client.h
@@ -399,14 +399,6 @@
   // The provisional datasource is now committed.  The first part of the
   // response body has been received, and the encoding of the response
   // body is known.
-  // The mojo::ScopedMessagePipeHandle is a DocumentInterfaceBroker handle. When
-  // a load commits and a new Document is created, Blink creates a new
-  // DocumentInterfaceBroker endpoint to ensure that interface requests in the
-  // newly committed Document are associated with the correct origin (even if
-  // the origin of the old and the new Document are the same). The one
-  // exception is if the Window object is reused; in that case, the old
-  // DocumentInterfaceBroker handle will be reused, and the endpoint won't be
-  // bound to any requests.
   // When a load commits and a new Document is created, WebLocalFrameClient
   // creates a new BrowserInterfaceBroker endpoint to ensure that interface
   // receivers in the newly committed Document are associated with the correct
@@ -417,7 +409,6 @@
   virtual void DidCommitProvisionalLoad(
       const WebHistoryItem&,
       WebHistoryCommitType,
-      mojo::ScopedMessagePipeHandle,
       bool should_reset_browser_interface_broker) {}
 
   // The frame's document has just been initialized.
@@ -627,8 +618,6 @@
   virtual void DidChangeActiveSchedulerTrackedFeatures(uint64_t features_mask) {
   }
 
-  virtual void VisibilityChanged(blink::mojom::FrameVisibility visibility) {}
-
   // UseCounter ----------------------------------------------------------
   // Blink exhibited a certain loading behavior that the browser process will
   // use for segregated histograms.
diff --git a/third_party/blink/public/web/web_remote_frame.h b/third_party/blink/public/web/web_remote_frame.h
index d924afe8..f7da7a8 100644
--- a/third_party/blink/public/web/web_remote_frame.h
+++ b/third_party/blink/public/web/web_remote_frame.h
@@ -73,7 +73,6 @@
                                           const FramePolicy&,
                                           WebLocalFrameClient*,
                                           blink::InterfaceRegistry*,
-                                          mojo::ScopedMessagePipeHandle,
                                           WebFrame* previous_sibling,
                                           const WebFrameOwnerProperties&,
                                           FrameOwnerElementType,
diff --git a/third_party/blink/public/web/web_remote_frame_client.h b/third_party/blink/public/web/web_remote_frame_client.h
index 6455f45..a933b2a 100644
--- a/third_party/blink/public/web/web_remote_frame_client.h
+++ b/third_party/blink/public/web/web_remote_frame_client.h
@@ -53,8 +53,6 @@
   virtual void UpdateRemoteViewportIntersection(
       const ViewportIntersectionState& intersection_state) {}
 
-  virtual void VisibilityChanged(blink::mojom::FrameVisibility visibility) {}
-
   // Set or clear the inert property on the remote frame.
   virtual void SetIsInert(bool) {}
 
diff --git a/third_party/blink/renderer/README.md b/third_party/blink/renderer/README.md
index 06a71a0..b6725da2 100644
--- a/third_party/blink/renderer/README.md
+++ b/third_party/blink/renderer/README.md
@@ -99,13 +99,6 @@
 In terms of dependencies, `controller/` can depend on `core/`, `platform/` and `modules/`,
 but not vice versa.
 
-### `devtools/`
-
-The `devtools/` directory contains a frontend of the Chrome DevTools,
-including the build scripts and DevTools webapp.
-
-In terms of dependencies, `devtools/` is a stand-alone directory.
-
 ### `build/`
 
 The `build/` directory contains scripts to build Blink.
@@ -127,7 +120,7 @@
 
 See [this diagram](https://docs.google.com/document/d/1yYei-V76q3Mb-5LeJfNUMitmj6cqfA5gZGcWXoPaPYQ/edit).
 
-`devtools/` and `build/` are stand-alone directories.
+`build/` is a stand-alone directory.
 
 ### Type dependencies
 
diff --git a/third_party/blink/renderer/core/BUILD.gn b/third_party/blink/renderer/core/BUILD.gn
index 19572ce..b5d9b79f 100644
--- a/third_party/blink/renderer/core/BUILD.gn
+++ b/third_party/blink/renderer/core/BUILD.gn
@@ -273,14 +273,14 @@
     "testing/death_aware_script_wrappable.h",
     "testing/dictionary_test.cc",
     "testing/dictionary_test.h",
-    "testing/document_interface_broker_test_helpers.cc",
-    "testing/document_interface_broker_test_helpers.h",
     "testing/dummy_modulator.cc",
     "testing/dummy_modulator.h",
     "testing/dummy_page_holder.cc",
     "testing/dummy_page_holder.h",
     "testing/fake_local_frame_host.cc",
     "testing/fake_local_frame_host.h",
+    "testing/fake_remote_frame_host.cc",
+    "testing/fake_remote_frame_host.h",
     "testing/garbage_collected_script_wrappable.cc",
     "testing/garbage_collected_script_wrappable.h",
     "testing/gc_object_liveness_observer.h",
@@ -307,8 +307,6 @@
     "testing/sequence_test.h",
     "testing/static_selection.cc",
     "testing/static_selection.h",
-    "testing/test_document_interface_broker.cc",
-    "testing/test_document_interface_broker.h",
     "testing/type_conversions.h",
     "testing/union_types_test.cc",
     "testing/union_types_test.h",
diff --git a/third_party/blink/renderer/core/dom/document.cc b/third_party/blink/renderer/core/dom/document.cc
index 76128b7..99aa1671 100644
--- a/third_party/blink/renderer/core/dom/document.cc
+++ b/third_party/blink/renderer/core/dom/document.cc
@@ -51,7 +51,6 @@
 #include "third_party/blink/public/common/features.h"
 #include "third_party/blink/public/common/thread_safe_browser_interface_broker_proxy.h"
 #include "third_party/blink/public/mojom/feature_policy/feature_policy.mojom-blink.h"
-#include "third_party/blink/public/mojom/frame/document_interface_broker.mojom-blink.h"
 #include "third_party/blink/public/mojom/insecure_input/insecure_input_service.mojom-blink.h"
 #include "third_party/blink/public/mojom/ukm/ukm.mojom-blink.h"
 #include "third_party/blink/public/platform/interface_provider.h"
@@ -2048,7 +2047,8 @@
 Element* Document::ScrollingElementNoLayout() {
   if (RuntimeEnabledFeatures::ScrollTopLeftInteropEnabled()) {
     if (InQuirksMode()) {
-      DCHECK(lifecycle_.GetState() >= DocumentLifecycle::kStyleClean);
+      DCHECK(!IsActive() ||
+             lifecycle_.GetState() >= DocumentLifecycle::kStyleClean);
       HTMLBodyElement* body = FirstBodyElement();
       if (body && body->GetLayoutObject() &&
           body->GetLayoutObject()->HasOverflowClip())
@@ -8039,13 +8039,6 @@
   return is_secure;
 }
 
-mojo::ScopedMessagePipeHandle Document::SetDocumentInterfaceBrokerForTesting(
-    mojo::ScopedMessagePipeHandle blink_handle) {
-  DCHECK(GetFrame());
-  return GetFrame()->SetDocumentInterfaceBrokerForTesting(
-      std::move(blink_handle));
-}
-
 void Document::DidEnforceInsecureRequestPolicy() {
   if (!GetFrame())
     return;
@@ -8142,13 +8135,6 @@
   return &GetFrame()->GetInterfaceProvider();
 }
 
-mojom::blink::DocumentInterfaceBroker* Document::GetDocumentInterfaceBroker() {
-  if (!GetFrame())
-    return nullptr;
-
-  return &GetFrame()->GetDocumentInterfaceBroker();
-}
-
 BrowserInterfaceBrokerProxy& Document::GetBrowserInterfaceBroker() {
   if (!GetFrame())
     return GetEmptyBrowserInterfaceBroker();
@@ -8167,13 +8153,6 @@
   return resource_coordinator_.get();
 }
 
-void Document::BindDocumentInterfaceBroker(
-    mojo::ScopedMessagePipeHandle js_handle) {
-  if (!GetFrame())
-    return;
-  GetFrame()->BindDocumentInterfaceBroker(std::move(js_handle));
-}
-
 FrameOrWorkerScheduler* Document::GetScheduler() {
   DCHECK(IsMainThread());
 
@@ -8232,10 +8211,7 @@
 
 StylePropertyMapReadOnly* Document::RemoveComputedStyleMapItem(
     Element* element) {
-  StylePropertyMapReadOnly* computed_style =
-      element_computed_style_map_.at(element);
-  element_computed_style_map_.erase(element);
-  return computed_style;
+  return element_computed_style_map_.Take(element);
 }
 
 void Document::Trace(Visitor* visitor) {
diff --git a/third_party/blink/renderer/core/dom/document.h b/third_party/blink/renderer/core/dom/document.h
index d84d6f6..8261c70a 100644
--- a/third_party/blink/renderer/core/dom/document.h
+++ b/third_party/blink/renderer/core/dom/document.h
@@ -1359,11 +1359,6 @@
     secure_context_state_ = state;
   }
 
-  void BindDocumentInterfaceBroker(mojo::ScopedMessagePipeHandle js_handle);
-
-  mojo::ScopedMessagePipeHandle SetDocumentInterfaceBrokerForTesting(
-      mojo::ScopedMessagePipeHandle blink_handle);
-
   CanvasFontCache* GetCanvasFontCache();
 
   // Used by unit tests so that all parsing will be main thread for
@@ -1430,7 +1425,6 @@
 
   CoreProbeSink* GetProbeSink() final;
   service_manager::InterfaceProvider* GetInterfaceProvider() final;
-  mojom::blink::DocumentInterfaceBroker* GetDocumentInterfaceBroker() final;
 
   BrowserInterfaceBrokerProxy& GetBrowserInterfaceBroker() final;
 
diff --git a/third_party/blink/renderer/core/dom/node.cc b/third_party/blink/renderer/core/dom/node.cc
index 48fa8a9..6e1d0cb 100644
--- a/third_party/blink/renderer/core/dom/node.cc
+++ b/third_party/blink/renderer/core/dom/node.cc
@@ -845,7 +845,7 @@
     Document& document,
     ExceptionState& exception_state) {
   bool needs_check =
-      IsHTMLScriptElement(parent) && document.IsTrustedTypesEnabledForDoc();
+      IsA<HTMLScriptElement>(parent) && document.IsTrustedTypesEnabledForDoc();
 
   if (nodes.size() == 1)
     return NodeOrStringToNode(nodes[0], document, needs_check, exception_state);
@@ -3255,7 +3255,7 @@
     Node* child,
     ExceptionState& exception_state) const {
   DCHECK(child);
-  bool needs_check = IsHTMLScriptElement(this) && child->IsTextNode() &&
+  bool needs_check = IsA<HTMLScriptElement>(this) && child->IsTextNode() &&
                      GetDocument().IsTrustedTypesEnabledForDoc();
   if (!needs_check)
     return child;
diff --git a/third_party/blink/renderer/core/editing/commands/delete_selection_command.cc b/third_party/blink/renderer/core/editing/commands/delete_selection_command.cc
index 75aae10..6ff6906 100644
--- a/third_party/blink/renderer/core/editing/commands/delete_selection_command.cc
+++ b/third_party/blink/renderer/core/editing/commands/delete_selection_command.cc
@@ -58,7 +58,7 @@
 }
 
 static bool IsTableRowEmpty(Node* row) {
-  if (!IsHTMLTableRowElement(row))
+  if (!IsA<HTMLTableRowElement>(row))
     return false;
 
   row->GetDocument().UpdateStyleAndLayout();
@@ -252,10 +252,10 @@
   start_root_ = RootEditableElementOf(start);
   end_root_ = RootEditableElementOf(end);
 
-  start_table_row_ =
-      ToHTMLTableRowElement(EnclosingNodeOfType(start, &IsHTMLTableRowElement));
-  end_table_row_ =
-      ToHTMLTableRowElement(EnclosingNodeOfType(end, &IsHTMLTableRowElement));
+  start_table_row_ = To<HTMLTableRowElement>(
+      EnclosingNodeOfType(start, &IsA<HTMLTableRowElement>));
+  end_table_row_ = To<HTMLTableRowElement>(
+      EnclosingNodeOfType(end, &IsA<HTMLTableRowElement>));
 
   // Don't move content out of a table cell.
   // If the cell is non-editable, enclosingNodeOfType won't return it by
diff --git a/third_party/blink/renderer/core/editing/finder/find_buffer.cc b/third_party/blink/renderer/core/editing/finder/find_buffer.cc
index 913bc92..6354101 100644
--- a/third_party/blink/renderer/core/editing/finder/find_buffer.cc
+++ b/third_party/blink/renderer/core/editing/finder/find_buffer.cc
@@ -107,7 +107,7 @@
          IsHTMLObjectElement(*element) || IsA<HTMLProgressElement>(*element) ||
          (IsA<HTMLSelectElement>(*element) &&
           To<HTMLSelectElement>(*element).UsesMenuList()) ||
-         IsA<HTMLStyleElement>(*element) || IsHTMLScriptElement(*element) ||
+         IsA<HTMLStyleElement>(*element) || IsA<HTMLScriptElement>(*element) ||
          IsHTMLVideoElement(*element) || IsA<HTMLAudioElement>(*element) ||
          (element->GetDisplayLockContext() &&
           !element->GetDisplayLockContext()->IsActivatable(
diff --git a/third_party/blink/renderer/core/editing/serializers/serialization.cc b/third_party/blink/renderer/core/editing/serializers/serialization.cc
index 749f32b..bee97c5 100644
--- a/third_party/blink/renderer/core/editing/serializers/serialization.cc
+++ b/third_party/blink/renderer/core/editing/serializers/serialization.cc
@@ -130,7 +130,7 @@
     return nullptr;
 
   if (common_ancestor_block->HasTagName(html_names::kTbodyTag) ||
-      IsHTMLTableRowElement(*common_ancestor_block))
+      IsA<HTMLTableRowElement>(*common_ancestor_block))
     return Traversal<HTMLTableElement>::FirstAncestor(*common_ancestor_block);
 
   if (IsNonTableCellHTMLBlockElement(common_ancestor_block))
diff --git a/third_party/blink/renderer/core/execution_context/execution_context.h b/third_party/blink/renderer/core/execution_context/execution_context.h
index 727817f..7ead7f12 100644
--- a/third_party/blink/renderer/core/execution_context/execution_context.h
+++ b/third_party/blink/renderer/core/execution_context/execution_context.h
@@ -36,7 +36,6 @@
 #include "base/optional.h"
 #include "base/unguessable_token.h"
 #include "services/network/public/mojom/referrer_policy.mojom-blink-forward.h"
-#include "third_party/blink/public/mojom/frame/document_interface_broker.mojom-blink-forward.h"
 #include "third_party/blink/public/mojom/frame/lifecycle.mojom-blink-forward.h"
 #include "third_party/blink/renderer/bindings/core/v8/sanitize_script_errors.h"
 #include "third_party/blink/renderer/core/core_export.h"
@@ -276,10 +275,6 @@
     return nullptr;
   }
 
-  virtual mojom::blink::DocumentInterfaceBroker* GetDocumentInterfaceBroker() {
-    return nullptr;
-  }
-
   virtual BrowserInterfaceBrokerProxy& GetBrowserInterfaceBroker() = 0;
 
   virtual FrameOrWorkerScheduler* GetScheduler() = 0;
diff --git a/third_party/blink/renderer/core/exported/local_frame_client_impl.cc b/third_party/blink/renderer/core/exported/local_frame_client_impl.cc
index 3f3401ed..41ef375 100644
--- a/third_party/blink/renderer/core/exported/local_frame_client_impl.cc
+++ b/third_party/blink/renderer/core/exported/local_frame_client_impl.cc
@@ -161,16 +161,8 @@
 
 }  // namespace
 
-LocalFrameClientImpl::LocalFrameClientImpl(
-    WebLocalFrameImpl* frame,
-    mojo::ScopedMessagePipeHandle document_interface_broker_handle)
-    : web_frame_(frame) {
-  DCHECK(document_interface_broker_handle.is_valid());
-  document_interface_broker_.Bind(
-      mojo::PendingRemote<mojom::blink::DocumentInterfaceBroker>(
-          std::move(document_interface_broker_handle),
-          mojom::blink::DocumentInterfaceBroker::Version_));
-}
+LocalFrameClientImpl::LocalFrameClientImpl(WebLocalFrameImpl* frame)
+    : web_frame_(frame) {}
 
 LocalFrameClientImpl::~LocalFrameClientImpl() = default;
 
@@ -443,17 +435,8 @@
   }
 
   if (web_frame_->Client()) {
-    mojo::PendingReceiver<mojom::blink::DocumentInterfaceBroker>
-        document_interface_broker_receiver;
-    if (global_object_reuse_policy != GlobalObjectReusePolicy::kUseExisting) {
-      document_interface_broker_.reset();
-      document_interface_broker_receiver =
-          document_interface_broker_.BindNewPipeAndPassReceiver();
-    }
-
     web_frame_->Client()->DidCommitProvisionalLoad(
         WebHistoryItem(item), commit_type,
-        document_interface_broker_receiver.PassPipe(),
         global_object_reuse_policy == GlobalObjectReusePolicy::kCreateNew);
     if (web_frame_->GetFrame()->IsLocalRoot()) {
       // This update should be sent as soon as loading the new document begins
@@ -766,12 +749,6 @@
   }
 }
 
-void LocalFrameClientImpl::VisibilityChanged(
-    blink::mojom::FrameVisibility visibility) {
-  if (WebLocalFrameClient* client = web_frame_->Client())
-    client->VisibilityChanged(visibility);
-}
-
 DocumentLoader* LocalFrameClientImpl::CreateDocumentLoader(
     LocalFrame* frame,
     WebNavigationType navigation_type,
@@ -1061,40 +1038,11 @@
   return web_frame_->Client()->GetInterfaceProvider();
 }
 
-mojom::blink::DocumentInterfaceBroker*
-LocalFrameClientImpl::GetDocumentInterfaceBroker() {
-  DCHECK(document_interface_broker_.is_bound());
-  return document_interface_broker_.get();
-}
-
 blink::BrowserInterfaceBrokerProxy&
 LocalFrameClientImpl::GetBrowserInterfaceBroker() {
   return *web_frame_->Client()->GetBrowserInterfaceBroker();
 }
 
-void LocalFrameClientImpl::BindDocumentInterfaceBroker(
-    mojo::ScopedMessagePipeHandle js_handle) {
-  document_interface_broker_receivers_.Add(
-      this, mojo::PendingReceiver<mojom::blink::DocumentInterfaceBroker>(
-                std::move(js_handle)));
-}
-
-mojo::ScopedMessagePipeHandle
-LocalFrameClientImpl::SetDocumentInterfaceBrokerForTesting(
-    mojo::ScopedMessagePipeHandle blink_handle) {
-  // Ensure all pending calls get dispatched before the implementation swap
-  document_interface_broker_receivers_.FlushForTesting();
-
-  mojo::PendingRemote<mojom::blink::DocumentInterfaceBroker> test_broker(
-      std::move(blink_handle), mojom::blink::DocumentInterfaceBroker::Version_);
-
-  mojo::ScopedMessagePipeHandle real_handle =
-      document_interface_broker_.Unbind().PassPipe();
-  document_interface_broker_.Bind(std::move(test_broker));
-
-  return real_handle;
-}
-
 AssociatedInterfaceProvider*
 LocalFrameClientImpl::GetRemoteNavigationAssociatedInterfaces() {
   return web_frame_->Client()->GetRemoteNavigationAssociatedInterfaces();
diff --git a/third_party/blink/renderer/core/exported/local_frame_client_impl.h b/third_party/blink/renderer/core/exported/local_frame_client_impl.h
index 38f5e390..8ba32f2 100644
--- a/third_party/blink/renderer/core/exported/local_frame_client_impl.h
+++ b/third_party/blink/renderer/core/exported/local_frame_client_impl.h
@@ -41,7 +41,6 @@
 #include "mojo/public/cpp/bindings/receiver.h"
 #include "mojo/public/cpp/bindings/receiver_set.h"
 #include "mojo/public/cpp/bindings/remote.h"
-#include "third_party/blink/public/mojom/frame/document_interface_broker.mojom-blink.h"
 #include "third_party/blink/public/platform/web_insecure_request_policy.h"
 #include "third_party/blink/renderer/core/frame/local_frame_client.h"
 #include "third_party/blink/renderer/core/frame/web_local_frame_impl.h"
@@ -59,7 +58,7 @@
 
 class LocalFrameClientImpl final : public LocalFrameClient {
  public:
-  LocalFrameClientImpl(WebLocalFrameImpl*, mojo::ScopedMessagePipeHandle);
+  explicit LocalFrameClientImpl(WebLocalFrameImpl*);
   ~LocalFrameClientImpl() override;
 
   void Trace(blink::Visitor*) override;
@@ -159,7 +158,6 @@
   bool ShouldTrackUseCounter(const KURL&) override;
   void SelectorMatchChanged(const Vector<String>& added_selectors,
                             const Vector<String>& removed_selectors) override;
-  void VisibilityChanged(blink::mojom::FrameVisibility visibility) override;
 
   // Creates a WebDocumentLoaderImpl that is a DocumentLoader but also has:
   // - storage to store an extra data that can be used by the content layer
@@ -248,21 +246,6 @@
 
   service_manager::InterfaceProvider* GetInterfaceProvider() override;
 
-  // Binds |js_handle| to the current implementation bound to
-  // |document_interface_broker_| to share the same broker between C++ and
-  // JavaScript clients.
-  void BindDocumentInterfaceBroker(
-      mojo::ScopedMessagePipeHandle js_handle) override;
-
-  mojom::blink::DocumentInterfaceBroker* GetDocumentInterfaceBroker() override;
-
-  // Binds |document_interface_broker_| to |blink_handle|. Used in tests to set
-  // a custom override for DocumentInterfaceBroker methods. Returns the handle
-  // to the previously bound 'production' implementation, which will be used to
-  // forward the calls to methods that have not been overridden.
-  mojo::ScopedMessagePipeHandle SetDocumentInterfaceBrokerForTesting(
-      mojo::ScopedMessagePipeHandle blink_handle) override;
-
   blink::BrowserInterfaceBrokerProxy& GetBrowserInterfaceBroker() override;
 
   AssociatedInterfaceProvider* GetRemoteNavigationAssociatedInterfaces()
@@ -318,18 +301,6 @@
       std::unique_ptr<blink::URLLoaderFactoryBundleInfo> info) override;
 
  private:
-  struct DocumentInterfaceBrokerForwarderTraits {
-    using Interface = mojom::blink::DocumentInterfaceBroker;
-    using PointerType = WeakPersistent<LocalFrameClientImpl>;
-    static bool IsNull(PointerType ptr) {
-      return !ptr || !ptr->document_interface_broker_;
-    }
-    static Interface* GetRawPointer(PointerType* ptr) {
-      return (*ptr)->GetDocumentInterfaceBroker();
-    }
-  };
-  friend struct DocumentInterfaceBrokerForwarderTraits;
-
   bool IsLocalFrameClientImpl() const override { return true; }
   WebDevToolsAgentImpl* DevToolsAgent();
 
@@ -339,19 +310,6 @@
 
   String user_agent_;
   blink::UserAgentMetadata user_agent_metadata_;
-
-  mojo::Remote<mojom::blink::DocumentInterfaceBroker>
-      document_interface_broker_;
-
-  // |document_interface_broker_receivers_| basically just forwards the broker
-  // methods to GetDocumentInterfaceBroker()
-  // via DocumentInterfaceBrokerForwarderTraits.
-  // Used to connect JavaScript clients of DocumentInterfaceBroker with the same
-  // implementation that |document_interface_broker_| is bound to.
-  mojo::ReceiverSetBase<mojo::Receiver<mojom::blink::DocumentInterfaceBroker,
-                                       DocumentInterfaceBrokerForwarderTraits>,
-                        void>
-      document_interface_broker_receivers_;
 };
 
 DEFINE_TYPE_CASTS(LocalFrameClientImpl,
diff --git a/third_party/blink/renderer/core/exported/local_frame_client_impl_test.cc b/third_party/blink/renderer/core/exported/local_frame_client_impl_test.cc
index 6f9d0fe..72d5ced 100644
--- a/third_party/blink/renderer/core/exported/local_frame_client_impl_test.cc
+++ b/third_party/blink/renderer/core/exported/local_frame_client_impl_test.cc
@@ -39,7 +39,6 @@
 #include "third_party/blink/public/web/web_view.h"
 #include "third_party/blink/renderer/core/frame/frame_test_helpers.h"
 #include "third_party/blink/renderer/core/frame/web_local_frame_impl.h"
-#include "third_party/blink/renderer/core/testing/document_interface_broker_test_helpers.h"
 #include "third_party/blink/renderer/platform/weborigin/kurl.h"
 #include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
 
@@ -111,23 +110,5 @@
   EXPECT_TRUE(default_user_agent.Equals(UserAgent()));
 }
 
-TEST_F(LocalFrameClientImplTest, TestDocumentInterfaceBrokerOverride) {
-  mojo::PendingRemote<mojom::blink::DocumentInterfaceBroker> doc;
-  FrameHostTestDocumentInterfaceBroker frame_interface_broker(
-      &MainFrame()->GetFrame()->GetDocumentInterfaceBroker(),
-      doc.InitWithNewPipeAndPassReceiver());
-  MainFrame()->GetFrame()->SetDocumentInterfaceBrokerForTesting(doc.PassPipe());
-
-  mojo::Remote<mojom::blink::FrameHostTestInterface> frame_test;
-  MainFrame()
-      ->GetFrame()
-      ->GetDocumentInterfaceBroker()
-      .GetFrameHostTestInterface(frame_test.BindNewPipeAndPassReceiver());
-  frame_test->GetName(base::BindOnce([](const WTF::String& result) {
-    EXPECT_EQ(result, kGetNameTestResponse);
-  }));
-  frame_interface_broker.Flush();
-}
-
 }  // namespace
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/exported/web_frame_test.cc b/third_party/blink/renderer/core/exported/web_frame_test.cc
index 0a56e715..76ea7a0 100644
--- a/third_party/blink/renderer/core/exported/web_frame_test.cc
+++ b/third_party/blink/renderer/core/exported/web_frame_test.cc
@@ -143,6 +143,8 @@
 #include "third_party/blink/renderer/core/scroll/scrollbar.h"
 #include "third_party/blink/renderer/core/scroll/scrollbar_test_suite.h"
 #include "third_party/blink/renderer/core/testing/core_unit_test_helper.h"
+#include "third_party/blink/renderer/core/testing/fake_local_frame_host.h"
+#include "third_party/blink/renderer/core/testing/fake_remote_frame_host.h"
 #include "third_party/blink/renderer/core/testing/null_execution_context.h"
 #include "third_party/blink/renderer/core/testing/scoped_fake_plugin_registry.h"
 #include "third_party/blink/renderer/core/testing/sim/sim_request.h"
@@ -4226,7 +4228,6 @@
   // frame_test_helpers::TestWebFrameClient:
   void DidCommitProvisionalLoad(const WebHistoryItem&,
                                 WebHistoryCommitType,
-                                mojo::ScopedMessagePipeHandle,
                                 bool) override {
     Frame()->View()->ResetScrollAndScaleState();
   }
@@ -6352,7 +6353,6 @@
   // frame_test_helpers::TestWebFrameClient:
   void DidCommitProvisionalLoad(const WebHistoryItem&,
                                 WebHistoryCommitType,
-                                mojo::ScopedMessagePipeHandle,
                                 bool) override {
     did_load_ = true;
   }
@@ -9428,7 +9428,6 @@
   // frame_test_helpers::TestWebFrameClient:
   void DidCommitProvisionalLoad(const WebHistoryItem&,
                                 WebHistoryCommitType history_commit_type,
-                                mojo::ScopedMessagePipeHandle,
                                 bool) override {
     history_commit_type_ = history_commit_type;
     remote_frame_->Swap(Frame());
@@ -9656,7 +9655,6 @@
   // frame_test_helpers::TestWebFrameClient:
   void DidCommitProvisionalLoad(const WebHistoryItem&,
                                 WebHistoryCommitType history_commit_type,
-                                mojo::ScopedMessagePipeHandle,
                                 bool) override {
     history_commit_type_ = history_commit_type;
   }
@@ -10563,7 +10561,6 @@
   }
   void DidCommitProvisionalLoad(const WebHistoryItem&,
                                 WebHistoryCommitType,
-                                mojo::ScopedMessagePipeHandle,
                                 bool) override {
     EXPECT_EQ(2, callback_count_++);
   }
@@ -10586,13 +10583,12 @@
   web_view_helper.InitializeAndLoad(base_url_ + "foo.html", &client);
 }
 
-class TestWebRemoteFrameClientForVisibility
-    : public frame_test_helpers::TestWebRemoteFrameClient {
+class TestRemoteFrameHostForVisibility : public FakeRemoteFrameHost {
  public:
-  TestWebRemoteFrameClientForVisibility() = default;
-  ~TestWebRemoteFrameClientForVisibility() override = default;
+  TestRemoteFrameHostForVisibility() = default;
+  ~TestRemoteFrameHostForVisibility() override = default;
 
-  // frame_test_helpers::TestWebRemoteFrameClient:
+  // FakeRemoteFrameHost:
   void VisibilityChanged(blink::mojom::FrameVisibility visibility) override {
     visibility_ = visibility;
   }
@@ -10613,6 +10609,8 @@
         web_view_helper_.InitializeAndLoad(base_url_ + "single_iframe.html")
             ->MainFrameImpl();
     web_view_helper_.Resize(WebSize(640, 480));
+    remote_frame_host_.Init(
+        remote_frame_client_.GetAssociatedInterfaceProvider());
     web_remote_frame_ = frame_test_helpers::CreateRemote(&remote_frame_client_);
   }
 
@@ -10634,12 +10632,13 @@
 
   WebLocalFrame* MainFrame() { return frame_; }
   WebRemoteFrameImpl* RemoteFrame() { return web_remote_frame_; }
-  TestWebRemoteFrameClientForVisibility* RemoteFrameClient() {
-    return &remote_frame_client_;
+  TestRemoteFrameHostForVisibility* RemoteFrameHost() {
+    return &remote_frame_host_;
   }
 
  private:
-  TestWebRemoteFrameClientForVisibility remote_frame_client_;
+  TestRemoteFrameHostForVisibility remote_frame_host_;
+  frame_test_helpers::TestWebRemoteFrameClient remote_frame_client_;
   frame_test_helpers::WebViewHelper web_view_helper_;
   WebLocalFrame* frame_;
   Persistent<WebRemoteFrameImpl> web_remote_frame_;
@@ -10650,24 +10649,24 @@
   ExecuteScriptOnMainFrame(WebScriptSource(
       "document.querySelector('iframe').style.display = 'none';"));
   EXPECT_EQ(blink::mojom::FrameVisibility::kNotRendered,
-            RemoteFrameClient()->visibility());
+            RemoteFrameHost()->visibility());
 
   ExecuteScriptOnMainFrame(WebScriptSource(
       "document.querySelector('iframe').style.display = 'block';"));
   EXPECT_EQ(blink::mojom::FrameVisibility::kRenderedInViewport,
-            RemoteFrameClient()->visibility());
+            RemoteFrameHost()->visibility());
 
   ExecuteScriptOnMainFrame(WebScriptSource(
       "var padding = document.createElement('div');"
       "padding.style = 'width: 400px; height: 800px;';"
       "document.body.insertBefore(padding, document.body.firstChild);"));
   EXPECT_EQ(blink::mojom::FrameVisibility::kRenderedOutOfViewport,
-            RemoteFrameClient()->visibility());
+            RemoteFrameHost()->visibility());
 
   ExecuteScriptOnMainFrame(
       WebScriptSource("document.scrollingElement.scrollTop = 800;"));
   EXPECT_EQ(blink::mojom::FrameVisibility::kRenderedInViewport,
-            RemoteFrameClient()->visibility());
+            RemoteFrameHost()->visibility());
 }
 
 TEST_F(WebRemoteFrameVisibilityChangeTest, ParentVisibilityChange) {
@@ -10676,16 +10675,15 @@
       WebScriptSource("document.querySelector('iframe').parentElement.style."
                       "display = 'none';"));
   EXPECT_EQ(blink::mojom::FrameVisibility::kNotRendered,
-            RemoteFrameClient()->visibility());
+            RemoteFrameHost()->visibility());
 }
 
-class TestWebLocalFrameClientForVisibility
-    : public frame_test_helpers::TestWebFrameClient {
+class TestLocalFrameHostForVisibility : public FakeLocalFrameHost {
  public:
-  TestWebLocalFrameClientForVisibility() = default;
-  ~TestWebLocalFrameClientForVisibility() override = default;
+  TestLocalFrameHostForVisibility() = default;
+  ~TestLocalFrameHostForVisibility() override = default;
 
-  // frame_test_helpers::TestWebFrameClient:
+  // FakeLocalFrameHost:
   void VisibilityChanged(blink::mojom::FrameVisibility visibility) override {
     visibility_ = visibility;
   }
@@ -10704,6 +10702,7 @@
   WebLocalFrameVisibilityChangeTest() {
     RegisterMockedHttpURLLoad("visible_iframe.html");
     RegisterMockedHttpURLLoad("single_iframe.html");
+    child_host_.Init(child_client_.GetRemoteNavigationAssociatedInterfaces());
     frame_ = web_view_helper_
                  .InitializeAndLoad(base_url_ + "single_iframe.html", this)
                  ->MainFrameImpl();
@@ -10735,10 +10734,11 @@
     return CreateLocalChild(*parent, scope, &child_client_);
   }
 
-  TestWebLocalFrameClientForVisibility& ChildClient() { return child_client_; }
+  TestLocalFrameHostForVisibility& ChildHost() { return child_host_; }
 
  private:
-  TestWebLocalFrameClientForVisibility child_client_;
+  TestLocalFrameHostForVisibility child_host_;
+  frame_test_helpers::TestWebFrameClient child_client_;
   frame_test_helpers::WebViewHelper web_view_helper_;
   WebLocalFrame* frame_;
 };
@@ -10747,24 +10747,24 @@
   ExecuteScriptOnMainFrame(WebScriptSource(
       "document.querySelector('iframe').style.display = 'none';"));
   EXPECT_EQ(blink::mojom::FrameVisibility::kNotRendered,
-            ChildClient().visibility());
+            ChildHost().visibility());
 
   ExecuteScriptOnMainFrame(WebScriptSource(
       "document.querySelector('iframe').style.display = 'block';"));
   EXPECT_EQ(blink::mojom::FrameVisibility::kRenderedInViewport,
-            ChildClient().visibility());
+            ChildHost().visibility());
 
   ExecuteScriptOnMainFrame(WebScriptSource(
       "var padding = document.createElement('div');"
       "padding.style = 'width: 400px; height: 800px;';"
       "document.body.insertBefore(padding, document.body.firstChild);"));
   EXPECT_EQ(blink::mojom::FrameVisibility::kRenderedOutOfViewport,
-            ChildClient().visibility());
+            ChildHost().visibility());
 
   ExecuteScriptOnMainFrame(
       WebScriptSource("document.scrollingElement.scrollTop = 800;"));
   EXPECT_EQ(blink::mojom::FrameVisibility::kRenderedInViewport,
-            ChildClient().visibility());
+            ChildHost().visibility());
 }
 
 TEST_F(WebLocalFrameVisibilityChangeTest, ParentVisibilityChange) {
@@ -10772,7 +10772,7 @@
       WebScriptSource("document.querySelector('iframe').parentElement.style."
                       "display = 'none';"));
   EXPECT_EQ(blink::mojom::FrameVisibility::kNotRendered,
-            ChildClient().visibility());
+            ChildHost().visibility());
 }
 
 static void EnableGlobalReuseForUnownedMainFrames(WebSettings* settings) {
diff --git a/third_party/blink/renderer/core/exported/web_remote_frame_impl.cc b/third_party/blink/renderer/core/exported/web_remote_frame_impl.cc
index 20baab55..d464250 100644
--- a/third_party/blink/renderer/core/exported/web_remote_frame_impl.cc
+++ b/third_party/blink/renderer/core/exported/web_remote_frame_impl.cc
@@ -191,14 +191,12 @@
     const FramePolicy& frame_policy,
     WebLocalFrameClient* client,
     blink::InterfaceRegistry* interface_registry,
-    mojo::ScopedMessagePipeHandle document_interface_broker_handle,
     WebFrame* previous_sibling,
     const WebFrameOwnerProperties& frame_owner_properties,
     FrameOwnerElementType frame_owner_element_type,
     WebFrame* opener) {
-  auto* child = MakeGarbageCollected<WebLocalFrameImpl>(
-      scope, client, interface_registry,
-      std::move(document_interface_broker_handle));
+  auto* child = MakeGarbageCollected<WebLocalFrameImpl>(scope, client,
+                                                        interface_registry);
   child->SetOpener(opener);
   InsertAfter(child, previous_sibling);
   auto* owner = MakeGarbageCollected<RemoteFrameOwner>(
diff --git a/third_party/blink/renderer/core/exported/web_remote_frame_impl.h b/third_party/blink/renderer/core/exported/web_remote_frame_impl.h
index 3593f32..74f8b63 100644
--- a/third_party/blink/renderer/core/exported/web_remote_frame_impl.h
+++ b/third_party/blink/renderer/core/exported/web_remote_frame_impl.h
@@ -63,7 +63,6 @@
                                   const FramePolicy&,
                                   WebLocalFrameClient*,
                                   blink::InterfaceRegistry*,
-                                  mojo::ScopedMessagePipeHandle,
                                   WebFrame* previous_sibling,
                                   const WebFrameOwnerProperties&,
                                   FrameOwnerElementType,
diff --git a/third_party/blink/renderer/core/exported/web_view_test.cc b/third_party/blink/renderer/core/exported/web_view_test.cc
index 2d236e4..4eca1d7 100644
--- a/third_party/blink/renderer/core/exported/web_view_test.cc
+++ b/third_party/blink/renderer/core/exported/web_view_test.cc
@@ -49,7 +49,6 @@
 #include "third_party/blink/public/common/browser_interface_broker_proxy.h"
 #include "third_party/blink/public/common/frame/frame_owner_element_type.h"
 #include "third_party/blink/public/common/page/page_zoom.h"
-#include "third_party/blink/public/mojom/frame/document_interface_broker.mojom-blink.h"
 #include "third_party/blink/public/mojom/manifest/display_mode.mojom-shared.h"
 #include "third_party/blink/public/platform/web_coalesced_input_event.h"
 #include "third_party/blink/public/platform/web_cursor_info.h"
@@ -507,11 +506,7 @@
 
   frame_test_helpers::TestWebFrameClient web_frame_client;
   WebLocalFrame* frame = WebLocalFrame::CreateMainFrame(
-      web_view, &web_frame_client, nullptr,
-      mojo::PendingRemote<mojom::blink::DocumentInterfaceBroker>()
-          .InitWithNewPipeAndPassReceiver()
-          .PassPipe(),
-      nullptr);
+      web_view, &web_frame_client, nullptr, nullptr);
   web_frame_client.Bind(frame);
 
   {
@@ -2647,11 +2642,7 @@
   frame_test_helpers::TestWebFrameClient web_frame_client;
   frame_test_helpers::TestWebWidgetClient web_widget_client;
   WebLocalFrame* local_frame = WebLocalFrame::CreateMainFrame(
-      web_view, &web_frame_client, nullptr,
-      mojo::PendingRemote<mojom::blink::DocumentInterfaceBroker>()
-          .InitWithNewPipeAndPassReceiver()
-          .PassPipe(),
-      nullptr);
+      web_view, &web_frame_client, nullptr, nullptr);
   web_frame_client.Bind(local_frame);
   blink::WebFrameWidget::CreateForMainFrame(&web_widget_client, local_frame);
 
diff --git a/third_party/blink/renderer/core/frame/csp/csp_directive_list.cc b/third_party/blink/renderer/core/frame/csp/csp_directive_list.cc
index 9bb9570..37c38f97 100644
--- a/third_party/blink/renderer/core/frame/csp/csp_directive_list.cc
+++ b/third_party/blink/renderer/core/frame/csp/csp_directive_list.cc
@@ -686,9 +686,10 @@
   if (IsMatchingNoncePresent(directive, nonce))
     return true;
 
-  if (inline_type == ContentSecurityPolicy::InlineType::kScript && element &&
-      IsHTMLScriptElement(element) &&
-      !ToHTMLScriptElement(element)->Loader()->IsParserInserted() &&
+  auto* html_script_element = DynamicTo<HTMLScriptElement>(element);
+  if (html_script_element &&
+      inline_type == ContentSecurityPolicy::InlineType::kScript &&
+      !html_script_element->Loader()->IsParserInserted() &&
       AllowDynamic(type)) {
     return true;
   }
diff --git a/third_party/blink/renderer/core/frame/frame_client.h b/third_party/blink/renderer/core/frame/frame_client.h
index 9cb7d27..6ac152a3 100644
--- a/third_party/blink/renderer/core/frame/frame_client.h
+++ b/third_party/blink/renderer/core/frame/frame_client.h
@@ -35,8 +35,6 @@
 
   virtual void FrameFocused() const = 0;
 
-  virtual void VisibilityChanged(blink::mojom::FrameVisibility visibility) = 0;
-
   virtual base::UnguessableToken GetDevToolsFrameToken() const = 0;
 
   // Transfers user activation state from |source_frame| to the this frame.
diff --git a/third_party/blink/renderer/core/frame/frame_serializer.cc b/third_party/blink/renderer/core/frame/frame_serializer.cc
index b6bc15f09..43dc10b76 100644
--- a/third_party/blink/renderer/core/frame/frame_serializer.cc
+++ b/third_party/blink/renderer/core/frame/frame_serializer.cc
@@ -147,7 +147,7 @@
 
 bool SerializerMarkupAccumulator::ShouldIgnoreElement(
     const Element& element) const {
-  if (IsHTMLScriptElement(element))
+  if (IsA<HTMLScriptElement>(element))
     return true;
   if (IsA<HTMLNoScriptElement>(element))
     return true;
diff --git a/third_party/blink/renderer/core/frame/frame_test.cc b/third_party/blink/renderer/core/frame/frame_test.cc
index aa5e866..8fbde69 100644
--- a/third_party/blink/renderer/core/frame/frame_test.cc
+++ b/third_party/blink/renderer/core/frame/frame_test.cc
@@ -9,7 +9,6 @@
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/blink/renderer/core/dom/user_gesture_indicator.h"
 #include "third_party/blink/renderer/core/loader/document_loader.h"
-#include "third_party/blink/renderer/core/testing/document_interface_broker_test_helpers.h"
 #include "third_party/blink/renderer/core/testing/page_test_base.h"
 #include "third_party/blink/renderer/platform/testing/runtime_enabled_features_test_helpers.h"
 #include "third_party/blink/renderer/platform/testing/unit_test_helpers.h"
@@ -185,23 +184,4 @@
       LocalFrame::ConsumeTransientUserActivation(GetDocument().GetFrame()));
 }
 
-TEST_F(FrameTest, TestDocumentInterfaceBrokerOverride) {
-  mojo::PendingRemote<mojom::blink::DocumentInterfaceBroker> doc;
-  FrameHostTestDocumentInterfaceBroker frame_interface_broker(
-      &GetDocument().GetFrame()->GetDocumentInterfaceBroker(),
-      doc.InitWithNewPipeAndPassReceiver());
-  GetDocument().GetFrame()->SetDocumentInterfaceBrokerForTesting(
-      doc.PassPipe());
-
-  mojo::Remote<mojom::blink::FrameHostTestInterface> frame_test;
-  GetDocument()
-      .GetFrame()
-      ->GetDocumentInterfaceBroker()
-      .GetFrameHostTestInterface(frame_test.BindNewPipeAndPassReceiver());
-  frame_test->GetName(base::BindOnce([](const WTF::String& result) {
-    EXPECT_EQ(result, kGetNameTestResponse);
-  }));
-  frame_interface_broker.Flush();
-}
-
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/frame/frame_test_helpers.cc b/third_party/blink/renderer/core/frame/frame_test_helpers.cc
index 999e5c6..d02f72b 100644
--- a/third_party/blink/renderer/core/frame/frame_test_helpers.cc
+++ b/third_party/blink/renderer/core/frame/frame_test_helpers.cc
@@ -212,11 +212,8 @@
                                     TestWebFrameClient* client) {
   std::unique_ptr<TestWebFrameClient> owned_client;
   client = CreateDefaultClientIfNeeded(client, owned_client);
-  auto* frame = To<WebLocalFrameImpl>(parent.CreateLocalChild(
-      scope, client, nullptr,
-      mojo::PendingRemote<mojom::blink::DocumentInterfaceBroker>()
-          .InitWithNewPipeAndPassReceiver()
-          .PassPipe()));
+  auto* frame =
+      To<WebLocalFrameImpl>(parent.CreateLocalChild(scope, client, nullptr));
   client->Bind(frame, std::move(owned_client));
   return frame;
 }
@@ -227,11 +224,8 @@
     std::unique_ptr<TestWebFrameClient> self_owned) {
   DCHECK(self_owned);
   TestWebFrameClient* client = self_owned.get();
-  auto* frame = To<WebLocalFrameImpl>(parent.CreateLocalChild(
-      scope, client, nullptr,
-      mojo::PendingRemote<mojom::blink::DocumentInterfaceBroker>()
-          .InitWithNewPipeAndPassReceiver()
-          .PassPipe()));
+  auto* frame =
+      To<WebLocalFrameImpl>(parent.CreateLocalChild(scope, client, nullptr));
   client->Bind(frame, std::move(self_owned));
   return frame;
 }
@@ -241,11 +235,7 @@
   std::unique_ptr<TestWebFrameClient> owned_client;
   client = CreateDefaultClientIfNeeded(client, owned_client);
   auto* frame = To<WebLocalFrameImpl>(WebLocalFrame::CreateProvisional(
-      client, nullptr,
-      mojo::PendingRemote<mojom::blink::DocumentInterfaceBroker>()
-          .InitWithNewPipeAndPassReceiver()
-          .PassPipe(),
-      &old_frame, FramePolicy()));
+      client, nullptr, &old_frame, FramePolicy()));
   client->Bind(frame, std::move(owned_client));
   std::unique_ptr<TestWebWidgetClient> widget_client;
   // Create a local root, if necessary.
@@ -274,7 +264,7 @@
   auto* frame = MakeGarbageCollected<WebRemoteFrameImpl>(
       WebTreeScopeType::kDocument, client,
       InterfaceRegistry::GetEmptyInterfaceRegistry(),
-      AssociatedInterfaceProvider::GetEmptyAssociatedInterfaceProvider());
+      client->GetAssociatedInterfaceProvider());
   client->Bind(frame, std::move(owned_client));
   return frame;
 }
@@ -289,9 +279,6 @@
   client = CreateDefaultClientIfNeeded(client, owned_client);
   auto* frame = To<WebLocalFrameImpl>(parent.CreateLocalChild(
       WebTreeScopeType::kDocument, name, FramePolicy(), client, nullptr,
-      mojo::PendingRemote<mojom::blink::DocumentInterfaceBroker>()
-          .InitWithNewPipeAndPassReceiver()
-          .PassPipe(),
       previous_sibling, properties, FrameOwnerElementType::kIframe, nullptr));
   client->Bind(frame, std::move(owned_client));
 
@@ -321,8 +308,7 @@
       WebTreeScopeType::kDocument, name, FramePolicy(),
       FrameOwnerElementType::kIframe, client,
       InterfaceRegistry::GetEmptyInterfaceRegistry(),
-      AssociatedInterfaceProvider::GetEmptyAssociatedInterfaceProvider(),
-      nullptr));
+      client->GetAssociatedInterfaceProvider(), nullptr));
   client->Bind(frame, std::move(owned_client));
   if (!security_origin)
     security_origin = SecurityOrigin::CreateUniqueOpaque();
@@ -356,11 +342,7 @@
   web_frame_client =
       CreateDefaultClientIfNeeded(web_frame_client, owned_web_frame_client);
   WebLocalFrame* frame = WebLocalFrame::CreateMainFrame(
-      web_view_, web_frame_client, nullptr,
-      mojo::PendingRemote<mojom::blink::DocumentInterfaceBroker>()
-          .InitWithNewPipeAndPassReceiver()
-          .PassPipe(),
-      opener);
+      web_view_, web_frame_client, nullptr, opener);
   web_frame_client->Bind(frame, std::move(owned_web_frame_client));
 
   test_web_widget_client_ = CreateDefaultClientIfNeeded(
@@ -431,8 +413,7 @@
   WebRemoteFrameImpl* frame = WebRemoteFrameImpl::CreateMainFrame(
       web_view_, web_remote_frame_client,
       InterfaceRegistry::GetEmptyInterfaceRegistry(),
-      AssociatedInterfaceProvider::GetEmptyAssociatedInterfaceProvider(),
-      nullptr);
+      web_remote_frame_client->GetAssociatedInterfaceProvider(), nullptr);
   web_remote_frame_client->Bind(frame,
                                 std::move(owned_web_remote_frame_client));
   if (!security_origin)
@@ -517,8 +498,11 @@
 
 TestWebFrameClient::TestWebFrameClient()
     : interface_provider_(new service_manager::InterfaceProvider()),
+      associated_interface_provider_(new AssociatedInterfaceProvider(nullptr)),
       effective_connection_type_(WebEffectiveConnectionType::kTypeUnknown) {}
 
+TestWebFrameClient::~TestWebFrameClient() = default;
+
 void TestWebFrameClient::Bind(WebLocalFrame* frame,
                               std::unique_ptr<TestWebFrameClient> self_owned) {
   DCHECK(!frame_);
@@ -615,7 +599,14 @@
   return new FakeWebPlugin(params);
 }
 
-TestWebRemoteFrameClient::TestWebRemoteFrameClient() = default;
+AssociatedInterfaceProvider*
+TestWebFrameClient::GetRemoteNavigationAssociatedInterfaces() {
+  return associated_interface_provider_.get();
+}
+
+TestWebRemoteFrameClient::TestWebRemoteFrameClient()
+    : associated_interface_provider_(new AssociatedInterfaceProvider(nullptr)) {
+}
 
 void TestWebRemoteFrameClient::Bind(
     WebRemoteFrame* frame,
diff --git a/third_party/blink/renderer/core/frame/frame_test_helpers.h b/third_party/blink/renderer/core/frame/frame_test_helpers.h
index fcb56d6..c255424 100644
--- a/third_party/blink/renderer/core/frame/frame_test_helpers.h
+++ b/third_party/blink/renderer/core/frame/frame_test_helpers.h
@@ -410,7 +410,7 @@
 class TestWebFrameClient : public WebLocalFrameClient {
  public:
   TestWebFrameClient();
-  ~TestWebFrameClient() override = default;
+  ~TestWebFrameClient() override;
 
   static bool IsLoading() { return loads_in_progress_ > 0; }
   Vector<String>& ConsoleMessages() { return console_messages_; }
@@ -452,6 +452,8 @@
                               unsigned source_line,
                               const WebString& stack_trace) override;
   WebPlugin* CreatePlugin(const WebPluginParams& params) override;
+  AssociatedInterfaceProvider* GetRemoteNavigationAssociatedInterfaces()
+      override;
 
  private:
   void CommitNavigation(std::unique_ptr<WebNavigationInfo>);
@@ -465,6 +467,8 @@
   // through this client.
   std::unique_ptr<service_manager::InterfaceProvider> interface_provider_;
 
+  std::unique_ptr<AssociatedInterfaceProvider> associated_interface_provider_;
+
   // This is null from when the client is created until it is initialized with
   // Bind().
   WebNavigationControl* frame_ = nullptr;
@@ -498,10 +502,16 @@
                           WebSecurityOrigin target_origin,
                           WebDOMMessageEvent) override {}
 
+  AssociatedInterfaceProvider* GetAssociatedInterfaceProvider() {
+    return associated_interface_provider_.get();
+  }
+
  private:
   // If set to a non-null value, self-deletes on frame detach.
   std::unique_ptr<TestWebRemoteFrameClient> self_owned_;
 
+  std::unique_ptr<AssociatedInterfaceProvider> associated_interface_provider_;
+
   // This is null from when the client is created until it is initialized with
   // Bind().
   WebRemoteFrame* frame_ = nullptr;
diff --git a/third_party/blink/renderer/core/frame/frame_view.cc b/third_party/blink/renderer/core/frame/frame_view.cc
index 855c72e..64837648 100644
--- a/third_party/blink/renderer/core/frame/frame_view.cc
+++ b/third_party/blink/renderer/core/frame/frame_view.cc
@@ -143,8 +143,7 @@
   }
   if (frame_visibility != frame_visibility_) {
     frame_visibility_ = frame_visibility;
-    if (FrameClient* client = GetFrame().Client())
-      client->VisibilityChanged(frame_visibility);
+    VisibilityChanged(frame_visibility);
   }
 }
 
diff --git a/third_party/blink/renderer/core/frame/frame_view.h b/third_party/blink/renderer/core/frame/frame_view.h
index a0884db..fba56b7 100644
--- a/third_party/blink/renderer/core/frame/frame_view.h
+++ b/third_party/blink/renderer/core/frame/frame_view.h
@@ -67,6 +67,8 @@
 
   bool DisplayLockedInParentFrame();
 
+  virtual void VisibilityChanged(blink::mojom::FrameVisibility visibilty) = 0;
+
  private:
   PhysicalRect rect_in_parent_;
   base::TimeTicks rect_in_parent_stable_since_;
diff --git a/third_party/blink/renderer/core/frame/local_frame.cc b/third_party/blink/renderer/core/frame/local_frame.cc
index 2e89bd6..24ea8a0 100644
--- a/third_party/blink/renderer/core/frame/local_frame.cc
+++ b/third_party/blink/renderer/core/frame/local_frame.cc
@@ -41,7 +41,6 @@
 #include "third_party/blink/public/common/frame/blocked_navigation_types.h"
 #include "third_party/blink/public/common/thread_safe_browser_interface_broker_proxy.h"
 #include "third_party/blink/public/mojom/ad_tagging/ad_frame.mojom-blink.h"
-#include "third_party/blink/public/mojom/frame/document_interface_broker.mojom-blink.h"
 #include "third_party/blink/public/mojom/frame/lifecycle.mojom-blink.h"
 #include "third_party/blink/public/platform/interface_provider.h"
 #include "third_party/blink/public/platform/interface_registry.h"
@@ -1135,30 +1134,11 @@
   return *Client()->GetInterfaceProvider();
 }
 
-void LocalFrame::BindDocumentInterfaceBroker(
-    mojo::ScopedMessagePipeHandle js_handle) {
-  DCHECK(Client());
-  Client()->BindDocumentInterfaceBroker(std::move(js_handle));
-}
-
-mojom::blink::DocumentInterfaceBroker&
-LocalFrame::GetDocumentInterfaceBroker() {
-  DCHECK(Client());
-  return *Client()->GetDocumentInterfaceBroker();
-}
-
 BrowserInterfaceBrokerProxy& LocalFrame::GetBrowserInterfaceBroker() {
   DCHECK(Client());
   return Client()->GetBrowserInterfaceBroker();
 }
 
-mojo::ScopedMessagePipeHandle LocalFrame::SetDocumentInterfaceBrokerForTesting(
-    mojo::ScopedMessagePipeHandle blink_handle) {
-  DCHECK(Client());
-  return Client()->SetDocumentInterfaceBrokerForTesting(
-      std::move(blink_handle));
-}
-
 AssociatedInterfaceProvider*
 LocalFrame::GetRemoteNavigationAssociatedInterfaces() {
   DCHECK(Client());
diff --git a/third_party/blink/renderer/core/frame/local_frame.h b/third_party/blink/renderer/core/frame/local_frame.h
index 0fdcfa4..a5e3023a 100644
--- a/third_party/blink/renderer/core/frame/local_frame.h
+++ b/third_party/blink/renderer/core/frame/local_frame.h
@@ -40,7 +40,6 @@
 #include "mojo/public/cpp/bindings/pending_receiver.h"
 #include "mojo/public/cpp/bindings/unique_receiver_set.h"
 #include "third_party/blink/public/mojom/ad_tagging/ad_frame.mojom-blink-forward.h"
-#include "third_party/blink/public/mojom/frame/document_interface_broker.mojom-blink-forward.h"
 #include "third_party/blink/public/mojom/frame/frame.mojom-blink.h"
 #include "third_party/blink/public/mojom/frame/lifecycle.mojom-blink-forward.h"
 #include "third_party/blink/public/mojom/loader/pause_subresource_loading_handle.mojom-blink-forward.h"
@@ -278,10 +277,6 @@
   bool CanNavigate(const Frame&, const KURL& destination_url = KURL());
 
   service_manager::InterfaceProvider& GetInterfaceProvider();
-  void BindDocumentInterfaceBroker(mojo::ScopedMessagePipeHandle js_handle);
-  mojom::blink::DocumentInterfaceBroker& GetDocumentInterfaceBroker();
-  mojo::ScopedMessagePipeHandle SetDocumentInterfaceBrokerForTesting(
-      mojo::ScopedMessagePipeHandle blink_handle);
 
   BrowserInterfaceBrokerProxy& GetBrowserInterfaceBroker();
 
diff --git a/third_party/blink/renderer/core/frame/local_frame_client.h b/third_party/blink/renderer/core/frame/local_frame_client.h
index 12937e8..41e8871 100644
--- a/third_party/blink/renderer/core/frame/local_frame_client.h
+++ b/third_party/blink/renderer/core/frame/local_frame_client.h
@@ -84,10 +84,6 @@
 namespace blink {
 namespace mojom {
 enum class WebFeature : int32_t;
-
-namespace blink {
-class DocumentInterfaceBroker;
-}  // namespace blink
 }  // namespace mojom
 
 class AssociatedInterfaceProvider;
@@ -372,28 +368,8 @@
     return nullptr;
   }
 
-  // Binds |js_handle| to the currently bound implementation of
-  // DocumentInterfaceBroker to share the same broker between C++ and JavaScript
-  // clients.
-  virtual void BindDocumentInterfaceBroker(
-      mojo::ScopedMessagePipeHandle js_handle) {}
-
-  virtual mojom::blink::DocumentInterfaceBroker* GetDocumentInterfaceBroker() {
-    return nullptr;
-  }
-
   virtual BrowserInterfaceBrokerProxy& GetBrowserInterfaceBroker() = 0;
 
-  // Used in tests to set a custom override for DocumentInterfaceBroker methods.
-  // |blink_handle| is bound to the test implementation on the caller side.
-  // Returns the handle to the previously bound 'production' implementation,
-  // which will be used to forward the calls to methods that have not been
-  // overridden.
-  virtual mojo::ScopedMessagePipeHandle SetDocumentInterfaceBrokerForTesting(
-      mojo::ScopedMessagePipeHandle blink_handle) {
-    return mojo::ScopedMessagePipeHandle();
-  }
-
   virtual AssociatedInterfaceProvider*
   GetRemoteNavigationAssociatedInterfaces() = 0;
 
diff --git a/third_party/blink/renderer/core/frame/local_frame_view.cc b/third_party/blink/renderer/core/frame/local_frame_view.cc
index 181f4d3..22243f5c 100644
--- a/third_party/blink/renderer/core/frame/local_frame_view.cc
+++ b/third_party/blink/renderer/core/frame/local_frame_view.cc
@@ -3979,6 +3979,11 @@
   }
 }
 
+void LocalFrameView::VisibilityChanged(
+    blink::mojom::FrameVisibility visibility) {
+  frame_->GetLocalFrameHostRemote().VisibilityChanged(visibility);
+}
+
 void LocalFrameView::RenderThrottlingStatusChanged() {
   TRACE_EVENT0("blink", "LocalFrameView::RenderThrottlingStatusChanged");
   DCHECK(!IsInPerformLayout());
diff --git a/third_party/blink/renderer/core/frame/local_frame_view.h b/third_party/blink/renderer/core/frame/local_frame_view.h
index 893ba552..f905fd60 100644
--- a/third_party/blink/renderer/core/frame/local_frame_view.h
+++ b/third_party/blink/renderer/core/frame/local_frame_view.h
@@ -704,6 +704,7 @@
   bool LifecycleUpdatesThrottled() const override {
     return lifecycle_updates_throttled_;
   }
+  void VisibilityChanged(blink::mojom::FrameVisibility visibility) override;
 
  private:
   LocalFrameView(LocalFrame&, IntRect);
diff --git a/third_party/blink/renderer/core/frame/remote_frame_client_impl.cc b/third_party/blink/renderer/core/frame/remote_frame_client_impl.cc
index 54b899a..822a7ca 100644
--- a/third_party/blink/renderer/core/frame/remote_frame_client_impl.cc
+++ b/third_party/blink/renderer/core/frame/remote_frame_client_impl.cc
@@ -162,11 +162,6 @@
                                      WebLocalFrameImpl::FromFrame(source));
 }
 
-void RemoteFrameClientImpl::VisibilityChanged(
-    blink::mojom::FrameVisibility visibility) {
-  web_frame_->Client()->VisibilityChanged(visibility);
-}
-
 void RemoteFrameClientImpl::SetIsInert(bool inert) {
   web_frame_->Client()->SetIsInert(inert);
 }
diff --git a/third_party/blink/renderer/core/frame/remote_frame_client_impl.h b/third_party/blink/renderer/core/frame/remote_frame_client_impl.h
index 79010765..8dcda98 100644
--- a/third_party/blink/renderer/core/frame/remote_frame_client_impl.h
+++ b/third_party/blink/renderer/core/frame/remote_frame_client_impl.h
@@ -49,7 +49,6 @@
   void UpdateRemoteViewportIntersection(
       const ViewportIntersectionState& intersection_state) override;
   void AdvanceFocus(WebFocusType, LocalFrame*) override;
-  void VisibilityChanged(blink::mojom::FrameVisibility) override;
   void SetIsInert(bool) override;
   void UpdateRenderThrottlingStatus(bool is_throttled,
                                     bool subtree_throttled) override;
diff --git a/third_party/blink/renderer/core/frame/remote_frame_view.cc b/third_party/blink/renderer/core/frame/remote_frame_view.cc
index c114bd5..38cf607 100644
--- a/third_party/blink/renderer/core/frame/remote_frame_view.cc
+++ b/third_party/blink/renderer/core/frame/remote_frame_view.cc
@@ -244,6 +244,11 @@
                                                         IsSubtreeThrottled());
 }
 
+void RemoteFrameView::VisibilityChanged(
+    blink::mojom::FrameVisibility visibility) {
+  remote_frame_->GetRemoteFrameHostRemote().VisibilityChanged(visibility);
+}
+
 bool RemoteFrameView::CanThrottleRendering() const {
   return IsSubtreeThrottled() || IsHiddenForThrottling();
 }
diff --git a/third_party/blink/renderer/core/frame/remote_frame_view.h b/third_party/blink/renderer/core/frame/remote_frame_view.h
index 57184e76..9007a0f 100644
--- a/third_party/blink/renderer/core/frame/remote_frame_view.h
+++ b/third_party/blink/renderer/core/frame/remote_frame_view.h
@@ -65,6 +65,7 @@
 
   bool CanThrottleRendering() const override;
   void VisibilityForThrottlingChanged() override;
+  void VisibilityChanged(blink::mojom::FrameVisibility visibility) override;
 
   // Compute the interest rect of this frame in its unscrolled space. This may
   // be used by the OOPIF's compositor to limit the amount of rastered tiles,
diff --git a/third_party/blink/renderer/core/frame/web_local_frame_impl.cc b/third_party/blink/renderer/core/frame/web_local_frame_impl.cc
index 41107ab..4c685ca 100644
--- a/third_party/blink/renderer/core/frame/web_local_frame_impl.cc
+++ b/third_party/blink/renderer/core/frame/web_local_frame_impl.cc
@@ -1598,40 +1598,34 @@
     WebView* web_view,
     WebLocalFrameClient* client,
     InterfaceRegistry* interface_registry,
-    mojo::ScopedMessagePipeHandle document_interface_broker_handle,
     WebFrame* opener,
     const WebString& name,
     WebSandboxFlags sandbox_flags,
     const FeaturePolicy::FeatureState& opener_feature_state) {
   return WebLocalFrameImpl::CreateMainFrame(
-      web_view, client, interface_registry,
-      std::move(document_interface_broker_handle), opener, name, sandbox_flags,
+      web_view, client, interface_registry, opener, name, sandbox_flags,
       opener_feature_state);
 }
 
 WebLocalFrame* WebLocalFrame::CreateProvisional(
     WebLocalFrameClient* client,
     InterfaceRegistry* interface_registry,
-    mojo::ScopedMessagePipeHandle document_interface_broker_handle,
     WebFrame* previous_frame,
     const FramePolicy& frame_policy) {
-  return WebLocalFrameImpl::CreateProvisional(
-      client, interface_registry, std::move(document_interface_broker_handle),
-      previous_frame, frame_policy);
+  return WebLocalFrameImpl::CreateProvisional(client, interface_registry,
+                                              previous_frame, frame_policy);
 }
 
 WebLocalFrameImpl* WebLocalFrameImpl::CreateMainFrame(
     WebView* web_view,
     WebLocalFrameClient* client,
     InterfaceRegistry* interface_registry,
-    mojo::ScopedMessagePipeHandle document_interface_broker_handle,
     WebFrame* opener,
     const WebString& name,
     WebSandboxFlags sandbox_flags,
     const FeaturePolicy::FeatureState& opener_feature_state) {
   WebLocalFrameImpl* frame = MakeGarbageCollected<WebLocalFrameImpl>(
-      WebTreeScopeType::kDocument, client, interface_registry,
-      std::move(document_interface_broker_handle));
+      WebTreeScopeType::kDocument, client, interface_registry);
   frame->SetOpener(opener);
   Page& page = *static_cast<WebViewImpl*>(web_view)->GetPage();
   DCHECK(!page.MainFrame());
@@ -1645,14 +1639,13 @@
 WebLocalFrameImpl* WebLocalFrameImpl::CreateProvisional(
     WebLocalFrameClient* client,
     blink::InterfaceRegistry* interface_registry,
-    mojo::ScopedMessagePipeHandle document_interface_broker_handle,
     WebFrame* previous_web_frame,
     const FramePolicy& frame_policy) {
   DCHECK(client);
   auto* web_frame = MakeGarbageCollected<WebLocalFrameImpl>(
       previous_web_frame->InShadowTree() ? WebTreeScopeType::kShadow
                                          : WebTreeScopeType::kDocument,
-      client, interface_registry, std::move(document_interface_broker_handle));
+      client, interface_registry);
   Frame* previous_frame = ToCoreFrame(*previous_web_frame);
   web_frame->SetParent(previous_web_frame->Parent());
   web_frame->SetOpener(previous_web_frame->Opener());
@@ -1697,11 +1690,9 @@
 WebLocalFrameImpl* WebLocalFrameImpl::CreateLocalChild(
     WebTreeScopeType scope,
     WebLocalFrameClient* client,
-    blink::InterfaceRegistry* interface_registry,
-    mojo::ScopedMessagePipeHandle document_interface_broker_handle) {
+    blink::InterfaceRegistry* interface_registry) {
   WebLocalFrameImpl* frame = MakeGarbageCollected<WebLocalFrameImpl>(
-      scope, client, interface_registry,
-      std::move(document_interface_broker_handle));
+      scope, client, interface_registry);
   AppendChild(frame);
   return frame;
 }
@@ -1709,13 +1700,10 @@
 WebLocalFrameImpl::WebLocalFrameImpl(
     WebTreeScopeType scope,
     WebLocalFrameClient* client,
-    blink::InterfaceRegistry* interface_registry,
-    mojo::ScopedMessagePipeHandle document_interface_broker_handle)
+    blink::InterfaceRegistry* interface_registry)
     : WebNavigationControl(scope),
       client_(client),
-      local_frame_client_(MakeGarbageCollected<LocalFrameClientImpl>(
-          this,
-          std::move(document_interface_broker_handle))),
+      local_frame_client_(MakeGarbageCollected<LocalFrameClientImpl>(this)),
       autofill_client_(nullptr),
       find_in_page_(
           MakeGarbageCollected<FindInPage>(*this, interface_registry)),
@@ -2107,14 +2095,19 @@
   Vector<String> report_endpoints;
   for (const WebString& end_point : violation.report_endpoints)
     report_endpoints.push_back(end_point);
+  auto directive_type =
+      ContentSecurityPolicy::GetDirectiveType(violation.effective_directive);
+  LocalFrame* context_frame =
+      directive_type == ContentSecurityPolicy::DirectiveType::kFrameAncestors
+          ? GetFrame()
+          : nullptr;
   document->GetContentSecurityPolicy()->ReportViolation(
-      violation.directive,
-      ContentSecurityPolicy::GetDirectiveType(violation.effective_directive),
-      violation.console_message, violation.blocked_url, report_endpoints,
-      violation.use_reporting_api, violation.header,
+      violation.directive, directive_type, violation.console_message,
+      violation.blocked_url, report_endpoints, violation.use_reporting_api,
+      violation.header,
       static_cast<ContentSecurityPolicyHeaderType>(violation.disposition),
       ContentSecurityPolicy::ViolationType::kURLViolation,
-      std::move(source_location), nullptr /* LocalFrame */,
+      std::move(source_location), context_frame,
       violation.after_redirect ? RedirectStatus::kFollowedRedirect
                                : RedirectStatus::kNoRedirect,
       nullptr /* Element */);
diff --git a/third_party/blink/renderer/core/frame/web_local_frame_impl.h b/third_party/blink/renderer/core/frame/web_local_frame_impl.h
index 7313ce1e..ef0067c 100644
--- a/third_party/blink/renderer/core/frame/web_local_frame_impl.h
+++ b/third_party/blink/renderer/core/frame/web_local_frame_impl.h
@@ -103,8 +103,7 @@
   // WebLocalFrame overrides:
   WebLocalFrameImpl* CreateLocalChild(WebTreeScopeType,
                                       WebLocalFrameClient*,
-                                      blink::InterfaceRegistry*,
-                                      mojo::ScopedMessagePipeHandle) override;
+                                      blink::InterfaceRegistry*) override;
   WebLocalFrameClient* Client() const override { return client_; }
   void SetAutofillClient(WebAutofillClient*) override;
   WebAutofillClient* AutofillClient() override;
@@ -355,21 +354,18 @@
   static WebLocalFrameImpl* CreateMainFrame(WebView*,
                                             WebLocalFrameClient*,
                                             InterfaceRegistry*,
-                                            mojo::ScopedMessagePipeHandle,
                                             WebFrame* opener,
                                             const WebString& name,
                                             WebSandboxFlags,
                                             const FeaturePolicy::FeatureState&);
   static WebLocalFrameImpl* CreateProvisional(WebLocalFrameClient*,
                                               InterfaceRegistry*,
-                                              mojo::ScopedMessagePipeHandle,
                                               WebFrame*,
                                               const FramePolicy&);
 
   WebLocalFrameImpl(WebTreeScopeType,
                     WebLocalFrameClient*,
-                    blink::InterfaceRegistry*,
-                    mojo::ScopedMessagePipeHandle);
+                    blink::InterfaceRegistry*);
   ~WebLocalFrameImpl() override;
 
   LocalFrame* CreateChildFrame(const AtomicString& name,
diff --git a/third_party/blink/renderer/core/html/html_element.cc b/third_party/blink/renderer/core/html/html_element.cc
index d1faacf..0b737e5e 100644
--- a/third_party/blink/renderer/core/html/html_element.cc
+++ b/third_party/blink/renderer/core/html/html_element.cc
@@ -1128,7 +1128,7 @@
     // Skip bdi, script, style and text form controls.
     auto* element = DynamicTo<Element>(node);
     if (DeprecatedEqualIgnoringCase(node->nodeName(), "bdi") ||
-        IsHTMLScriptElement(*node) || IsA<HTMLStyleElement>(*node) ||
+        IsA<HTMLScriptElement>(*node) || IsA<HTMLStyleElement>(*node) ||
         (element && element->IsTextControl()) ||
         (element && element->ShadowPseudoId() == "-webkit-input-placeholder")) {
       node = FlatTreeTraversal::NextSkippingChildren(*node, this);
diff --git a/third_party/blink/renderer/core/html/html_table_cell_element.cc b/third_party/blink/renderer/core/html/html_table_cell_element.cc
index dcc3341..42963945 100644
--- a/third_party/blink/renderer/core/html/html_table_cell_element.cc
+++ b/third_party/blink/renderer/core/html/html_table_cell_element.cc
@@ -72,7 +72,7 @@
 }
 
 int HTMLTableCellElement::cellIndex() const {
-  if (!IsHTMLTableRowElement(parentElement()))
+  if (!IsA<HTMLTableRowElement>(parentElement()))
     return -1;
 
   int index = 0;
diff --git a/third_party/blink/renderer/core/html/html_table_rows_collection.cc b/third_party/blink/renderer/core/html/html_table_rows_collection.cc
index 2235f2dd..d685076 100644
--- a/third_party/blink/renderer/core/html/html_table_rows_collection.cc
+++ b/third_party/blink/renderer/core/html/html_table_rows_collection.cc
@@ -78,7 +78,7 @@
   else if (IsInSection(*previous, html_names::kTbodyTag))
     child = Traversal<HTMLElement>::NextSibling(*previous->parentNode());
   for (; child; child = Traversal<HTMLElement>::NextSibling(*child)) {
-    if (auto* row = ToHTMLTableRowElementOrNull(child))
+    if (auto* row = DynamicTo<HTMLTableRowElement>(child))
       return row;
     if (child->HasTagName(html_names::kTbodyTag)) {
       if (HTMLTableRowElement* row =
@@ -115,7 +115,7 @@
 
   for (HTMLElement* child = Traversal<HTMLElement>::LastChild(table); child;
        child = Traversal<HTMLElement>::PreviousSibling(*child)) {
-    if (auto* row = ToHTMLTableRowElementOrNull(child))
+    if (auto* row = DynamicTo<HTMLTableRowElement>(child))
       return row;
     if (child->HasTagName(html_names::kTbodyTag)) {
       if (HTMLTableRowElement* last_row =
@@ -152,7 +152,7 @@
 
 Element* HTMLTableRowsCollection::VirtualItemAfter(Element* previous) const {
   return RowAfter(To<HTMLTableElement>(ownerNode()),
-                  ToHTMLTableRowElement(previous));
+                  To<HTMLTableRowElement>(previous));
 }
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/html/html_table_rows_collection.h b/third_party/blink/renderer/core/html/html_table_rows_collection.h
index 866e65a83..4a03fcb7 100644
--- a/third_party/blink/renderer/core/html/html_table_rows_collection.h
+++ b/third_party/blink/renderer/core/html/html_table_rows_collection.h
@@ -43,7 +43,7 @@
   HTMLTableRowsCollection(ContainerNode&, CollectionType);
 
   HTMLTableRowElement* Item(unsigned offset) const {
-    return ToHTMLTableRowElement(HTMLCollection::item(offset));
+    return To<HTMLTableRowElement>(HTMLCollection::item(offset));
   }
 
   static HTMLTableRowElement* RowAfter(HTMLTableElement&, HTMLTableRowElement*);
diff --git a/third_party/blink/renderer/core/html/parser/html_construction_site.cc b/third_party/blink/renderer/core/html/parser/html_construction_site.cc
index 602de54..23d6a01 100644
--- a/third_party/blink/renderer/core/html/parser/html_construction_site.cc
+++ b/third_party/blink/renderer/core/html/parser/html_construction_site.cc
@@ -97,7 +97,7 @@
 }
 
 static bool ShouldUseLengthLimit(const ContainerNode& node) {
-  return !IsHTMLScriptElement(node) && !IsA<HTMLStyleElement>(node) &&
+  return !IsA<HTMLScriptElement>(node) && !IsA<HTMLStyleElement>(node) &&
          !IsSVGScriptElement(node);
 }
 
@@ -743,7 +743,7 @@
       .SetAlreadyStarted(is_parsing_fragment_ && flags.IsCreatedByParser());
   HTMLScriptElement* element = nullptr;
   if (const auto* is_attribute = token->GetAttributeItem(html_names::kIsAttr)) {
-    element = ToHTMLScriptElement(OwnerDocumentForCurrentNode().CreateElement(
+    element = To<HTMLScriptElement>(OwnerDocumentForCurrentNode().CreateElement(
         html_names::kScriptTag, flags, is_attribute->Value()));
   } else {
     element = MakeGarbageCollected<HTMLScriptElement>(
diff --git a/third_party/blink/renderer/core/loader/empty_clients.cc b/third_party/blink/renderer/core/loader/empty_clients.cc
index 2cc6662..cf9c781 100644
--- a/third_party/blink/renderer/core/loader/empty_clients.cc
+++ b/third_party/blink/renderer/core/loader/empty_clients.cc
@@ -120,26 +120,6 @@
                                               std::move(navigation_params));
 }
 
-mojom::blink::DocumentInterfaceBroker*
-EmptyLocalFrameClient::GetDocumentInterfaceBroker() {
-  if (!document_interface_broker_.is_bound())
-    ignore_result(document_interface_broker_.BindNewPipeAndPassReceiver());
-  return document_interface_broker_.get();
-}
-
-mojo::ScopedMessagePipeHandle
-EmptyLocalFrameClient::SetDocumentInterfaceBrokerForTesting(
-    mojo::ScopedMessagePipeHandle blink_handle) {
-  mojo::PendingRemote<mojom::blink::DocumentInterfaceBroker> test_broker(
-      std::move(blink_handle), mojom::blink::DocumentInterfaceBroker::Version_);
-
-  mojo::ScopedMessagePipeHandle real_handle =
-      document_interface_broker_.Unbind().PassPipe();
-  document_interface_broker_.Bind(std::move(test_broker));
-
-  return real_handle;
-}
-
 LocalFrame* EmptyLocalFrameClient::CreateFrame(const AtomicString&,
                                                HTMLFrameOwnerElement*) {
   return nullptr;
diff --git a/third_party/blink/renderer/core/loader/empty_clients.h b/third_party/blink/renderer/core/loader/empty_clients.h
index 583983c..098703b 100644
--- a/third_party/blink/renderer/core/loader/empty_clients.h
+++ b/third_party/blink/renderer/core/loader/empty_clients.h
@@ -37,7 +37,6 @@
 #include "mojo/public/cpp/bindings/remote.h"
 #include "services/service_manager/public/cpp/interface_provider.h"
 #include "third_party/blink/public/common/browser_interface_broker_proxy.h"
-#include "third_party/blink/public/mojom/frame/document_interface_broker.mojom-blink.h"
 #include "third_party/blink/public/platform/platform.h"
 #include "third_party/blink/public/platform/web_focus_type.h"
 #include "third_party/blink/public/platform/web_menu_source_type.h"
@@ -376,16 +375,10 @@
                                 int32_t world_id) override {}
   bool AllowScriptExtensions() override { return false; }
 
-  void VisibilityChanged(blink::mojom::FrameVisibility) override {}
-
   service_manager::InterfaceProvider* GetInterfaceProvider() override {
     return &interface_provider_;
   }
 
-  mojom::blink::DocumentInterfaceBroker* GetDocumentInterfaceBroker() override;
-  mojo::ScopedMessagePipeHandle SetDocumentInterfaceBrokerForTesting(
-      mojo::ScopedMessagePipeHandle blink_handle) override;
-
   BrowserInterfaceBrokerProxy& GetBrowserInterfaceBroker() override {
     return GetEmptyBrowserInterfaceBroker();
   }
@@ -435,8 +428,6 @@
   WebTextCheckClient* text_check_client_;
 
   service_manager::InterfaceProvider interface_provider_;
-  mojo::Remote<mojom::blink::DocumentInterfaceBroker>
-      document_interface_broker_;
 
   DISALLOW_COPY_AND_ASSIGN(EmptyLocalFrameClient);
 };
@@ -475,7 +466,6 @@
   void UpdateRemoteViewportIntersection(
       const ViewportIntersectionState& intersection_state) override {}
   void AdvanceFocus(WebFocusType, LocalFrame* source) override {}
-  void VisibilityChanged(blink::mojom::FrameVisibility) override {}
   void SetIsInert(bool) override {}
   void UpdateRenderThrottlingStatus(bool is_throttled,
                                     bool subtree_throttled) override {}
diff --git a/third_party/blink/renderer/core/mojo/mojo.cc b/third_party/blink/renderer/core/mojo/mojo.cc
index 6a22d23..aab3fa5c 100644
--- a/third_party/blink/renderer/core/mojo/mojo.cc
+++ b/third_party/blink/renderer/core/mojo/mojo.cc
@@ -9,7 +9,6 @@
 
 #include "mojo/public/cpp/system/message_pipe.h"
 #include "services/service_manager/public/cpp/interface_provider.h"
-#include "third_party/blink/public/mojom/frame/document_interface_broker.mojom-blink.h"
 #include "third_party/blink/public/platform/interface_provider.h"
 #include "third_party/blink/public/platform/platform.h"
 #include "third_party/blink/renderer/core/dom/document.h"
@@ -128,32 +127,4 @@
   }
 }
 
-// static
-MojoHandle* Mojo::getDocumentInterfaceBrokerHandle(ScriptState* script_state) {
-  ExecutionContext* execution_context = ExecutionContext::From(script_state);
-  DCHECK(execution_context);
-  Document* document = static_cast<Document*>(execution_context);
-  DCHECK(document);
-
-  mojo::MessagePipe pipe;
-  document->BindDocumentInterfaceBroker(std::move(pipe.handle0));
-  return MakeGarbageCollected<MojoHandle>(
-      mojo::ScopedHandle::From(std::move(pipe.handle1)));
-}
-
-// static
-MojoHandle* Mojo::replaceDocumentInterfaceBrokerForTesting(
-    ScriptState* script_state,
-    MojoHandle* test_broker_handle) {
-  ExecutionContext* execution_context = ExecutionContext::From(script_state);
-  DCHECK(execution_context);
-  Document* document = static_cast<Document*>(execution_context);
-  DCHECK(document);
-
-  return MakeGarbageCollected<MojoHandle>(
-      mojo::ScopedHandle::From(document->SetDocumentInterfaceBrokerForTesting(
-          mojo::ScopedMessagePipeHandle(mojo::MessagePipeHandle(
-              test_broker_handle->TakeHandle().release().value())))));
-}
-
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/mojo/mojo.h b/third_party/blink/renderer/core/mojo/mojo.h
index 4fe5d108..b0e8b78 100644
--- a/third_party/blink/renderer/core/mojo/mojo.h
+++ b/third_party/blink/renderer/core/mojo/mojo.h
@@ -56,9 +56,6 @@
                             MojoHandle*,
                             const String& scope,
                             bool use_browser_interface_broker);
-  static MojoHandle* getDocumentInterfaceBrokerHandle(ScriptState*);
-  static MojoHandle* replaceDocumentInterfaceBrokerForTesting(ScriptState*,
-                                                              MojoHandle*);
 };
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/mojo/mojo.idl b/third_party/blink/renderer/core/mojo/mojo.idl
index 7bcfb127d..9bfcfc6 100644
--- a/third_party/blink/renderer/core/mojo/mojo.idl
+++ b/third_party/blink/renderer/core/mojo/mojo.idl
@@ -47,6 +47,4 @@
     static MojoCreateSharedBufferResult createSharedBuffer(unsigned long numBytes);
 
     [CallWith=ScriptState] static void bindInterface(DOMString interfaceName, MojoHandle request_handle, optional MojoScope scope = "context", optional boolean useBrowserInterfaceBroker = false);
-    [CallWith=ScriptState] static MojoHandle getDocumentInterfaceBrokerHandle();
-    [CallWith=ScriptState] static MojoHandle replaceDocumentInterfaceBrokerForTesting(MojoHandle test_broker_handle);
 };
diff --git a/third_party/blink/renderer/core/mojo/mojo_handle.cc b/third_party/blink/renderer/core/mojo/mojo_handle.cc
index 7615fe3..02038395 100644
--- a/third_party/blink/renderer/core/mojo/mojo_handle.cc
+++ b/third_party/blink/renderer/core/mojo/mojo_handle.cc
@@ -243,12 +243,10 @@
   result_dict->setResult(result);
   if (result == MOJO_RESULT_OK) {
     ArrayBufferContents contents(
-        data, num_bytes,
-        [](void* buffer, size_t length, void* alloc_data) {
+        data, num_bytes, [](void* buffer, size_t length, void* alloc_data) {
           MojoResult result = MojoUnmapBuffer(buffer);
           DCHECK_EQ(result, MOJO_RESULT_OK);
-        },
-        ArrayBufferContents::kNotShared);
+        });
     result_dict->setBuffer(DOMArrayBuffer::Create(contents));
   }
   return result_dict;
diff --git a/third_party/blink/renderer/core/paint/ng/ng_box_fragment_painter.cc b/third_party/blink/renderer/core/paint/ng/ng_box_fragment_painter.cc
index 6cf8252..63aef74 100644
--- a/third_party/blink/renderer/core/paint/ng/ng_box_fragment_painter.cc
+++ b/third_party/blink/renderer/core/paint/ng/ng_box_fragment_painter.cc
@@ -635,7 +635,7 @@
     paint_rect.size = box_fragment_.Size();
     if (layout_object.IsTableCell()) {
       paint_rect.size =
-          PhysicalSize(To<LayoutTableCell>(layout_object).PixelSnappedSize());
+          PhysicalSize(ToLayoutBox(layout_object).PixelSnappedSize());
     }
     background_client = &GetDisplayItemClient();
   }
diff --git a/third_party/blink/renderer/core/script/script_element_base.cc b/third_party/blink/renderer/core/script/script_element_base.cc
index 97c24ce..135e5704 100644
--- a/third_party/blink/renderer/core/script/script_element_base.cc
+++ b/third_party/blink/renderer/core/script/script_element_base.cc
@@ -11,7 +11,7 @@
 
 ScriptLoader* ScriptLoaderFromElement(Element* element) {
   ScriptLoader* script_loader = nullptr;
-  if (auto* html_script = ToHTMLScriptElementOrNull(*element))
+  if (auto* html_script = DynamicTo<HTMLScriptElement>(*element))
     script_loader = html_script->Loader();
   else if (auto* svg_script = ToSVGScriptElementOrNull(*element))
     script_loader = svg_script->Loader();
diff --git a/third_party/blink/renderer/core/testing/document_interface_broker_test_helpers.cc b/third_party/blink/renderer/core/testing/document_interface_broker_test_helpers.cc
deleted file mode 100644
index 9a00ab9..0000000
--- a/third_party/blink/renderer/core/testing/document_interface_broker_test_helpers.cc
+++ /dev/null
@@ -1,27 +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 "third_party/blink/renderer/core/testing/document_interface_broker_test_helpers.h"
-
-#include <utility>
-
-namespace blink {
-
-void FrameHostTestInterfaceImpl::BindAndFlush(
-    mojo::PendingReceiver<mojom::blink::FrameHostTestInterface> receiver) {
-  receiver_.Bind(std::move(receiver));
-  receiver_.WaitForIncomingCall();
-}
-
-void FrameHostTestInterfaceImpl::GetName(GetNameCallback callback) {
-  std::move(callback).Run(kGetNameTestResponse);
-}
-
-void FrameHostTestDocumentInterfaceBroker::GetFrameHostTestInterface(
-    mojo::PendingReceiver<mojom::blink::FrameHostTestInterface> receiver) {
-  FrameHostTestInterfaceImpl impl;
-  impl.BindAndFlush(std::move(receiver));
-}
-
-}  // namespace blink
diff --git a/third_party/blink/renderer/core/testing/document_interface_broker_test_helpers.h b/third_party/blink/renderer/core/testing/document_interface_broker_test_helpers.h
deleted file mode 100644
index f00a018a..0000000
--- a/third_party/blink/renderer/core/testing/document_interface_broker_test_helpers.h
+++ /dev/null
@@ -1,58 +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 THIRD_PARTY_BLINK_RENDERER_CORE_TESTING_DOCUMENT_INTERFACE_BROKER_TEST_HELPERS_H_
-#define THIRD_PARTY_BLINK_RENDERER_CORE_TESTING_DOCUMENT_INTERFACE_BROKER_TEST_HELPERS_H_
-
-#include <utility>
-
-#include "mojo/public/cpp/bindings/pending_receiver.h"
-#include "mojo/public/cpp/bindings/receiver.h"
-#include "third_party/blink/public/mojom/frame/document_interface_broker.mojom-blink-forward.h"
-#include "third_party/blink/public/mojom/frame/frame_host_test_interface.mojom-blink.h"
-#include "third_party/blink/renderer/core/testing/test_document_interface_broker.h"
-
-namespace blink {
-
-constexpr char kGetNameTestResponse[] = "BlinkTestName";
-
-// These classes can be used for verifying that overriding mechanism for
-// DocumentInterfaceBroker method calls works as expected with LocalFrameClient
-// implementations. See frame_test.cc and local_frame_client_impl.cc for
-// examples.
-class FrameHostTestInterfaceImpl : public mojom::blink::FrameHostTestInterface {
- public:
-  FrameHostTestInterfaceImpl() = default;
-  ~FrameHostTestInterfaceImpl() override {}
-
-  void BindAndFlush(
-      mojo::PendingReceiver<mojom::blink::FrameHostTestInterface> receiver);
-
- protected:
-  void Ping(const KURL& url, const WTF::String& event) override {}
-  void GetName(GetNameCallback callback) override;
-
- private:
-  mojo::Receiver<mojom::blink::FrameHostTestInterface> receiver_{this};
-
-  DISALLOW_COPY_AND_ASSIGN(FrameHostTestInterfaceImpl);
-};
-
-class FrameHostTestDocumentInterfaceBroker
-    : public TestDocumentInterfaceBroker {
- public:
-  FrameHostTestDocumentInterfaceBroker(
-      mojom::blink::DocumentInterfaceBroker* document_interface_broker,
-      mojo::PendingReceiver<mojom::blink::DocumentInterfaceBroker> receiver)
-      : TestDocumentInterfaceBroker(document_interface_broker,
-                                    std::move(receiver)) {}
-
-  void GetFrameHostTestInterface(
-      mojo::PendingReceiver<mojom::blink::FrameHostTestInterface> receiver)
-      override;
-};
-
-}  // namespace blink
-
-#endif  // THIRD_PARTY_BLINK_RENDERER_CORE_TESTING_DOCUMENT_INTERFACE_BROKER_TEST_HELPERS_H_
diff --git a/third_party/blink/renderer/core/testing/fake_local_frame_host.cc b/third_party/blink/renderer/core/testing/fake_local_frame_host.cc
index f4937b54..e3af385 100644
--- a/third_party/blink/renderer/core/testing/fake_local_frame_host.cc
+++ b/third_party/blink/renderer/core/testing/fake_local_frame_host.cc
@@ -42,6 +42,9 @@
 
 void FakeLocalFrameHost::EvictFromBackForwardCache() {}
 
+void FakeLocalFrameHost::VisibilityChanged(
+    mojom::blink::FrameVisibility visibility) {}
+
 void FakeLocalFrameHost::BindFrameHostReceiver(
     mojo::ScopedInterfaceEndpointHandle handle) {
   receiver_.Bind(mojo::PendingAssociatedReceiver<mojom::blink::LocalFrameHost>(
diff --git a/third_party/blink/renderer/core/testing/fake_local_frame_host.h b/third_party/blink/renderer/core/testing/fake_local_frame_host.h
index 0958660..b803afa0 100644
--- a/third_party/blink/renderer/core/testing/fake_local_frame_host.h
+++ b/third_party/blink/renderer/core/testing/fake_local_frame_host.h
@@ -36,6 +36,7 @@
   void SetNeedsOcclusionTracking(bool needs_tracking) override;
   void LifecycleStateChanged(mojom::blink::FrameLifecycleState state) override;
   void EvictFromBackForwardCache() override;
+  void VisibilityChanged(mojom::blink::FrameVisibility visibility) override;
 
  private:
   void BindFrameHostReceiver(mojo::ScopedInterfaceEndpointHandle handle);
diff --git a/third_party/blink/renderer/core/testing/fake_remote_frame_host.cc b/third_party/blink/renderer/core/testing/fake_remote_frame_host.cc
new file mode 100644
index 0000000..d8f333ff
--- /dev/null
+++ b/third_party/blink/renderer/core/testing/fake_remote_frame_host.cc
@@ -0,0 +1,28 @@
+// 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 "third_party/blink/renderer/core/testing/fake_remote_frame_host.h"
+
+namespace blink {
+
+void FakeRemoteFrameHost::Init(blink::AssociatedInterfaceProvider* provider) {
+  provider->OverrideBinderForTesting(
+      mojom::blink::RemoteFrameHost::Name_,
+      base::BindRepeating(&FakeRemoteFrameHost::BindFrameHostReceiver,
+                          base::Unretained(this)));
+}
+
+void FakeRemoteFrameHost::SetInheritedEffectiveTouchAction(
+    cc::TouchAction touch_action) {}
+
+void FakeRemoteFrameHost::VisibilityChanged(
+    mojom::blink::FrameVisibility visibility) {}
+
+void FakeRemoteFrameHost::BindFrameHostReceiver(
+    mojo::ScopedInterfaceEndpointHandle handle) {
+  receiver_.Bind(mojo::PendingAssociatedReceiver<mojom::blink::RemoteFrameHost>(
+      std::move(handle)));
+}
+
+}  // namespace blink
diff --git a/third_party/blink/renderer/core/testing/fake_remote_frame_host.h b/third_party/blink/renderer/core/testing/fake_remote_frame_host.h
new file mode 100644
index 0000000..46a31b6
--- /dev/null
+++ b/third_party/blink/renderer/core/testing/fake_remote_frame_host.h
@@ -0,0 +1,35 @@
+// 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 THIRD_PARTY_BLINK_RENDERER_CORE_TESTING_FAKE_REMOTE_FRAME_HOST_H_
+#define THIRD_PARTY_BLINK_RENDERER_CORE_TESTING_FAKE_REMOTE_FRAME_HOST_H_
+
+#include "mojo/public/cpp/bindings/associated_receiver_set.h"
+#include "mojo/public/cpp/bindings/pending_associated_receiver.h"
+#include "third_party/blink/public/common/associated_interfaces/associated_interface_provider.h"
+#include "third_party/blink/public/mojom/frame/frame.mojom-blink.h"
+
+namespace blink {
+
+// This class implements a RemoteFrameHost that can be attached to the
+// AssociatedInterfaceProvider so that it will be called when the renderer
+// normally sends a request to the browser process. But for a unittest
+// setup it can be intercepted by this class.
+class FakeRemoteFrameHost : public mojom::blink::RemoteFrameHost {
+ public:
+  FakeRemoteFrameHost() = default;
+
+  void Init(blink::AssociatedInterfaceProvider* provider);
+  void SetInheritedEffectiveTouchAction(cc::TouchAction touch_action) override;
+  void VisibilityChanged(mojom::blink::FrameVisibility visibility) override;
+
+ private:
+  void BindFrameHostReceiver(mojo::ScopedInterfaceEndpointHandle handle);
+
+  mojo::AssociatedReceiver<mojom::blink::RemoteFrameHost> receiver_{this};
+};
+
+}  // namespace blink
+
+#endif  // THIRD_PARTY_BLINK_RENDERER_CORE_TESTING_FAKE_REMOTE_FRAME_HOST_H_
diff --git a/third_party/blink/renderer/core/testing/test_document_interface_broker.cc b/third_party/blink/renderer/core/testing/test_document_interface_broker.cc
deleted file mode 100644
index 9ad8a94..0000000
--- a/third_party/blink/renderer/core/testing/test_document_interface_broker.cc
+++ /dev/null
@@ -1,28 +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 "third_party/blink/renderer/core/testing/test_document_interface_broker.h"
-
-#include <utility>
-
-namespace blink {
-
-TestDocumentInterfaceBroker::TestDocumentInterfaceBroker(
-    mojom::blink::DocumentInterfaceBroker* document_interface_broker,
-    mojo::PendingReceiver<mojom::blink::DocumentInterfaceBroker> receiver)
-    : real_broker_(document_interface_broker),
-      receiver_(this, std::move(receiver)) {}
-
-TestDocumentInterfaceBroker::~TestDocumentInterfaceBroker() {}
-
-mojom::blink::DocumentInterfaceBroker*
-TestDocumentInterfaceBroker::GetForwardingInterface() {
-  return real_broker_;
-}
-
-void TestDocumentInterfaceBroker::Flush() {
-  receiver_.FlushForTesting();
-}
-
-}  // namespace blink
diff --git a/third_party/blink/renderer/core/testing/test_document_interface_broker.h b/third_party/blink/renderer/core/testing/test_document_interface_broker.h
deleted file mode 100644
index 5786f31..0000000
--- a/third_party/blink/renderer/core/testing/test_document_interface_broker.h
+++ /dev/null
@@ -1,34 +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 THIRD_PARTY_BLINK_RENDERER_CORE_TESTING_TEST_DOCUMENT_INTERFACE_BROKER_H_
-#define THIRD_PARTY_BLINK_RENDERER_CORE_TESTING_TEST_DOCUMENT_INTERFACE_BROKER_H_
-
-#include "mojo/public/cpp/bindings/pending_receiver.h"
-#include "mojo/public/cpp/bindings/receiver.h"
-#include "third_party/blink/public/mojom/frame/document_interface_broker.mojom-blink-test-utils.h"
-
-namespace blink {
-
-// This class can be subclassed to override specific methods of
-// LocalFrameClient's DocumentInterfaceBroker in tests. The rest of the calls
-// will be forwarded to the implementation passed to the constructor (typically
-// returned by LocalFrameClient::GetDocumentInterfaceBroker()).
-class TestDocumentInterfaceBroker
-    : public mojom::blink::DocumentInterfaceBrokerInterceptorForTesting {
- public:
-  TestDocumentInterfaceBroker(
-      mojom::blink::DocumentInterfaceBroker* document_interface_broker,
-      mojo::PendingReceiver<mojom::blink::DocumentInterfaceBroker> receiver);
-  ~TestDocumentInterfaceBroker() override;
-  mojom::blink::DocumentInterfaceBroker* GetForwardingInterface() override;
-  void Flush();
-
- private:
-  mojom::blink::DocumentInterfaceBroker* real_broker_;
-  mojo::Receiver<mojom::blink::DocumentInterfaceBroker> receiver_;
-};
-}  // namespace blink
-
-#endif  // THIRD_PARTY_BLINK_RENDERER_CORE_TESTING_TEST_DOCUMENT_INTERFACE_BROKER_H_
diff --git a/third_party/blink/renderer/core/typed_arrays/array_buffer/array_buffer_contents.cc b/third_party/blink/renderer/core/typed_arrays/array_buffer/array_buffer_contents.cc
index 4bdb167..a868934 100644
--- a/third_party/blink/renderer/core/typed_arrays/array_buffer/array_buffer_contents.cc
+++ b/third_party/blink/renderer/core/typed_arrays/array_buffer/array_buffer_contents.cc
@@ -36,18 +36,12 @@
 
 ArrayBufferContents::ArrayBufferContents(void* data,
                                          size_t length,
-                                         DataDeleter deleter,
-                                         SharingType is_shared) {
+                                         DataDeleter deleter) {
   if (!data) {
     return;
   }
-  if (is_shared == kNotShared) {
-    backing_store_ =
-        v8::ArrayBuffer::NewBackingStore(data, length, deleter, nullptr);
-  } else {
-    backing_store_ =
-        v8::SharedArrayBuffer::NewBackingStore(data, length, deleter, nullptr);
-  }
+  backing_store_ =
+      v8::ArrayBuffer::NewBackingStore(data, length, deleter, nullptr);
 }
 
 ArrayBufferContents::ArrayBufferContents(
diff --git a/third_party/blink/renderer/core/typed_arrays/array_buffer/array_buffer_contents.h b/third_party/blink/renderer/core/typed_arrays/array_buffer/array_buffer_contents.h
index 166aa9a9..a597d88 100644
--- a/third_party/blink/renderer/core/typed_arrays/array_buffer/array_buffer_contents.h
+++ b/third_party/blink/renderer/core/typed_arrays/array_buffer/array_buffer_contents.h
@@ -63,10 +63,7 @@
                       size_t element_byte_size,
                       SharingType is_shared,
                       InitializationPolicy);
-  ArrayBufferContents(void* data,
-                      size_t length,
-                      DataDeleter deleter,
-                      SharingType is_shared);
+  ArrayBufferContents(void* data, size_t length, DataDeleter deleter);
   ArrayBufferContents(ArrayBufferContents&&) = default;
   explicit ArrayBufferContents(std::shared_ptr<v8::BackingStore> backing_store);
 
diff --git a/third_party/blink/renderer/modules/accessibility/ax_node_object.cc b/third_party/blink/renderer/modules/accessibility/ax_node_object.cc
index d5c87ce..45c76003 100644
--- a/third_party/blink/renderer/modules/accessibility/ax_node_object.cc
+++ b/third_party/blink/renderer/modules/accessibility/ax_node_object.cc
@@ -421,8 +421,8 @@
   if (!current_element)
     return false;
   if (IsHTMLTableCellElement(*current_element))
-    return IsHTMLTableRowElement(*parent_node);
-  if (IsHTMLTableRowElement(*current_element))
+    return IsA<HTMLTableRowElement>(*parent_node);
+  if (IsA<HTMLTableRowElement>(*current_element))
     return IsHTMLTableSectionElement(parent_html_element);
 
   // In case of ListboxRole and its child, ListBoxOptionRole, inheritance of
@@ -637,7 +637,7 @@
     return IsDataTable() ? ax::mojom::Role::kTable
                          : ax::mojom::Role::kLayoutTable;
   }
-  if (IsHTMLTableRowElement(*GetNode()))
+  if (IsA<HTMLTableRowElement>(*GetNode()))
     return DetermineTableRowRole();
   if (IsHTMLTableCellElement(*GetNode()))
     return DetermineTableCellRole();
diff --git a/third_party/blink/renderer/modules/accessibility/ax_object_cache_impl.cc b/third_party/blink/renderer/modules/accessibility/ax_object_cache_impl.cc
index 456153a..c850c45 100644
--- a/third_party/blink/renderer/modules/accessibility/ax_object_cache_impl.cc
+++ b/third_party/blink/renderer/modules/accessibility/ax_object_cache_impl.cc
@@ -615,7 +615,7 @@
   // removal of a <tr> or <td>.
   // Get parent table from DOM, because AXObject/layout tree are incomplete.
   ContainerNode* containing_table = nullptr;
-  if (IsHTMLTableCellElement(node) || IsHTMLTableRowElement(node))
+  if (IsHTMLTableCellElement(node) || IsA<HTMLTableRowElement>(node))
     containing_table = FindParentTable(node);
 
   if (containing_table) {
diff --git a/third_party/blink/renderer/modules/indexeddb/idb_database_error.h b/third_party/blink/renderer/modules/indexeddb/idb_database_error.h
index a1d390f9..e7b6644 100644
--- a/third_party/blink/renderer/modules/indexeddb/idb_database_error.h
+++ b/third_party/blink/renderer/modules/indexeddb/idb_database_error.h
@@ -30,18 +30,16 @@
 #define THIRD_PARTY_BLINK_RENDERER_MODULES_INDEXEDDB_IDB_DATABASE_ERROR_H_
 
 #include "third_party/blink/renderer/modules/modules_export.h"
+#include "third_party/blink/renderer/platform/bindings/exception_code.h"
 #include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
 
 namespace blink {
 
 class IDBDatabaseError {
-  // The code refers to the DOMException error names table here:
-  // https://heycam.github.io/webidl/#idl-DOMException-error-names
-  // TODO(dmurph): Make |code| a DOMExceptionCode
  public:
-  explicit IDBDatabaseError(int32_t code) : code_(code) {}
+  explicit IDBDatabaseError(DOMExceptionCode code) : code_(code) {}
 
-  IDBDatabaseError(int32_t code, String message)
+  IDBDatabaseError(DOMExceptionCode code, String message)
       : code_(code), message_(std::move(message)) {}
 
   IDBDatabaseError(const IDBDatabaseError& error) = default;
@@ -50,11 +48,11 @@
 
   IDBDatabaseError& operator=(const IDBDatabaseError& error) = default;
 
-  int32_t Code() const { return code_; }
+  DOMExceptionCode Code() const { return code_; }
   const String& Message() const { return message_; }
 
  private:
-  int32_t code_;
+  DOMExceptionCode code_;
   String message_;
 };
 
diff --git a/third_party/blink/renderer/modules/indexeddb/indexed_db_database_callbacks_impl.cc b/third_party/blink/renderer/modules/indexeddb/indexed_db_database_callbacks_impl.cc
index 083a560..9263a35 100644
--- a/third_party/blink/renderer/modules/indexeddb/indexed_db_database_callbacks_impl.cc
+++ b/third_party/blink/renderer/modules/indexeddb/indexed_db_database_callbacks_impl.cc
@@ -11,6 +11,7 @@
 #include "third_party/blink/renderer/modules/indexeddb/idb_observation.h"
 #include "third_party/blink/renderer/modules/indexeddb/indexed_db_blink_mojom_traits.h"
 #include "third_party/blink/renderer/modules/indexeddb/web_idb_database_callbacks.h"
+#include "third_party/blink/renderer/platform/bindings/exception_code.h"
 
 namespace blink {
 
@@ -32,7 +33,9 @@
 void IndexedDBDatabaseCallbacksImpl::Abort(int64_t transaction_id,
                                            int32_t code,
                                            const String& message) {
-  callbacks_->OnAbort(transaction_id, IDBDatabaseError(code, message));
+  callbacks_->OnAbort(
+      transaction_id,
+      IDBDatabaseError(static_cast<DOMExceptionCode>(code), message));
 }
 
 void IndexedDBDatabaseCallbacksImpl::Complete(int64_t transaction_id) {
diff --git a/third_party/blink/renderer/modules/webgpu/gpu_buffer.cc b/third_party/blink/renderer/modules/webgpu/gpu_buffer.cc
index 1650037..20a99ca 100644
--- a/third_party/blink/renderer/modules/webgpu/gpu_buffer.cc
+++ b/third_party/blink/renderer/modules/webgpu/gpu_buffer.cc
@@ -61,12 +61,11 @@
 DOMArrayBuffer* CreateArrayBufferForMappedData(void* data, size_t data_length) {
   DCHECK(data);
 
-  ArrayBufferContents contents(
-      data, data_length,
-      [](void* data, size_t length, void* info) {
-        // DataDeleter does nothing because Dawn wire owns the memory.
-      },
-      ArrayBufferContents::SharingType::kNotShared);
+  ArrayBufferContents contents(data, data_length,
+                               [](void* data, size_t length, void* info) {
+                                 // DataDeleter does nothing because Dawn wire
+                                 // owns the memory.
+                               });
 
   return DOMArrayBuffer::Create(contents);
 }
diff --git a/third_party/blink/renderer/platform/bindings/dom_data_store.cc b/third_party/blink/renderer/platform/bindings/dom_data_store.cc
index e0b9911..5e10e70 100644
--- a/third_party/blink/renderer/platform/bindings/dom_data_store.cc
+++ b/third_party/blink/renderer/platform/bindings/dom_data_store.cc
@@ -10,16 +10,20 @@
     : is_main_world_(is_main_world) {}
 
 void DOMDataStore::Dispose() {
-  for (auto& it : wrapper_map_) {
+  for (const auto& it : wrapper_map_) {
     // Explicitly reset references so that a following V8 GC will not find them
     // and treat them as roots. There's optimizations (see
     // EmbedderHeapTracer::IsRootForNonTracingGC) that would not treat them as
     // roots and then Blink would not be able to find and remove them from a DOM
     // world. Explicitly resetting on disposal avoids that problem
-    it.value.Clear();
+    it.value->ref.Clear();
   }
 }
 
+void DOMDataStore::WrappedReference::Trace(Visitor* visitor) {
+  visitor->Trace(ref);
+}
+
 void DOMDataStore::Trace(Visitor* visitor) {
   visitor->Trace(wrapper_map_);
 }
diff --git a/third_party/blink/renderer/platform/bindings/dom_data_store.h b/third_party/blink/renderer/platform/bindings/dom_data_store.h
index 0d8cfbc5..975c6b56 100644
--- a/third_party/blink/renderer/platform/bindings/dom_data_store.h
+++ b/third_party/blink/renderer/platform/bindings/dom_data_store.h
@@ -35,7 +35,6 @@
 #include "base/optional.h"
 #include "third_party/blink/renderer/platform/bindings/dom_wrapper_world.h"
 #include "third_party/blink/renderer/platform/bindings/script_wrappable.h"
-#include "third_party/blink/renderer/platform/bindings/trace_wrapper_v8_reference.h"
 #include "third_party/blink/renderer/platform/bindings/wrapper_type_info.h"
 #include "third_party/blink/renderer/platform/heap/unified_heap_marking_visitor.h"
 #include "third_party/blink/renderer/platform/wtf/allocator/allocator.h"
@@ -119,7 +118,7 @@
       return object->MainWorldWrapper(isolate);
     auto it = wrapper_map_.find(object);
     if (it != wrapper_map_.end())
-      return it->value.NewLocal(isolate);
+      return it->value->ref.NewLocal(isolate);
     return v8::Local<v8::Object>();
   }
 
@@ -133,12 +132,13 @@
       return object->SetWrapper(isolate, wrapper_type_info, wrapper);
 
     auto result = wrapper_map_.insert(
-        object, TraceWrapperV8Reference<v8::Object>(isolate, wrapper));
+        object, MakeGarbageCollected<WrappedReference>(isolate, wrapper));
     if (LIKELY(result.is_new_entry)) {
-      wrapper_type_info->ConfigureWrapper(&result.stored_value->value.Get());
+      wrapper_type_info->ConfigureWrapper(
+          &result.stored_value->value->ref.Get());
     } else {
-      DCHECK(!result.stored_value->value.IsEmpty());
-      wrapper = result.stored_value->value.NewLocal(isolate);
+      DCHECK(!result.stored_value->value->ref.IsEmpty());
+      wrapper = result.stored_value->value->ref.NewLocal(isolate);
     }
     return result.is_new_entry;
   }
@@ -149,8 +149,8 @@
     DCHECK(!is_main_world_);
     const auto& it = wrapper_map_.find(object);
     if (it != wrapper_map_.end()) {
-      if (it->value.Get() == handle) {
-        it->value.Clear();
+      if (it->value->ref.Get() == handle) {
+        it->value->ref.Clear();
         wrapper_map_.erase(it);
         return true;
       }
@@ -164,7 +164,7 @@
       return object->SetReturnValue(return_value);
     auto it = wrapper_map_.find(object);
     if (it != wrapper_map_.end()) {
-      return_value.Set(it->value.Get());
+      return_value.Set(it->value->ref.Get());
       return true;
     }
     return false;
@@ -198,10 +198,24 @@
     return wrappable->IsEqualTo(holder);
   }
 
+  // Wrapper around TraceWrapperV8Reference to allow use in ephemeron hash map
+  // below.
+  class PLATFORM_EXPORT WrappedReference final
+      : public GarbageCollected<WrappedReference> {
+   public:
+    WrappedReference() = default;
+    WrappedReference(v8::Isolate* isolate, v8::Local<v8::Object> handle)
+        : ref(isolate, handle) {}
+
+    virtual void Trace(Visitor*);
+
+    TraceWrapperV8Reference<v8::Object> ref;
+  };
+
   bool is_main_world_;
-  // Ephemeron map: V8 wrapper will be kept alive as long as ScriptWrappable is.
-  HeapHashMap<WeakMember<const ScriptWrappable>,
-              TraceWrapperV8Reference<v8::Object>>
+  // Ephemeron map: WrappedReference will be kept alive as long as
+  // ScriptWrappable is alive.
+  HeapHashMap<WeakMember<const ScriptWrappable>, Member<WrappedReference>>
       wrapper_map_;
 
   DISALLOW_COPY_AND_ASSIGN(DOMDataStore);
diff --git a/third_party/blink/renderer/platform/runtime_enabled_features.json5 b/third_party/blink/renderer/platform/runtime_enabled_features.json5
index 8d7a5004..865ae4ed 100644
--- a/third_party/blink/renderer/platform/runtime_enabled_features.json5
+++ b/third_party/blink/renderer/platform/runtime_enabled_features.json5
@@ -773,12 +773,12 @@
       origin_trial_feature_name: "HrefTranslate",
       status: "experimental",
     },
-    // TODO(911943): HTML Imports is temporarily being left enabled after
-    // the planned removal milestone of M80.
+    // TODO(937746): Web Components v0 is disabled by default, and will be
+    // removed after M87.
     {
       name: "HTMLImports",
       origin_trial_feature_name: "WebComponentsV0",
-      status: "stable",
+      status: "test",
     },
     {
       name: "IDBObserver",
@@ -927,6 +927,7 @@
     },
     {
       name: "MathMLCore",
+      status:"test",
     },
     {
       name:"MeasureMemory",
diff --git a/third_party/blink/web_tests/NeverFixTests b/third_party/blink/web_tests/NeverFixTests
index 654c607a..ec3b3fd 100644
--- a/third_party/blink/web_tests/NeverFixTests
+++ b/third_party/blink/web_tests/NeverFixTests
@@ -1944,9 +1944,6 @@
 virtual/speech-with-unified-autoplay/external/wpt/speech-api/SpeechRecognition-stop-manual.https.html [ Skip ]
 [ Retina ] external/wpt/pointerevents/pointerevent_touch-action-table-test_touch-manual.html [ Skip ]
 
-# MathML is not implemented.
-crbug.com/6606 external/wpt/mathml [ Skip ]
-
 # ==== Tests incompatible with the default WPT Origin Isolation start here ==VV
 # The section below lists web tests that are incompatible with the WPT Origin
 # Isolation mode of Site Isolation (see
diff --git a/third_party/blink/web_tests/SlowTests b/third_party/blink/web_tests/SlowTests
index 327b3472..9bee405 100644
--- a/third_party/blink/web_tests/SlowTests
+++ b/third_party/blink/web_tests/SlowTests
@@ -667,4 +667,14 @@
 # Slow when using Vulkan.
 crbug.com/994331 compositing/lots-of-img-layers.html [ Slow ]
 
-crbug.com/1020379 http/tests/devtools/a11y-axe-core/performance/landing-page-a11y-test.js [ Slow ]
\ No newline at end of file
+crbug.com/1020379 http/tests/devtools/a11y-axe-core/performance/landing-page-a11y-test.js [ Slow ]
+
+# Slow when using Vulkan on Linux with Intel
+crbug.com/1018884 [ Linux ] animations/change-keyframes.html [ Slow ]
+crbug.com/1018884 [ Linux ] animations/composition/word-spacing-composition.html [ Slow ]
+crbug.com/1018884 [ Linux ] animations/custom-properties/color-type-interpolation.html [ Slow ]
+crbug.com/1018884 [ Linux ] animations/direction-and-fill/animation-direction-alternate.html [ Slow ]
+crbug.com/1018884 [ Linux ] animations/events/compositor-start-event-timing.html [ Slow ]
+crbug.com/1018884 [ Linux ] animations/interpolation/webkit-mask-box-image-source-interpolation.html [ Slow ]
+crbug.com/1018884 [ Linux ] animations/prefixed/keyframes-unprefixed-01.html [ Slow ]
+crbug.com/1018884 [ Linux ] animations/responsive/animations-responsive-columnCount.html [ Slow ]
\ No newline at end of file
diff --git a/third_party/blink/web_tests/TestExpectations b/third_party/blink/web_tests/TestExpectations
index c135618..dcb0aa6 100644
--- a/third_party/blink/web_tests/TestExpectations
+++ b/third_party/blink/web_tests/TestExpectations
@@ -1486,6 +1486,210 @@
 
 # ====== LayoutNG-only failures until here ======
 
+# ====== MathMLCore-only tests from here ======
+
+crbug.com/6606 external/wpt/mathml/presentation-markup/direction/direction-006.html [ Failure ]
+crbug.com/6606 external/wpt/mathml/presentation-markup/direction/direction-007.html [ Failure ]
+crbug.com/6606 external/wpt/mathml/presentation-markup/direction/direction-008.html [ Failure ]
+crbug.com/6606 external/wpt/mathml/presentation-markup/direction/direction-009.html [ Failure ]
+crbug.com/6606 external/wpt/mathml/presentation-markup/direction/direction-010.html [ Failure ]
+crbug.com/6606 external/wpt/mathml/presentation-markup/direction/direction-overall.html [ Failure ]
+crbug.com/6606 external/wpt/mathml/presentation-markup/direction/direction-token.html [ Failure ]
+crbug.com/6606 external/wpt/mathml/presentation-markup/direction/direction.html [ Failure ]
+crbug.com/6606 external/wpt/mathml/presentation-markup/fractions/frac-1.html [ Failure ]
+crbug.com/6606 external/wpt/mathml/presentation-markup/fractions/frac-bar-001.html [ Failure ]
+crbug.com/6606 external/wpt/mathml/presentation-markup/fractions/frac-bar-002.html [ Failure ]
+crbug.com/6606 external/wpt/mathml/presentation-markup/fractions/frac-color-001.html [ Failure ]
+crbug.com/6606 external/wpt/mathml/presentation-markup/fractions/frac-color-002.html [ Failure ]
+crbug.com/6606 external/wpt/mathml/presentation-markup/fractions/frac-created-dynamically-2.html [ Failure ]
+crbug.com/6606 external/wpt/mathml/presentation-markup/fractions/frac-created-dynamically-3.html [ Failure ]
+crbug.com/6606 external/wpt/mathml/presentation-markup/fractions/frac-created-dynamically.html [ Failure ]
+crbug.com/6606 external/wpt/mathml/presentation-markup/fractions/frac-default-padding.html [ Failure ]
+crbug.com/6606 external/wpt/mathml/presentation-markup/fractions/frac-legacy-bevelled-attribute.tentative.html [ Failure ]
+crbug.com/6606 external/wpt/mathml/presentation-markup/fractions/frac-linethickness-001.html [ Failure ]
+crbug.com/6606 external/wpt/mathml/presentation-markup/fractions/frac-linethickness-002.html [ Failure ]
+crbug.com/6606 external/wpt/mathml/presentation-markup/fractions/frac-linethickness-003.html [ Failure ]
+crbug.com/6606 external/wpt/mathml/presentation-markup/fractions/frac-linethickness-004.html [ Failure ]
+crbug.com/6606 external/wpt/mathml/presentation-markup/fractions/frac-mrow-001.html [ Failure ]
+crbug.com/6606 external/wpt/mathml/presentation-markup/fractions/frac-numalign-denomalign-001.html [ Failure ]
+crbug.com/6606 external/wpt/mathml/presentation-markup/fractions/frac-parameters-1.html [ Failure ]
+crbug.com/6606 external/wpt/mathml/presentation-markup/fractions/frac-parameters-2.html [ Failure ]
+crbug.com/6606 external/wpt/mathml/presentation-markup/fractions/frac-parameters-gap-001.html [ Failure ]
+crbug.com/6606 external/wpt/mathml/presentation-markup/fractions/frac-parameters-gap-002.html [ Failure ]
+crbug.com/6606 external/wpt/mathml/presentation-markup/fractions/frac-parameters-gap-003.html [ Failure ]
+crbug.com/6606 external/wpt/mathml/presentation-markup/fractions/frac-parameters-gap-004.html [ Failure ]
+crbug.com/6606 external/wpt/mathml/presentation-markup/fractions/frac-parameters-gap-005.html [ Failure ]
+crbug.com/6606 external/wpt/mathml/presentation-markup/fractions/frac-parameters-gap-006.html [ Failure ]
+crbug.com/6606 external/wpt/mathml/presentation-markup/fractions/frac-rendering-from-in-flow.html [ Failure ]
+crbug.com/6606 external/wpt/mathml/presentation-markup/fractions/frac-visibility-001.html [ Failure ]
+crbug.com/6606 external/wpt/mathml/presentation-markup/menclose/legacy-menclose-radical-notation.html [ Failure ]
+crbug.com/6606 external/wpt/mathml/presentation-markup/mrow/inferred-mrow-baseline.html [ Failure ]
+crbug.com/6606 external/wpt/mathml/presentation-markup/mrow/inferred-mrow-stretchy.html [ Failure ]
+crbug.com/6606 external/wpt/mathml/presentation-markup/mrow/legacy-mfenced-element-001.html [ Failure ]
+crbug.com/6606 external/wpt/mathml/presentation-markup/mrow/legacy-mrow-like-elements-001.html [ Failure ]
+crbug.com/6606 external/wpt/mathml/presentation-markup/mrow/legacy-mrow-like-elements-002.html [ Failure ]
+crbug.com/6606 external/wpt/mathml/presentation-markup/mrow/legacy-mstyle-attributes.html [ Failure ]
+crbug.com/6606 external/wpt/mathml/presentation-markup/operators/embellished-operator-001.html [ Failure ]
+crbug.com/6606 external/wpt/mathml/presentation-markup/operators/embellished-operator-002.html [ Failure ]
+crbug.com/6606 external/wpt/mathml/presentation-markup/operators/mo-axis-height-1.html [ Failure ]
+crbug.com/6606 external/wpt/mathml/presentation-markup/operators/mo-form-dynamic.html [ Failure ]
+crbug.com/6606 external/wpt/mathml/presentation-markup/operators/mo-form-fallback.html [ Failure ]
+crbug.com/6606 external/wpt/mathml/presentation-markup/operators/mo-form-minus-plus.html [ Failure ]
+crbug.com/6606 external/wpt/mathml/presentation-markup/operators/mo-form.html [ Failure ]
+crbug.com/6606 external/wpt/mathml/presentation-markup/operators/mo-movablelimits-default.html [ Failure ]
+crbug.com/6606 external/wpt/mathml/presentation-markup/operators/mo-movablelimits-dynamic.html [ Failure ]
+crbug.com/6606 external/wpt/mathml/presentation-markup/operators/mo-movablelimits-from-in-flow.html [ Failure ]
+crbug.com/6606 external/wpt/mathml/presentation-markup/operators/mo-movablelimits.html [ Failure ]
+crbug.com/6606 external/wpt/mathml/presentation-markup/operators/mo-paint-lspace-rspace.html [ Failure ]
+crbug.com/6606 external/wpt/mathml/presentation-markup/operators/operator-dictionary-001.html [ Failure ]
+crbug.com/6606 external/wpt/mathml/presentation-markup/operators/operator-dictionary-002.html [ Failure ]
+crbug.com/6606 external/wpt/mathml/presentation-markup/operators/operator-dictionary-003.html [ Failure ]
+crbug.com/6606 external/wpt/mathml/presentation-markup/operators/operator-dictionary-004.html [ Failure ]
+crbug.com/6606 external/wpt/mathml/presentation-markup/operators/operator-dictionary-005.html [ Failure ]
+crbug.com/6606 external/wpt/mathml/presentation-markup/operators/operator-dictionary-006.html [ Failure ]
+crbug.com/6606 external/wpt/mathml/presentation-markup/radicals/radical-rendering-from-in-flow.html [ Failure ]
+crbug.com/6606 external/wpt/mathml/presentation-markup/radicals/root-parameters-1.html [ Failure ]
+crbug.com/6606 external/wpt/mathml/presentation-markup/scripts/cramped-001.html [ Failure ]
+crbug.com/6606 external/wpt/mathml/presentation-markup/scripts/empty-underover.html [ Failure ]
+crbug.com/6606 external/wpt/mathml/presentation-markup/scripts/subsup-1.html [ Failure ]
+crbug.com/6606 external/wpt/mathml/presentation-markup/scripts/subsup-2.html [ Failure ]
+crbug.com/6606 external/wpt/mathml/presentation-markup/scripts/subsup-3.html [ Failure ]
+crbug.com/6606 external/wpt/mathml/presentation-markup/scripts/subsup-4.html [ Failure ]
+crbug.com/6606 external/wpt/mathml/presentation-markup/scripts/subsup-5.html [ Failure ]
+crbug.com/6606 external/wpt/mathml/presentation-markup/scripts/subsup-legacy-scriptshift-attributes-001.tentative.html [ Failure ]
+crbug.com/6606 external/wpt/mathml/presentation-markup/scripts/subsup-parameters-1.html [ Failure ]
+crbug.com/6606 external/wpt/mathml/presentation-markup/scripts/subsup-parameters-2.html [ Failure ]
+crbug.com/6606 external/wpt/mathml/presentation-markup/scripts/underover-1.html [ Failure ]
+crbug.com/6606 external/wpt/mathml/presentation-markup/scripts/underover-legacy-align-attribute-001.html [ Failure ]
+crbug.com/6606 external/wpt/mathml/presentation-markup/scripts/underover-parameters-1.html [ Failure ]
+crbug.com/6606 external/wpt/mathml/presentation-markup/scripts/underover-parameters-2.html [ Failure ]
+crbug.com/6606 external/wpt/mathml/presentation-markup/scripts/underover-parameters-3.html [ Failure ]
+crbug.com/6606 external/wpt/mathml/presentation-markup/scripts/underover-parameters-4.html [ Failure ]
+crbug.com/6606 external/wpt/mathml/presentation-markup/spaces/mspace-children.html [ Failure ]
+crbug.com/6606 external/wpt/mathml/presentation-markup/spaces/space-1.html [ Failure ]
+crbug.com/6606 external/wpt/mathml/presentation-markup/spaces/space-2.html [ Failure ]
+crbug.com/6606 external/wpt/mathml/presentation-markup/spaces/space-like-001.html [ Failure ]
+crbug.com/6606 external/wpt/mathml/presentation-markup/spaces/space-like-002.html [ Failure ]
+crbug.com/6606 external/wpt/mathml/presentation-markup/spaces/space-like-003.html [ Failure ]
+crbug.com/6606 external/wpt/mathml/presentation-markup/spaces/space-like-004.html [ Failure ]
+crbug.com/6606 external/wpt/mathml/presentation-markup/tables/table-001.html [ Failure ]
+crbug.com/6606 external/wpt/mathml/presentation-markup/tables/table-002.html [ Failure ]
+crbug.com/6606 external/wpt/mathml/presentation-markup/tables/table-axis-height.html [ Failure ]
+crbug.com/6606 external/wpt/mathml/relations/css-styling/attribute-mapping-001.html [ Failure ]
+crbug.com/6606 external/wpt/mathml/relations/css-styling/attribute-mapping-002.html [ Failure ]
+crbug.com/6606 external/wpt/mathml/relations/css-styling/color-002.html [ Failure ]
+crbug.com/6606 external/wpt/mathml/relations/css-styling/color-003.html [ Failure ]
+crbug.com/6606 external/wpt/mathml/relations/css-styling/color-004.html [ Failure ]
+crbug.com/6606 external/wpt/mathml/relations/css-styling/display-1.html [ Failure ]
+crbug.com/6606 external/wpt/mathml/relations/css-styling/display-contents.html [ Failure ]
+crbug.com/6606 external/wpt/mathml/relations/css-styling/displaystyle-011.html [ Failure ]
+crbug.com/6606 external/wpt/mathml/relations/css-styling/displaystyle-012.html [ Failure ]
+crbug.com/6606 external/wpt/mathml/relations/css-styling/displaystyle-013.html [ Failure ]
+crbug.com/6606 external/wpt/mathml/relations/css-styling/displaystyle-014.html [ Failure ]
+crbug.com/6606 external/wpt/mathml/relations/css-styling/displaystyle-015.html [ Failure ]
+crbug.com/6606 external/wpt/mathml/relations/css-styling/displaystyle-1.html [ Failure ]
+crbug.com/6606 external/wpt/mathml/relations/css-styling/displaystyle-2.html [ Failure ]
+crbug.com/6606 external/wpt/mathml/relations/css-styling/dynamic-dir-1.html [ Failure ]
+crbug.com/6606 external/wpt/mathml/relations/css-styling/ignored-properties-001.html [ Failure ]
+crbug.com/6606 external/wpt/mathml/relations/css-styling/lengths-1.html [ Failure ]
+crbug.com/6606 external/wpt/mathml/relations/css-styling/lengths-2.html [ Failure ]
+crbug.com/6606 external/wpt/mathml/relations/css-styling/mathbackground-001.html [ Failure ]
+crbug.com/6606 external/wpt/mathml/relations/css-styling/mathbackground-002.html [ Failure ]
+crbug.com/6606 external/wpt/mathml/relations/css-styling/mathbackground-003.html [ Failure ]
+crbug.com/6606 external/wpt/mathml/relations/css-styling/mathbackground-004.html [ Failure ]
+crbug.com/6606 external/wpt/mathml/relations/css-styling/mathcolor-001.html [ Failure ]
+crbug.com/6606 external/wpt/mathml/relations/css-styling/mathcolor-002.html [ Failure ]
+crbug.com/6606 external/wpt/mathml/relations/css-styling/mathcolor-003.html [ Failure ]
+crbug.com/6606 external/wpt/mathml/relations/css-styling/mathcolor-004.html [ Failure ]
+crbug.com/6606 external/wpt/mathml/relations/css-styling/mathsize-attribute-css-keywords.html [ Failure ]
+crbug.com/6606 external/wpt/mathml/relations/css-styling/mathsize-attribute-legacy-values.html [ Failure ]
+crbug.com/6606 external/wpt/mathml/relations/css-styling/mathsize-attribute.html [ Failure ]
+crbug.com/6606 external/wpt/mathml/relations/css-styling/mathvariant-auto.html [ Failure ]
+crbug.com/6606 external/wpt/mathml/relations/css-styling/mathvariant-bold-fraktur.html [ Failure ]
+crbug.com/6606 external/wpt/mathml/relations/css-styling/mathvariant-bold-italic.html [ Failure ]
+crbug.com/6606 external/wpt/mathml/relations/css-styling/mathvariant-bold-sans-serif.html [ Failure ]
+crbug.com/6606 external/wpt/mathml/relations/css-styling/mathvariant-bold-script.html [ Failure ]
+crbug.com/6606 external/wpt/mathml/relations/css-styling/mathvariant-bold.html [ Failure ]
+crbug.com/6606 external/wpt/mathml/relations/css-styling/mathvariant-case-sensitivity.html [ Failure ]
+crbug.com/6606 external/wpt/mathml/relations/css-styling/mathvariant-double-struck.html [ Failure ]
+crbug.com/6606 external/wpt/mathml/relations/css-styling/mathvariant-fraktur.html [ Failure ]
+crbug.com/6606 external/wpt/mathml/relations/css-styling/mathvariant-initial.html [ Failure ]
+crbug.com/6606 external/wpt/mathml/relations/css-styling/mathvariant-italic.html [ Failure ]
+crbug.com/6606 external/wpt/mathml/relations/css-styling/mathvariant-looped.html [ Failure ]
+crbug.com/6606 external/wpt/mathml/relations/css-styling/mathvariant-monospace.html [ Failure ]
+crbug.com/6606 external/wpt/mathml/relations/css-styling/mathvariant-sans-serif-bold-italic.html [ Failure ]
+crbug.com/6606 external/wpt/mathml/relations/css-styling/mathvariant-sans-serif-italic.html [ Failure ]
+crbug.com/6606 external/wpt/mathml/relations/css-styling/mathvariant-sans-serif.html [ Failure ]
+crbug.com/6606 external/wpt/mathml/relations/css-styling/mathvariant-script.html [ Failure ]
+crbug.com/6606 external/wpt/mathml/relations/css-styling/mathvariant-stretched.html [ Failure ]
+crbug.com/6606 external/wpt/mathml/relations/css-styling/mathvariant-tailed.html [ Failure ]
+crbug.com/6606 external/wpt/mathml/relations/css-styling/not-participating-to-parent-layout.html [ Failure ]
+crbug.com/6606 external/wpt/mathml/relations/css-styling/padding-border-margin/border-001.html [ Failure ]
+crbug.com/6606 external/wpt/mathml/relations/css-styling/padding-border-margin/border-002.html [ Failure ]
+crbug.com/6606 external/wpt/mathml/relations/css-styling/padding-border-margin/margin-001.html [ Failure ]
+crbug.com/6606 external/wpt/mathml/relations/css-styling/padding-border-margin/margin-002.html [ Failure ]
+crbug.com/6606 external/wpt/mathml/relations/css-styling/padding-border-margin/padding-001.html [ Failure ]
+crbug.com/6606 external/wpt/mathml/relations/css-styling/padding-border-margin/padding-002.html [ Failure ]
+crbug.com/6606 external/wpt/mathml/relations/css-styling/padding-border-margin/padding-border-margin-001.html [ Failure ]
+crbug.com/6606 external/wpt/mathml/relations/css-styling/visibility-002.html [ Failure ]
+crbug.com/6606 external/wpt/mathml/relations/css-styling/visibility-003.html [ Failure ]
+crbug.com/6606 external/wpt/mathml/relations/css-styling/visibility-004.html [ Failure ]
+crbug.com/6606 external/wpt/mathml/relations/css-styling/width-height-001.html [ Failure ]
+crbug.com/6606 external/wpt/mathml/relations/css-styling/writing-mode/writing-mode-001.html [ Failure ]
+crbug.com/6606 external/wpt/mathml/relations/css-styling/writing-mode/writing-mode-002.html [ Failure ]
+crbug.com/6606 external/wpt/mathml/relations/html5-tree/clipboard-event-handlers.tentative.html [ Failure ]
+crbug.com/6606 external/wpt/mathml/relations/html5-tree/color-attributes-1.html [ Failure ]
+crbug.com/6606 external/wpt/mathml/relations/html5-tree/css-inline-style-interface.tentative.html [ Failure ]
+crbug.com/6606 external/wpt/mathml/relations/html5-tree/display-1.html [ Failure ]
+crbug.com/6606 external/wpt/mathml/relations/html5-tree/display-2.html [ Failure ]
+crbug.com/6606 external/wpt/mathml/relations/html5-tree/href-click-1.html [ Failure ]
+crbug.com/6606 external/wpt/mathml/relations/html5-tree/href-click-2.html [ Failure ]
+crbug.com/6606 external/wpt/mathml/relations/html5-tree/html-or-foreign-element-interfaces.tentative.html [ Failure ]
+crbug.com/6606 external/wpt/mathml/relations/html5-tree/integration-point-1.html [ Failure ]
+crbug.com/6606 external/wpt/mathml/relations/html5-tree/integration-point-2.html [ Failure ]
+crbug.com/6606 external/wpt/mathml/relations/html5-tree/integration-point-4.html [ Failure ]
+crbug.com/6606 external/wpt/mathml/relations/html5-tree/math-global-event-handlers.tentative.html [ Failure ]
+crbug.com/6606 external/wpt/mathml/relations/html5-tree/required-extensions-2.html [ Failure ]
+crbug.com/6606 external/wpt/mathml/relations/html5-tree/tabindex-001.html [ Failure ]
+
+# These tests time out when MathML is not implemented.
+# See https://github.com/mathml-refresh/mathml/issues/167
+crbug.com/6606 external/wpt/mathml/relations/html5-tree/css-inline-style-dynamic.tentative.html [ Timeout ]
+crbug.com/6606 external/wpt/mathml/relations/html5-tree/href-click-3.html [ Timeout ]
+
+# This test fails on macOS.
+crbug.com/6606 [ Mac ] external/wpt/mathml/relations/text-and-math/use-typo-metrics-1.html [ Failure ]
+
+# Tentative tests for new CSS proposals
+crbug.com/626703 external/wpt/css/css-text/text-transform/math/text-transform-math-fraktur-001.tentative.html [ Failure ]
+crbug.com/626703 external/wpt/css/css-text/text-transform/math/text-transform-math-stretched-001.tentative.html [ Failure ]
+crbug.com/626703 external/wpt/css/css-text/text-transform/math/text-transform-math-looped-001.tentative.html [ Failure ]
+crbug.com/626703 external/wpt/css/css-text/text-transform/math/text-transform-math-auto-002.tentative.html [ Failure ]
+crbug.com/626703 external/wpt/css/css-text/text-transform/math/text-transform-math-double-struck-001.tentative.html [ Failure ]
+crbug.com/626703 external/wpt/css/css-text/text-transform/math/text-transform-math-initial-001.tentative.html [ Failure ]
+crbug.com/626703 external/wpt/css/css-text/text-transform/math/text-transform-math-italic-001.tentative.html [ Failure ]
+crbug.com/626703 external/wpt/css/css-text/text-transform/math/text-transform-math-bold-italic-001.tentative.html [ Failure ]
+crbug.com/626703 external/wpt/css/css-text/text-transform/math/text-transform-math-bold-script-001.tentative.html [ Failure ]
+crbug.com/626703 external/wpt/css/css-text/text-transform/math/text-transform-math-tailed-001.tentative.html [ Failure ]
+crbug.com/626703 external/wpt/css/css-text/text-transform/math/text-transform-math-sans-serif-italic-001.tentative.html [ Failure ]
+crbug.com/626703 external/wpt/css/css-text/text-transform/math/text-transform-math-bold-001.tentative.html [ Failure ]
+crbug.com/626703 external/wpt/css/css-text/text-transform/math/text-transform-math-bold-sans-serif-001.tentative.html [ Failure ]
+crbug.com/626703 external/wpt/css/css-text/text-transform/math/text-transform-math-monospace-001.tentative.html [ Failure ]
+crbug.com/626703 external/wpt/css/css-text/text-transform/math/text-transform-math-bold-fraktur-001.tentative.html [ Failure ]
+crbug.com/626703 external/wpt/css/css-text/text-transform/math/text-transform-math-auto-001.tentative.html [ Failure ]
+crbug.com/626703 external/wpt/css/css-text/text-transform/math/text-transform-math-script-001.tentative.html [ Failure ]
+crbug.com/626703 external/wpt/css/css-text/text-transform/math/text-transform-math-sans-serif-bold-italic-001.tentative.html [ Failure ]
+crbug.com/626703 external/wpt/css/css-text/text-transform/math/text-transform-math-sans-serif-001.tentative.html [ Failure ]
+crbug.com/626703 [ Linux ] external/wpt/css/css-fonts/math-script-level-and-math-style/math-script-level-auto-and-math-style-002.tentative.html [ Failure ]
+crbug.com/626703 [ Mac ] external/wpt/css/css-fonts/math-script-level-and-math-style/math-script-level-auto-and-math-style-002.tentative.html [ Failure ]
+crbug.com/626703 [ Win ] external/wpt/css/css-fonts/math-script-level-and-math-style/math-script-level-auto-and-math-style-002.tentative.html [ Failure ]
+crbug.com/626703 [ Linux ] external/wpt/css/css-fonts/math-script-level-and-math-style/math-script-level-auto-and-math-style-003.tentative.html [ Failure ]
+crbug.com/626703 [ Mac ] external/wpt/css/css-fonts/math-script-level-and-math-style/math-script-level-auto-and-math-style-003.tentative.html [ Failure ]
+crbug.com/626703 [ Win ] external/wpt/css/css-fonts/math-script-level-and-math-style/math-script-level-auto-and-math-style-003.tentative.html [ Failure ]
+crbug.com/626703 [ Linux ] external/wpt/css/css-fonts/math-script-level-and-math-style/math-script-level-auto-and-math-style-004.tentative.html [ Failure ]
+crbug.com/626703 [ Mac ] external/wpt/css/css-fonts/math-script-level-and-math-style/math-script-level-auto-and-math-style-004.tentative.html [ Failure ]
+crbug.com/626703 [ Win ] external/wpt/css/css-fonts/math-script-level-and-math-style/math-script-level-auto-and-math-style-004.tentative.html [ Failure ]
+
 # ====== Style team owned tests from here ======
 
 crbug.com/753671 external/wpt/css/css-content/quotes-001.html [ Failure ]
@@ -2748,25 +2952,6 @@
 crbug.com/626703 external/wpt/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/broadcastchannel-success.https.html [ Timeout ]
 crbug.com/626703 [ Win10 ] virtual/omt-worker-fetch/external/wpt/fetch/api/request/destination/fetch-destination-worker.https.html [ Crash ]
 crbug.com/626703 external/wpt/webauthn/idlharness-manual.https.window.js [ Skip ]
-crbug.com/626703 external/wpt/css/css-text/text-transform/math/text-transform-math-fraktur-001.tentative.html [ Failure ]
-crbug.com/626703 external/wpt/css/css-text/text-transform/math/text-transform-math-stretched-001.tentative.html [ Failure ]
-crbug.com/626703 external/wpt/css/css-text/text-transform/math/text-transform-math-looped-001.tentative.html [ Failure ]
-crbug.com/626703 external/wpt/css/css-text/text-transform/math/text-transform-math-auto-002.tentative.html [ Failure ]
-crbug.com/626703 external/wpt/css/css-text/text-transform/math/text-transform-math-double-struck-001.tentative.html [ Failure ]
-crbug.com/626703 external/wpt/css/css-text/text-transform/math/text-transform-math-initial-001.tentative.html [ Failure ]
-crbug.com/626703 external/wpt/css/css-text/text-transform/math/text-transform-math-italic-001.tentative.html [ Failure ]
-crbug.com/626703 external/wpt/css/css-text/text-transform/math/text-transform-math-bold-italic-001.tentative.html [ Failure ]
-crbug.com/626703 external/wpt/css/css-text/text-transform/math/text-transform-math-bold-script-001.tentative.html [ Failure ]
-crbug.com/626703 external/wpt/css/css-text/text-transform/math/text-transform-math-tailed-001.tentative.html [ Failure ]
-crbug.com/626703 external/wpt/css/css-text/text-transform/math/text-transform-math-sans-serif-italic-001.tentative.html [ Failure ]
-crbug.com/626703 external/wpt/css/css-text/text-transform/math/text-transform-math-bold-001.tentative.html [ Failure ]
-crbug.com/626703 external/wpt/css/css-text/text-transform/math/text-transform-math-bold-sans-serif-001.tentative.html [ Failure ]
-crbug.com/626703 external/wpt/css/css-text/text-transform/math/text-transform-math-monospace-001.tentative.html [ Failure ]
-crbug.com/626703 external/wpt/css/css-text/text-transform/math/text-transform-math-bold-fraktur-001.tentative.html [ Failure ]
-crbug.com/626703 external/wpt/css/css-text/text-transform/math/text-transform-math-auto-001.tentative.html [ Failure ]
-crbug.com/626703 external/wpt/css/css-text/text-transform/math/text-transform-math-script-001.tentative.html [ Failure ]
-crbug.com/626703 external/wpt/css/css-text/text-transform/math/text-transform-math-sans-serif-bold-italic-001.tentative.html [ Failure ]
-crbug.com/626703 external/wpt/css/css-text/text-transform/math/text-transform-math-sans-serif-001.tentative.html [ Failure ]
 crbug.com/626703 [ Mac10.12 ] external/wpt/html/semantics/embedded-content/media-elements/track/track-element/track-cues-missed.html [ Timeout ]
 crbug.com/626703 [ Retina ] virtual/at-property/external/wpt/css/css-properties-values-api/registered-property-cssom.html [ Timeout ]
 crbug.com/626703 [ Retina ] external/wpt/fetch/cross-origin-resource-policy/fetch.https.any.worker.html [ Pass Timeout ]
@@ -2796,15 +2981,6 @@
 crbug.com/626703 virtual/cache-storage-sequence/external/wpt/service-workers/service-worker/ready.https.html [ Timeout ]
 crbug.com/626703 virtual/cache-storage-eager-reading/external/wpt/service-workers/service-worker/ready.https.html [ Timeout ]
 crbug.com/626703 virtual/cache-storage-high-priority-match/external/wpt/service-workers/service-worker/ready.https.html [ Timeout ]
-crbug.com/626703 [ Linux ] external/wpt/css/css-fonts/math-script-level-and-math-style/math-script-level-auto-and-math-style-002.tentative.html [ Failure ]
-crbug.com/626703 [ Mac ] external/wpt/css/css-fonts/math-script-level-and-math-style/math-script-level-auto-and-math-style-002.tentative.html [ Failure ]
-crbug.com/626703 [ Win ] external/wpt/css/css-fonts/math-script-level-and-math-style/math-script-level-auto-and-math-style-002.tentative.html [ Failure ]
-crbug.com/626703 [ Linux ] external/wpt/css/css-fonts/math-script-level-and-math-style/math-script-level-auto-and-math-style-003.tentative.html [ Failure ]
-crbug.com/626703 [ Mac ] external/wpt/css/css-fonts/math-script-level-and-math-style/math-script-level-auto-and-math-style-003.tentative.html [ Failure ]
-crbug.com/626703 [ Win ] external/wpt/css/css-fonts/math-script-level-and-math-style/math-script-level-auto-and-math-style-003.tentative.html [ Failure ]
-crbug.com/626703 [ Linux ] external/wpt/css/css-fonts/math-script-level-and-math-style/math-script-level-auto-and-math-style-004.tentative.html [ Failure ]
-crbug.com/626703 [ Mac ] external/wpt/css/css-fonts/math-script-level-and-math-style/math-script-level-auto-and-math-style-004.tentative.html [ Failure ]
-crbug.com/626703 [ Win ] external/wpt/css/css-fonts/math-script-level-and-math-style/math-script-level-auto-and-math-style-004.tentative.html [ Failure ]
 crbug.com/626703 [ Linux ] external/wpt/css/css-sizing/slice-intrinsic-size.html [ Failure ]
 crbug.com/626703 [ Mac ] external/wpt/css/css-sizing/slice-intrinsic-size.html [ Failure ]
 crbug.com/626703 [ Win ] external/wpt/css/css-sizing/slice-intrinsic-size.html [ Failure ]
@@ -5726,3 +5902,9 @@
 
 # Temporarily disabled to land scope changes in DevTools
 crbug.com/963183 http/tests/devtools/sources/debugger-pause/set-return-value.js [ Pass Failure ]
+
+# Adjusting v8 console implementation, pass failure until v8 change lands
+crbug.com/948678 external/wpt/console/idlharness.any.html [ Pass Failure ]
+crbug.com/948678 external/wpt/console/idlharness.any.worker.html [ Pass Failure ]
+
+crbug.com/892983 external/wpt/css/css-pseudo/first-line-with-before-after.html [ Failure ]
diff --git a/third_party/blink/web_tests/VirtualTestSuites b/third_party/blink/web_tests/VirtualTestSuites
index 907b0b5..3ed9aef 100644
--- a/third_party/blink/web_tests/VirtualTestSuites
+++ b/third_party/blink/web_tests/VirtualTestSuites
@@ -620,6 +620,11 @@
     "args": ["--enable-font-antialiasing"]
   },
   {
+    "prefix": "mathml-disabled",
+    "bases": [],
+    "args": ["--disable-blink-features=MathMLCore"]
+  },
+  {
     "prefix": "hdr",
     "bases": [],
     "args": ["--force-color-profile=scrgb-linear"]
diff --git a/third_party/blink/web_tests/external/wpt/client-hints/accept-ch-feature-policy-navigation.https.html b/third_party/blink/web_tests/external/wpt/client-hints/accept-ch-feature-policy-navigation.https.html
new file mode 100644
index 0000000..dff960d
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/client-hints/accept-ch-feature-policy-navigation.https.html
@@ -0,0 +1,33 @@
+<html>
+<body>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/common/get-host-info.sub.js"></script>
+<script src="resources/feature-policy-navigation.js"></script>
+<script>
+(async () => {
+  await test_frame(
+    "HTTPS_REMOTE_ORIGIN",
+    "device-memory=true&dpr=false&viewport-width=false",
+    "",
+    "Client hints loaded on cross-origin iframe request with feature policy.");
+  await test_frame(
+    "HTTPS_ORIGIN",
+    "device-memory=true&dpr=false&viewport-width=true",
+    "",
+    "Client hints loaded on same-origin iframe request with feature policy.");
+  await test_frame(
+    "HTTPS_REMOTE_ORIGIN",
+    "",
+    "",
+    "Iframe trying to set Accept-CH-Lifetime.", "/client-hints/resources/iframe-accept-ch-lifetime.html");
+  await test_frame(
+    "HTTPS_REMOTE_ORIGIN",
+    "device-memory=true&dpr=false&viewport-width=false",
+    "",
+    "Client hints loaded on cross-origin iframe request with feature policy after attempting to set independently.");
+})();
+
+</script>
+</body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/client-hints/accept-ch-feature-policy-navigation.https.html.headers b/third_party/blink/web_tests/external/wpt/client-hints/accept-ch-feature-policy-navigation.https.html.headers
new file mode 100644
index 0000000..f9595a79
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/client-hints/accept-ch-feature-policy-navigation.https.html.headers
@@ -0,0 +1,3 @@
+Accept-CH: Device-Memory, DPR, Viewport-Width
+Accept-CH-Lifetime: 1
+Feature-Policy: ch-device-memory *; ch-dpr 'none'; ch-viewport-width 'self'; ch-lang 'none'
diff --git a/third_party/blink/web_tests/external/wpt/client-hints/accept-ch-no-feature-policy-navigation.https.html b/third_party/blink/web_tests/external/wpt/client-hints/accept-ch-no-feature-policy-navigation.https.html
new file mode 100644
index 0000000..5fb6c22f
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/client-hints/accept-ch-no-feature-policy-navigation.https.html
@@ -0,0 +1,24 @@
+<html>
+<body>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/common/get-host-info.sub.js"></script>
+<script src="resources/feature-policy-navigation.js"></script>
+<script>
+(async () => {
+  await test_frame(
+    "HTTPS_REMOTE_ORIGIN",
+    "device-memory=false&dpr=false&viewport-width=false",
+    "",
+    "Client hints not loaded on cross-origin iframe request with no feature policy.");
+  await test_frame(
+    "HTTPS_ORIGIN",
+    "device-memory=true&dpr=true&viewport-width=true",
+    "",
+    "Client hints loaded on same-origin iframe request with no feature policy.");
+})();
+
+</script>
+</body>
+</html>
+
diff --git a/third_party/blink/web_tests/external/wpt/client-hints/accept-ch-no-feature-policy-navigation.https.html.headers b/third_party/blink/web_tests/external/wpt/client-hints/accept-ch-no-feature-policy-navigation.https.html.headers
new file mode 100644
index 0000000..7eb28a0
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/client-hints/accept-ch-no-feature-policy-navigation.https.html.headers
@@ -0,0 +1,2 @@
+Accept-CH: Device-Memory, DPR, Viewport-Width
+Accept-CH-Lifetime: 1
diff --git a/third_party/blink/web_tests/external/wpt/client-hints/resources/accept_ch_lifetime.html.headers b/third_party/blink/web_tests/external/wpt/client-hints/resources/accept_ch_lifetime.html.headers
index bd90f6e..370f986 100644
--- a/third_party/blink/web_tests/external/wpt/client-hints/resources/accept_ch_lifetime.html.headers
+++ b/third_party/blink/web_tests/external/wpt/client-hints/resources/accept_ch_lifetime.html.headers
@@ -1,3 +1,3 @@
-Accept-CH: device-memory
+Accept-CH: device-memory, DPR
 Accept-CH-Lifetime: 1
 Access-Control-Allow-Origin: *
diff --git a/third_party/blink/web_tests/external/wpt/client-hints/resources/expect-client-hints-headers-iframe.py b/third_party/blink/web_tests/external/wpt/client-hints/resources/expect-client-hints-headers-iframe.py
new file mode 100644
index 0000000..e72d77c
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/client-hints/resources/expect-client-hints-headers-iframe.py
@@ -0,0 +1,21 @@
+def main(request, response):
+    """
+    Simple handler that returns an HTML response that passes when the required
+    Client Hints are received as request headers.
+    """
+    values = [ "Device-Memory", "DPR", "Viewport-Width" ]
+
+    result = "PASS"
+    log = ""
+    for value in values:
+        should = (request.GET[value.lower()] == "true")
+        present = request.headers.get(value.lower()) or request.headers.get(value)
+        log += value + " " + str(should) + " " + str(present) +", "
+        if (should and not present) or (not should and present):
+            result = "FAIL " + value + " " + str(should) + " " + str(present)
+            break
+
+    response.headers.append("Access-Control-Allow-Origin", "*")
+    body = "<script>console.log('" + log +"'); window.parent.postMessage('" + result + "', '*');</script>"
+
+    response.content = body
diff --git a/third_party/blink/web_tests/external/wpt/client-hints/resources/feature-policy-navigation.js b/third_party/blink/web_tests/external/wpt/client-hints/resources/feature-policy-navigation.js
new file mode 100644
index 0000000..4487d9a
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/client-hints/resources/feature-policy-navigation.js
@@ -0,0 +1,19 @@
+const test_frame = (origin, hints, allow, message, url = "/client-hints/resources/expect-client-hints-headers-iframe.py?") => {
+  promise_test(() => {
+    return new Promise((resolve, reject) => {
+      let frame = document.createElement('iframe');
+      frame.src = get_host_info()[origin] + url + hints;
+      frame.allow = allow;
+      window.addEventListener('message', function(e) {
+        try {
+          assert_equals(typeof e.data, "string");
+          assert_equals(e.data, "PASS");
+        } catch {
+          reject(e.data);
+        }
+        resolve();
+      });
+      document.body.appendChild(frame);
+    });
+  }, message);
+}
diff --git a/third_party/blink/web_tests/external/wpt/client-hints/resources/iframe-accept-ch-lifetime.html b/third_party/blink/web_tests/external/wpt/client-hints/resources/iframe-accept-ch-lifetime.html
new file mode 100644
index 0000000..693e4f94
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/client-hints/resources/iframe-accept-ch-lifetime.html
@@ -0,0 +1,3 @@
+<script>
+  window.parent.postMessage('PASS', '*');
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/client-hints/resources/iframe-accept-ch-lifetime.html.headers b/third_party/blink/web_tests/external/wpt/client-hints/resources/iframe-accept-ch-lifetime.html.headers
new file mode 100644
index 0000000..8e6dc72d
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/client-hints/resources/iframe-accept-ch-lifetime.html.headers
@@ -0,0 +1,4 @@
+Accept-CH: DPR
+Accept-CH-Lifetime: 1
+Access-Control-Allow-Origin: *
+
diff --git a/third_party/blink/web_tests/external/wpt/client-hints/sec-ch-ua.https-expected.txt b/third_party/blink/web_tests/external/wpt/client-hints/sec-ch-ua.https-expected.txt
deleted file mode 100644
index 0bed3d3b..0000000
--- a/third_party/blink/web_tests/external/wpt/client-hints/sec-ch-ua.https-expected.txt
+++ /dev/null
@@ -1,5 +0,0 @@
-This is a testharness.js-based test.
-FAIL Open HTTPS window prior to opt-in: `Sec-CH-UA` header with minor version. assert_not_equals: The `Sec-CH-UA` header is delivered. got disallowed value ""
-FAIL Open HTTPS window post-opt-in: `Sec-CH-UA` header with minor version. assert_not_equals: The `Sec-CH-UA` header is delivered. got disallowed value ""
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/css/css-pseudo/first-line-with-before-after-ref.html b/third_party/blink/web_tests/external/wpt/css/css-pseudo/first-line-with-before-after-ref.html
new file mode 100644
index 0000000..fcee7995
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-pseudo/first-line-with-before-after-ref.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<style>
+  #target::first-line { font-size: 150%; }
+</style>
+
+<div id="target"><span style='color:red'>red</span> <span style='color:green'>green</span> <span style='color:blue'>blue</span><br />
+  <span style='color:red'>red</span> <span style='color:green'>green</span> <span style='color:blue'>blue</span>
+</div>
+
+<p>Both lines above (inside and outside <code>::first-line</code>) should have the same colors.</p>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-pseudo/first-line-with-before-after.html b/third_party/blink/web_tests/external/wpt/css/css-pseudo/first-line-with-before-after.html
new file mode 100644
index 0000000..f91a22a
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-pseudo/first-line-with-before-after.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<title>::before and ::after styles should apply inside ::first-line</title>
+<link rel="help" href="https://drafts.csswg.org/css-pseudo-4/#generated-content">
+<link rel="match" href="first-line-with-before-after-ref.html">
+<style>
+  #target::first-line {
+    font-size: 150%;
+  }
+  #target::before, #child2::before {
+    content: "red ";
+    color: red;
+  }
+  #child1, #child2 {
+    color: green;
+  }
+  #child1::after, #target::after {
+    content: " blue";
+    color: blue;
+  }
+</style>
+
+<div id="target">
+  <span id='child1'>green</span>
+  <br />
+  <span id='child2'>green</span>
+</div>
+
+<p>Both lines above (inside and outside <code>::first-line</code>) should have the same colors.</p>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-intrinsic-size/intrinsic-size-001-ref.html b/third_party/blink/web_tests/external/wpt/css/css-sizing/intrinsic-size/intrinsic-size-001-ref.html
similarity index 100%
rename from third_party/blink/web_tests/external/wpt/css/css-intrinsic-size/intrinsic-size-001-ref.html
rename to third_party/blink/web_tests/external/wpt/css/css-sizing/intrinsic-size/intrinsic-size-001-ref.html
diff --git a/third_party/blink/web_tests/external/wpt/css/css-intrinsic-size/intrinsic-size-001.html b/third_party/blink/web_tests/external/wpt/css/css-sizing/intrinsic-size/intrinsic-size-001.html
similarity index 78%
rename from third_party/blink/web_tests/external/wpt/css/css-intrinsic-size/intrinsic-size-001.html
rename to third_party/blink/web_tests/external/wpt/css/css-sizing/intrinsic-size/intrinsic-size-001.html
index 43c18a0..4d17602a 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-intrinsic-size/intrinsic-size-001.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-sizing/intrinsic-size/intrinsic-size-001.html
@@ -4,6 +4,7 @@
 <link rel="author" title="Vladimir Levin" href="mailto:vmpstr@chromium.org">
 <link rel="help" href="https://drafts.csswg.org/css-sizing-4/#intrinsic-size-override">
 <link rel="match" href="intrinsic-size-001-ref.html">
+<meta name="assert" content="intrinsic-size is used to size the div as if it had a single child of this size">
 
 <style>
 #target {
diff --git a/third_party/blink/web_tests/external/wpt/css/css-intrinsic-size/intrinsic-size-002-ref.html b/third_party/blink/web_tests/external/wpt/css/css-sizing/intrinsic-size/intrinsic-size-002-ref.html
similarity index 100%
rename from third_party/blink/web_tests/external/wpt/css/css-intrinsic-size/intrinsic-size-002-ref.html
rename to third_party/blink/web_tests/external/wpt/css/css-sizing/intrinsic-size/intrinsic-size-002-ref.html
diff --git a/third_party/blink/web_tests/external/wpt/css/css-intrinsic-size/intrinsic-size-002.html b/third_party/blink/web_tests/external/wpt/css/css-sizing/intrinsic-size/intrinsic-size-002.html
similarity index 81%
rename from third_party/blink/web_tests/external/wpt/css/css-intrinsic-size/intrinsic-size-002.html
rename to third_party/blink/web_tests/external/wpt/css/css-sizing/intrinsic-size/intrinsic-size-002.html
index d7c3455..55f2094 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-intrinsic-size/intrinsic-size-002.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-sizing/intrinsic-size/intrinsic-size-002.html
@@ -4,6 +4,7 @@
 <link rel="author" title="Vladimir Levin" href="mailto:vmpstr@chromium.org">
 <link rel="help" href="https://drafts.csswg.org/css-sizing-4/#intrinsic-size-override">
 <link rel="match" href="intrinsic-size-002-ref.html">
+<meta name="assert" content="intrinsic-size is used to size the div, with parent's max-content width respecting it">
 
 <style>
 #border {
diff --git a/third_party/blink/web_tests/external/wpt/css/css-intrinsic-size/intrinsic-size-003-ref.html b/third_party/blink/web_tests/external/wpt/css/css-sizing/intrinsic-size/intrinsic-size-003-ref.html
similarity index 100%
rename from third_party/blink/web_tests/external/wpt/css/css-intrinsic-size/intrinsic-size-003-ref.html
rename to third_party/blink/web_tests/external/wpt/css/css-sizing/intrinsic-size/intrinsic-size-003-ref.html
diff --git a/third_party/blink/web_tests/external/wpt/css/css-intrinsic-size/intrinsic-size-003.html b/third_party/blink/web_tests/external/wpt/css/css-sizing/intrinsic-size/intrinsic-size-003.html
similarity index 83%
rename from third_party/blink/web_tests/external/wpt/css/css-intrinsic-size/intrinsic-size-003.html
rename to third_party/blink/web_tests/external/wpt/css/css-sizing/intrinsic-size/intrinsic-size-003.html
index da51eb3..3dd41ba 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-intrinsic-size/intrinsic-size-003.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-sizing/intrinsic-size/intrinsic-size-003.html
@@ -4,6 +4,7 @@
 <link rel="author" title="Vladimir Levin" href="mailto:vmpstr@chromium.org">
 <link rel="help" href="https://drafts.csswg.org/css-sizing-4/#intrinsic-size-override">
 <link rel="match" href="intrinsic-size-003-ref.html">
+<meta name="assert" content="intrinsic-size's width is ignored if width is specified">
 
 <style>
 #target {
diff --git a/third_party/blink/web_tests/external/wpt/css/css-intrinsic-size/intrinsic-size-004-ref.html b/third_party/blink/web_tests/external/wpt/css/css-sizing/intrinsic-size/intrinsic-size-004-ref.html
similarity index 100%
rename from third_party/blink/web_tests/external/wpt/css/css-intrinsic-size/intrinsic-size-004-ref.html
rename to third_party/blink/web_tests/external/wpt/css/css-sizing/intrinsic-size/intrinsic-size-004-ref.html
diff --git a/third_party/blink/web_tests/external/wpt/css/css-intrinsic-size/intrinsic-size-004.html b/third_party/blink/web_tests/external/wpt/css/css-sizing/intrinsic-size/intrinsic-size-004.html
similarity index 83%
rename from third_party/blink/web_tests/external/wpt/css/css-intrinsic-size/intrinsic-size-004.html
rename to third_party/blink/web_tests/external/wpt/css/css-sizing/intrinsic-size/intrinsic-size-004.html
index f24ffbe..9cce7ae2 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-intrinsic-size/intrinsic-size-004.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-sizing/intrinsic-size/intrinsic-size-004.html
@@ -4,6 +4,7 @@
 <link rel="author" title="Vladimir Levin" href="mailto:vmpstr@chromium.org">
 <link rel="help" href="https://drafts.csswg.org/css-sizing-4/#intrinsic-size-override">
 <link rel="match" href="intrinsic-size-004-ref.html">
+<meta name="assert" content="div is sized to intrinsic-width if width is min-content">
 
 <style>
 #target {
diff --git a/third_party/blink/web_tests/external/wpt/css/css-intrinsic-size/intrinsic-size-005-ref.html b/third_party/blink/web_tests/external/wpt/css/css-sizing/intrinsic-size/intrinsic-size-005-ref.html
similarity index 100%
rename from third_party/blink/web_tests/external/wpt/css/css-intrinsic-size/intrinsic-size-005-ref.html
rename to third_party/blink/web_tests/external/wpt/css/css-sizing/intrinsic-size/intrinsic-size-005-ref.html
diff --git a/third_party/blink/web_tests/external/wpt/css/css-intrinsic-size/intrinsic-size-005.html b/third_party/blink/web_tests/external/wpt/css/css-sizing/intrinsic-size/intrinsic-size-005.html
similarity index 83%
rename from third_party/blink/web_tests/external/wpt/css/css-intrinsic-size/intrinsic-size-005.html
rename to third_party/blink/web_tests/external/wpt/css/css-sizing/intrinsic-size/intrinsic-size-005.html
index ffbfe8b..92b777d 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-intrinsic-size/intrinsic-size-005.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-sizing/intrinsic-size/intrinsic-size-005.html
@@ -4,6 +4,7 @@
 <link rel="author" title="Vladimir Levin" href="mailto:vmpstr@chromium.org">
 <link rel="help" href="https://drafts.csswg.org/css-sizing-4/#intrinsic-size-override">
 <link rel="match" href="intrinsic-size-004-ref.html">
+<meta name="assert" content="div is sized to intrinsic-width if width is max-content">
 
 <style>
 #target {
diff --git a/third_party/blink/web_tests/external/wpt/css/css-intrinsic-size/intrinsic-size-006-ref.html b/third_party/blink/web_tests/external/wpt/css/css-sizing/intrinsic-size/intrinsic-size-006-ref.html
similarity index 100%
rename from third_party/blink/web_tests/external/wpt/css/css-intrinsic-size/intrinsic-size-006-ref.html
rename to third_party/blink/web_tests/external/wpt/css/css-sizing/intrinsic-size/intrinsic-size-006-ref.html
diff --git a/third_party/blink/web_tests/external/wpt/css/css-intrinsic-size/intrinsic-size-006.html b/third_party/blink/web_tests/external/wpt/css/css-sizing/intrinsic-size/intrinsic-size-006.html
similarity index 82%
rename from third_party/blink/web_tests/external/wpt/css/css-intrinsic-size/intrinsic-size-006.html
rename to third_party/blink/web_tests/external/wpt/css/css-sizing/intrinsic-size/intrinsic-size-006.html
index 3540df273..82c6e8e 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-intrinsic-size/intrinsic-size-006.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-sizing/intrinsic-size/intrinsic-size-006.html
@@ -4,6 +4,7 @@
 <link rel="author" title="Vladimir Levin" href="mailto:vmpstr@chromium.org">
 <link rel="help" href="https://drafts.csswg.org/css-sizing-4/#intrinsic-size-override">
 <link rel="match" href="intrinsic-size-006-ref.html">
+<meta name="assert" content="content dimensions are ignored if intrinsic-size is specified">
 
 <style>
 #target {
diff --git a/third_party/blink/web_tests/external/wpt/css/css-intrinsic-size/intrinsic-size-007-ref.html b/third_party/blink/web_tests/external/wpt/css/css-sizing/intrinsic-size/intrinsic-size-007-ref.html
similarity index 100%
rename from third_party/blink/web_tests/external/wpt/css/css-intrinsic-size/intrinsic-size-007-ref.html
rename to third_party/blink/web_tests/external/wpt/css/css-sizing/intrinsic-size/intrinsic-size-007-ref.html
diff --git a/third_party/blink/web_tests/external/wpt/css/css-intrinsic-size/intrinsic-size-007.html b/third_party/blink/web_tests/external/wpt/css/css-sizing/intrinsic-size/intrinsic-size-007.html
similarity index 86%
rename from third_party/blink/web_tests/external/wpt/css/css-intrinsic-size/intrinsic-size-007.html
rename to third_party/blink/web_tests/external/wpt/css/css-sizing/intrinsic-size/intrinsic-size-007.html
index b0ff2c8..85fcae6 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-intrinsic-size/intrinsic-size-007.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-sizing/intrinsic-size/intrinsic-size-007.html
@@ -4,6 +4,7 @@
 <link rel="author" title="Vladimir Levin" href="mailto:vmpstr@chromium.org">
 <link rel="help" href="https://drafts.csswg.org/css-sizing-4/#intrinsic-size-override">
 <link rel="match" href="intrinsic-size-007-ref.html">
+<meta name="assert" content="intrinsic-size sizes the content box, not the border box">
 
 <style>
 #border {
diff --git a/third_party/blink/web_tests/external/wpt/css/css-intrinsic-size/intrinsic-size-008-ref.html b/third_party/blink/web_tests/external/wpt/css/css-sizing/intrinsic-size/intrinsic-size-008-ref.html
similarity index 100%
rename from third_party/blink/web_tests/external/wpt/css/css-intrinsic-size/intrinsic-size-008-ref.html
rename to third_party/blink/web_tests/external/wpt/css/css-sizing/intrinsic-size/intrinsic-size-008-ref.html
diff --git a/third_party/blink/web_tests/external/wpt/css/css-intrinsic-size/intrinsic-size-008.html b/third_party/blink/web_tests/external/wpt/css/css-sizing/intrinsic-size/intrinsic-size-008.html
similarity index 89%
rename from third_party/blink/web_tests/external/wpt/css/css-intrinsic-size/intrinsic-size-008.html
rename to third_party/blink/web_tests/external/wpt/css/css-sizing/intrinsic-size/intrinsic-size-008.html
index 27178f6..f3b344f6 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-intrinsic-size/intrinsic-size-008.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-sizing/intrinsic-size/intrinsic-size-008.html
@@ -4,6 +4,7 @@
 <link rel="author" title="Vladimir Levin" href="mailto:vmpstr@chromium.org">
 <link rel="help" href="https://drafts.csswg.org/css-sizing-4/#intrinsic-size-override">
 <link rel="match" href="intrinsic-size-008-ref.html">
+<meta name="assert" content="intrinsic-size specifies physical dimensions, and respects writing modes">
 
 <style>
 .border {
diff --git a/third_party/blink/web_tests/external/wpt/css/css-intrinsic-size/intrinsic-size-009-ref.html b/third_party/blink/web_tests/external/wpt/css/css-sizing/intrinsic-size/intrinsic-size-009-ref.html
similarity index 100%
rename from third_party/blink/web_tests/external/wpt/css/css-intrinsic-size/intrinsic-size-009-ref.html
rename to third_party/blink/web_tests/external/wpt/css/css-sizing/intrinsic-size/intrinsic-size-009-ref.html
diff --git a/third_party/blink/web_tests/external/wpt/css/css-intrinsic-size/intrinsic-size-009.html b/third_party/blink/web_tests/external/wpt/css/css-sizing/intrinsic-size/intrinsic-size-009.html
similarity index 88%
rename from third_party/blink/web_tests/external/wpt/css/css-intrinsic-size/intrinsic-size-009.html
rename to third_party/blink/web_tests/external/wpt/css/css-sizing/intrinsic-size/intrinsic-size-009.html
index 3d837b9..06a02fe 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-intrinsic-size/intrinsic-size-009.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-sizing/intrinsic-size/intrinsic-size-009.html
@@ -4,6 +4,7 @@
 <link rel="author" title="Vladimir Levin" href="mailto:vmpstr@chromium.org">
 <link rel="help" href="https://drafts.csswg.org/css-sizing-4/#intrinsic-size-override">
 <link rel="match" href="intrinsic-size-009-ref.html">
+<meta name="assert" content="intrinsic-size sizes select multiple">
 
 <style>
 .border {
diff --git a/third_party/blink/web_tests/external/wpt/css/css-intrinsic-size/intrinsic-size-010-ref.html b/third_party/blink/web_tests/external/wpt/css/css-sizing/intrinsic-size/intrinsic-size-010-ref.html
similarity index 100%
rename from third_party/blink/web_tests/external/wpt/css/css-intrinsic-size/intrinsic-size-010-ref.html
rename to third_party/blink/web_tests/external/wpt/css/css-sizing/intrinsic-size/intrinsic-size-010-ref.html
diff --git a/third_party/blink/web_tests/external/wpt/css/css-intrinsic-size/intrinsic-size-010.html b/third_party/blink/web_tests/external/wpt/css/css-sizing/intrinsic-size/intrinsic-size-010.html
similarity index 87%
rename from third_party/blink/web_tests/external/wpt/css/css-intrinsic-size/intrinsic-size-010.html
rename to third_party/blink/web_tests/external/wpt/css/css-sizing/intrinsic-size/intrinsic-size-010.html
index 73d23c7..eea76ffb 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-intrinsic-size/intrinsic-size-010.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-sizing/intrinsic-size/intrinsic-size-010.html
@@ -4,6 +4,7 @@
 <link rel="author" title="Vladimir Levin" href="mailto:vmpstr@chromium.org">
 <link rel="help" href="https://drafts.csswg.org/css-sizing-4/#intrinsic-size-override">
 <link rel="match" href="intrinsic-size-010-ref.html">
+<meta name="assert" content="items are flexed correctly when they are sized by intrinsic-size in a flex row">
 
 <style>
 #flex {
diff --git a/third_party/blink/web_tests/external/wpt/css/css-intrinsic-size/intrinsic-size-011-ref.html b/third_party/blink/web_tests/external/wpt/css/css-sizing/intrinsic-size/intrinsic-size-011-ref.html
similarity index 100%
rename from third_party/blink/web_tests/external/wpt/css/css-intrinsic-size/intrinsic-size-011-ref.html
rename to third_party/blink/web_tests/external/wpt/css/css-sizing/intrinsic-size/intrinsic-size-011-ref.html
diff --git a/third_party/blink/web_tests/external/wpt/css/css-intrinsic-size/intrinsic-size-011.html b/third_party/blink/web_tests/external/wpt/css/css-sizing/intrinsic-size/intrinsic-size-011.html
similarity index 87%
rename from third_party/blink/web_tests/external/wpt/css/css-intrinsic-size/intrinsic-size-011.html
rename to third_party/blink/web_tests/external/wpt/css/css-sizing/intrinsic-size/intrinsic-size-011.html
index d050160..c7888f9 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-intrinsic-size/intrinsic-size-011.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-sizing/intrinsic-size/intrinsic-size-011.html
@@ -4,6 +4,7 @@
 <link rel="author" title="Vladimir Levin" href="mailto:vmpstr@chromium.org">
 <link rel="help" href="https://drafts.csswg.org/css-sizing-4/#intrinsic-size-override">
 <link rel="match" href="intrinsic-size-011-ref.html">
+<meta name="assert" content="items are flexed correctly when they are sized by intrinsic-size in a flex column">
 
 <style>
 #flex {
diff --git a/third_party/blink/web_tests/external/wpt/css/css-intrinsic-size/intrinsic-size-012-ref.html b/third_party/blink/web_tests/external/wpt/css/css-sizing/intrinsic-size/intrinsic-size-012-ref.html
similarity index 100%
rename from third_party/blink/web_tests/external/wpt/css/css-intrinsic-size/intrinsic-size-012-ref.html
rename to third_party/blink/web_tests/external/wpt/css/css-sizing/intrinsic-size/intrinsic-size-012-ref.html
diff --git a/third_party/blink/web_tests/external/wpt/css/css-intrinsic-size/intrinsic-size-012.html b/third_party/blink/web_tests/external/wpt/css/css-sizing/intrinsic-size/intrinsic-size-012.html
similarity index 81%
rename from third_party/blink/web_tests/external/wpt/css/css-intrinsic-size/intrinsic-size-012.html
rename to third_party/blink/web_tests/external/wpt/css/css-sizing/intrinsic-size/intrinsic-size-012.html
index a5ba047..889b07a 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-intrinsic-size/intrinsic-size-012.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-sizing/intrinsic-size/intrinsic-size-012.html
@@ -4,6 +4,7 @@
 <link rel="author" title="Vladimir Levin" href="mailto:vmpstr@chromium.org">
 <link rel="help" href="https://drafts.csswg.org/css-sizing-4/#intrinsic-size-override">
 <link rel="match" href="intrinsic-size-012-ref.html">
+<meta name="assert" content="intrinsic-size overrides replaced content's intrinsic dimensions">
 
 <style>
 #target {
diff --git a/third_party/blink/web_tests/external/wpt/css/css-intrinsic-size/intrinsic-size-013-ref.html b/third_party/blink/web_tests/external/wpt/css/css-sizing/intrinsic-size/intrinsic-size-013-ref.html
similarity index 100%
rename from third_party/blink/web_tests/external/wpt/css/css-intrinsic-size/intrinsic-size-013-ref.html
rename to third_party/blink/web_tests/external/wpt/css/css-sizing/intrinsic-size/intrinsic-size-013-ref.html
diff --git a/third_party/blink/web_tests/external/wpt/css/css-intrinsic-size/intrinsic-size-013.html b/third_party/blink/web_tests/external/wpt/css/css-sizing/intrinsic-size/intrinsic-size-013.html
similarity index 91%
rename from third_party/blink/web_tests/external/wpt/css/css-intrinsic-size/intrinsic-size-013.html
rename to third_party/blink/web_tests/external/wpt/css/css-sizing/intrinsic-size/intrinsic-size-013.html
index dca4c17..1082100 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-intrinsic-size/intrinsic-size-013.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-sizing/intrinsic-size/intrinsic-size-013.html
@@ -5,6 +5,7 @@
 <link rel="author" title="Vladimir Levin" href="mailto:vmpstr@chromium.org">
 <link rel="help" href="https://drafts.csswg.org/css-sizing-4/#intrinsic-size-override">
 <link rel="match" href="intrinsic-size-013-ref.html">
+<meta name="assert" content="changes in intrinsic-size cause reflow">
 <script src="/common/reftest-wait.js"></script>
 
 <style>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-intrinsic-size/intrinsic-size-014-ref.html b/third_party/blink/web_tests/external/wpt/css/css-sizing/intrinsic-size/intrinsic-size-014-ref.html
similarity index 100%
rename from third_party/blink/web_tests/external/wpt/css/css-intrinsic-size/intrinsic-size-014-ref.html
rename to third_party/blink/web_tests/external/wpt/css/css-sizing/intrinsic-size/intrinsic-size-014-ref.html
diff --git a/third_party/blink/web_tests/external/wpt/css/css-intrinsic-size/intrinsic-size-014.html b/third_party/blink/web_tests/external/wpt/css/css-sizing/intrinsic-size/intrinsic-size-014.html
similarity index 88%
rename from third_party/blink/web_tests/external/wpt/css/css-intrinsic-size/intrinsic-size-014.html
rename to third_party/blink/web_tests/external/wpt/css/css-sizing/intrinsic-size/intrinsic-size-014.html
index d69f58f..4dd45a2 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-intrinsic-size/intrinsic-size-014.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-sizing/intrinsic-size/intrinsic-size-014.html
@@ -4,6 +4,7 @@
 <link rel="author" title="Vladimir Levin" href="mailto:vmpstr@chromium.org">
 <link rel="help" href="https://drafts.csswg.org/css-sizing-4/#intrinsic-size-override">
 <link rel="match" href="intrinsic-size-014-ref.html">
+<meta name="assert" content="intrinsic-size sizes fieldsets">
 
 <style>
 fieldset {
diff --git a/third_party/blink/web_tests/external/wpt/css/css-intrinsic-size/intrinsic-size-015-ref.html b/third_party/blink/web_tests/external/wpt/css/css-sizing/intrinsic-size/intrinsic-size-015-ref.html
similarity index 100%
rename from third_party/blink/web_tests/external/wpt/css/css-intrinsic-size/intrinsic-size-015-ref.html
rename to third_party/blink/web_tests/external/wpt/css/css-sizing/intrinsic-size/intrinsic-size-015-ref.html
diff --git a/third_party/blink/web_tests/external/wpt/css/css-intrinsic-size/intrinsic-size-015.html b/third_party/blink/web_tests/external/wpt/css/css-sizing/intrinsic-size/intrinsic-size-015.html
similarity index 85%
rename from third_party/blink/web_tests/external/wpt/css/css-intrinsic-size/intrinsic-size-015.html
rename to third_party/blink/web_tests/external/wpt/css/css-sizing/intrinsic-size/intrinsic-size-015.html
index c7a977540..5ca5e49 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-intrinsic-size/intrinsic-size-015.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-sizing/intrinsic-size/intrinsic-size-015.html
@@ -4,6 +4,7 @@
 <link rel="author" title="Vladimir Levin" href="mailto:vmpstr@chromium.org">
 <link rel="help" href="https://drafts.csswg.org/css-contain-1/#containment-size">
 <link rel="match" href="intrinsic-size-015-ref.html">
+<meta name="assert" content="intrinsic-size sizes an inline-flex element">
 <style>
 div {
   display: inline-flex;
diff --git a/third_party/blink/web_tests/external/wpt/css/css-intrinsic-size/intrinsic-size-016-ref.html b/third_party/blink/web_tests/external/wpt/css/css-sizing/intrinsic-size/intrinsic-size-016-ref.html
similarity index 100%
rename from third_party/blink/web_tests/external/wpt/css/css-intrinsic-size/intrinsic-size-016-ref.html
rename to third_party/blink/web_tests/external/wpt/css/css-sizing/intrinsic-size/intrinsic-size-016-ref.html
diff --git a/third_party/blink/web_tests/external/wpt/css/css-intrinsic-size/intrinsic-size-016.html b/third_party/blink/web_tests/external/wpt/css/css-sizing/intrinsic-size/intrinsic-size-016.html
similarity index 89%
rename from third_party/blink/web_tests/external/wpt/css/css-intrinsic-size/intrinsic-size-016.html
rename to third_party/blink/web_tests/external/wpt/css/css-sizing/intrinsic-size/intrinsic-size-016.html
index 2c1c917b..ce6ac5c 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-intrinsic-size/intrinsic-size-016.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-sizing/intrinsic-size/intrinsic-size-016.html
@@ -4,6 +4,7 @@
 <link rel="author" title="Vladimir Levin" href="mailto:vmpstr@chromium.org">
 <link rel="help" href="https://drafts.csswg.org/css-sizing-4/#intrinsic-size-override">
 <link rel="match" href="intrinsic-size-016-ref.html">
+<meta name="assert" content="intrinsic-size take 'priority' over size-containment in flex">
 
 <style>
 #flex {
diff --git a/third_party/blink/web_tests/external/wpt/css/css-intrinsic-size/intrinsic-size-017-ref.html b/third_party/blink/web_tests/external/wpt/css/css-sizing/intrinsic-size/intrinsic-size-017-ref.html
similarity index 100%
rename from third_party/blink/web_tests/external/wpt/css/css-intrinsic-size/intrinsic-size-017-ref.html
rename to third_party/blink/web_tests/external/wpt/css/css-sizing/intrinsic-size/intrinsic-size-017-ref.html
diff --git a/third_party/blink/web_tests/external/wpt/css/css-intrinsic-size/intrinsic-size-017.html b/third_party/blink/web_tests/external/wpt/css/css-sizing/intrinsic-size/intrinsic-size-017.html
similarity index 84%
rename from third_party/blink/web_tests/external/wpt/css/css-intrinsic-size/intrinsic-size-017.html
rename to third_party/blink/web_tests/external/wpt/css/css-sizing/intrinsic-size/intrinsic-size-017.html
index d9d33477..5e2122f 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-intrinsic-size/intrinsic-size-017.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-sizing/intrinsic-size/intrinsic-size-017.html
@@ -4,6 +4,7 @@
 <link rel="author" title="Vladimir Levin" href="mailto:vmpstr@chromium.org">
 <link rel="help" href="https://drafts.csswg.org/css-sizing-4/#intrinsic-size-override">
 <link rel="match" href="intrinsic-size-017-ref.html">
+<meta name="assert" content="intrinsic-size specifies physical dimensions on replaced content">
 
 <style>
 body {
diff --git a/third_party/blink/web_tests/external/wpt/css/css-intrinsic-size/intrinsic-size-018-ref.html b/third_party/blink/web_tests/external/wpt/css/css-sizing/intrinsic-size/intrinsic-size-018-ref.html
similarity index 100%
rename from third_party/blink/web_tests/external/wpt/css/css-intrinsic-size/intrinsic-size-018-ref.html
rename to third_party/blink/web_tests/external/wpt/css/css-sizing/intrinsic-size/intrinsic-size-018-ref.html
diff --git a/third_party/blink/web_tests/external/wpt/css/css-intrinsic-size/intrinsic-size-018.html b/third_party/blink/web_tests/external/wpt/css/css-sizing/intrinsic-size/intrinsic-size-018.html
similarity index 86%
rename from third_party/blink/web_tests/external/wpt/css/css-intrinsic-size/intrinsic-size-018.html
rename to third_party/blink/web_tests/external/wpt/css/css-sizing/intrinsic-size/intrinsic-size-018.html
index e31b0a41..6893183 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-intrinsic-size/intrinsic-size-018.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-sizing/intrinsic-size/intrinsic-size-018.html
@@ -4,6 +4,7 @@
 <link rel="author" title="Vladimir Levin" href="mailto:vmpstr@chromium.org">
 <link rel="help" href="https://drafts.csswg.org/css-sizing-4/#intrinsic-size-override">
 <link rel="match" href="intrinsic-size-018-ref.html">
+<meta name="assert" content="width / height ignore intrinsic-size">
 
 <style>
 #target {
diff --git a/third_party/blink/web_tests/external/wpt/css/css-intrinsic-size/intrinsic-size-019-ref.html b/third_party/blink/web_tests/external/wpt/css/css-sizing/intrinsic-size/intrinsic-size-019-ref.html
similarity index 100%
rename from third_party/blink/web_tests/external/wpt/css/css-intrinsic-size/intrinsic-size-019-ref.html
rename to third_party/blink/web_tests/external/wpt/css/css-sizing/intrinsic-size/intrinsic-size-019-ref.html
diff --git a/third_party/blink/web_tests/external/wpt/css/css-intrinsic-size/intrinsic-size-019.html b/third_party/blink/web_tests/external/wpt/css/css-sizing/intrinsic-size/intrinsic-size-019.html
similarity index 83%
rename from third_party/blink/web_tests/external/wpt/css/css-intrinsic-size/intrinsic-size-019.html
rename to third_party/blink/web_tests/external/wpt/css/css-sizing/intrinsic-size/intrinsic-size-019.html
index 5718639..cae8e07 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-intrinsic-size/intrinsic-size-019.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-sizing/intrinsic-size/intrinsic-size-019.html
@@ -4,6 +4,7 @@
 <link rel="author" title="Vladimir Levin" href="mailto:vmpstr@chromium.org">
 <link rel="help" href="https://drafts.csswg.org/css-sizing-4/#intrinsic-size-override">
 <link rel="match" href="intrinsic-size-019-ref.html">
+<meta name="assert" content="div is sized to intrinsic-width when width is fit-content">
 
 <style>
 #target {
diff --git a/third_party/blink/web_tests/external/wpt/css/css-intrinsic-size/intrinsic-size-020-ref.html b/third_party/blink/web_tests/external/wpt/css/css-sizing/intrinsic-size/intrinsic-size-020-ref.html
similarity index 100%
rename from third_party/blink/web_tests/external/wpt/css/css-intrinsic-size/intrinsic-size-020-ref.html
rename to third_party/blink/web_tests/external/wpt/css/css-sizing/intrinsic-size/intrinsic-size-020-ref.html
diff --git a/third_party/blink/web_tests/external/wpt/css/css-intrinsic-size/intrinsic-size-020.html b/third_party/blink/web_tests/external/wpt/css/css-sizing/intrinsic-size/intrinsic-size-020.html
similarity index 82%
rename from third_party/blink/web_tests/external/wpt/css/css-intrinsic-size/intrinsic-size-020.html
rename to third_party/blink/web_tests/external/wpt/css/css-sizing/intrinsic-size/intrinsic-size-020.html
index c4055bb..a7286ce 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-intrinsic-size/intrinsic-size-020.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-sizing/intrinsic-size/intrinsic-size-020.html
@@ -4,6 +4,7 @@
 <link rel="author" title="Vladimir Levin" href="mailto:vmpstr@chromium.org">
 <link rel="help" href="https://drafts.csswg.org/css-sizing-4/#intrinsic-size-override">
 <link rel="match" href="intrinsic-size-020-ref.html">
+<meta name="assert" content="replaced content is sized to intrinsic-width when width is min-content">
 
 <style>
 #target {
diff --git a/third_party/blink/web_tests/external/wpt/css/css-intrinsic-size/intrinsic-size-021-ref.html b/third_party/blink/web_tests/external/wpt/css/css-sizing/intrinsic-size/intrinsic-size-021-ref.html
similarity index 100%
rename from third_party/blink/web_tests/external/wpt/css/css-intrinsic-size/intrinsic-size-021-ref.html
rename to third_party/blink/web_tests/external/wpt/css/css-sizing/intrinsic-size/intrinsic-size-021-ref.html
diff --git a/third_party/blink/web_tests/external/wpt/css/css-intrinsic-size/intrinsic-size-021.html b/third_party/blink/web_tests/external/wpt/css/css-sizing/intrinsic-size/intrinsic-size-021.html
similarity index 86%
rename from third_party/blink/web_tests/external/wpt/css/css-intrinsic-size/intrinsic-size-021.html
rename to third_party/blink/web_tests/external/wpt/css/css-sizing/intrinsic-size/intrinsic-size-021.html
index 93e610b..04591924 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-intrinsic-size/intrinsic-size-021.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-sizing/intrinsic-size/intrinsic-size-021.html
@@ -4,6 +4,7 @@
 <link rel="author" title="Vladimir Levin" href="mailto:vmpstr@chromium.org">
 <link rel="help" href="https://drafts.csswg.org/css-sizing-4/#intrinsic-size-override">
 <link rel="match" href="intrinsic-size-021-ref.html">
+<meta name="assert" content="flex container is sized by the largest intrinsic-height of content, even with align-self: stretch">
 
 <style>
 #flex {
diff --git a/third_party/blink/web_tests/external/wpt/css/css-intrinsic-size/intrinsic-size-022-ref.html b/third_party/blink/web_tests/external/wpt/css/css-sizing/intrinsic-size/intrinsic-size-022-ref.html
similarity index 100%
rename from third_party/blink/web_tests/external/wpt/css/css-intrinsic-size/intrinsic-size-022-ref.html
rename to third_party/blink/web_tests/external/wpt/css/css-sizing/intrinsic-size/intrinsic-size-022-ref.html
diff --git a/third_party/blink/web_tests/external/wpt/css/css-intrinsic-size/intrinsic-size-022.html b/third_party/blink/web_tests/external/wpt/css/css-sizing/intrinsic-size/intrinsic-size-022.html
similarity index 86%
rename from third_party/blink/web_tests/external/wpt/css/css-intrinsic-size/intrinsic-size-022.html
rename to third_party/blink/web_tests/external/wpt/css/css-sizing/intrinsic-size/intrinsic-size-022.html
index 5331df38..3593b9c 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-intrinsic-size/intrinsic-size-022.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-sizing/intrinsic-size/intrinsic-size-022.html
@@ -4,6 +4,7 @@
 <link rel="author" title="Vladimir Levin" href="mailto:vmpstr@chromium.org">
 <link rel="help" href="https://drafts.csswg.org/css-sizing-4/#intrinsic-size-override">
 <link rel="match" href="intrinsic-size-022-ref.html">
+<meta name="assert" content="grid container is sized by intrinsic-size, even if definite track sizes overflow">
 
 <style>
 #grid {
diff --git a/third_party/blink/web_tests/external/wpt/css/css-intrinsic-size/intrinsic-size-023-ref.html b/third_party/blink/web_tests/external/wpt/css/css-sizing/intrinsic-size/intrinsic-size-023-ref.html
similarity index 100%
rename from third_party/blink/web_tests/external/wpt/css/css-intrinsic-size/intrinsic-size-023-ref.html
rename to third_party/blink/web_tests/external/wpt/css/css-sizing/intrinsic-size/intrinsic-size-023-ref.html
diff --git a/third_party/blink/web_tests/external/wpt/css/css-intrinsic-size/intrinsic-size-023.html b/third_party/blink/web_tests/external/wpt/css/css-sizing/intrinsic-size/intrinsic-size-023.html
similarity index 86%
rename from third_party/blink/web_tests/external/wpt/css/css-intrinsic-size/intrinsic-size-023.html
rename to third_party/blink/web_tests/external/wpt/css/css-sizing/intrinsic-size/intrinsic-size-023.html
index aa946e82a..c64ef66a 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-intrinsic-size/intrinsic-size-023.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-sizing/intrinsic-size/intrinsic-size-023.html
@@ -4,6 +4,7 @@
 <link rel="author" title="Vladimir Levin" href="mailto:vmpstr@chromium.org">
 <link rel="help" href="https://drafts.csswg.org/css-sizing-4/#intrinsic-size-override">
 <link rel="match" href="intrinsic-size-023-ref.html">
+<meta name="assert" content="grid container is sized by intrinsic-size, with fr-tracks using that space">
 
 <style>
 #grid {
diff --git a/third_party/blink/web_tests/external/wpt/css/css-intrinsic-size/intrinsic-size-024-ref.html b/third_party/blink/web_tests/external/wpt/css/css-sizing/intrinsic-size/intrinsic-size-024-ref.html
similarity index 100%
rename from third_party/blink/web_tests/external/wpt/css/css-intrinsic-size/intrinsic-size-024-ref.html
rename to third_party/blink/web_tests/external/wpt/css/css-sizing/intrinsic-size/intrinsic-size-024-ref.html
diff --git a/third_party/blink/web_tests/external/wpt/css/css-intrinsic-size/intrinsic-size-024.html b/third_party/blink/web_tests/external/wpt/css/css-sizing/intrinsic-size/intrinsic-size-024.html
similarity index 87%
rename from third_party/blink/web_tests/external/wpt/css/css-intrinsic-size/intrinsic-size-024.html
rename to third_party/blink/web_tests/external/wpt/css/css-sizing/intrinsic-size/intrinsic-size-024.html
index f9fdd884..b11317d 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-intrinsic-size/intrinsic-size-024.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-sizing/intrinsic-size/intrinsic-size-024.html
@@ -4,6 +4,7 @@
 <link rel="author" title="Vladimir Levin" href="mailto:vmpstr@chromium.org">
 <link rel="help" href="https://drafts.csswg.org/css-sizing-4/#intrinsic-size-override">
 <link rel="match" href="intrinsic-size-024-ref.html">
+<meta name="assert" content="intrinsic-size sizes grid container, even if definite tracks are smaller">
 
 <style>
 #grid {
diff --git a/third_party/blink/web_tests/external/wpt/css/css-intrinsic-size/intrinsic-size-025-ref.html b/third_party/blink/web_tests/external/wpt/css/css-sizing/intrinsic-size/intrinsic-size-025-ref.html
similarity index 100%
rename from third_party/blink/web_tests/external/wpt/css/css-intrinsic-size/intrinsic-size-025-ref.html
rename to third_party/blink/web_tests/external/wpt/css/css-sizing/intrinsic-size/intrinsic-size-025-ref.html
diff --git a/third_party/blink/web_tests/external/wpt/css/css-intrinsic-size/intrinsic-size-025.html b/third_party/blink/web_tests/external/wpt/css/css-sizing/intrinsic-size/intrinsic-size-025.html
similarity index 92%
rename from third_party/blink/web_tests/external/wpt/css/css-intrinsic-size/intrinsic-size-025.html
rename to third_party/blink/web_tests/external/wpt/css/css-sizing/intrinsic-size/intrinsic-size-025.html
index d80b977..ab9c56f 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-intrinsic-size/intrinsic-size-025.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-sizing/intrinsic-size/intrinsic-size-025.html
@@ -4,6 +4,7 @@
 <link rel="author" title="Vladimir Levin" href="mailto:vmpstr@chromium.org">
 <link rel="help" href="https://drafts.csswg.org/css-sizing-4/#intrinsic-size-override">
 <link rel="match" href="intrinsic-size-025-ref.html">
+<meta name="assert" content="definite size auto-fit uses intrinsic-size for the available space">
 
 <style>
 .grid {
diff --git a/third_party/blink/web_tests/external/wpt/css/css-intrinsic-size/intrinsic-size-026-ref.html b/third_party/blink/web_tests/external/wpt/css/css-sizing/intrinsic-size/intrinsic-size-026-ref.html
similarity index 100%
rename from third_party/blink/web_tests/external/wpt/css/css-intrinsic-size/intrinsic-size-026-ref.html
rename to third_party/blink/web_tests/external/wpt/css/css-sizing/intrinsic-size/intrinsic-size-026-ref.html
diff --git a/third_party/blink/web_tests/external/wpt/css/css-intrinsic-size/intrinsic-size-026.html b/third_party/blink/web_tests/external/wpt/css/css-sizing/intrinsic-size/intrinsic-size-026.html
similarity index 92%
rename from third_party/blink/web_tests/external/wpt/css/css-intrinsic-size/intrinsic-size-026.html
rename to third_party/blink/web_tests/external/wpt/css/css-sizing/intrinsic-size/intrinsic-size-026.html
index fb5412b..100f542 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-intrinsic-size/intrinsic-size-026.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-sizing/intrinsic-size/intrinsic-size-026.html
@@ -4,6 +4,7 @@
 <link rel="author" title="Vladimir Levin" href="mailto:vmpstr@chromium.org">
 <link rel="help" href="https://drafts.csswg.org/css-sizing-4/#intrinsic-size-override">
 <link rel="match" href="intrinsic-size-026-ref.html">
+<meta name="assert" content="auto-fit columns, with intrinsic-size interacting with min- and max- width">
 
 <style>
 .grid {
diff --git a/third_party/blink/web_tests/external/wpt/css/css-intrinsic-size/intrinsic-size-with-auto-001-ref.html b/third_party/blink/web_tests/external/wpt/css/css-sizing/intrinsic-size/intrinsic-size-with-auto-001-ref.html
similarity index 100%
rename from third_party/blink/web_tests/external/wpt/css/css-intrinsic-size/intrinsic-size-with-auto-001-ref.html
rename to third_party/blink/web_tests/external/wpt/css/css-sizing/intrinsic-size/intrinsic-size-with-auto-001-ref.html
diff --git a/third_party/blink/web_tests/external/wpt/css/css-intrinsic-size/intrinsic-size-with-auto-001.html b/third_party/blink/web_tests/external/wpt/css/css-sizing/intrinsic-size/intrinsic-size-with-auto-001.html
similarity index 89%
rename from third_party/blink/web_tests/external/wpt/css/css-intrinsic-size/intrinsic-size-with-auto-001.html
rename to third_party/blink/web_tests/external/wpt/css/css-sizing/intrinsic-size/intrinsic-size-with-auto-001.html
index 41b89e3d..9c2dac0 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-intrinsic-size/intrinsic-size-with-auto-001.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-sizing/intrinsic-size/intrinsic-size-with-auto-001.html
@@ -4,6 +4,7 @@
 <link rel="author" title="Vladimir Levin" href="mailto:vmpstr@chromium.org">
 <link rel="help" href="https://drafts.csswg.org/css-sizing-4/#intrinsic-size-override">
 <link rel="match" href="intrinsic-size-with-auto-001-ref.html">
+<meta name="assert" content="intrinsic-size auto is treated as 0 when overflow is not 'visible'">
 
 <style>
 div {
diff --git a/third_party/blink/web_tests/external/wpt/css/css-intrinsic-size/intrinsic-size-with-auto-002-ref.html b/third_party/blink/web_tests/external/wpt/css/css-sizing/intrinsic-size/intrinsic-size-with-auto-002-ref.html
similarity index 100%
rename from third_party/blink/web_tests/external/wpt/css/css-intrinsic-size/intrinsic-size-with-auto-002-ref.html
rename to third_party/blink/web_tests/external/wpt/css/css-sizing/intrinsic-size/intrinsic-size-with-auto-002-ref.html
diff --git a/third_party/blink/web_tests/external/wpt/css/css-intrinsic-size/intrinsic-size-with-auto-002.html b/third_party/blink/web_tests/external/wpt/css/css-sizing/intrinsic-size/intrinsic-size-with-auto-002.html
similarity index 89%
rename from third_party/blink/web_tests/external/wpt/css/css-intrinsic-size/intrinsic-size-with-auto-002.html
rename to third_party/blink/web_tests/external/wpt/css/css-sizing/intrinsic-size/intrinsic-size-with-auto-002.html
index 446f2154..5b39024 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-intrinsic-size/intrinsic-size-with-auto-002.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-sizing/intrinsic-size/intrinsic-size-with-auto-002.html
@@ -4,6 +4,7 @@
 <link rel="author" title="Vladimir Levin" href="mailto:vmpstr@chromium.org">
 <link rel="help" href="https://drafts.csswg.org/css-sizing-4/#intrinsic-size-override">
 <link rel="match" href="intrinsic-size-with-auto-002-ref.html">
+<meta name="assert" content="intrinsic-size auto is treated as 0 when overflow is not 'visible'">
 
 <style>
 div {
diff --git a/third_party/blink/web_tests/external/wpt/css/css-intrinsic-size/intrinsic-size-with-auto-003-ref.html b/third_party/blink/web_tests/external/wpt/css/css-sizing/intrinsic-size/intrinsic-size-with-auto-003-ref.html
similarity index 100%
rename from third_party/blink/web_tests/external/wpt/css/css-intrinsic-size/intrinsic-size-with-auto-003-ref.html
rename to third_party/blink/web_tests/external/wpt/css/css-sizing/intrinsic-size/intrinsic-size-with-auto-003-ref.html
diff --git a/third_party/blink/web_tests/external/wpt/css/css-intrinsic-size/intrinsic-size-with-auto-003.html b/third_party/blink/web_tests/external/wpt/css/css-sizing/intrinsic-size/intrinsic-size-with-auto-003.html
similarity index 88%
rename from third_party/blink/web_tests/external/wpt/css/css-intrinsic-size/intrinsic-size-with-auto-003.html
rename to third_party/blink/web_tests/external/wpt/css/css-sizing/intrinsic-size/intrinsic-size-with-auto-003.html
index c5da411..07ec0326 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-intrinsic-size/intrinsic-size-with-auto-003.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-sizing/intrinsic-size/intrinsic-size-with-auto-003.html
@@ -4,6 +4,7 @@
 <link rel="author" title="Vladimir Levin" href="mailto:vmpstr@chromium.org">
 <link rel="help" href="https://drafts.csswg.org/css-sizing-4/#intrinsic-size-override">
 <link rel="match" href="intrinsic-size-with-auto-003-ref.html">
+<meta name="assert" content="intrinsic-size auto is treated as 0 when overflow is not 'visible'">
 
 <style>
 img {
diff --git a/third_party/blink/web_tests/external/wpt/css/css-intrinsic-size/intrinsic-size-with-legacy-001-ref.html b/third_party/blink/web_tests/external/wpt/css/css-sizing/intrinsic-size/intrinsic-size-with-legacy-001-ref.html
similarity index 100%
rename from third_party/blink/web_tests/external/wpt/css/css-intrinsic-size/intrinsic-size-with-legacy-001-ref.html
rename to third_party/blink/web_tests/external/wpt/css/css-sizing/intrinsic-size/intrinsic-size-with-legacy-001-ref.html
diff --git a/third_party/blink/web_tests/external/wpt/css/css-intrinsic-size/intrinsic-size-with-legacy-001.html b/third_party/blink/web_tests/external/wpt/css/css-sizing/intrinsic-size/intrinsic-size-with-legacy-001.html
similarity index 82%
rename from third_party/blink/web_tests/external/wpt/css/css-intrinsic-size/intrinsic-size-with-legacy-001.html
rename to third_party/blink/web_tests/external/wpt/css/css-sizing/intrinsic-size/intrinsic-size-with-legacy-001.html
index e565b56..df662e5 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-intrinsic-size/intrinsic-size-with-legacy-001.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-sizing/intrinsic-size/intrinsic-size-with-legacy-001.html
@@ -4,6 +4,7 @@
 <link rel="author" title="Vladimir Levin" href="mailto:vmpstr@chromium.org">
 <link rel="help" href="https://drafts.csswg.org/css-sizing-4/#intrinsic-size-override">
 <link rel="match" href="intrinsic-size-with-legacy-001-ref.html">
+<meta name="assert" content="intrinsic-size legacy is treated the same as if it was not specified.">
 
 <style>
 #target {
diff --git a/third_party/blink/web_tests/external/wpt/css/css-intrinsic-size/intrinsic-size-with-legacy-002-ref.html b/third_party/blink/web_tests/external/wpt/css/css-sizing/intrinsic-size/intrinsic-size-with-legacy-002-ref.html
similarity index 100%
rename from third_party/blink/web_tests/external/wpt/css/css-intrinsic-size/intrinsic-size-with-legacy-002-ref.html
rename to third_party/blink/web_tests/external/wpt/css/css-sizing/intrinsic-size/intrinsic-size-with-legacy-002-ref.html
diff --git a/third_party/blink/web_tests/external/wpt/css/css-intrinsic-size/intrinsic-size-with-legacy-002.html b/third_party/blink/web_tests/external/wpt/css/css-sizing/intrinsic-size/intrinsic-size-with-legacy-002.html
similarity index 89%
rename from third_party/blink/web_tests/external/wpt/css/css-intrinsic-size/intrinsic-size-with-legacy-002.html
rename to third_party/blink/web_tests/external/wpt/css/css-sizing/intrinsic-size/intrinsic-size-with-legacy-002.html
index bc11418..66d008c 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-intrinsic-size/intrinsic-size-with-legacy-002.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-sizing/intrinsic-size/intrinsic-size-with-legacy-002.html
@@ -4,6 +4,7 @@
 <link rel="author" title="Vladimir Levin" href="mailto:vmpstr@chromium.org">
 <link rel="help" href="https://drafts.csswg.org/css-sizing-4/#intrinsic-size-override">
 <link rel="match" href="intrinsic-size-with-legacy-002-ref.html">
+<meta name="assert" content="intrinsic-size legacy is treated the same as if it was not specified.">
 
 <style>
 #flex {
diff --git a/third_party/blink/web_tests/external/wpt/css/css-intrinsic-size/intrinsic-size-with-legacy-003-ref.html b/third_party/blink/web_tests/external/wpt/css/css-sizing/intrinsic-size/intrinsic-size-with-legacy-003-ref.html
similarity index 100%
rename from third_party/blink/web_tests/external/wpt/css/css-intrinsic-size/intrinsic-size-with-legacy-003-ref.html
rename to third_party/blink/web_tests/external/wpt/css/css-sizing/intrinsic-size/intrinsic-size-with-legacy-003-ref.html
diff --git a/third_party/blink/web_tests/external/wpt/css/css-intrinsic-size/intrinsic-size-with-legacy-003.html b/third_party/blink/web_tests/external/wpt/css/css-sizing/intrinsic-size/intrinsic-size-with-legacy-003.html
similarity index 83%
rename from third_party/blink/web_tests/external/wpt/css/css-intrinsic-size/intrinsic-size-with-legacy-003.html
rename to third_party/blink/web_tests/external/wpt/css/css-sizing/intrinsic-size/intrinsic-size-with-legacy-003.html
index 78502f4b..90e4f53 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-intrinsic-size/intrinsic-size-with-legacy-003.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-sizing/intrinsic-size/intrinsic-size-with-legacy-003.html
@@ -4,6 +4,7 @@
 <link rel="author" title="Vladimir Levin" href="mailto:vmpstr@chromium.org">
 <link rel="help" href="https://drafts.csswg.org/css-sizing-4/#intrinsic-size-override">
 <link rel="match" href="intrinsic-size-with-legacy-003-ref.html">
+<meta name="assert" content="intrinsic-size legacy is treated the same as if it was not specified.">
 
 <style>
 #one {
diff --git a/third_party/blink/web_tests/external/wpt/css/css-intrinsic-size/intrinsic-size-with-legacy-004-ref.html b/third_party/blink/web_tests/external/wpt/css/css-sizing/intrinsic-size/intrinsic-size-with-legacy-004-ref.html
similarity index 100%
rename from third_party/blink/web_tests/external/wpt/css/css-intrinsic-size/intrinsic-size-with-legacy-004-ref.html
rename to third_party/blink/web_tests/external/wpt/css/css-sizing/intrinsic-size/intrinsic-size-with-legacy-004-ref.html
diff --git a/third_party/blink/web_tests/external/wpt/css/css-intrinsic-size/intrinsic-size-with-legacy-004.html b/third_party/blink/web_tests/external/wpt/css/css-sizing/intrinsic-size/intrinsic-size-with-legacy-004.html
similarity index 84%
rename from third_party/blink/web_tests/external/wpt/css/css-intrinsic-size/intrinsic-size-with-legacy-004.html
rename to third_party/blink/web_tests/external/wpt/css/css-sizing/intrinsic-size/intrinsic-size-with-legacy-004.html
index 7187222..dfeed45 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-intrinsic-size/intrinsic-size-with-legacy-004.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-sizing/intrinsic-size/intrinsic-size-with-legacy-004.html
@@ -4,6 +4,7 @@
 <link rel="author" title="Vladimir Levin" href="mailto:vmpstr@chromium.org">
 <link rel="help" href="https://drafts.csswg.org/css-sizing-4/#intrinsic-size-override">
 <link rel="match" href="intrinsic-size-with-legacy-004-ref.html">
+<meta name="assert" content="intrinsic-size legacy is treated the same as if it was not specified.">
 
 <style>
 img {
diff --git a/third_party/blink/web_tests/external/wpt/css/css-intrinsic-size/intrinsic-size-with-legacy-005-ref.html b/third_party/blink/web_tests/external/wpt/css/css-sizing/intrinsic-size/intrinsic-size-with-legacy-005-ref.html
similarity index 100%
rename from third_party/blink/web_tests/external/wpt/css/css-intrinsic-size/intrinsic-size-with-legacy-005-ref.html
rename to third_party/blink/web_tests/external/wpt/css/css-sizing/intrinsic-size/intrinsic-size-with-legacy-005-ref.html
diff --git a/third_party/blink/web_tests/external/wpt/css/css-intrinsic-size/intrinsic-size-with-legacy-005.html b/third_party/blink/web_tests/external/wpt/css/css-sizing/intrinsic-size/intrinsic-size-with-legacy-005.html
similarity index 90%
rename from third_party/blink/web_tests/external/wpt/css/css-intrinsic-size/intrinsic-size-with-legacy-005.html
rename to third_party/blink/web_tests/external/wpt/css/css-sizing/intrinsic-size/intrinsic-size-with-legacy-005.html
index 0ec60fd..baf9bef 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-intrinsic-size/intrinsic-size-with-legacy-005.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-sizing/intrinsic-size/intrinsic-size-with-legacy-005.html
@@ -4,6 +4,7 @@
 <link rel="author" title="Vladimir Levin" href="mailto:vmpstr@chromium.org">
 <link rel="help" href="https://drafts.csswg.org/css-sizing-4/#intrinsic-size-override">
 <link rel="match" href="intrinsic-size-with-legacy-005-ref.html">
+<meta name="assert" content="intrinsic-size legacy is treated the same as if it was not specified.">
 
 <style>
 .grid {
diff --git a/third_party/blink/web_tests/external/wpt/css/css-intrinsic-size/intrinsic-size-with-legacy-006-ref.html b/third_party/blink/web_tests/external/wpt/css/css-sizing/intrinsic-size/intrinsic-size-with-legacy-006-ref.html
similarity index 100%
rename from third_party/blink/web_tests/external/wpt/css/css-intrinsic-size/intrinsic-size-with-legacy-006-ref.html
rename to third_party/blink/web_tests/external/wpt/css/css-sizing/intrinsic-size/intrinsic-size-with-legacy-006-ref.html
diff --git a/third_party/blink/web_tests/external/wpt/css/css-intrinsic-size/intrinsic-size-with-legacy-006.html b/third_party/blink/web_tests/external/wpt/css/css-sizing/intrinsic-size/intrinsic-size-with-legacy-006.html
similarity index 90%
rename from third_party/blink/web_tests/external/wpt/css/css-intrinsic-size/intrinsic-size-with-legacy-006.html
rename to third_party/blink/web_tests/external/wpt/css/css-sizing/intrinsic-size/intrinsic-size-with-legacy-006.html
index e488253..41ef1fd 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-intrinsic-size/intrinsic-size-with-legacy-006.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-sizing/intrinsic-size/intrinsic-size-with-legacy-006.html
@@ -4,6 +4,7 @@
 <link rel="author" title="Vladimir Levin" href="mailto:vmpstr@chromium.org">
 <link rel="help" href="https://drafts.csswg.org/css-sizing-4/#intrinsic-size-override">
 <link rel="match" href="intrinsic-size-with-legacy-006-ref.html">
+<meta name="assert" content="intrinsic-size legacy is treated the same as if it was not specified.">
 
 <style>
 .grid {
diff --git a/third_party/blink/web_tests/external/wpt/css/css-intrinsic-size/intrinsic-size-with-legacy-007-ref.html b/third_party/blink/web_tests/external/wpt/css/css-sizing/intrinsic-size/intrinsic-size-with-legacy-007-ref.html
similarity index 100%
rename from third_party/blink/web_tests/external/wpt/css/css-intrinsic-size/intrinsic-size-with-legacy-007-ref.html
rename to third_party/blink/web_tests/external/wpt/css/css-sizing/intrinsic-size/intrinsic-size-with-legacy-007-ref.html
diff --git a/third_party/blink/web_tests/external/wpt/css/css-intrinsic-size/intrinsic-size-with-legacy-007.html b/third_party/blink/web_tests/external/wpt/css/css-sizing/intrinsic-size/intrinsic-size-with-legacy-007.html
similarity index 90%
rename from third_party/blink/web_tests/external/wpt/css/css-intrinsic-size/intrinsic-size-with-legacy-007.html
rename to third_party/blink/web_tests/external/wpt/css/css-sizing/intrinsic-size/intrinsic-size-with-legacy-007.html
index 0e749f70..df2c57b 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-intrinsic-size/intrinsic-size-with-legacy-007.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-sizing/intrinsic-size/intrinsic-size-with-legacy-007.html
@@ -4,6 +4,7 @@
 <link rel="author" title="Vladimir Levin" href="mailto:vmpstr@chromium.org">
 <link rel="help" href="https://drafts.csswg.org/css-sizing-4/#intrinsic-size-override">
 <link rel="match" href="intrinsic-size-with-legacy-007-ref.html">
+<meta name="assert" content="intrinsic-size legacy is treated the same as if it was not specified.">
 
 <style>
 .grid {
diff --git a/third_party/blink/web_tests/external/wpt/css/css-intrinsic-size/intrinsic-size-with-legacy-008-ref.html b/third_party/blink/web_tests/external/wpt/css/css-sizing/intrinsic-size/intrinsic-size-with-legacy-008-ref.html
similarity index 100%
rename from third_party/blink/web_tests/external/wpt/css/css-intrinsic-size/intrinsic-size-with-legacy-008-ref.html
rename to third_party/blink/web_tests/external/wpt/css/css-sizing/intrinsic-size/intrinsic-size-with-legacy-008-ref.html
diff --git a/third_party/blink/web_tests/external/wpt/css/css-intrinsic-size/intrinsic-size-with-legacy-008.html b/third_party/blink/web_tests/external/wpt/css/css-sizing/intrinsic-size/intrinsic-size-with-legacy-008.html
similarity index 94%
rename from third_party/blink/web_tests/external/wpt/css/css-intrinsic-size/intrinsic-size-with-legacy-008.html
rename to third_party/blink/web_tests/external/wpt/css/css-sizing/intrinsic-size/intrinsic-size-with-legacy-008.html
index 973683a..45b8fbd 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-intrinsic-size/intrinsic-size-with-legacy-008.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-sizing/intrinsic-size/intrinsic-size-with-legacy-008.html
@@ -4,6 +4,7 @@
 <link rel="author" title="Vladimir Levin" href="mailto:vmpstr@chromium.org">
 <link rel="help" href="https://drafts.csswg.org/css-sizing-4/#intrinsic-size-override">
 <link rel="match" href="intrinsic-size-with-legacy-008-ref.html">
+<meta name="assert" content="intrinsic-size legacy is treated the same as if it was not specified.">
 
 <style>
 .grid {
diff --git a/third_party/blink/web_tests/external/wpt/css/css-intrinsic-size/parsing/intrinsic-block-size-computed.html b/third_party/blink/web_tests/external/wpt/css/css-sizing/intrinsic-size/parsing/intrinsic-block-size-computed.html
similarity index 100%
rename from third_party/blink/web_tests/external/wpt/css/css-intrinsic-size/parsing/intrinsic-block-size-computed.html
rename to third_party/blink/web_tests/external/wpt/css/css-sizing/intrinsic-size/parsing/intrinsic-block-size-computed.html
diff --git a/third_party/blink/web_tests/external/wpt/css/css-intrinsic-size/parsing/intrinsic-block-size-invalid.html b/third_party/blink/web_tests/external/wpt/css/css-sizing/intrinsic-size/parsing/intrinsic-block-size-invalid.html
similarity index 100%
rename from third_party/blink/web_tests/external/wpt/css/css-intrinsic-size/parsing/intrinsic-block-size-invalid.html
rename to third_party/blink/web_tests/external/wpt/css/css-sizing/intrinsic-size/parsing/intrinsic-block-size-invalid.html
diff --git a/third_party/blink/web_tests/external/wpt/css/css-intrinsic-size/parsing/intrinsic-block-size-valid.html b/third_party/blink/web_tests/external/wpt/css/css-sizing/intrinsic-size/parsing/intrinsic-block-size-valid.html
similarity index 100%
rename from third_party/blink/web_tests/external/wpt/css/css-intrinsic-size/parsing/intrinsic-block-size-valid.html
rename to third_party/blink/web_tests/external/wpt/css/css-sizing/intrinsic-size/parsing/intrinsic-block-size-valid.html
diff --git a/third_party/blink/web_tests/external/wpt/css/css-intrinsic-size/parsing/intrinsic-height-computed.html b/third_party/blink/web_tests/external/wpt/css/css-sizing/intrinsic-size/parsing/intrinsic-height-computed.html
similarity index 100%
rename from third_party/blink/web_tests/external/wpt/css/css-intrinsic-size/parsing/intrinsic-height-computed.html
rename to third_party/blink/web_tests/external/wpt/css/css-sizing/intrinsic-size/parsing/intrinsic-height-computed.html
diff --git a/third_party/blink/web_tests/external/wpt/css/css-intrinsic-size/parsing/intrinsic-height-invalid.html b/third_party/blink/web_tests/external/wpt/css/css-sizing/intrinsic-size/parsing/intrinsic-height-invalid.html
similarity index 100%
rename from third_party/blink/web_tests/external/wpt/css/css-intrinsic-size/parsing/intrinsic-height-invalid.html
rename to third_party/blink/web_tests/external/wpt/css/css-sizing/intrinsic-size/parsing/intrinsic-height-invalid.html
diff --git a/third_party/blink/web_tests/external/wpt/css/css-intrinsic-size/parsing/intrinsic-height-valid.html b/third_party/blink/web_tests/external/wpt/css/css-sizing/intrinsic-size/parsing/intrinsic-height-valid.html
similarity index 100%
rename from third_party/blink/web_tests/external/wpt/css/css-intrinsic-size/parsing/intrinsic-height-valid.html
rename to third_party/blink/web_tests/external/wpt/css/css-sizing/intrinsic-size/parsing/intrinsic-height-valid.html
diff --git a/third_party/blink/web_tests/external/wpt/css/css-intrinsic-size/parsing/intrinsic-inline-size-computed.html b/third_party/blink/web_tests/external/wpt/css/css-sizing/intrinsic-size/parsing/intrinsic-inline-size-computed.html
similarity index 100%
rename from third_party/blink/web_tests/external/wpt/css/css-intrinsic-size/parsing/intrinsic-inline-size-computed.html
rename to third_party/blink/web_tests/external/wpt/css/css-sizing/intrinsic-size/parsing/intrinsic-inline-size-computed.html
diff --git a/third_party/blink/web_tests/external/wpt/css/css-intrinsic-size/parsing/intrinsic-inline-size-invalid.html b/third_party/blink/web_tests/external/wpt/css/css-sizing/intrinsic-size/parsing/intrinsic-inline-size-invalid.html
similarity index 100%
rename from third_party/blink/web_tests/external/wpt/css/css-intrinsic-size/parsing/intrinsic-inline-size-invalid.html
rename to third_party/blink/web_tests/external/wpt/css/css-sizing/intrinsic-size/parsing/intrinsic-inline-size-invalid.html
diff --git a/third_party/blink/web_tests/external/wpt/css/css-intrinsic-size/parsing/intrinsic-inline-size-valid.html b/third_party/blink/web_tests/external/wpt/css/css-sizing/intrinsic-size/parsing/intrinsic-inline-size-valid.html
similarity index 100%
rename from third_party/blink/web_tests/external/wpt/css/css-intrinsic-size/parsing/intrinsic-inline-size-valid.html
rename to third_party/blink/web_tests/external/wpt/css/css-sizing/intrinsic-size/parsing/intrinsic-inline-size-valid.html
diff --git a/third_party/blink/web_tests/external/wpt/css/css-intrinsic-size/parsing/intrinsic-size-computed.html b/third_party/blink/web_tests/external/wpt/css/css-sizing/intrinsic-size/parsing/intrinsic-size-computed.html
similarity index 100%
rename from third_party/blink/web_tests/external/wpt/css/css-intrinsic-size/parsing/intrinsic-size-computed.html
rename to third_party/blink/web_tests/external/wpt/css/css-sizing/intrinsic-size/parsing/intrinsic-size-computed.html
diff --git a/third_party/blink/web_tests/external/wpt/css/css-intrinsic-size/parsing/intrinsic-size-invalid.html b/third_party/blink/web_tests/external/wpt/css/css-sizing/intrinsic-size/parsing/intrinsic-size-invalid.html
similarity index 100%
rename from third_party/blink/web_tests/external/wpt/css/css-intrinsic-size/parsing/intrinsic-size-invalid.html
rename to third_party/blink/web_tests/external/wpt/css/css-sizing/intrinsic-size/parsing/intrinsic-size-invalid.html
diff --git a/third_party/blink/web_tests/external/wpt/css/css-intrinsic-size/parsing/intrinsic-size-sets-computed-dimensions.html b/third_party/blink/web_tests/external/wpt/css/css-sizing/intrinsic-size/parsing/intrinsic-size-sets-computed-dimensions.html
similarity index 100%
rename from third_party/blink/web_tests/external/wpt/css/css-intrinsic-size/parsing/intrinsic-size-sets-computed-dimensions.html
rename to third_party/blink/web_tests/external/wpt/css/css-sizing/intrinsic-size/parsing/intrinsic-size-sets-computed-dimensions.html
diff --git a/third_party/blink/web_tests/external/wpt/css/css-intrinsic-size/parsing/intrinsic-size-valid.html b/third_party/blink/web_tests/external/wpt/css/css-sizing/intrinsic-size/parsing/intrinsic-size-valid.html
similarity index 100%
rename from third_party/blink/web_tests/external/wpt/css/css-intrinsic-size/parsing/intrinsic-size-valid.html
rename to third_party/blink/web_tests/external/wpt/css/css-sizing/intrinsic-size/parsing/intrinsic-size-valid.html
diff --git a/third_party/blink/web_tests/external/wpt/css/css-intrinsic-size/parsing/intrinsic-width-computed.html b/third_party/blink/web_tests/external/wpt/css/css-sizing/intrinsic-size/parsing/intrinsic-width-computed.html
similarity index 100%
rename from third_party/blink/web_tests/external/wpt/css/css-intrinsic-size/parsing/intrinsic-width-computed.html
rename to third_party/blink/web_tests/external/wpt/css/css-sizing/intrinsic-size/parsing/intrinsic-width-computed.html
diff --git a/third_party/blink/web_tests/external/wpt/css/css-intrinsic-size/parsing/intrinsic-width-invalid.html b/third_party/blink/web_tests/external/wpt/css/css-sizing/intrinsic-size/parsing/intrinsic-width-invalid.html
similarity index 100%
rename from third_party/blink/web_tests/external/wpt/css/css-intrinsic-size/parsing/intrinsic-width-invalid.html
rename to third_party/blink/web_tests/external/wpt/css/css-sizing/intrinsic-size/parsing/intrinsic-width-invalid.html
diff --git a/third_party/blink/web_tests/external/wpt/css/css-intrinsic-size/parsing/intrinsic-width-valid.html b/third_party/blink/web_tests/external/wpt/css/css-sizing/intrinsic-size/parsing/intrinsic-width-valid.html
similarity index 100%
rename from third_party/blink/web_tests/external/wpt/css/css-intrinsic-size/parsing/intrinsic-width-valid.html
rename to third_party/blink/web_tests/external/wpt/css/css-sizing/intrinsic-size/parsing/intrinsic-width-valid.html
diff --git a/third_party/blink/web_tests/external/wpt/css/css-intrinsic-size/resources/dice.png b/third_party/blink/web_tests/external/wpt/css/css-sizing/intrinsic-size/resources/dice.png
similarity index 100%
rename from third_party/blink/web_tests/external/wpt/css/css-intrinsic-size/resources/dice.png
rename to third_party/blink/web_tests/external/wpt/css/css-sizing/intrinsic-size/resources/dice.png
Binary files differ
diff --git a/third_party/blink/web_tests/http/tests/credentialmanager/publickeycredential-basics.html b/third_party/blink/web_tests/http/tests/credentialmanager/publickeycredential-basics.html
index 4a76b66..165a519c 100644
--- a/third_party/blink/web_tests/http/tests/credentialmanager/publickeycredential-basics.html
+++ b/third_party/blink/web_tests/http/tests/credentialmanager/publickeycredential-basics.html
@@ -13,7 +13,6 @@
 <script src="/gen/third_party/blink/public/mojom/credentialmanager/credential_manager.mojom-lite.js"></script>
 <script src="/gen/third_party/blink/public/mojom/webauthn/authenticator.mojom-lite.js"></script>
 <script src="/gen/third_party/blink/public/mojom/webauthn/virtual_authenticator.mojom-lite.js"></script>
-<script src="/gen/third_party/blink/public/mojom/frame/document_interface_broker.mojom-lite.js"></script>
 <script src="resources/virtual-navigator-credentials.js"></script>
 <script>
 
diff --git a/third_party/blink/web_tests/http/tests/inspector-protocol/network/interception-redirect-client-hint-expected.txt b/third_party/blink/web_tests/http/tests/inspector-protocol/network/interception-redirect-client-hint-expected.txt
new file mode 100644
index 0000000..d63d4b7
--- /dev/null
+++ b/third_party/blink/web_tests/http/tests/inspector-protocol/network/interception-redirect-client-hint-expected.txt
@@ -0,0 +1,8 @@
+Test that UA client hints are added on redirect.
+
+Got request: GET http://127.0.1.1:8000/whatever
+sec-ch-ua: content_shell 99
+Got request: GET http://127.0.0.1:8000/devtools/resources/empty.html
+sec-ch-ua: content_shell 99
+dpr: 1
+
diff --git a/third_party/blink/web_tests/http/tests/inspector-protocol/network/interception-redirect-client-hint.js b/third_party/blink/web_tests/http/tests/inspector-protocol/network/interception-redirect-client-hint.js
new file mode 100644
index 0000000..4d7da0d
--- /dev/null
+++ b/third_party/blink/web_tests/http/tests/inspector-protocol/network/interception-redirect-client-hint.js
@@ -0,0 +1,37 @@
+// 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.
+
+(async function(testRunner) {
+  document.head.innerHTML = ' <meta http-equiv="Accept-CH" content="DPR"> <meta http-equiv="Accept-CH-Lifetime" content="1">';
+  var {page, session, dp} = await testRunner.startBlank(`Test that UA client hints are added on redirect.\n`);
+
+  dp.Network.enable();
+  dp.Page.enable();
+
+  dp.Network.setRequestInterception({patterns: [{}]});
+  dp.Runtime.evaluate({expression: `
+    document.body.innerHTML = '<iframe src="http://127.0.1.1:8000/whatever"></iframe>';
+  `});
+
+  dp.Network.onRequestIntercepted(event => {
+    const request = event.params.request;
+    testRunner.log(`Got request: ${request.method} ${request.url}`);
+    for (const header of ["sec-ch-ua", "dpr"]) {
+      if (`${request.headers[header]}` != "undefined") {
+        testRunner.log(`${header}: ${request.headers[header]}`);
+      }
+    }
+    const url = `${request.url}`;
+    if (url.includes("empty.html")) {
+      testRunner.completeTest()
+    }
+  });
+  let params = (await dp.Network.onceRequestIntercepted()).params;
+  const response = "HTTP/1.1 303 See other\r\n" +
+      "Location: http://127.0.0.1:8000/devtools/resources/empty.html\r\n\r\n";
+  dp.Network.continueInterceptedRequest({interceptionId: params.interceptionId, rawResponse: btoa(response)});
+  params = (await dp.Network.onceRequestIntercepted()).params;
+  dp.Network.continueInterceptedRequest({interceptionId: params.interceptionId});
+});
+
diff --git a/third_party/blink/web_tests/http/tests/mojo/document-interface-broker-override.html b/third_party/blink/web_tests/http/tests/mojo/document-interface-broker-override.html
deleted file mode 100644
index 930f4a2..0000000
--- a/third_party/blink/web_tests/http/tests/mojo/document-interface-broker-override.html
+++ /dev/null
@@ -1,43 +0,0 @@
-<body>
-<script src="/js-test-resources/document-interface-broker-helpers.js"></script>
-<script src="/resources/testharness.js"></script>
-<script src="/resources/testharnessreport.js"></script>
-<script src="/gen/layout_test_data/mojo/public/js/mojo_bindings_lite.js"></script>
-<script src="/gen/mojo/public/mojom/base/unguessable_token.mojom-lite.js"></script>
-<script src="/gen/url/mojom/url.mojom-lite.js"></script>
-<script src="/gen/third_party/blink/public/mojom/frame/frame_host_test_interface.mojom-lite.js"></script>
-<script src="/gen/third_party/blink/public/mojom/frame/document_interface_broker.mojom-lite.js"></script>
-<script>
-'use strict';
-
-promise_test(async t => {
-  // Create a test implementation of FrameHostTestInterface
-  const frameHostTestImpl = new blink.mojom.FrameHostTestInterfaceCallbackRouter;
-  frameHostTestImpl.getName.addListener(() => ({ name: 'TestFrameHostTestImpl' }));
-
-  const broker = new blink.mojom.DocumentInterfaceBrokerRemote(
-      Mojo.getDocumentInterfaceBrokerHandle());
-
-  const testInterfaceBeforeOverride = new blink.mojom.FrameHostTestInterfaceRemote;
-  broker.getFrameHostTestInterface(testInterfaceBeforeOverride.$.bindNewPipeAndPassReceiver());
-
-  setDocumentInterfaceBrokerOverrides({ getFrameHostTestInterface: request => {
-    frameHostTestImpl.$.bindHandle(request.handle);
-  }});
-
-  const testInterfaceAfterOverride = new blink.mojom.FrameHostTestInterfaceRemote;
-  broker.getFrameHostTestInterface(testInterfaceAfterOverride.$.bindNewPipeAndPassReceiver());
-
-  // Verify that RenderFrameHostImpl's implementation gets called without an override
-  let reply = await testInterfaceBeforeOverride.getName();
-  assert_equals(reply.name, 'RenderFrameHostImpl');
-
-  // Verify that the test implementation gets called after the override
-  reply = await testInterfaceAfterOverride.getName();
-  assert_equals(reply.name, 'TestFrameHostTestImpl');
-},
-'Appropriate DocumentInterfaceBroker implementations are called before and after overriding');
-
-</script>
- </body>
- </html>
diff --git a/third_party/blink/web_tests/http/tests/navigation/form-targets-cross-site-frame-get-expected.txt b/third_party/blink/web_tests/http/tests/navigation/form-targets-cross-site-frame-get-expected.txt
index b8ac378..d8ecec6 100644
--- a/third_party/blink/web_tests/http/tests/navigation/form-targets-cross-site-frame-get-expected.txt
+++ b/third_party/blink/web_tests/http/tests/navigation/form-targets-cross-site-frame-get-expected.txt
@@ -14,6 +14,7 @@
 HTTP_CONNECTION = keep-alive
 HTTP_HOST = localhost:8080
 HTTP_REFERER = http://127.0.0.1:8000/navigation/form-targets-cross-site-frame-get.html
+HTTP_SEC_CH_UA = content_shell 99
 HTTP_SEC_FETCH_DEST = iframe
 HTTP_SEC_FETCH_MODE = navigate
 HTTP_SEC_FETCH_SITE = cross-site
diff --git a/third_party/blink/web_tests/http/tests/navigation/form-targets-cross-site-frame-no-referrer-expected.txt b/third_party/blink/web_tests/http/tests/navigation/form-targets-cross-site-frame-no-referrer-expected.txt
index 83f2240..ad7dd16 100644
--- a/third_party/blink/web_tests/http/tests/navigation/form-targets-cross-site-frame-no-referrer-expected.txt
+++ b/third_party/blink/web_tests/http/tests/navigation/form-targets-cross-site-frame-no-referrer-expected.txt
@@ -15,6 +15,7 @@
 HTTP_CONNECTION = keep-alive
 HTTP_HOST = localhost:8080
 HTTP_ORIGIN = null
+HTTP_SEC_CH_UA = content_shell 99
 HTTP_SEC_FETCH_DEST = iframe
 HTTP_SEC_FETCH_MODE = navigate
 HTTP_SEC_FETCH_SITE = cross-site
diff --git a/third_party/blink/web_tests/http/tests/navigation/form-targets-cross-site-frame-post-expected.txt b/third_party/blink/web_tests/http/tests/navigation/form-targets-cross-site-frame-post-expected.txt
index 56e67f46..abf8d42 100644
--- a/third_party/blink/web_tests/http/tests/navigation/form-targets-cross-site-frame-post-expected.txt
+++ b/third_party/blink/web_tests/http/tests/navigation/form-targets-cross-site-frame-post-expected.txt
@@ -16,6 +16,7 @@
 HTTP_HOST = localhost:8080
 HTTP_ORIGIN = http://127.0.0.1:8000
 HTTP_REFERER = http://127.0.0.1:8000/navigation/form-targets-cross-site-frame-post.html
+HTTP_SEC_CH_UA = content_shell 99
 HTTP_SEC_FETCH_DEST = iframe
 HTTP_SEC_FETCH_MODE = navigate
 HTTP_SEC_FETCH_SITE = cross-site
diff --git a/third_party/blink/web_tests/http/tests/navigation/form-with-enctype-targets-cross-site-frame-expected.txt b/third_party/blink/web_tests/http/tests/navigation/form-with-enctype-targets-cross-site-frame-expected.txt
index 16e7e64..946ddcbc 100644
--- a/third_party/blink/web_tests/http/tests/navigation/form-with-enctype-targets-cross-site-frame-expected.txt
+++ b/third_party/blink/web_tests/http/tests/navigation/form-with-enctype-targets-cross-site-frame-expected.txt
@@ -16,6 +16,7 @@
 HTTP_HOST = localhost:8080
 HTTP_ORIGIN = http://127.0.0.1:8000
 HTTP_REFERER = http://127.0.0.1:8000/navigation/form-with-enctype-targets-cross-site-frame.html
+HTTP_SEC_CH_UA = content_shell 99
 HTTP_SEC_FETCH_DEST = iframe
 HTTP_SEC_FETCH_MODE = navigate
 HTTP_SEC_FETCH_SITE = cross-site
diff --git a/third_party/blink/web_tests/http/tests/navigation/post-basic-expected.txt b/third_party/blink/web_tests/http/tests/navigation/post-basic-expected.txt
index 6753a6b..be505ee 100644
--- a/third_party/blink/web_tests/http/tests/navigation/post-basic-expected.txt
+++ b/third_party/blink/web_tests/http/tests/navigation/post-basic-expected.txt
@@ -12,6 +12,7 @@
 HTTP_HOST = 127.0.0.1:8000
 HTTP_ORIGIN = http://127.0.0.1:8000
 HTTP_REFERER = http://127.0.0.1:8000/navigation/resources/page-that-posts.html
+HTTP_SEC_CH_UA = content_shell 99
 HTTP_SEC_FETCH_DEST = document
 HTTP_SEC_FETCH_MODE = navigate
 HTTP_SEC_FETCH_SITE = same-origin
diff --git a/third_party/blink/web_tests/http/tests/navigation/post-frames-expected.txt b/third_party/blink/web_tests/http/tests/navigation/post-frames-expected.txt
index 2f68b2d..ccd1c90 100644
--- a/third_party/blink/web_tests/http/tests/navigation/post-frames-expected.txt
+++ b/third_party/blink/web_tests/http/tests/navigation/post-frames-expected.txt
@@ -17,6 +17,7 @@
 HTTP_HOST = 127.0.0.1:8000
 HTTP_ORIGIN = http://127.0.0.1:8000
 HTTP_REFERER = http://127.0.0.1:8000/navigation/resources/page-that-posts.html
+HTTP_SEC_CH_UA = content_shell 99
 HTTP_SEC_FETCH_DEST = iframe
 HTTP_SEC_FETCH_MODE = navigate
 HTTP_SEC_FETCH_SITE = same-origin
diff --git a/third_party/blink/web_tests/http/tests/navigation/post-frames-goback1-expected.txt b/third_party/blink/web_tests/http/tests/navigation/post-frames-goback1-expected.txt
index 3cc4535..45d842f 100644
--- a/third_party/blink/web_tests/http/tests/navigation/post-frames-goback1-expected.txt
+++ b/third_party/blink/web_tests/http/tests/navigation/post-frames-goback1-expected.txt
@@ -16,6 +16,7 @@
 HTTP_HOST = 127.0.0.1:8000
 HTTP_ORIGIN = http://127.0.0.1:8000
 HTTP_REFERER = http://127.0.0.1:8000/navigation/post-frames-goback1.html
+HTTP_SEC_CH_UA = content_shell 99
 HTTP_SEC_FETCH_DEST = iframe
 HTTP_SEC_FETCH_MODE = navigate
 HTTP_SEC_FETCH_SITE = same-origin
diff --git a/third_party/blink/web_tests/http/tests/navigation/post-goback1-expected.txt b/third_party/blink/web_tests/http/tests/navigation/post-goback1-expected.txt
index 44c870e..a43e1d1 100644
--- a/third_party/blink/web_tests/http/tests/navigation/post-goback1-expected.txt
+++ b/third_party/blink/web_tests/http/tests/navigation/post-goback1-expected.txt
@@ -12,6 +12,7 @@
 HTTP_HOST = 127.0.0.1:8000
 HTTP_ORIGIN = http://127.0.0.1:8000
 HTTP_REFERER = http://127.0.0.1:8000/navigation/resources/page-that-posts.html
+HTTP_SEC_CH_UA = content_shell 99
 HTTP_SEC_FETCH_DEST = document
 HTTP_SEC_FETCH_MODE = navigate
 HTTP_SEC_FETCH_SITE = same-origin
diff --git a/third_party/blink/web_tests/http/tests/navigation/rename-subframe-goback-expected.txt b/third_party/blink/web_tests/http/tests/navigation/rename-subframe-goback-expected.txt
index 8ea7757..7e74608 100644
--- a/third_party/blink/web_tests/http/tests/navigation/rename-subframe-goback-expected.txt
+++ b/third_party/blink/web_tests/http/tests/navigation/rename-subframe-goback-expected.txt
@@ -20,6 +20,7 @@
 HTTP_HOST = 127.0.0.1:8000
 HTTP_ORIGIN = http://127.0.0.1:8000
 HTTP_REFERER = http://127.0.0.1:8000/navigation/resources/page-that-posts.html
+HTTP_SEC_CH_UA = content_shell 99
 HTTP_SEC_FETCH_DEST = iframe
 HTTP_SEC_FETCH_MODE = navigate
 HTTP_SEC_FETCH_SITE = same-origin
diff --git a/third_party/blink/web_tests/http/tests/security/contentSecurityPolicy/1.1/form-action-leak-path-on-redirect-expected.txt b/third_party/blink/web_tests/http/tests/security/contentSecurityPolicy/1.1/form-action-leak-path-on-redirect-expected.txt
index d78cc87..d4ed2e1 100644
--- a/third_party/blink/web_tests/http/tests/security/contentSecurityPolicy/1.1/form-action-leak-path-on-redirect-expected.txt
+++ b/third_party/blink/web_tests/http/tests/security/contentSecurityPolicy/1.1/form-action-leak-path-on-redirect-expected.txt
@@ -8,6 +8,7 @@
 HTTP_CONNECTION = keep-alive
 HTTP_HOST = 127.0.0.1:8000
 HTTP_REFERER = http://127.0.0.1:8000/security/contentSecurityPolicy/1.1/form-action-leak-path-on-redirect.html
+HTTP_SEC_CH_UA = content_shell 99
 HTTP_SEC_FETCH_DEST = document
 HTTP_SEC_FETCH_MODE = navigate
 HTTP_SEC_FETCH_SITE = same-origin
diff --git a/third_party/blink/web_tests/http/tests/security/contentSecurityPolicy/1.1/form-action-src-allowed-expected.txt b/third_party/blink/web_tests/http/tests/security/contentSecurityPolicy/1.1/form-action-src-allowed-expected.txt
index 699d86f9..4fdbb732 100644
--- a/third_party/blink/web_tests/http/tests/security/contentSecurityPolicy/1.1/form-action-src-allowed-expected.txt
+++ b/third_party/blink/web_tests/http/tests/security/contentSecurityPolicy/1.1/form-action-src-allowed-expected.txt
@@ -11,6 +11,7 @@
 HTTP_HOST = 127.0.0.1:8000
 HTTP_ORIGIN = http://127.0.0.1:8000
 HTTP_REFERER = http://127.0.0.1:8000/security/contentSecurityPolicy/1.1/form-action-src-allowed.html
+HTTP_SEC_CH_UA = content_shell 99
 HTTP_SEC_FETCH_DEST = document
 HTTP_SEC_FETCH_MODE = navigate
 HTTP_SEC_FETCH_SITE = same-origin
diff --git a/third_party/blink/web_tests/http/tests/security/contentSecurityPolicy/1.1/form-action-src-allowed-with-redirect-expected.txt b/third_party/blink/web_tests/http/tests/security/contentSecurityPolicy/1.1/form-action-src-allowed-with-redirect-expected.txt
index 36a810c..29d8dd4 100644
--- a/third_party/blink/web_tests/http/tests/security/contentSecurityPolicy/1.1/form-action-src-allowed-with-redirect-expected.txt
+++ b/third_party/blink/web_tests/http/tests/security/contentSecurityPolicy/1.1/form-action-src-allowed-with-redirect-expected.txt
@@ -8,6 +8,7 @@
 HTTP_CONNECTION = keep-alive
 HTTP_HOST = 127.0.0.1:8000
 HTTP_REFERER = http://127.0.0.1:8000/security/contentSecurityPolicy/1.1/form-action-src-allowed-with-redirect.html
+HTTP_SEC_CH_UA = content_shell 99
 HTTP_SEC_FETCH_DEST = document
 HTTP_SEC_FETCH_MODE = navigate
 HTTP_SEC_FETCH_SITE = same-origin
diff --git a/third_party/blink/web_tests/http/tests/security/contentSecurityPolicy/1.1/form-action-src-default-ignored-expected.txt b/third_party/blink/web_tests/http/tests/security/contentSecurityPolicy/1.1/form-action-src-default-ignored-expected.txt
index eee3cd51..545c258 100644
--- a/third_party/blink/web_tests/http/tests/security/contentSecurityPolicy/1.1/form-action-src-default-ignored-expected.txt
+++ b/third_party/blink/web_tests/http/tests/security/contentSecurityPolicy/1.1/form-action-src-default-ignored-expected.txt
@@ -11,6 +11,7 @@
 HTTP_HOST = 127.0.0.1:8000
 HTTP_ORIGIN = http://127.0.0.1:8000
 HTTP_REFERER = http://127.0.0.1:8000/security/contentSecurityPolicy/1.1/form-action-src-default-ignored.html
+HTTP_SEC_CH_UA = content_shell 99
 HTTP_SEC_FETCH_DEST = document
 HTTP_SEC_FETCH_MODE = navigate
 HTTP_SEC_FETCH_SITE = same-origin
diff --git a/third_party/blink/web_tests/http/tests/security/contentSecurityPolicy/1.1/form-action-src-default-ignored-with-redirect-expected.txt b/third_party/blink/web_tests/http/tests/security/contentSecurityPolicy/1.1/form-action-src-default-ignored-with-redirect-expected.txt
index 962a40a..ac02f24 100644
--- a/third_party/blink/web_tests/http/tests/security/contentSecurityPolicy/1.1/form-action-src-default-ignored-with-redirect-expected.txt
+++ b/third_party/blink/web_tests/http/tests/security/contentSecurityPolicy/1.1/form-action-src-default-ignored-with-redirect-expected.txt
@@ -8,6 +8,7 @@
 HTTP_CONNECTION = keep-alive
 HTTP_HOST = 127.0.0.1:8000
 HTTP_REFERER = http://127.0.0.1:8000/security/contentSecurityPolicy/1.1/form-action-src-default-ignored-with-redirect.html
+HTTP_SEC_CH_UA = content_shell 99
 HTTP_SEC_FETCH_DEST = document
 HTTP_SEC_FETCH_MODE = navigate
 HTTP_SEC_FETCH_SITE = same-origin
diff --git a/third_party/blink/web_tests/http/tests/security/contentSecurityPolicy/1.1/form-action-src-get-allowed-expected.txt b/third_party/blink/web_tests/http/tests/security/contentSecurityPolicy/1.1/form-action-src-get-allowed-expected.txt
index 6c2b09e..06a7109 100644
--- a/third_party/blink/web_tests/http/tests/security/contentSecurityPolicy/1.1/form-action-src-get-allowed-expected.txt
+++ b/third_party/blink/web_tests/http/tests/security/contentSecurityPolicy/1.1/form-action-src-get-allowed-expected.txt
@@ -9,6 +9,7 @@
 HTTP_CONNECTION = keep-alive
 HTTP_HOST = 127.0.0.1:8000
 HTTP_REFERER = http://127.0.0.1:8000/security/contentSecurityPolicy/1.1/form-action-src-get-allowed.html
+HTTP_SEC_CH_UA = content_shell 99
 HTTP_SEC_FETCH_DEST = document
 HTTP_SEC_FETCH_MODE = navigate
 HTTP_SEC_FETCH_SITE = same-origin
diff --git a/third_party/blink/web_tests/http/tests/security/contentSecurityPolicy/1.1/form-action-src-get-allowed-with-redirect-expected.txt b/third_party/blink/web_tests/http/tests/security/contentSecurityPolicy/1.1/form-action-src-get-allowed-with-redirect-expected.txt
index 4747b394..ee81c4b 100644
--- a/third_party/blink/web_tests/http/tests/security/contentSecurityPolicy/1.1/form-action-src-get-allowed-with-redirect-expected.txt
+++ b/third_party/blink/web_tests/http/tests/security/contentSecurityPolicy/1.1/form-action-src-get-allowed-with-redirect-expected.txt
@@ -8,6 +8,7 @@
 HTTP_CONNECTION = keep-alive
 HTTP_HOST = 127.0.0.1:8000
 HTTP_REFERER = http://127.0.0.1:8000/security/contentSecurityPolicy/1.1/form-action-src-get-allowed-with-redirect.html
+HTTP_SEC_CH_UA = content_shell 99
 HTTP_SEC_FETCH_DEST = document
 HTTP_SEC_FETCH_MODE = navigate
 HTTP_SEC_FETCH_SITE = same-origin
diff --git a/third_party/blink/web_tests/http/tests/serviceworker/fetch-event-headers.html b/third_party/blink/web_tests/http/tests/serviceworker/fetch-event-headers.html
index 76f86e9..b4227922 100644
--- a/third_party/blink/web_tests/http/tests/serviceworker/fetch-event-headers.html
+++ b/third_party/blink/web_tests/http/tests/serviceworker/fetch-event-headers.html
@@ -31,7 +31,7 @@
           header_names.sort();
           assert_array_equals(
             header_names,
-            ["accept", "upgrade-insecure-requests", "user-agent"],
+            ["accept", "sec-ch-ua", "upgrade-insecure-requests", "user-agent"],
             'event.request has the expected headers.');
 
           return service_worker_unregister_and_done(t, scope);
diff --git a/third_party/blink/web_tests/http/tests/serviceworker/webexposed/global-interface-listing-service-worker-expected.txt b/third_party/blink/web_tests/http/tests/serviceworker/webexposed/global-interface-listing-service-worker-expected.txt
index c1f2a4f..12996e81 100644
--- a/third_party/blink/web_tests/http/tests/serviceworker/webexposed/global-interface-listing-service-worker-expected.txt
+++ b/third_party/blink/web_tests/http/tests/serviceworker/webexposed/global-interface-listing-service-worker-expected.txt
@@ -834,8 +834,6 @@
     static method createDataPipe
     static method createMessagePipe
     static method createSharedBuffer
-    static method getDocumentInterfaceBrokerHandle
-    static method replaceDocumentInterfaceBrokerForTesting
     attribute @@toStringTag
     attribute RESULT_ABORTED
     attribute RESULT_ALREADY_EXISTS
diff --git a/third_party/blink/web_tests/http/tests/xmlviewer/dumpAsText/mathml-expected.txt b/third_party/blink/web_tests/http/tests/xmlviewer/dumpAsText/mathml-expected.txt
index 337e9cd..cd3d768 100644
--- a/third_party/blink/web_tests/http/tests/xmlviewer/dumpAsText/mathml-expected.txt
+++ b/third_party/blink/web_tests/http/tests/xmlviewer/dumpAsText/mathml-expected.txt
@@ -1,16 +1 @@
-This XML file does not appear to have any style information associated with it. The document tree is shown below.
-
-<!--
- If you can see this, this test has failed, except if you are
-     not using a MathML-aware client. 
--->
-<FAIL xmlns:a="http://www.w3.org/1998/Math/MathML">
-<header>
-This tests that xml viewer is not used when there is a tag in MATHML namespace.
-</header>
-<a:math>
-<a:mrow>
-<a:mi>SUCCESS</a:mi>
-</a:mrow>
-</a:math>
-</FAIL>
+This tests that xml viewer is not used when there is a tag in MATHML namespace. SUCCESS
diff --git a/third_party/blink/web_tests/resources/document-interface-broker-helpers.js b/third_party/blink/web_tests/resources/document-interface-broker-helpers.js
deleted file mode 100644
index 6cc6e88..0000000
--- a/third_party/blink/web_tests/resources/document-interface-broker-helpers.js
+++ /dev/null
@@ -1,30 +0,0 @@
-'use strict';
-
-/**
- * Allows to override specific interface request handlers for
- * DocumentInterfaceBroker for testing purposes
- * @param {Object} overrides an object where the keys are names of
- *     DocumentInterfaceBroker's methods and the values are corresponding handler
- *     functions taking a request parameter and binding its handle to the relevant
- *     testing implementation.
- * Example:
- *   const testFooImpl = new FooInterfaceCallbackRouter;
- *   ... override FooInterface methods ...
- *   setDocumentInterfaceBrokerOverrides({getFooInterface: request => {
- *     testFooImpl.$.bindHandle(request.handle);
- *   }});
- */
-function setDocumentInterfaceBrokerOverrides(overrides) {
-  const {handle0, handle1} = Mojo.createMessagePipe();
-  const realBrokerRemote = new blink.mojom.DocumentInterfaceBrokerRemote(
-      Mojo.replaceDocumentInterfaceBrokerForTesting(handle0));
-
-  for (const method of Object.keys(overrides)) {
-    realBrokerRemote[method] = overrides[method];
-  }
-
-  // Use the real broker (with overrides) as the implementation of the JS-side broker
-  const testBrokerReceiver =
-    new blink.mojom.DocumentInterfaceBrokerReceiver(realBrokerRemote);
-  testBrokerReceiver.$.bindHandle(handle1);
-}
diff --git a/third_party/blink/web_tests/virtual/mathml-disabled/README.txt b/third_party/blink/web_tests/virtual/mathml-disabled/README.txt
new file mode 100644
index 0000000..6523084
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/mathml-disabled/README.txt
@@ -0,0 +1 @@
+This suite runs tests with --disable-blink-features=MathMLCore
diff --git a/third_party/blink/web_tests/mathml/fallback-expected.html b/third_party/blink/web_tests/virtual/mathml-disabled/fallback-expected.html
similarity index 100%
rename from third_party/blink/web_tests/mathml/fallback-expected.html
rename to third_party/blink/web_tests/virtual/mathml-disabled/fallback-expected.html
diff --git a/third_party/blink/web_tests/mathml/fallback.html b/third_party/blink/web_tests/virtual/mathml-disabled/fallback.html
similarity index 100%
rename from third_party/blink/web_tests/mathml/fallback.html
rename to third_party/blink/web_tests/virtual/mathml-disabled/fallback.html
diff --git a/third_party/blink/web_tests/virtual/out-of-blink-frame-ancestors/external/wpt/content-security-policy/frame-ancestors/report-blocked-frame.sub-expected.txt b/third_party/blink/web_tests/virtual/out-of-blink-frame-ancestors/external/wpt/content-security-policy/frame-ancestors/report-blocked-frame.sub-expected.txt
deleted file mode 100644
index b2bc79c..0000000
--- a/third_party/blink/web_tests/virtual/out-of-blink-frame-ancestors/external/wpt/content-security-policy/frame-ancestors/report-blocked-frame.sub-expected.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-This is a testharness.js-based test.
-FAIL Violation report status OK. assert_equals: No such report. expected "" but got "false"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/virtual/stable/webexposed/element-instance-property-listing-expected.txt b/third_party/blink/web_tests/virtual/stable/webexposed/element-instance-property-listing-expected.txt
index e6661bf6..cce08677 100644
--- a/third_party/blink/web_tests/virtual/stable/webexposed/element-instance-property-listing-expected.txt
+++ b/third_party/blink/web_tests/virtual/stable/webexposed/element-instance-property-listing-expected.txt
@@ -700,7 +700,6 @@
     property hreflang
     property imageSizes
     property imageSrcset
-    property import
     property integrity
     property media
     property referrerPolicy
diff --git a/third_party/blink/web_tests/virtual/stable/webexposed/global-interface-listing-expected.txt b/third_party/blink/web_tests/virtual/stable/webexposed/global-interface-listing-expected.txt
index a92eec57..2365233 100644
--- a/third_party/blink/web_tests/virtual/stable/webexposed/global-interface-listing-expected.txt
+++ b/third_party/blink/web_tests/virtual/stable/webexposed/global-interface-listing-expected.txt
@@ -2842,7 +2842,6 @@
     getter hreflang
     getter imageSizes
     getter imageSrcset
-    getter import
     getter integrity
     getter media
     getter referrerPolicy
diff --git a/third_party/blink/web_tests/webexposed/global-interface-listing-dedicated-worker-expected.txt b/third_party/blink/web_tests/webexposed/global-interface-listing-dedicated-worker-expected.txt
index f826dfe2..37974b2 100644
--- a/third_party/blink/web_tests/webexposed/global-interface-listing-dedicated-worker-expected.txt
+++ b/third_party/blink/web_tests/webexposed/global-interface-listing-dedicated-worker-expected.txt
@@ -806,8 +806,6 @@
 [Worker]     static method createDataPipe
 [Worker]     static method createMessagePipe
 [Worker]     static method createSharedBuffer
-[Worker]     static method getDocumentInterfaceBrokerHandle
-[Worker]     static method replaceDocumentInterfaceBrokerForTesting
 [Worker]     attribute @@toStringTag
 [Worker]     attribute RESULT_ABORTED
 [Worker]     attribute RESULT_ALREADY_EXISTS
diff --git a/third_party/blink/web_tests/webexposed/global-interface-listing-expected.txt b/third_party/blink/web_tests/webexposed/global-interface-listing-expected.txt
index 531b4df..b1d280a4 100644
--- a/third_party/blink/web_tests/webexposed/global-interface-listing-expected.txt
+++ b/third_party/blink/web_tests/webexposed/global-interface-listing-expected.txt
@@ -4987,8 +4987,6 @@
     static method createDataPipe
     static method createMessagePipe
     static method createSharedBuffer
-    static method getDocumentInterfaceBrokerHandle
-    static method replaceDocumentInterfaceBrokerForTesting
     attribute @@toStringTag
     attribute RESULT_ABORTED
     attribute RESULT_ALREADY_EXISTS
diff --git a/third_party/blink/web_tests/webexposed/global-interface-listing-shared-worker-expected.txt b/third_party/blink/web_tests/webexposed/global-interface-listing-shared-worker-expected.txt
index 1e2f3bc5..f9e3191 100644
--- a/third_party/blink/web_tests/webexposed/global-interface-listing-shared-worker-expected.txt
+++ b/third_party/blink/web_tests/webexposed/global-interface-listing-shared-worker-expected.txt
@@ -788,8 +788,6 @@
 [Worker]     static method createDataPipe
 [Worker]     static method createMessagePipe
 [Worker]     static method createSharedBuffer
-[Worker]     static method getDocumentInterfaceBrokerHandle
-[Worker]     static method replaceDocumentInterfaceBrokerForTesting
 [Worker]     attribute @@toStringTag
 [Worker]     attribute RESULT_ABORTED
 [Worker]     attribute RESULT_ALREADY_EXISTS
diff --git a/third_party/libusb/src/libusb/io.c b/third_party/libusb/src/libusb/io.c
index 4f22963..01dcbe3 100644
--- a/third_party/libusb/src/libusb/io.c
+++ b/third_party/libusb/src/libusb/io.c
@@ -2331,7 +2331,7 @@
 	struct usbi_transfer *transfer;
 	struct timespec cur_ts;
 	struct timeval cur_tv;
-	struct timeval *next_timeout;
+	struct timeval next_timeout;
 	int r;
 	int found = 0;
 
@@ -2356,6 +2356,7 @@
 			continue;
 
 		found = 1;
+		next_timeout = transfer->timeout;
 		break;
 	}
 	usbi_mutex_unlock(&ctx->flying_transfers_lock);
@@ -2365,8 +2366,6 @@
 		return 0;
 	}
 
-	next_timeout = &transfer->timeout;
-
 	r = usbi_backend->clock_gettime(USBI_CLOCK_MONOTONIC, &cur_ts);
 	if (r < 0) {
 		usbi_err(ctx, "failed to read monotonic clock, errno=%d", errno);
@@ -2374,11 +2373,11 @@
 	}
 	TIMESPEC_TO_TIMEVAL(&cur_tv, &cur_ts);
 
-	if (!timercmp(&cur_tv, next_timeout, <)) {
+	if (!timercmp(&cur_tv, &next_timeout, <)) {
 		usbi_dbg("first timeout already expired");
 		timerclear(tv);
 	} else {
-		timersub(next_timeout, &cur_tv, tv);
+		timersub(&next_timeout, &cur_tv, tv);
 		usbi_dbg("next timeout in %d.%06ds", tv->tv_sec, tv->tv_usec);
 	}
 
diff --git a/tools/android/roll/android_deps/buildSrc/src/main/groovy/ChromiumDepGraph.groovy b/tools/android/roll/android_deps/buildSrc/src/main/groovy/ChromiumDepGraph.groovy
index 02e22f4..120f93b8 100644
--- a/tools/android/roll/android_deps/buildSrc/src/main/groovy/ChromiumDepGraph.groovy
+++ b/tools/android/roll/android_deps/buildSrc/src/main/groovy/ChromiumDepGraph.groovy
@@ -24,38 +24,39 @@
     // It is provided here from manual lookups. Note that licenseUrl must provide textual content
     // rather than be an html page.
     final def FALLBACK_PROPERTIES = [
-        'com_google_errorprone_error_prone_annotations': new DependencyDescription(
+        'com_google_errorprone_error_prone_annotations': new PropertyOverride(
             url: "https://errorprone.info/",
             licenseUrl: "https://www.apache.org/licenses/LICENSE-2.0.txt",
             licenseName: "Apache 2.0"),
-        'com_google_googlejavaformat_google_java_format': new DependencyDescription(
+        'com_google_googlejavaformat_google_java_format': new PropertyOverride(
             url: "https://github.com/google/google-java-format",
             licenseUrl: "https://www.apache.org/licenses/LICENSE-2.0.txt",
             licenseName: "Apache 2.0"),
-        'com_google_guava_guava': new DependencyDescription(
+        'com_google_guava_guava': new PropertyOverride(
             url: "https://github.com/google/guava",
             licenseUrl: "https://www.apache.org/licenses/LICENSE-2.0.txt",
             licenseName: "Apache 2.0"),
-        'org_codehaus_mojo_animal_sniffer_annotations': new DependencyDescription(
+        'org_codehaus_mojo_animal_sniffer_annotations': new PropertyOverride(
             url: "http://www.mojohaus.org/animal-sniffer/animal-sniffer-annotations/",
             licenseUrl: "https://raw.githubusercontent.com/mojohaus/animal-sniffer/master/animal-sniffer-annotations/pom.xml",
             licensePath: "licenses/Codehaus_License-2009.txt",
             licenseName: "MIT"),
-        'org_checkerframework_checker_compat_qual': new DependencyDescription(
+        'org_checkerframework_checker_compat_qual': new PropertyOverride(
             licenseUrl: "https://raw.githubusercontent.com/typetools/checker-framework/master/LICENSE.txt",
             licenseName: "GPL v2 with the classpath exception"),
-        'com_google_protobuf_protobuf_lite': new DependencyDescription(
+        'com_google_protobuf_protobuf_lite': new PropertyOverride(
             url: "https://github.com/protocolbuffers/protobuf/blob/master/java/lite.md",
             licenseUrl: "https://raw.githubusercontent.com/protocolbuffers/protobuf/master/LICENSE",
             licenseName: "BSD"),
-        'com_google_ar_core': new DependencyDescription(
+        'com_google_ar_core': new PropertyOverride(
             url: "https://github.com/google-ar/arcore-android-sdk",
             licenseUrl: "https://raw.githubusercontent.com/google-ar/arcore-android-sdk/master/LICENSE",
             licenseName: "Apache 2.0"),
-        'javax_annotation_jsr250_api': new DependencyDescription(
+        'javax_annotation_jsr250_api': new PropertyOverride(
+            isShipped: false,  // Annotations are stripped by R8.
             licenseName: "CDDLv1.0",
             licensePath: "licenses/CDDLv1.0.txt"),
-        'net_sf_kxml_kxml2': new DependencyDescription(
+        'net_sf_kxml_kxml2': new PropertyOverride(
             licenseUrl: "https://raw.githubusercontent.com/stefanhaustein/kxml2/master/license.txt",
             licenseName: "MIT"),
     ]
@@ -66,13 +67,11 @@
     void collectDependencies() {
         def compileConfig = project.configurations.getByName('compile').resolvedConfiguration
         def buildCompileConfig = project.configurations.getByName('buildCompile').resolvedConfiguration
-        def annotationProcessorConfig = project.configurations.getByName('annotationProcessor').resolvedConfiguration
         def testCompileConfig = project.configurations.getByName('testCompile').resolvedConfiguration
         List<String> topLevelIds = []
         Set<ResolvedConfiguration> deps = []
         deps += compileConfig.firstLevelModuleDependencies
         deps += buildCompileConfig.firstLevelModuleDependencies
-        deps += annotationProcessorConfig.firstLevelModuleDependencies
         deps += testCompileConfig.firstLevelModuleDependencies
 
         deps.each { dependency ->
@@ -104,6 +103,14 @@
             dep.testOnly = false
             dep.isShipped = true
         }
+
+        // Has a side-effect of ensuring we don't have any stale fallbackProperties.
+        FALLBACK_PROPERTIES.each { id, fallbackProperties ->
+            if (fallbackProperties?.isShipped != null) {
+              def dep = dependencies.get(id)
+              dep.isShipped = fallbackProperties.isShipped
+            }
+        }
     }
 
     private ResolvedArtifactResult getPomFromArtifact(ComponentIdentifier componentId) {
@@ -196,6 +203,7 @@
 
     private customizeDep(DependencyDescription dep) {
         if (dep.id?.startsWith("com_google_android_")) {
+            project.logger.debug("Using Android license for ${dep.id}")
             dep.licenseUrl = ""
             // This should match fetch_all._ANDROID_SDK_LICENSE_PATH.
             dep.licensePath = "licenses/Android_SDK_License-December_9_2016.txt"
@@ -203,29 +211,30 @@
                 dep.url = "https://developers.google.com/android/guides/setup"
             }
         } else if (dep.licenseUrl?.equals("http://openjdk.java.net/legal/gplv2+ce.html")) {
+            project.logger.debug("Detected GPL v2 /w classpath license for ${dep.id}")
             // This avoids using html in a LICENSE file.
             dep.licenseUrl = ""
+            dep.licenseName = "GPL v2 with the classpath exception"
             dep.licensePath = "licenses/GNU_v2_with_Classpath_Exception_1991.txt"
-        } else {
-            def fallbackProperties = FALLBACK_PROPERTIES.get(dep.id)
-            if (fallbackProperties != null) {
-                project.logger.debug("Using fallback properties for ${dep.id}")
-                if (fallbackProperties.licenseName != null) {
-                  dep.licenseName = fallbackProperties.licenseName
-                }
-                if (fallbackProperties.licenseUrl != null) {
-                  dep.licenseUrl = fallbackProperties.licenseUrl
-                }
-                if (fallbackProperties.licensePath != null) {
-                    dep.licensePath = fallbackProperties.licensePath
-                }
-                if (fallbackProperties.url != null) {
-                    dep.url = fallbackProperties.url
-                }
-                if (fallbackProperties.cipdSuffix != null) {
-                  dep.cipdSuffix = fallbackProperties.cipdSuffix
-                }
-                dep.licenseAndroidCompatible = fallbackProperties.licenseAndroidCompatible
+        }
+
+        def fallbackProperties = FALLBACK_PROPERTIES.get(dep.id)
+        if (fallbackProperties != null) {
+            project.logger.debug("Using fallback properties for ${dep.id}")
+            if (fallbackProperties.licenseName != null) {
+              dep.licenseName = fallbackProperties.licenseName
+            }
+            if (fallbackProperties.licenseUrl != null) {
+              dep.licenseUrl = fallbackProperties.licenseUrl
+            }
+            if (fallbackProperties.licensePath != null) {
+                dep.licensePath = fallbackProperties.licensePath
+            }
+            if (fallbackProperties.url != null) {
+                dep.url = fallbackProperties.url
+            }
+            if (fallbackProperties.cipdSuffix != null) {
+              dep.cipdSuffix = fallbackProperties.cipdSuffix
             }
         }
 
@@ -271,4 +280,11 @@
         List<String> children
         String cipdSuffix
     }
+
+    static class PropertyOverride {
+      String url
+      String licenseName, licenseUrl, licensePath
+      String cipdSuffix
+      Boolean isShipped
+    }
 }
diff --git a/tools/android/roll/android_deps/buildSrc/src/main/groovy/ChromiumPlugin.groovy b/tools/android/roll/android_deps/buildSrc/src/main/groovy/ChromiumPlugin.groovy
index 374f3c3..8dbba5f 100644
--- a/tools/android/roll/android_deps/buildSrc/src/main/groovy/ChromiumPlugin.groovy
+++ b/tools/android/roll/android_deps/buildSrc/src/main/groovy/ChromiumPlugin.groovy
@@ -22,12 +22,6 @@
 
             /** Libraries that are only used during build. These support android. */
             buildCompile
-
-            /**
-             * Marks that the dependency will only be used during building, as an annotation
-             * processor. It will not be usable in the APK, and not marked as supporting android.
-             */
-            annotationProcessor
         }
     }
 }
diff --git a/tools/android/roll/android_deps/fetch_all.py b/tools/android/roll/android_deps/fetch_all.py
index e1d9830..49f65a6d 100755
--- a/tools/android/roll/android_deps/fetch_all.py
+++ b/tools/android/roll/android_deps/fetch_all.py
@@ -118,7 +118,7 @@
   raise Exception(message)
 
 
-def RunCommand(args):
+def RunCommand(args, print_stdout=False):
   """Run a new shell command.
 
   This function runs without printing anything.
@@ -130,7 +130,8 @@
     return status, and standard output + error merged in a single message.
   """
   logging.debug('Run %s', args)
-  p = subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
+  stdout = None if print_stdout else subprocess.PIPE
+  p = subprocess.Popen(args, stdout=stdout)
   pout, _ = p.communicate()
   if p.returncode != 0:
     RaiseCommandException(args, p.returncode, None, pout)
@@ -390,7 +391,8 @@
     build_gradle = ReadFile(build_gradle_path)
 
     print('# Resetting and re-syncing workspace. (may take a while).')
-    RunCommand(['gclient', 'sync', '--reset', '--nohooks', '-r', 'src@HEAD'])
+    RunCommand(['gclient', 'sync', '--reset', '--nohooks', '-r', 'src@HEAD'],
+               print_stdout=args.debug)
 
     print('# Restoring build.gradle.')
     WriteFile(build_gradle_path, build_gradle)
@@ -440,7 +442,7 @@
     if args.debug:
       gradle_cmd.append('--debug')
 
-    RunCommand(gradle_cmd)
+    RunCommand(gradle_cmd, print_stdout=args.debug)
 
     libs_dir = os.path.join(build_dir, args.git_dir, _ANDROID_DEPS_LIBS_SUBDIR)
 
@@ -449,7 +451,7 @@
         'gn', 'format',
         os.path.join(build_dir, args.git_dir, _ANDROID_DEPS_BUILD_GN)
     ]
-    RunCommand(gn_args)
+    RunCommand(gn_args, print_stdout=args.debug)
 
     print('# Generate Android .aar info and third-party license files.')
     aar_files = FindInDirectory(libs_dir, '*.aar')
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
index fc7dd5b..d851843 100644
--- a/tools/metrics/histograms/enums.xml
+++ b/tools/metrics/histograms/enums.xml
@@ -5268,7 +5268,7 @@
   <int value="208" label="RFH_CHILD_FRAME_NEEDS_OWNER_ELEMENT_TYPE"/>
   <int value="209" label="OBSOLETE_RFH_INVALID_WEB_REPORTING_CRASH_ID"/>
   <int value="210" label="RFH_DETACH_MAIN_FRAME"/>
-  <int value="211" label="RFH_DOCUMENT_INTERFACE_BROKER_MISSING"/>
+  <int value="211" label="RFH_BROWSER_INTERFACE_BROKER_MISSING"/>
   <int value="212" label="RFPH_POST_MESSAGE_INVALID_SOURCE_ORIGIN"/>
   <int value="213" label="INVALID_INITIATOR_ORIGIN"/>
   <int value="214" label="RFHI_BEGIN_NAVIGATION_MISSING_INITIATOR_ORIGIN"/>
@@ -21253,6 +21253,7 @@
   <int value="1402" label="AUTOTESTPRIVATE_ACTIVATEDESKATINDEX"/>
   <int value="1403" label="AUTOTESTPRIVATE_REMOVEACTIVEDESK"/>
   <int value="1404" label="TERMINALPRIVATE_GETCROSHSETTINGS"/>
+  <int value="1405" label="AUTOTESTPRIVATE_ENABLEASSISTANTANDWAITFORREADY"/>
 </enum>
 
 <enum name="ExtensionIconState">
diff --git a/tools/metrics/histograms/histograms.xml b/tools/metrics/histograms/histograms.xml
index e57b536..f118ddcc 100644
--- a/tools/metrics/histograms/histograms.xml
+++ b/tools/metrics/histograms/histograms.xml
@@ -16790,7 +16790,8 @@
 
 <!-- https://www.chromestatus.com/metrics/css/animated -->
 
-  <owner>loonybear@chromium.org</owner>
+  <owner>chasej@chromium.org</owner>
+  <owner>feature-control@chromium.org</owner>
   <summary>
     Records usage of animated CSS properties used on a page, either statically
     or dynamically, from the time the page is initialised to when it is closed
@@ -16808,12 +16809,13 @@
 
 <histogram
     name="Blink.UseCounter.AnimatedCSSProperties_TestBrowserProcessLogging"
-    enum="MappedCSSProperties">
+    enum="MappedCSSProperties" expires_after="2018-06-12">
   <obsolete>
     Renamed to Blink.UseCounter.AnimatedCSSProperties in 03/2018, M69. The old
     blink UseCounter is flawed in OOPIF, so moved to the browser side instead.
   </obsolete>
-  <owner>loonybear@chromium.org</owner>
+  <owner>chasej@chromium.org</owner>
+  <owner>feature-control@chromium.org</owner>
   <summary>
     Records usage of animated CSS properties used across all frames in a page,
     either statically or dynamically, from the time the page is initialised to
@@ -16829,7 +16831,8 @@
 
 <!-- https://www.chromestatus.com/metrics/css/animated -->
 
-  <owner>loonybear@chromium.org</owner>
+  <owner>chasej@chromium.org</owner>
+  <owner>feature-control@chromium.org</owner>
   <summary>
     Records usage of CSS properties used on a page, either statically or
     dynamically, from the time the page is initialised to when it is closed or
@@ -16867,7 +16870,8 @@
     Renamed to Blink.UseCounter.CSSProperties in 03/2018, M69. The old blink
     UseCounter is flawed in OOPIF, so moved to the browser side instead.
   </obsolete>
-  <owner>loonybear@chromium.org</owner>
+  <owner>chasej@chromium.org</owner>
+  <owner>feature-control@chromium.org</owner>
   <summary>
     Records usage of CSS properties used across all frames in a page, either
     statically or dynamically, from the time the page is initialised to when it
@@ -16879,7 +16883,8 @@
 
 <histogram name="Blink.UseCounter.Extensions.Features" enum="FeatureObserver"
     expires_after="2020-03-22">
-  <owner>loonybear@chromium.org</owner>
+  <owner>chasej@chromium.org</owner>
+  <owner>feature-control@chromium.org</owner>
   <summary>
     Count of how many page loads use various features for pages with a
     chrome-extension:// URL only. The PageVisits bucket is incremented for each
@@ -16988,7 +16993,8 @@
 
 <histogram name="Blink.UseCounter.FeaturePolicy.PotentialViolation"
     enum="FeaturePolicyFeature" expires_after="2019-12-15">
-  <owner>ekaramad@chromium.org</owner>
+  <owner>iclelland@chromium.org</owner>
+  <owner>feature-control@chromium.org</owner>
   <summary>
     Tracks potential violations of feature policies in a document. This is
     emitted once all the conditions for violating the feature policy are met in
@@ -17019,7 +17025,8 @@
 
 <!-- https://www.chromestatus.com/metrics/css/animated -->
 
-  <owner>loonybear@chromium.org</owner>
+  <owner>chasej@chromium.org</owner>
+  <owner>feature-control@chromium.org</owner>
   <summary>
     Count of how many page loads use various features across all frames in a
     page. The PageVisits bucket is incremented for each page load, and the other
@@ -17038,8 +17045,10 @@
   </details>
 </histogram>
 
-<histogram name="Blink.UseCounter.Features_Legacy" enum="FeatureObserver">
-  <owner>loonybear@chromium.org</owner>
+<histogram name="Blink.UseCounter.Features_Legacy" enum="FeatureObserver"
+    expires_after="M81">
+  <owner>chasej@chromium.org</owner>
+  <owner>feature-control@chromium.org</owner>
   <summary>
     Count of how many page loads use various features. The PageVisits bucket is
     incremented for each page load, and the other buckets incremented at most
@@ -17058,7 +17067,8 @@
     Renamed to Blink.UseCounter.Features in 03/2018, M67. The old blink
     UseCounter is flawed in OOPIF, so moved to the browser side (this) instead.
   </obsolete>
-  <owner>loonybear@chromium.org</owner>
+  <owner>chasej@chromium.org</owner>
+  <owner>feature-control@chromium.org</owner>
   <summary>
     Count of how many page loads use various features across all frames in a
     page. The PageVisits bucket is incremented for each page load, and the other
@@ -17082,8 +17092,12 @@
   </details>
 </histogram>
 
-<histogram name="Blink.UseCounter.MainFrame.Features" enum="FeatureObserver">
-  <owner>loonybear@chromium.org</owner>
+<histogram name="Blink.UseCounter.MainFrame.Features" enum="FeatureObserver"
+    expires_after="never">
+<!-- expires-never: Companion to Blink.UseCounter.Features, which is also expires-never. -->
+
+  <owner>chasej@chromium.org</owner>
+  <owner>feature-control@chromium.org</owner>
   <summary>
     Count of how many page loads use various features across in the main frame
     of a page. The PageVisits bucket is incremented at the beginning of each
@@ -17103,7 +17117,8 @@
     Removed in 06/2018, M69 since the histogram is not really useful. See
     https://crbug.com/804645.
   </obsolete>
-  <owner>loonybear@chromium.org</owner>
+  <owner>chasej@chromium.org</owner>
+  <owner>feature-control@chromium.org</owner>
   <summary>
     Like Blink.UseCounter.AnimatedCSSProperties but specifically for the case of
     CSS properties used inside of an SVG image.
@@ -17123,7 +17138,8 @@
     Removed in 06/2018, M69 since the histogram is not really useful. See
     https://crbug.com/804645.
   </obsolete>
-  <owner>loonybear@chromium.org</owner>
+  <owner>chasej@chromium.org</owner>
+  <owner>feature-control@chromium.org</owner>
   <summary>
     Like Blink.UseCounter.CSSProperties but specifically for the case of CSS
     properties used inside of an SVG image.
@@ -135320,7 +135336,7 @@
 </histogram>
 
 <histogram name="Setup.Install.RemoveProfileStatistics"
-    enum="BooleanDeletedOrNot" expires_after="M79">
+    enum="BooleanDeletedOrNot" expires_after="M87">
   <owner>waffles@chromium.org</owner>
   <summary>
     Hit following a successful install or update when the legacy profile count
@@ -136497,7 +136513,9 @@
 </histogram>
 
 <histogram name="Signin.GaiaCookieManager.Logout"
-    enum="SigninGaiaCookieManagerLogout" expires_after="2019-11-01">
+    enum="SigninGaiaCookieManagerLogout" expires_after="never">
+<!-- expires-never: monitors logout requests to http://accounts.google.com/Logout endpoint -->
+
   <owner>droger@chromium.org</owner>
   <owner>msarda@chromium.org</owner>
   <summary>
@@ -136926,7 +136944,10 @@
 </histogram>
 
 <histogram name="Signin.SetCookieSuccess" enum="BooleanSuccess"
-    expires_after="2019-11-01">
+    expires_after="never">
+<!-- expires-never: For monitoring setting cookies in the cookie jar after an
+OAuth Multilogin call. -->
+
   <owner>msarda@chromium.org</owner>
   <owner>droger@chromium.org</owner>
   <summary>
diff --git a/tools/perf/benchmark.csv b/tools/perf/benchmark.csv
index 3c3aec7..ef922be 100644
--- a/tools/perf/benchmark.csv
+++ b/tools/perf/benchmark.csv
@@ -50,15 +50,11 @@
 resource_sizes_monochrome_public_minimal_apks,"agrieve@chromium.org, jbudorick@chromium.org, perezju@chromium.org",Build,https://chromium.googlesource.com/chromium/src/+/HEAD/tools/binary_size/README.md#resource_sizes_py,
 resource_sizes_system_webview_apk,"agrieve@chromium.org, jbudorick@chromium.org, perezju@chromium.org",Build,https://chromium.googlesource.com/chromium/src/+/HEAD/tools/binary_size/README.md#resource_sizes_py,
 resource_sizes_system_webview_google_apk,"agrieve@chromium.org, jbudorick@chromium.org, perezju@chromium.org",Build,https://chromium.googlesource.com/chromium/src/+/HEAD/tools/binary_size/README.md#resource_sizes_py,
-sizes (linux),thestig@chromium.org,thomasanderson@chromium.org,Internals>PlatformIntegration,
-sizes (mac),tapted@chromium.org,,,
-sizes (win),grt@chromium.org,Internals>PlatformIntegration,,
 speedometer,hablich@chromium.org,Blink,,
 speedometer-future,hablich@chromium.org,Blink,,
 speedometer2,hablich@chromium.org,Blink,,
 speedometer2-future,hablich@chromium.org,Blink,,
 startup.mobile,"pasko@chromium.org, chrome-android-perf-status@chromium.org",Speed>Metrics>SystemHealthRegressions,,
-supersize_archive,agrieve@chromium.org,,,
 system_health.common_desktop,"charliea@chromium.org, sullivan@chromium.org, tdresser@chromium.org, chrome-speed-metrics-dev@chromium.org",Speed>Metrics>SystemHealthRegressions,https://bit.ly/system-health-benchmarks,"2016,2018,2019,accessibility,emerging_market,health_check,images,infinite_scroll,international,javascript_heavy,keyboard_input,scroll,tabs_switching,webgl"
 system_health.common_mobile,"charliea@chromium.org, sullivan@chromium.org, tdresser@chromium.org, perezju@chromium.org, chrome-speed-metrics-dev@chromium.org",Speed>Metrics>SystemHealthRegressions,https://bit.ly/system-health-benchmarks,"2016,2018,2019,emerging_market,health_check,images,infinite_scroll,international,javascript_heavy"
 system_health.memory_desktop,perezju@chromium.org,,https://bit.ly/system-health-benchmarks,"2016,2018,2019,accessibility,emerging_market,health_check,images,infinite_scroll,international,javascript_heavy,keyboard_input,scroll,tabs_switching,webgl"
diff --git a/tools/perf/benchmarks/blink_perf_unittest.py b/tools/perf/benchmarks/blink_perf_unittest.py
index 3128d33..62b925a 100644
--- a/tools/perf/benchmarks/blink_perf_unittest.py
+++ b/tools/perf/benchmarks/blink_perf_unittest.py
@@ -32,6 +32,10 @@
     self.blink_page_test = blink_perf._BlinkPerfMeasurement()
     # pylint: enable=protected-access
 
+  def HasChromeTraces(self):
+    return any(name.startswith('trace/traceEvents/')
+               for name in self.test_result['outputArtifacts'])
+
   @staticmethod
   def CreateStorySetForTest(url):
     story_set = story.StorySet(
@@ -46,7 +50,7 @@
   def testBlinkPerfTracingMetricsForMeasureTime(self):
     measurements = self.RunPageTest(
         self.blink_page_test, 'file://append-child-measure-time.html')
-    self.assertIn('trace.html', self.test_result['outputArtifacts'])
+    self.assertTrue(self.HasChromeTraces())
 
     frame_view_layouts = measurements['LocalFrameView::layout']['samples']
     # append-child-measure-time.html specifies 5 iterationCount.
@@ -61,7 +65,7 @@
   def testBlinkPerfTracingMetricsForMeasureFrameTime(self):
     measurements = self.RunPageTest(
         self.blink_page_test, 'file://color-changes-measure-frame-time.html')
-    self.assertIn('trace.html', self.test_result['outputArtifacts'])
+    self.assertTrue(self.HasChromeTraces())
 
     frame_view_prepaints = measurements[
         'LocalFrameView::RunPrePaintLifecyclePhase']['samples']
@@ -79,7 +83,7 @@
   def testBlinkPerfTracingMetricsForMeasurePageLoadTime(self):
     measurements = self.RunPageTest(
         self.blink_page_test, 'file://simple-html-measure-page-load-time.html')
-    self.assertIn('trace.html', self.test_result['outputArtifacts'])
+    self.assertTrue(self.HasChromeTraces())
 
     create_child_frame = measurements[
         'WebLocalFrameImpl::createChildframe']['samples']
@@ -97,7 +101,7 @@
   def testBlinkPerfTracingMetricsForMeasureAsync(self):
     measurements = self.RunPageTest(
         self.blink_page_test, 'file://simple-blob-measure-async.html')
-    self.assertIn('trace.html', self.test_result['outputArtifacts'])
+    self.assertTrue(self.HasChromeTraces())
 
     blob_requests = measurements['BlobRequest']['samples']
     blob_readers = measurements['BlobReader']['samples']
@@ -124,12 +128,12 @@
 
   def testBlinkPerfLifecycleMethods(self):
     self.RunPageTest(self.blink_page_test, 'file://lifecycle-methods.html')
-    self.assertNotIn('trace.html', self.test_result['outputArtifacts'])
+    self.assertFalse(self.HasChromeTraces())
 
   def testExtraChromeCategories(self):
     self.options.extra_chrome_categories = 'cc,blink'
     self.RunPageTest(self.blink_page_test, 'file://lifecycle-methods.html')
-    self.assertIn('trace.html', self.test_result['outputArtifacts'])
+    self.assertTrue(self.HasChromeTraces())
 
 
 # pylint: disable=protected-access
diff --git a/tools/perf/core/benchmark_runner.py b/tools/perf/core/benchmark_runner.py
index 76c83f1..6cedb30 100644
--- a/tools/perf/core/benchmark_runner.py
+++ b/tools/perf/core/benchmark_runner.py
@@ -15,8 +15,7 @@
 
 
 def main(config, args=None):
-  results_arg_parser = results_processor.ArgumentParser(
-      legacy_formats=command_line.LEGACY_OUTPUT_FORMATS)
+  results_arg_parser = results_processor.ArgumentParser()
   options = command_line.ParseArgs(
       environment=config, args=args, results_arg_parser=results_arg_parser)
   results_processor.ProcessOptions(options)
diff --git a/tools/perf/core/perf_data_generator.py b/tools/perf/core/perf_data_generator.py
index c75a66c..f0f802e 100755
--- a/tools/perf/core/perf_data_generator.py
+++ b/tools/perf/core/perf_data_generator.py
@@ -912,26 +912,9 @@
 }
 
 
-# If you change this dictionary, run tools/perf/generate_perf_data
-NON_WATERFALL_BENCHMARKS = {
-    'sizes (mac)':
-        BenchmarkMetadata('tapted@chromium.org'),
-    'sizes (win)': BenchmarkMetadata('grt@chromium.org',
-                                     'Internals>PlatformIntegration'),
-    'sizes (linux)': BenchmarkMetadata(
-        'thestig@chromium.org', 'thomasanderson@chromium.org',
-        'Internals>PlatformIntegration'),
-    'supersize_archive': BenchmarkMetadata('agrieve@chromium.org'),
-}
-
 # Valid test suite (benchmark) names should match this regex.
 RE_VALID_TEST_SUITE_NAME = r'^[\w._-]+$'
 
-# Do not add names to this list. TODO(crbug.com/1020510): Rename these so all
-# test suites match the regex above.
-BAD_TEST_SUITE_NAMES = frozenset(
-    ['sizes (mac)', 'sizes (win)', 'sizes (linux)'])
-
 
 def _get_telemetry_perf_benchmarks_metadata():
   metadata = {}
@@ -1041,14 +1024,12 @@
 
   csv_data = []
   benchmark_metadatas = merge_dicts(
-      GTEST_BENCHMARKS, OTHER_BENCHMARKS, TELEMETRY_PERF_BENCHMARKS,
-      NON_WATERFALL_BENCHMARKS)
+      GTEST_BENCHMARKS, OTHER_BENCHMARKS, TELEMETRY_PERF_BENCHMARKS)
   _verify_benchmark_owners(benchmark_metadatas)
 
   undocumented_benchmarks = set()
   for benchmark_name in benchmark_metadatas:
-    if (not re.match(RE_VALID_TEST_SUITE_NAME, benchmark_name) and
-        benchmark_name not in BAD_TEST_SUITE_NAMES):
+    if not re.match(RE_VALID_TEST_SUITE_NAME, benchmark_name):
       raise ValueError('Invalid benchmark name: %s' % benchmark_name)
     if not benchmark_metadatas[benchmark_name].documentation_url:
       undocumented_benchmarks.add(benchmark_name)
diff --git a/tools/perf/core/results_processor/command_line.py b/tools/perf/core/results_processor/command_line.py
index 63d1dd9..94b99b5 100644
--- a/tools/perf/core/results_processor/command_line.py
+++ b/tools/perf/core/results_processor/command_line.py
@@ -17,21 +17,14 @@
 from py_utils import cloud_storage
 
 from core.results_processor import formatters
+from core.results_processor import util
 
 
-# These formats are always handled natively, and never handed over to Telemetry.
-HANDLED_NATIVELY = ['none', 'json-test-results', 'histograms', 'html', 'csv']
-
-TELEMETRY_TEST_PATH_FORMAT = 'telemetry'
-GTEST_TEST_PATH_FORMAT = 'gtest'
-
-def ArgumentParser(standalone=False, legacy_formats=None):
+def ArgumentParser(standalone=False):
   """Create an ArgumentParser defining options required by the processor."""
-  if standalone:
-    all_output_formats = formatters.FORMATTERS.keys()
-  else:
-    all_output_formats = sorted(
-        set(HANDLED_NATIVELY).union(legacy_formats or ()))
+  all_output_formats = formatters.FORMATTERS.keys()
+  if not standalone:
+    all_output_formats.append('none')
   parser, group = _CreateTopLevelParser(standalone)
   parser.add_argument(
       '-v', '--verbose', action='count', dest='verbosity',
@@ -42,7 +35,7 @@
       help=Sentences(
           'Output format to produce.',
           'May be used multiple times to produce multiple outputs.',
-          'Avaliable formats: %s.' % ', '.join(all_output_formats),
+          'Avaliable formats: %(choices)s.',
           '' if standalone else 'Defaults to: html.'))
   group.add_argument(
       '--intermediate-dir', metavar='DIR_PATH', required=standalone,
@@ -70,8 +63,8 @@
       help='Label to identify the results generated by this run.')
   group.add_argument(
       '--test-path-format', metavar='FORMAT',
-      choices=[TELEMETRY_TEST_PATH_FORMAT, GTEST_TEST_PATH_FORMAT],
-      default=TELEMETRY_TEST_PATH_FORMAT,
+      choices=[util.TELEMETRY_TEST_PATH_FORMAT, util.GTEST_TEST_PATH_FORMAT],
+      default=util.TELEMETRY_TEST_PATH_FORMAT,
       help=Sentences(
           'How to interpret the testPath attribute.',
           'Available options: %(choices)s. Default: %(default)s.'))
@@ -89,7 +82,7 @@
   return parser
 
 
-def ProcessOptions(options, standalone=False):
+def ProcessOptions(options):
   """Adjust result processing options as needed before running benchmarks.
 
   Note: The intended scope of this function is limited to only adjust options
@@ -102,8 +95,6 @@
 
   Args:
     options: An options object with values parsed from the command line.
-    standalone: Whether this is a standalone Results Processor run (as
-      opposed to the run with Telemetry).
   """
   if options.verbosity >= 2:
     logging.getLogger().setLevel(logging.DEBUG)
@@ -140,19 +131,10 @@
   else:
     options.upload_bucket = None
 
-  if options.output_formats:
-    chosen_formats = sorted(set(options.output_formats))
-  else:
-    chosen_formats = ['html']
-
-  options.output_formats = []
-  for output_format in chosen_formats:
-    if output_format == 'none':
-      continue
-    elif standalone or output_format in HANDLED_NATIVELY:
-      options.output_formats.append(output_format)
-    else:
-      options.legacy_output_formats.append(output_format)
+  if not options.output_formats:
+    options.output_formats = ['html']
+  elif 'none' in options.output_formats:
+    options.output_formats.remove('none')
 
 
 def _CreateTopLevelParser(standalone):
diff --git a/tools/perf/core/results_processor/command_line_unittest.py b/tools/perf/core/results_processor/command_line_unittest.py
index 5bf0ac1..147bc2a3 100644
--- a/tools/perf/core/results_processor/command_line_unittest.py
+++ b/tools/perf/core/results_processor/command_line_unittest.py
@@ -17,7 +17,6 @@
 import mock
 
 from core.results_processor import command_line
-from core.results_processor import formatters
 
 
 # To easily mock module level symbols within the command_line module.
@@ -27,7 +26,6 @@
 
 class ProcessOptionsTestCase(unittest.TestCase):
   def setUp(self):
-    self.legacy_formats = []
     self.standalone = False
 
     # Mock os module within results_processor so path manipulations do not
@@ -53,8 +51,7 @@
     mock.patch.stopall()
 
   def ParseArgs(self, args):
-    parser = command_line.ArgumentParser(
-        standalone=self.standalone, legacy_formats=self.legacy_formats)
+    parser = command_line.ArgumentParser(standalone=self.standalone)
     options = parser.parse_args(args)
     command_line.ProcessOptions(options)
     return options
@@ -132,23 +129,6 @@
     with self.assertRaises(SystemExit):
       self.ParseArgs(['--output-format', 'unknown'])
 
-  @mock.patch(module('HANDLED_NATIVELY'), ['new-format'])
-  def testOutputFormatsSplit(self):
-    self.legacy_formats = ['old-format']
-    options = self.ParseArgs(
-        ['--output-format', 'new-format', '--output-format', 'old-format'])
-    self.assertEqual(options.output_formats, ['new-format'])
-    self.assertEqual(options.legacy_output_formats, ['old-format'])
-
-  @mock.patch(module('HANDLED_NATIVELY'), ['new-format'])
-  def testNoDuplicateOutputFormats(self):
-    self.legacy_formats = ['old-format']
-    options = self.ParseArgs(
-        ['--output-format', 'new-format', '--output-format', 'old-format',
-         '--output-format', 'new-format', '--output-format', 'old-format'])
-    self.assertEqual(options.output_formats, ['new-format'])
-    self.assertEqual(options.legacy_output_formats, ['old-format'])
-
 
 class StandaloneTestProcessOptions(ProcessOptionsTestCase):
   def setUp(self):
@@ -170,11 +150,3 @@
     self.assertEqual(options.output_formats, ['json-test-results'])
     self.assertEqual(options.intermediate_dir, '/path/to/curdir/some_dir')
     self.assertEqual(options.output_dir, '/path/to/output_dir')
-
-
-class TestNativelyHandledFormats(unittest.TestCase):
-  def testNativelyHandledFormatsHaveFormatters(self):
-    for output_format in command_line.HANDLED_NATIVELY:
-      if output_format == 'none':
-        continue
-      self.assertIn(output_format, formatters.FORMATTERS)
diff --git a/tools/perf/core/results_processor/compute_metrics.py b/tools/perf/core/results_processor/compute_metrics.py
index e016c8870..193d964 100644
--- a/tools/perf/core/results_processor/compute_metrics.py
+++ b/tools/perf/core/results_processor/compute_metrics.py
@@ -2,7 +2,6 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
-import json
 import logging
 import os
 import time
@@ -15,14 +14,6 @@
 # Aggregated trace is saved under this name.
 HTML_TRACE_NAME = 'trace.html'
 
-# Results of metric computation are stored under this key in test_results.
-HISTOGRAM_DICTS_KEY = 'histogram_dicts'
-
-# This file is written by telemetry, it contains output of metric computation.
-# This is a temporary hack to keep things working while we gradually move
-# code from Telemetry to Results Processor.
-HISTOGRAM_DICTS_FILE = 'histogram_dicts.json'
-
 
 def _RunMetric(test_result):
   metrics = [tag['value'] for tag in test_result['tags']
@@ -61,18 +52,9 @@
 
   For each test run that has an aggregate trace and some TBMv2 metrics listed
   in its tags, compute the metrics and return the list of all resulting
-  histograms. Note: the order of histograms in the results may be different
-  from the order of tests in intermediate_results.
+  histograms.
   """
   artifacts = test_result.get('outputArtifacts', {})
-  # TODO(crbug.com/981349): If metrics have already been computed in
-  # Telemetry, we read it from the file. Remove this branch after Telemetry
-  # does not compute metrics anymore.
-  if HISTOGRAM_DICTS_FILE in artifacts:
-    with open(artifacts[HISTOGRAM_DICTS_FILE]['filePath']) as f:
-      test_result['_histograms'].ImportDicts(json.load(f))
-    del artifacts[HISTOGRAM_DICTS_FILE]
-    return
 
   if test_result['status'] == 'SKIP':
     return
@@ -85,7 +67,6 @@
                        / (2 ** 20))
   # Bails out on traces that are too big. See crbug.com/812631 for more
   # details.
-  # TODO(crbug.com/1010041): Return a non-zero exit code in this case.
   if trace_size_in_mib > 400:
     util.SetUnexpectedFailure(test_result)
     logging.error('%s: Trace size is too big: %s MiB',
diff --git a/tools/perf/core/results_processor/formatters/json3_output.py b/tools/perf/core/results_processor/formatters/json3_output.py
index a406bdd2..48235ce 100644
--- a/tools/perf/core/results_processor/formatters/json3_output.py
+++ b/tools/perf/core/results_processor/formatters/json3_output.py
@@ -12,7 +12,6 @@
 import datetime
 import json
 import os
-import urllib
 
 from core.results_processor import util
 
@@ -22,14 +21,14 @@
 
 def ProcessIntermediateResults(test_results, options):
   """Process intermediate results and write output in output_dir."""
-  results = Convert(test_results, options.output_dir)
+  results = Convert(test_results, options.output_dir, options.test_path_format)
   output_file = os.path.join(options.output_dir, OUTPUT_FILENAME)
   with open(output_file, 'w') as f:
     json.dump(results, f, sort_keys=True, indent=4, separators=(',', ': '))
   return output_file
 
 
-def Convert(test_results, base_dir):
+def Convert(test_results, base_dir, test_path_format):
   """Convert intermediate results to the JSON Test Results Format.
 
   Args:
@@ -44,8 +43,7 @@
   status_counter = collections.Counter()
 
   for result in test_results:
-    benchmark_name, story_name = result['testPath'].split('/')
-    story_name = urllib.unquote(story_name)
+    benchmark_name, story_name = util.SplitTestPath(result, test_path_format)
     actual_status = result['status']
     expected_status = actual_status if result['expected'] else 'PASS'
     status_counter[actual_status] += 1
diff --git a/tools/perf/core/results_processor/formatters/json3_output_unittest.py b/tools/perf/core/results_processor/formatters/json3_output_unittest.py
index ef0f1427..00b62a72 100644
--- a/tools/perf/core/results_processor/formatters/json3_output_unittest.py
+++ b/tools/perf/core/results_processor/formatters/json3_output_unittest.py
@@ -12,10 +12,12 @@
 class Json3OutputTest(unittest.TestCase):
   def setUp(self):
     self.base_dir = 'base_dir'
+    self.test_path_format = 'telemetry'
 
   def Convert(self, test_results):
     test_results_copy = copy.deepcopy(test_results)
-    results = json3_output.Convert(test_results_copy, self.base_dir)
+    results = json3_output.Convert(
+        test_results_copy, self.base_dir, self.test_path_format)
     # Convert should not modify the original intermediate results.
     self.assertEqual(test_results_copy, test_results)
     return results
@@ -51,6 +53,8 @@
     self.assertNotIn('shard', test_result)
     self.assertEqual(results['num_failures_by_type'], {'PASS': 1})
 
+  # TODO(crbug.com/983993): Remove this test when all stories have
+  # url-friendly names without special characters.
   def testUrlAsStoryName(self):
     results = self.Convert([
         testing.TestResult('benchmark/http%3A%2F%2Fexample.com')
diff --git a/tools/perf/core/results_processor/processor.py b/tools/perf/core/results_processor/processor.py
index 97766796..d5a75c3 100644
--- a/tools/perf/core/results_processor/processor.py
+++ b/tools/perf/core/results_processor/processor.py
@@ -199,11 +199,9 @@
   for name, artifact in artifacts.iteritems():
     if 'remoteUrl' in artifact:
       continue
-    # TODO(crbug.com/981349): Remove check for HISTOGRAM_DICTS_FILE
-    # after Telemetry does not save histograms as an artifact anymore.
-    # Another TODO(crbug.com/981349): Think of a more general way to
+    # TODO(crbug.com/981349): Think of a more general way to
     # specify which artifacts deserve uploading.
-    if name in [compute_metrics.HISTOGRAM_DICTS_FILE, MEASUREMENTS_NAME]:
+    if name == MEASUREMENTS_NAME:
       continue
     remote_name = '/'.join([run_identifier, test_result['testPath'], name])
     urlsafe_remote_name = re.sub(r'[^A-Za-z0-9/.-]+', '_', remote_name)
@@ -220,15 +218,6 @@
                else trace_artifact.get('filePath'))
 
 
-def _SplitTestPath(test_result, test_path_format):
-  if test_path_format == command_line.TELEMETRY_TEST_PATH_FORMAT:
-    return test_result['testPath'].split('/', 1)
-  elif test_path_format == command_line.GTEST_TEST_PATH_FORMAT:
-    return test_result['testPath'].split('.', 1)
-  else:
-    raise ValueError('Unknown test path format: %s', test_path_format)
-
-
 def AddDiagnosticsToHistograms(test_result, test_suite_start, results_label,
                                test_path_format):
   """Add diagnostics to all histograms of a test result.
@@ -249,7 +238,7 @@
       test_result['_histograms'].AddSharedDiagnosticToAllHistograms(
           name, generic_set.GenericSet(diag))
 
-  test_suite, test_case = _SplitTestPath(test_result, test_path_format)
+  test_suite, test_case = util.SplitTestPath(test_result, test_path_format)
   if 'startTime' in test_result:
     test_start_ms = util.IsoTimestampToEpoch(test_result['startTime']) * 1e3
   else:
@@ -323,5 +312,5 @@
   """Entry point for the standalone version of the results_processor script."""
   parser = command_line.ArgumentParser(standalone=True)
   options = parser.parse_args(args)
-  command_line.ProcessOptions(options, standalone=True)
+  command_line.ProcessOptions(options)
   return ProcessResults(options)
diff --git a/tools/perf/core/results_processor/processor_test.py b/tools/perf/core/results_processor/processor_test.py
index 60b09a57..7c0d37e 100644
--- a/tools/perf/core/results_processor/processor_test.py
+++ b/tools/perf/core/results_processor/processor_test.py
@@ -31,6 +31,16 @@
 from tracing.value import histogram_set
 from tracing_build import render_histograms_viewer
 
+import mock
+
+# We use sampleMetric defined in
+# third_party/catapult/tracing/tracing/metrics/sample_metric.html
+# to test processing of test results.
+# This metric ignores the trace data and outputs a histogram with
+# the following name and unit:
+SAMPLE_HISTOGRAM_NAME = 'foo'
+SAMPLE_HISTOGRAM_UNIT = 'sizeInBytes_smallerIsBetter'
+
 
 class ResultsProcessorIntegrationTests(unittest.TestCase):
   def setUp(self):
@@ -46,13 +56,12 @@
     testing.SerializeIntermediateResults(test_results, os.path.join(
         self.intermediate_dir, processor.TEST_RESULTS))
 
-  def CreateHistogramsArtifact(self, hist):
-    """Create an artifact with histograms."""
-    histogram_dicts = [hist.AsDict()]
+  def CreateHtmlTraceArtifact(self):
+    """Create an empty file as a fake html trace."""
     with tempfile.NamedTemporaryFile(
         dir=self.intermediate_dir, delete=False) as artifact_file:
-      json.dump(histogram_dicts, artifact_file)
-    return (compute_metrics.HISTOGRAM_DICTS_FILE,
+      pass
+    return (compute_metrics.HTML_TRACE_NAME,
             testing.Artifact(artifact_file.name))
 
   def CreateDiagnosticsArtifact(self, **diagnostics):
@@ -68,6 +77,13 @@
       json.dump({'measurements': measurements}, artifact_file)
     return processor.MEASUREMENTS_NAME, testing.Artifact(artifact_file.name)
 
+  def ReadSampleHistogramsFromCsv(self):
+    with open(os.path.join(self.output_dir, csv_output.OUTPUT_FILENAME)) as f:
+      # Filtering out rows with histograms other than SAMPLE_HISTOGRAM_NAME,
+      # e.g. metrics_duration.
+      return [row for row in csv.DictReader(f)
+              if row['name'] == SAMPLE_HISTOGRAM_NAME]
+
   def testJson3Output(self):
     self.SerializeIntermediateResults(
         testing.TestResult(
@@ -169,23 +185,26 @@
         testing.TestResult(
             'benchmark/story',
             output_artifacts=[
-                self.CreateHistogramsArtifact(
-                    histogram.Histogram('a', 'unitless')),
+                self.CreateHtmlTraceArtifact(),
                 self.CreateDiagnosticsArtifact(
                     benchmarks=['benchmark'],
                     osNames=['linux'],
                     documentationUrls=[['documentation', 'url']])
             ],
+            tags=['tbmv2:sampleMetric'],
             start_time='2009-02-13T23:31:30.987000Z',
         ),
     )
 
-    processor.main([
-        '--output-format', 'histograms',
-        '--output-dir', self.output_dir,
-        '--intermediate-dir', self.intermediate_dir,
-        '--results-label', 'label',
-    ])
+    with mock.patch('py_utils.cloud_storage.Insert') as cloud_patch:
+      cloud_patch.return_value = 'gs://trace.html'
+      processor.main([
+          '--output-format', 'histograms',
+          '--output-dir', self.output_dir,
+          '--intermediate-dir', self.intermediate_dir,
+          '--results-label', 'label',
+          '--upload-results',
+      ])
 
     with open(os.path.join(
         self.output_dir, histograms_output.OUTPUT_FILENAME)) as f:
@@ -193,11 +212,9 @@
 
     out_histograms = histogram_set.HistogramSet()
     out_histograms.ImportDicts(results)
-    self.assertEqual(len(out_histograms), 1)
 
-    hist = out_histograms.GetFirstHistogram()
-    self.assertEqual(hist.name, 'a')
-    self.assertEqual(hist.unit, 'unitless')
+    hist = out_histograms.GetHistogramNamed(SAMPLE_HISTOGRAM_NAME)
+    self.assertEqual(hist.unit, SAMPLE_HISTOGRAM_UNIT)
 
     self.assertEqual(hist.diagnostics['benchmarks'],
                      generic_set.GenericSet(['benchmark']))
@@ -209,15 +226,17 @@
                      generic_set.GenericSet(['label']))
     self.assertEqual(hist.diagnostics['benchmarkStart'],
                      date_range.DateRange(1234567890987))
+    self.assertEqual(hist.diagnostics['traceUrls'],
+                     generic_set.GenericSet(['gs://trace.html']))
 
   def testHistogramsOutputResetResults(self):
     self.SerializeIntermediateResults(
         testing.TestResult(
             'benchmark/story',
             output_artifacts=[
-                self.CreateHistogramsArtifact(
-                    histogram.Histogram('a', 'unitless')),
+                self.CreateHtmlTraceArtifact(),
             ],
+            tags=['tbmv2:sampleMetric'],
         ),
     )
 
@@ -242,9 +261,8 @@
 
     out_histograms = histogram_set.HistogramSet()
     out_histograms.ImportDicts(results)
-    self.assertEqual(len(out_histograms), 1)
 
-    hist = out_histograms.GetFirstHistogram()
+    hist = out_histograms.GetHistogramNamed(SAMPLE_HISTOGRAM_NAME)
     self.assertEqual(hist.diagnostics['labels'],
                      generic_set.GenericSet(['label2']))
 
@@ -253,9 +271,9 @@
         testing.TestResult(
             'benchmark/story',
             output_artifacts=[
-                self.CreateHistogramsArtifact(
-                    histogram.Histogram('a', 'unitless')),
+                self.CreateHtmlTraceArtifact(),
             ],
+            tags=['tbmv2:sampleMetric'],
         ),
     )
 
@@ -279,48 +297,15 @@
 
     out_histograms = histogram_set.HistogramSet()
     out_histograms.ImportDicts(results)
-    self.assertEqual(len(out_histograms), 2)
+
+    sample_histograms = out_histograms.GetHistogramsNamed(SAMPLE_HISTOGRAM_NAME)
+    self.assertEqual(len(sample_histograms), 2)
 
     expected_labels = set(['label1', 'label2'])
-    observed_labels = set(label for hist in out_histograms
-                           for label in hist.diagnostics['labels'])
+    observed_labels = set(label for hist in sample_histograms
+                          for label in hist.diagnostics['labels'])
     self.assertEqual(observed_labels, expected_labels)
 
-  def testHistogramsOutputNoMetricsFromTelemetry(self):
-    trace_file = os.path.join(self.output_dir, compute_metrics.HTML_TRACE_NAME)
-    with open(trace_file, 'w') as f:
-      pass
-
-    self.SerializeIntermediateResults(
-        testing.TestResult(
-            'benchmark/story',
-            output_artifacts={
-                compute_metrics.HTML_TRACE_NAME:
-                    testing.Artifact(trace_file, 'gs://trace.html')
-            },
-            tags=['tbmv2:sampleMetric'],
-        ),
-    )
-
-    processor.main([
-        '--output-format', 'histograms',
-        '--output-dir', self.output_dir,
-        '--intermediate-dir', self.intermediate_dir,
-    ])
-
-    with open(os.path.join(
-        self.output_dir, histograms_output.OUTPUT_FILENAME)) as f:
-      results = json.load(f)
-
-    out_histograms = histogram_set.HistogramSet()
-    out_histograms.ImportDicts(results)
-
-    # sampleMetric records a histogram with the name 'foo'.
-    hist = out_histograms.GetHistogramNamed('foo')
-    self.assertIsNotNone(hist)
-    self.assertEqual(hist.diagnostics['traceUrls'],
-                     generic_set.GenericSet(['gs://trace.html']))
-
   def testHistogramsOutputNoAggregatedTrace(self):
     json_trace = os.path.join(self.output_dir, 'trace.json')
     with open(json_trace, 'w') as f:
@@ -347,8 +332,7 @@
     out_histograms = histogram_set.HistogramSet()
     out_histograms.ImportDicts(results)
 
-    # sampleMetric records a histogram with the name 'foo'.
-    hist = out_histograms.GetHistogramNamed('foo')
+    hist = out_histograms.GetHistogramNamed(SAMPLE_HISTOGRAM_NAME)
     self.assertIsNotNone(hist)
     self.assertIn('traceUrls', hist.diagnostics)
 
@@ -418,13 +402,13 @@
         testing.TestResult(
             'benchmark/story',
             output_artifacts=[
-                self.CreateHistogramsArtifact(
-                    histogram.Histogram('a', 'unitless')),
+                self.CreateHtmlTraceArtifact(),
                 self.CreateDiagnosticsArtifact(
                     benchmarks=['benchmark'],
                     osNames=['linux'],
                     documentationUrls=[['documentation', 'url']]),
             ],
+            tags=['tbmv2:sampleMetric'],
             start_time='2009-02-13T23:31:30.987000Z',
         ),
     )
@@ -442,11 +426,9 @@
 
     out_histograms = histogram_set.HistogramSet()
     out_histograms.ImportDicts(results)
-    self.assertEqual(len(out_histograms), 1)
 
-    hist = out_histograms.GetFirstHistogram()
-    self.assertEqual(hist.name, 'a')
-    self.assertEqual(hist.unit, 'unitless')
+    hist = out_histograms.GetHistogramNamed(SAMPLE_HISTOGRAM_NAME)
+    self.assertEqual(hist.unit, SAMPLE_HISTOGRAM_UNIT)
 
     self.assertEqual(hist.diagnostics['benchmarks'],
                      generic_set.GenericSet(['benchmark']))
@@ -464,9 +446,9 @@
         testing.TestResult(
             'benchmark/story',
             output_artifacts=[
-                self.CreateHistogramsArtifact(
-                    histogram.Histogram('a', 'unitless')),
+                self.CreateHtmlTraceArtifact(),
             ],
+            tags=['tbmv2:sampleMetric'],
         ),
     )
 
@@ -491,9 +473,8 @@
 
     out_histograms = histogram_set.HistogramSet()
     out_histograms.ImportDicts(results)
-    self.assertEqual(len(out_histograms), 1)
 
-    hist = out_histograms.GetFirstHistogram()
+    hist = out_histograms.GetHistogramNamed(SAMPLE_HISTOGRAM_NAME)
     self.assertEqual(hist.diagnostics['labels'],
                      generic_set.GenericSet(['label2']))
 
@@ -502,9 +483,9 @@
         testing.TestResult(
             'benchmark/story',
             output_artifacts=[
-                self.CreateHistogramsArtifact(
-                    histogram.Histogram('a', 'unitless')),
+                self.CreateHtmlTraceArtifact(),
             ],
+            tags=['tbmv2:sampleMetric'],
         ),
     )
 
@@ -528,11 +509,12 @@
 
     out_histograms = histogram_set.HistogramSet()
     out_histograms.ImportDicts(results)
-    self.assertEqual(len(out_histograms), 2)
+    sample_histograms = out_histograms.GetHistogramsNamed(SAMPLE_HISTOGRAM_NAME)
+    self.assertEqual(len(sample_histograms), 2)
 
     expected_labels = set(['label1', 'label2'])
-    observed_labels = set(label for hist in out_histograms
-                           for label in hist.diagnostics['labels'])
+    observed_labels = set(label for hist in sample_histograms
+                          for label in hist.diagnostics['labels'])
     self.assertEqual(observed_labels, expected_labels)
 
   def testCsvOutput(self):
@@ -542,12 +524,13 @@
         testing.TestResult(
             'benchmark/story',
             output_artifacts=[
-                self.CreateHistogramsArtifact(test_hist),
+                self.CreateHtmlTraceArtifact(),
                 self.CreateDiagnosticsArtifact(
                     benchmarks=['benchmark'],
                     osNames=['linux'],
                     documentationUrls=[['documentation', 'url']]),
             ],
+            tags=['tbmv2:sampleMetric'],
             start_time='2009-02-13T23:31:30.987000Z',
         ),
     )
@@ -559,31 +542,28 @@
         '--results-label', 'label',
     ])
 
-    with open(os.path.join(self.output_dir, csv_output.OUTPUT_FILENAME)) as f:
-      lines = [line for line in f]
+    sample_rows = self.ReadSampleHistogramsFromCsv()
+    self.assertEqual(len(sample_rows), 1)
 
-    actual = list(zip(*csv.reader(lines)))
-    expected = [
-        ('name', 'a'), ('unit', 'ms'), ('avg', '3000'), ('count', '1'),
-        ('max', '3000'), ('min', '3000'), ('std', '0'), ('sum', '3000'),
-        ('architectures', ''), ('benchmarks', 'benchmark'),
-        ('benchmarkStart', '2009-02-13 23:31:30'), ('bots', ''),
-        ('builds', ''), ('deviceIds', ''), ('displayLabel', 'label'),
-        ('masters', ''), ('memoryAmounts', ''), ('osNames', 'linux'),
-        ('osVersions', ''), ('productVersions', ''),
-        ('stories', 'story'), ('storysetRepeats', '0'),
-        ('traceStart', '2009-02-13 23:31:30'), ('traceUrls', '')
-    ]
-    self.assertEqual(actual, expected)
+    actual = sample_rows[0]
+    self.assertEqual(actual['name'], SAMPLE_HISTOGRAM_NAME)
+    self.assertEqual(actual['unit'], 'B')
+    self.assertEqual(actual['avg'], '50')
+    self.assertEqual(actual['count'], '2')
+    self.assertEqual(actual['benchmarks'], 'benchmark')
+    self.assertEqual(actual['benchmarkStart'], '2009-02-13 23:31:30')
+    self.assertEqual(actual['displayLabel'], 'label')
+    self.assertEqual(actual['osNames'], 'linux')
+    self.assertEqual(actual['traceStart'], '2009-02-13 23:31:30')
 
   def testCsvOutputResetResults(self):
     self.SerializeIntermediateResults(
         testing.TestResult(
             'benchmark/story',
             output_artifacts=[
-                self.CreateHistogramsArtifact(
-                    histogram.Histogram('a', 'unitless')),
+                self.CreateHtmlTraceArtifact(),
             ],
+            tags=['tbmv2:sampleMetric'],
         ),
     )
 
@@ -602,20 +582,18 @@
         '--reset-results',
     ])
 
-    with open(os.path.join(self.output_dir, csv_output.OUTPUT_FILENAME)) as f:
-      lines = [line for line in f]
-
-    self.assertEqual(len(lines), 2)
-    self.assertIn('label2', lines[1])
+    sample_rows = self.ReadSampleHistogramsFromCsv()
+    self.assertEqual(len(sample_rows), 1)
+    self.assertEqual(sample_rows[0]['displayLabel'], 'label2')
 
   def testCsvOutputAppendResults(self):
     self.SerializeIntermediateResults(
         testing.TestResult(
             'benchmark/story',
             output_artifacts=[
-                self.CreateHistogramsArtifact(
-                    histogram.Histogram('a', 'unitless')),
+                self.CreateHtmlTraceArtifact(),
             ],
+            tags=['tbmv2:sampleMetric'],
         ),
     )
 
@@ -633,12 +611,10 @@
         '--results-label', 'label2',
     ])
 
-    with open(os.path.join(self.output_dir, csv_output.OUTPUT_FILENAME)) as f:
-      lines = [line for line in f]
-
-    self.assertEqual(len(lines), 3)
-    self.assertIn('label2', lines[1])
-    self.assertIn('label1', lines[2])
+    sample_rows = self.ReadSampleHistogramsFromCsv()
+    self.assertEqual(len(sample_rows), 2)
+    self.assertEqual(sample_rows[0]['displayLabel'], 'label2')
+    self.assertEqual(sample_rows[1]['displayLabel'], 'label1')
 
   def testExitCodeHasFailures(self):
     self.SerializeIntermediateResults(
diff --git a/tools/perf/core/results_processor/util.py b/tools/perf/core/results_processor/util.py
index 1f634d87..c2f7f1f 100644
--- a/tools/perf/core/results_processor/util.py
+++ b/tools/perf/core/results_processor/util.py
@@ -7,6 +7,11 @@
 import logging
 import multiprocessing
 from multiprocessing.dummy import Pool as ThreadPool
+import urllib
+
+
+TELEMETRY_TEST_PATH_FORMAT = 'telemetry'
+GTEST_TEST_PATH_FORMAT = 'gtest'
 
 
 def ApplyInParallel(function, work_list, on_failure=None):
@@ -49,6 +54,31 @@
     pool.terminate()
 
 
+def SplitTestPath(test_result, test_path_format):
+  """ Split a test path into test suite name and test case name.
+
+  Telemetry and Gtest have slightly different test path formats.
+  Telemetry uses '{benchmark_name}/{story_name}', e.g.
+  'system_health.common_desktop/load:news:cnn:2018'.
+  Gtest uses '{test_suite_name}.{test_case_name}', e.g.
+  'ZeroToFiveSequence/LuciTestResultParameterizedTest.Variant'
+  """
+  if test_path_format == TELEMETRY_TEST_PATH_FORMAT:
+    separator = '/'
+  elif test_path_format == GTEST_TEST_PATH_FORMAT:
+    separator = '.'
+  else:
+    raise ValueError('Unknown test path format: %s' % test_path_format)
+
+  # TODO(crbug.com/981349): Remove this after test paths are no longer
+  # url-quoted.
+  test_path = urllib.unquote(test_result['testPath'])
+  if separator not in test_path:
+    raise ValueError('Invalid test path: %s' % test_path)
+
+  return test_path.split(separator, 1)
+
+
 def IsoTimestampToEpoch(timestamp):
   """Convert ISO formatted time to seconds since epoch."""
   try:
diff --git a/tools/perf/core/undocumented_benchmarks.py b/tools/perf/core/undocumented_benchmarks.py
index 0e6d7bd..5ccd1b3 100644
--- a/tools/perf/core/undocumented_benchmarks.py
+++ b/tools/perf/core/undocumented_benchmarks.py
@@ -21,14 +21,11 @@
   'octane',
   'passthrough_command_buffer_perftests',
   'performance_browser_tests',
-  'sizes (mac)',
-  'sizes (win)',
   'speedometer',
   'speedometer-future',
   'speedometer2',
   'speedometer2-future',
   'startup.mobile',
-  'supersize_archive',
   'system_health.webview_startup',
   'tab_switching.typical_25',
   'tracing.tracing_with_background_memory_infra',
diff --git a/tools/perf/expectations.config b/tools/perf/expectations.config
index 6f8e07b..05aa905 100644
--- a/tools/perf/expectations.config
+++ b/tools/perf/expectations.config
@@ -357,6 +357,7 @@
 crbug.com/1008001 [ win ] system_health.memory_desktop/browse:tools:sheets:2019 [ Skip ]
 crbug.com/1017290 [ linux ] system_health.memory_desktop/browse:search:google_india:2018 [ Skip ]
 crbug.com/1017244 [ desktop ] system_health.memory_desktop/browse_accessibility:media:youtube [ Skip ]
+crbug.com/1017346 [ desktop ] system_health.memory_desktop/browse:media:youtube:2019 [ Skip ]
 
 # Benchmark: system_health.memory_mobile
 crbug.com/1013317 [ android ] system_health.memory_mobile/load:news:nytimes [ Skip ]
diff --git a/ui/accessibility/ax_node_position_unittest.cc b/ui/accessibility/ax_node_position_unittest.cc
index 98b25f0..a18dc39 100644
--- a/ui/accessibility/ax_node_position_unittest.cc
+++ b/ui/accessibility/ax_node_position_unittest.cc
@@ -6314,9 +6314,9 @@
              "TextPosition anchor_id=1 text_offset=0 "
              "affinity=downstream annotated_text=<L>ine 1\nLine 2",
              "TextPosition anchor_id=1 text_offset=0 "
-             "affinity=upstream annotated_text=<L>ine 1\nLine 2",
+             "affinity=downstream annotated_text=<L>ine 1\nLine 2",
              "TextPosition anchor_id=1 text_offset=0 "
-             "affinity=upstream annotated_text=<L>ine 1\nLine 2"}},
+             "affinity=downstream annotated_text=<L>ine 1\nLine 2"}},
         TextNavigationTestParam{
             base::BindRepeating([](const TestPositionType& position) {
               return position->CreatePreviousLineStartPosition(
@@ -6634,10 +6634,6 @@
     CreatePreviousLineEndPositionWithBoundaryBehaviorStopAtAnchorBoundary,
     AXPositionTextNavigationTestWithParam,
     testing::Values(
-        // Note that for the first two tests we can't go past the line ending at
-        // "Line 1" to test for "NullPosition'", because the text position at
-        // the beginning of the soft line break is equivalent to the position at
-        // the end of the line's text and so an infinite recursion will occur.
         TextNavigationTestParam{
             base::BindRepeating([](const TestPositionType& position) {
               return position->CreatePreviousLineEndPosition(
@@ -6776,9 +6772,9 @@
             {"TextPosition anchor_id=1 text_offset=6 "
              "affinity=downstream annotated_text=Line 1<\n>Line 2",
              "TextPosition anchor_id=1 text_offset=0 "
-             "affinity=upstream annotated_text=<L>ine 1\nLine 2",
+             "affinity=downstream annotated_text=<L>ine 1\nLine 2",
              "TextPosition anchor_id=1 text_offset=0 "
-             "affinity=upstream annotated_text=<L>ine 1\nLine 2"}},
+             "affinity=downstream annotated_text=<L>ine 1\nLine 2"}},
         TextNavigationTestParam{
             base::BindRepeating([](const TestPositionType& position) {
               return position->CreatePreviousLineEndPosition(
@@ -6800,9 +6796,9 @@
             ROOT_ID,
             5 /* text_offset on the last character of "Line 1". */,
             {"TextPosition anchor_id=1 text_offset=0 "
-             "affinity=upstream annotated_text=<L>ine 1\nLine 2",
+             "affinity=downstream annotated_text=<L>ine 1\nLine 2",
              "TextPosition anchor_id=1 text_offset=0 "
-             "affinity=upstream annotated_text=<L>ine 1\nLine 2"}},
+             "affinity=downstream annotated_text=<L>ine 1\nLine 2"}},
         TextNavigationTestParam{
             base::BindRepeating([](const TestPositionType& position) {
               return position->CreatePreviousLineEndPosition(
@@ -7196,9 +7192,9 @@
              "TextPosition anchor_id=1 text_offset=0 "
              "affinity=downstream annotated_text=<L>ine 1\nLine 2",
              "TextPosition anchor_id=1 text_offset=0 "
-             "affinity=upstream annotated_text=<L>ine 1\nLine 2",
+             "affinity=downstream annotated_text=<L>ine 1\nLine 2",
              "TextPosition anchor_id=1 text_offset=0 "
-             "affinity=upstream annotated_text=<L>ine 1\nLine 2"}},
+             "affinity=downstream annotated_text=<L>ine 1\nLine 2"}},
         TextNavigationTestParam{
             base::BindRepeating([](const TestPositionType& position) {
               return position->CreatePreviousParagraphStartPosition(
@@ -7310,6 +7306,8 @@
              "TextPosition anchor_id=1 text_offset=13 "
              "affinity=downstream annotated_text=Line 1\nLine 2<>",
              "TextPosition anchor_id=1 text_offset=13 "
+             "affinity=downstream annotated_text=Line 1\nLine 2<>",
+             "TextPosition anchor_id=1 text_offset=13 "
              "affinity=downstream annotated_text=Line 1\nLine 2<>"}},
         TextNavigationTestParam{
             base::BindRepeating([](const TestPositionType& position) {
@@ -7323,6 +7321,8 @@
              "TextPosition anchor_id=4 text_offset=13 "
              "affinity=downstream annotated_text=Line 1\nLine 2<>",
              "TextPosition anchor_id=4 text_offset=13 "
+             "affinity=downstream annotated_text=Line 1\nLine 2<>",
+             "TextPosition anchor_id=4 text_offset=13 "
              "affinity=downstream annotated_text=Line 1\nLine 2<>"}},
         TextNavigationTestParam{
             base::BindRepeating([](const TestPositionType& position) {
@@ -7359,9 +7359,9 @@
             ROOT_ID,
             0 /* text_offset */,
             {"TextPosition anchor_id=1 text_offset=0 "
-             "affinity=upstream annotated_text=<L>ine 1\nLine 2",
+             "affinity=downstream annotated_text=<L>ine 1\nLine 2",
              "TextPosition anchor_id=1 text_offset=0 "
-             "affinity=upstream annotated_text=<L>ine 1\nLine 2"}},
+             "affinity=downstream annotated_text=<L>ine 1\nLine 2"}},
         TextNavigationTestParam{
             base::BindRepeating([](const TestPositionType& position) {
               return position->CreateNextParagraphEndPosition(
@@ -7487,7 +7487,7 @@
             {"TextPosition anchor_id=1 text_offset=7 "
              "affinity=upstream annotated_text=Line 1\n<L>ine 2",
              "TextPosition anchor_id=1 text_offset=0 "
-             "affinity=upstream annotated_text=<L>ine 1\nLine 2",
+             "affinity=downstream annotated_text=<L>ine 1\nLine 2",
              "NullPosition"}},
         TextNavigationTestParam{
             base::BindRepeating([](const TestPositionType& position) {
@@ -7509,7 +7509,7 @@
             ROOT_ID,
             5 /* text_offset on the last character of "Line 1". */,
             {"TextPosition anchor_id=1 text_offset=0 "
-             "affinity=upstream annotated_text=<L>ine 1\nLine 2",
+             "affinity=downstream annotated_text=<L>ine 1\nLine 2",
              "NullPosition"}},
         TextNavigationTestParam{
             base::BindRepeating([](const TestPositionType& position) {
@@ -7548,10 +7548,6 @@
     CreatePreviousParagraphEndPositionWithBoundaryBehaviorStopAtAnchorBoundary,
     AXPositionTextNavigationTestWithParam,
     testing::Values(
-        // Note that for the first two tests we can't go past the line ending at
-        // "Line 1" to test for "NullPosition'", because the text position at
-        // the beginning of the soft line break is equivalent to the position at
-        // the end of the line's text and so an infinite recursion will occur.
         TextNavigationTestParam{
             base::BindRepeating([](const TestPositionType& position) {
               return position->CreatePreviousParagraphEndPosition(
@@ -7562,7 +7558,9 @@
             {"TextPosition anchor_id=1 text_offset=7 "
              "affinity=upstream annotated_text=Line 1\n<L>ine 2",
              "TextPosition anchor_id=1 text_offset=0 "
-             "affinity=upstream annotated_text=<L>ine 1\nLine 2"}},
+             "affinity=downstream annotated_text=<L>ine 1\nLine 2",
+             "TextPosition anchor_id=1 text_offset=0 "
+             "affinity=downstream annotated_text=<L>ine 1\nLine 2"}},
         TextNavigationTestParam{
             base::BindRepeating([](const TestPositionType& position) {
               return position->CreatePreviousParagraphEndPosition(
@@ -7573,6 +7571,8 @@
             {"TextPosition anchor_id=4 text_offset=7 "
              "affinity=upstream annotated_text=Line 1\n<L>ine 2",
              "TextPosition anchor_id=4 text_offset=0 "
+             "affinity=downstream annotated_text=<L>ine 1\nLine 2",
+             "TextPosition anchor_id=4 text_offset=0 "
              "affinity=downstream annotated_text=<L>ine 1\nLine 2"}},
         TextNavigationTestParam{
             base::BindRepeating([](const TestPositionType& position) {
@@ -7582,7 +7582,7 @@
             ROOT_ID,
             5 /* text_offset on the last character of "Line 1". */,
             {"TextPosition anchor_id=1 text_offset=0 "
-             "affinity=upstream annotated_text=<L>ine 1\nLine 2",
+             "affinity=downstream annotated_text=<L>ine 1\nLine 2",
              "TextPosition anchor_id=1 text_offset=0 "
              "affinity=downstream annotated_text=<L>ine 1\nLine 2"}},
         TextNavigationTestParam{
@@ -7715,9 +7715,9 @@
             {"TextPosition anchor_id=1 text_offset=7 "
              "affinity=upstream annotated_text=Line 1\n<L>ine 2",
              "TextPosition anchor_id=1 text_offset=0 "
-             "affinity=upstream annotated_text=<L>ine 1\nLine 2",
+             "affinity=downstream annotated_text=<L>ine 1\nLine 2",
              "TextPosition anchor_id=1 text_offset=0 "
-             "affinity=upstream annotated_text=<L>ine 1\nLine 2"}},
+             "affinity=downstream annotated_text=<L>ine 1\nLine 2"}},
         TextNavigationTestParam{
             base::BindRepeating([](const TestPositionType& position) {
               return position->CreatePreviousParagraphEndPosition(
@@ -7740,9 +7740,9 @@
             ROOT_ID,
             5 /* text_offset on the last character of "Line 1". */,
             {"TextPosition anchor_id=1 text_offset=0 "
-             "affinity=upstream annotated_text=<L>ine 1\nLine 2",
+             "affinity=downstream annotated_text=<L>ine 1\nLine 2",
              "TextPosition anchor_id=1 text_offset=0 "
-             "affinity=upstream annotated_text=<L>ine 1\nLine 2"}},
+             "affinity=downstream annotated_text=<L>ine 1\nLine 2"}},
         TextNavigationTestParam{
             base::BindRepeating([](const TestPositionType& position) {
               return position->CreatePreviousParagraphEndPosition(
diff --git a/ui/accessibility/ax_position.h b/ui/accessibility/ax_position.h
index d67ec0c..6143953 100644
--- a/ui/accessibility/ax_position.h
+++ b/ui/accessibility/ax_position.h
@@ -122,6 +122,9 @@
   using BoundaryConditionPredicate =
       base::RepeatingCallback<bool(const AXPositionInstance&)>;
 
+  using BoundaryTextOffsetsFunc =
+      base::RepeatingCallback<std::vector<int32_t>(const AXPositionInstance&)>;
+
   // When converting to an unignored position, determines how to adjust the new
   // position in order to make it valid.
   enum class AdjustmentBehavior { kMoveLeft, kMoveRight };
@@ -257,7 +260,7 @@
 
     std::string text = base::UTF16ToUTF8(GetText());
     DCHECK_GE(text_offset_, 0);
-    DCHECK_LE(text_offset_, static_cast<int>(text.length()));
+    DCHECK_LE(text_offset_, int{text.length()});
     std::string annotated_text;
     if (text_offset_ == MaxTextOffset()) {
       annotated_text = text + "<>";
@@ -375,8 +378,8 @@
       case AXPositionKind::TEXT_POSITION: {
         const std::vector<int32_t> word_starts =
             text_position->GetWordStartOffsets();
-        return base::Contains(
-            word_starts, static_cast<int32_t>(text_position->text_offset_));
+        return base::Contains(word_starts,
+                              int32_t{text_position->text_offset_});
       }
     }
   }
@@ -392,8 +395,7 @@
       case AXPositionKind::TEXT_POSITION: {
         const std::vector<int32_t> word_ends =
             text_position->GetWordEndOffsets();
-        return base::Contains(
-            word_ends, static_cast<int32_t>(text_position->text_offset_));
+        return base::Contains(word_ends, int32_t{text_position->text_offset_});
       }
     }
   }
@@ -516,7 +518,7 @@
         const AbortMovePredicate abort_move_predicate =
             base::BindRepeating(&AbortMoveAtParagraphBoundary,
                                 std::ref(crossed_potential_boundary_token));
-        auto previous_text_position = text_position->Clone();
+        AXPositionInstance previous_text_position = text_position->Clone();
         do {
           previous_text_position =
               previous_text_position->CreatePreviousTextAnchorPosition(
@@ -584,7 +586,7 @@
         const AbortMovePredicate abort_move_predicate =
             base::BindRepeating(&AbortMoveAtParagraphBoundary,
                                 std::ref(crossed_potential_boundary_token));
-        auto next_text_position =
+        AXPositionInstance next_text_position =
             text_position->CreateNextTextAnchorPosition(abort_move_predicate);
         if (next_text_position->IsNullPosition())
           return true;
@@ -632,7 +634,7 @@
         // the start of a page.
         // This will return a null position when an anchor movement would
         // cross a page boundary, or the start of document was reached.
-        auto previous_text_position =
+        AXPositionInstance previous_text_position =
             text_position->CreatePreviousTextAnchorPosition(
                 base::BindRepeating(&AbortMoveAtPageBoundary));
         return previous_text_position->IsNullPosition();
@@ -658,8 +660,9 @@
         // the end of a page.
         // This will return a null position when an anchor movement would
         // cross a page boundary, or the end of document was reached.
-        auto next_text_position = text_position->CreateNextTextAnchorPosition(
-            base::BindRepeating(&AbortMoveAtPageBoundary));
+        AXPositionInstance next_text_position =
+            text_position->CreateNextTextAnchorPosition(
+                base::BindRepeating(&AbortMoveAtPageBoundary));
         return next_text_position->IsNullPosition();
       }
     }
@@ -677,7 +680,7 @@
 
     // Iterate over anchors until a format boundary is found. This will return a
     // null position upon crossing a boundary.
-    auto previous_position = CreatePreviousLeafTreePosition(
+    AXPositionInstance previous_position = CreatePreviousLeafTreePosition(
         base::BindRepeating(&AbortMoveAtFormatBoundary));
     return previous_position->IsNullPosition();
   }
@@ -694,7 +697,7 @@
 
     // Iterate over anchors until a format boundary is found. This will return a
     // null position upon crossing a boundary.
-    auto next_position = CreateNextLeafTreePosition(
+    AXPositionInstance next_position = CreateNextLeafTreePosition(
         base::BindRepeating(&AbortMoveAtFormatBoundary));
     return next_position->IsNullPosition();
   }
@@ -2273,33 +2276,42 @@
       AXBoundaryBehavior boundary_behavior,
       AXTextBoundaryDirection boundary_direction,
       BoundaryConditionPredicate at_start_condition,
-      BoundaryConditionPredicate at_end_condition) const {
+      BoundaryConditionPredicate at_end_condition,
+      BoundaryTextOffsetsFunc get_start_offsets =
+          BoundaryTextOffsetsFunc()) const {
     const bool was_tree_position = IsTreePosition();
     AXPositionInstance text_position = AsLeafTextPosition();
     if (text_position->IsNullPosition())
       return text_position;
 
-    if (boundary_behavior == AXBoundaryBehavior::StopIfAlreadyAtBoundary &&
-        at_start_condition.Run(text_position)) {
-      AXPositionInstance clone = Clone();
-      clone->affinity_ = ax::mojom::TextAffinity::kDownstream;
-      return clone;
-    }
+    while (!at_start_condition.Run(text_position) ||
+           (boundary_behavior != AXBoundaryBehavior::StopIfAlreadyAtBoundary &&
+            *this == *text_position)) {
+      if (*this == *text_position) {
+        AXPositionInstance next_position =
+            text_position->CreatePositionAtNextOffsetBoundary(
+                boundary_direction, get_start_offsets);
+        if (*next_position != *text_position) {
+          text_position = std::move(next_position);
+          break;
+        }
+      }
 
-    do {
       AXPositionInstance next_position;
-      if (boundary_direction == AXTextBoundaryDirection::kForwards)
+      if (boundary_direction == AXTextBoundaryDirection::kForwards) {
         next_position = text_position->CreateNextLeafTextPosition();
-      else
+      } else {
         next_position = text_position->AtStartOfAnchor()
                             ? text_position->CreatePreviousLeafTextPosition()
                             : text_position->CreatePositionAtStartOfAnchor();
+      }
 
       if (next_position->IsNullPosition()) {
-        if (boundary_behavior == AXBoundaryBehavior::StopAtAnchorBoundary)
+        if (boundary_behavior == AXBoundaryBehavior::StopAtAnchorBoundary) {
           return (boundary_direction == AXTextBoundaryDirection::kForwards)
                      ? CreatePositionAtEndOfAnchor()
                      : CreatePositionAtStartOfAnchor();
+        }
         if (boundary_behavior == AXBoundaryBehavior::StopAtLastAnchorBoundary) {
           // We can't simply return the following position; break and after this
           // loop we'll try to do some adjustments to text_position.
@@ -2312,13 +2324,11 @@
         return next_position;
       }
 
-      // Continue searching for the next boundary end in the specified direction
-      // until the next logical text position is reached.
-      text_position = std::move(next_position);
-    } while (
-        !at_start_condition.Run(text_position) ||
-        (boundary_behavior != AXBoundaryBehavior::StopIfAlreadyAtBoundary &&
-         *this == *text_position));
+      // Continue searching for the next boundary start in the specified
+      // direction until the next logical text position is reached.
+      text_position = next_position->CreatePositionAtFirstOffsetBoundary(
+          boundary_direction, get_start_offsets);
+    }
 
     // If the boundary is in the same subtree, return a position rooted at this
     // position's anchor. This is necessary because we don't want to return a
@@ -2332,6 +2342,9 @@
                  : CreatePositionAtStartOfAnchor();
     }
 
+    // Affinity is only upstream at the end of a line, and so a start boundary
+    // will never have an upstream affinity.
+    text_position->affinity_ = ax::mojom::TextAffinity::kDownstream;
     if (was_tree_position)
       text_position = text_position->AsTreePosition();
     return text_position;
@@ -2341,41 +2354,44 @@
       AXBoundaryBehavior boundary_behavior,
       AXTextBoundaryDirection boundary_direction,
       BoundaryConditionPredicate at_start_condition,
-      BoundaryConditionPredicate at_end_condition) const {
+      BoundaryConditionPredicate at_end_condition,
+      BoundaryTextOffsetsFunc get_end_offsets =
+          BoundaryTextOffsetsFunc()) const {
     const bool was_tree_position = IsTreePosition();
     AXPositionInstance text_position = AsLeafTextPosition();
     if (text_position->IsNullPosition())
       return text_position;
 
-    if (boundary_behavior == AXBoundaryBehavior::StopIfAlreadyAtBoundary &&
-        at_end_condition.Run(text_position)) {
-      AXPositionInstance clone = Clone();
-      // If there is no ambiguity as to whether the position is at the end of
-      // the current boundary or the start of the next boundary, affinity should
-      // be reset in order to get consistent output from this method, regardless
-      // of input affinity.
-      clone->affinity_ = ax::mojom::TextAffinity::kDownstream;
-      if (at_start_condition.Run(clone))
-        clone->affinity_ = ax::mojom::TextAffinity::kUpstream;
-      return clone;
-    }
+    while (!at_end_condition.Run(text_position) ||
+           (boundary_behavior != AXBoundaryBehavior::StopIfAlreadyAtBoundary &&
+            *this == *text_position)) {
+      if (*this == *text_position) {
+        AXPositionInstance next_position =
+            text_position->CreatePositionAtNextOffsetBoundary(
+                boundary_direction, get_end_offsets);
+        if (*next_position != *text_position) {
+          text_position = std::move(next_position);
+          break;
+        }
+      }
 
-    do {
       AXPositionInstance next_position;
-      if (boundary_direction == AXTextBoundaryDirection::kForwards)
+      if (boundary_direction == AXTextBoundaryDirection::kForwards) {
         next_position = !text_position->AtEndOfAnchor()
                             ? text_position->CreatePositionAtEndOfAnchor()
                             : text_position->CreateNextLeafTextPosition()
                                   ->CreatePositionAtEndOfAnchor();
-      else
+      } else {
         next_position = text_position->CreatePreviousLeafTextPosition()
                             ->CreatePositionAtEndOfAnchor();
+      }
 
       if (next_position->IsNullPosition()) {
-        if (boundary_behavior == AXBoundaryBehavior::StopAtAnchorBoundary)
+        if (boundary_behavior == AXBoundaryBehavior::StopAtAnchorBoundary) {
           return (boundary_direction == AXTextBoundaryDirection::kForwards)
                      ? CreatePositionAtEndOfAnchor()
                      : CreatePositionAtStartOfAnchor();
+        }
         if (boundary_behavior == AXBoundaryBehavior::StopAtLastAnchorBoundary) {
           // We can't simply return the following position; break and after this
           // loop we'll try to do some adjustments to text_position.
@@ -2390,11 +2406,9 @@
 
       // Continue searching for the next boundary end in the specified direction
       // until the next logical text position is reached.
-      text_position = std::move(next_position);
-    } while (
-        !at_end_condition.Run(text_position) ||
-        (boundary_behavior != AXBoundaryBehavior::StopIfAlreadyAtBoundary &&
-         *this == *text_position));
+      text_position = next_position->CreatePositionAtFirstOffsetBoundary(
+          boundary_direction, get_end_offsets);
+    }
 
     // If the boundary is in the same subtree, return a position rooted at this
     // position's anchor. This is necessary because we don't want to return a
@@ -2408,6 +2422,24 @@
                  : CreatePositionAtStartOfAnchor();
     }
 
+    // If there is no ambiguity as to whether the position is at the end of
+    // the current boundary or the start of the next boundary, an upstream
+    // affinity should be reset to downstream in order to get consistent output
+    // from this method, regardless of input affinity.
+    //
+    // Note that there could be no ambiguity if the boundary is either at the
+    // start or the end of the current anchor, so we should always reset to
+    // downstream affinity in those cases.
+    if (text_position->affinity_ == ax::mojom::TextAffinity::kUpstream) {
+      AXPositionInstance downstream_position = text_position->Clone();
+      downstream_position->affinity_ = ax::mojom::TextAffinity::kDownstream;
+      if (downstream_position->AtStartOfAnchor() ||
+          downstream_position->AtEndOfAnchor() ||
+          !at_start_condition.Run(downstream_position)) {
+        text_position->affinity_ = ax::mojom::TextAffinity::kDownstream;
+      }
+    }
+
     if (was_tree_position)
       text_position = text_position->AsTreePosition();
     return text_position;
@@ -3056,6 +3088,16 @@
     return false;
   }
 
+  static std::vector<int32_t> GetWordStartOffsetsFunc(
+      const AXPositionInstance& position) {
+    return position->GetWordStartOffsets();
+  }
+
+  static std::vector<int32_t> GetWordEndOffsetsFunc(
+      const AXPositionInstance& position) {
+    return position->GetWordEndOffsets();
+  }
+
   AXPositionInstance CreateDocumentAncestorPosition() const {
     AXPositionInstance iterator = Clone();
     while (!iterator->IsNullPosition()) {
@@ -3081,6 +3123,90 @@
     return ancestor_position;
   }
 
+  // Creates a text position that is in the same anchor as the current position,
+  // but starting from the current text offset, adjusts to the next or the
+  // previous boundary offset depending on the boundary direction. If there is
+  // no next / previous offset, the current text offset is unchanged.
+  AXPositionInstance CreatePositionAtNextOffsetBoundary(
+      AXTextBoundaryDirection boundary_direction,
+      BoundaryTextOffsetsFunc get_offsets) const {
+    if (IsNullPosition() || get_offsets.is_null())
+      return Clone();
+
+    AXPositionInstance text_position = AsTextPosition();
+    const std::vector<int32_t> boundary_offsets =
+        get_offsets.Run(text_position);
+    if (boundary_offsets.empty())
+      return text_position;
+
+    switch (boundary_direction) {
+      case AXTextBoundaryDirection::kForwards: {
+        const auto offsets_iterator =
+            std::upper_bound(boundary_offsets.begin(), boundary_offsets.end(),
+                             int32_t{text_position->text_offset_});
+        // If there is no next offset, the current offset should be unchanged.
+        if (offsets_iterator < boundary_offsets.end()) {
+          text_position->text_offset_ = int{*offsets_iterator};
+          text_position->affinity_ = ax::mojom::TextAffinity::kDownstream;
+        }
+        break;
+      }
+      case AXTextBoundaryDirection::kBackwards: {
+        auto offsets_iterator =
+            std::lower_bound(boundary_offsets.begin(), boundary_offsets.end(),
+                             int32_t{text_position->text_offset_});
+        // If there is no previous offset, the current offset should be
+        // unchanged.
+        if (offsets_iterator > boundary_offsets.begin()) {
+          // Since we already checked if "boundary_offsets" are non-empty, we
+          // can safely move the iterator one position back, even if it's
+          // currently at the vector's end.
+          --offsets_iterator;
+          text_position->text_offset_ = int{*offsets_iterator};
+          text_position->affinity_ = ax::mojom::TextAffinity::kDownstream;
+        }
+        break;
+      }
+    }
+
+    return text_position;
+  }
+
+  // Creates a text position that is in the same anchor as the current position,
+  // but adjusts its text offset to be either at the first or last offset
+  // boundary, based on the boundary direction. When moving forward, the text
+  // position is adjusted to point to the first offset boundary. When moving
+  // backward, it is adjusted to point to the last offset boundary.
+  AXPositionInstance CreatePositionAtFirstOffsetBoundary(
+      AXTextBoundaryDirection boundary_direction,
+      BoundaryTextOffsetsFunc get_offsets) const {
+    if (IsNullPosition() || get_offsets.is_null())
+      return Clone();
+
+    AXPositionInstance text_position = AsTextPosition();
+    const std::vector<int32_t> boundary_offsets =
+        get_offsets.Run(text_position);
+    switch (boundary_direction) {
+      case AXTextBoundaryDirection::kForwards:
+        if (boundary_offsets.empty()) {
+          return text_position->CreatePositionAtStartOfAnchor();
+        } else {
+          text_position->text_offset_ = int{boundary_offsets[0]};
+          return text_position;
+        }
+        break;
+      case AXTextBoundaryDirection::kBackwards:
+        if (boundary_offsets.empty()) {
+          return text_position->CreatePositionAtEndOfAnchor();
+        } else {
+          text_position->text_offset_ =
+              int{boundary_offsets[boundary_offsets.size() - 1]};
+          return text_position;
+        }
+        break;
+    }
+  }
+
   AXPositionKind kind_;
   AXTreeID tree_id_;
   int32_t anchor_id_;
diff --git a/ui/accessibility/platform/ax_fragment_root_win.cc b/ui/accessibility/platform/ax_fragment_root_win.cc
index 28486bb..3bd8819e 100644
--- a/ui/accessibility/platform/ax_fragment_root_win.cc
+++ b/ui/accessibility/platform/ax_fragment_root_win.cc
@@ -208,11 +208,8 @@
 };
 
 AXFragmentRootWin::AXFragmentRootWin(gfx::AcceleratedWidget widget,
-                                     AXFragmentRootDelegateWin* delegate,
-                                     bool is_controller_for_root)
-    : widget_(widget),
-      delegate_(delegate),
-      is_controller_for_root_(is_controller_for_root) {
+                                     AXFragmentRootDelegateWin* delegate)
+    : widget_(widget), delegate_(delegate) {
   platform_node_ = ui::AXFragmentRootPlatformNodeWin::Create(this);
   AXFragmentRootMapWin::GetInstance().AddFragmentRoot(widget, this);
 }
@@ -345,10 +342,7 @@
         return child_index;
     }
   }
-
-  NOTREACHED();
-
-  return -1;
+  return 0;
 }
 
 }  // namespace ui
diff --git a/ui/accessibility/platform/ax_fragment_root_win.h b/ui/accessibility/platform/ax_fragment_root_win.h
index 3626e86..1b8b5f96 100644
--- a/ui/accessibility/platform/ax_fragment_root_win.h
+++ b/ui/accessibility/platform/ax_fragment_root_win.h
@@ -26,20 +26,10 @@
 // Since UIA derives some information from the underlying HWND hierarchy, we
 // expose one fragment root per HWND. The class that owns the HWND is expected
 // to own the corresponding AXFragmentRootWin.
-//
-// The usual way for UI Automation to obtain a fragment root is through
-// WM_GETOBJECT. However, if there's a relation such as "Controller For"
-// between element A in one window and element B in another window, UIA might
-// call element A to discover the relation, receive a pointer to element B,
-// then ask element B for its fragment root, without having sent WM_GETOBJECT
-// to element B's window.
-// The is_controller_for_root_ boolean member variable is used to determine
-// whether this is a fragment root witha  "Controller For" relation.
 class AX_EXPORT AXFragmentRootWin : public ui::AXPlatformNodeDelegateBase {
  public:
   AXFragmentRootWin(gfx::AcceleratedWidget widget,
-                    AXFragmentRootDelegateWin* delegate,
-                    bool is_controller_for_root = false);
+                    AXFragmentRootDelegateWin* delegate);
   ~AXFragmentRootWin() override;
 
   // Fragment roots register themselves in a map upon creation and unregister
@@ -65,8 +55,6 @@
   // If a child node is available, return its delegate.
   AXPlatformNodeDelegate* GetChildNodeDelegate() const;
 
-  bool IsControllerFor() const { return is_controller_for_root_; }
-
  private:
   // AXPlatformNodeDelegate overrides.
   gfx::NativeViewAccessible GetParent() override;
@@ -94,7 +82,6 @@
   AXFragmentRootDelegateWin* const delegate_;
   Microsoft::WRL::ComPtr<ui::AXFragmentRootPlatformNodeWin> platform_node_;
   ui::AXUniqueId unique_id_;
-  bool is_controller_for_root_;
 };
 
 }  // namespace ui
diff --git a/ui/accessibility/platform/ax_platform_node_win.cc b/ui/accessibility/platform/ax_platform_node_win.cc
index 17255c7..4c833ca 100644
--- a/ui/accessibility/platform/ax_platform_node_win.cc
+++ b/ui/accessibility/platform/ax_platform_node_win.cc
@@ -3680,7 +3680,7 @@
       // Otherwise, consult the platform-neutral tree.
       AXFragmentRootWin* fragment_root =
           AXFragmentRootWin::GetFragmentRootParentOf(GetNativeViewAccessible());
-      if (UNLIKELY(fragment_root) && !fragment_root->IsControllerFor()) {
+      if (UNLIKELY(fragment_root)) {
         neighbor = fragment_root->GetNativeViewAccessible();
       } else {
         neighbor = GetParent();
@@ -3728,8 +3728,7 @@
       // source node isn't Rx, return Rx.
       AXFragmentRootWin* fragment_root =
           AXFragmentRootWin::GetFragmentRootParentOf(neighbor);
-      if (UNLIKELY(fragment_root && fragment_root != GetDelegate() &&
-                   !fragment_root->IsControllerFor()))
+      if (UNLIKELY(fragment_root && fragment_root != GetDelegate()))
         neighbor = fragment_root->GetNativeViewAccessible();
     }
     neighbor->QueryInterface(IID_PPV_ARGS(element_provider));
diff --git a/ui/display/mac/screen_mac.mm b/ui/display/mac/screen_mac.mm
index 95675ad6..b19c27f 100644
--- a/ui/display/mac/screen_mac.mm
+++ b/ui/display/mac/screen_mac.mm
@@ -32,8 +32,8 @@
 namespace display {
 namespace {
 
-// The delay to handle the display configuration changes.
-// See comments in ScreenMac::HandleDisplayReconfiguration.
+// The delay to handle the display configuration changes. This is in place to
+// coalesce display update notifications and thereby avoid thrashing.
 const int64_t kConfigureDelayMs = 500;
 
 NSScreen* GetMatchingScreen(const gfx::Rect& match_rect) {
@@ -157,20 +157,27 @@
     CGDisplayRegisterReconfigurationCallback(
         ScreenMac::DisplayReconfigurationCallBack, this);
 
+    auto update_block = ^(NSNotification* notification) {
+      OnNSScreensMayHaveChanged();
+    };
+
     NSNotificationCenter* center = [NSNotificationCenter defaultCenter];
     screen_color_change_observer_.reset(
         [[center addObserverForName:NSScreenColorSpaceDidChangeNotification
                              object:nil
                               queue:nil
-                         usingBlock:^(NSNotification* notification) {
-                           configure_timer_.Reset();
-                           displays_require_update_ = true;
-                         }] retain]);
+                         usingBlock:update_block] retain]);
+    screen_params_change_observer_.reset([[center
+        addObserverForName:NSApplicationDidChangeScreenParametersNotification
+                    object:nil
+                     queue:nil
+                usingBlock:update_block] retain]);
   }
 
   ~ScreenMac() override {
     NSNotificationCenter* center = [NSNotificationCenter defaultCenter];
     [center removeObserver:screen_color_change_observer_];
+    [center removeObserver:screen_params_change_observer_];
 
     CGDisplayRemoveReconfigurationCallback(
         ScreenMac::DisplayReconfigurationCallBack, this);
@@ -195,16 +202,18 @@
   int GetNumDisplays() const override { return GetAllDisplays().size(); }
 
   const std::vector<Display>& GetAllDisplays() const override {
+    UpdateDisplaysIfNeeded();
     return displays_;
   }
 
   Display GetDisplayNearestWindow(
       gfx::NativeWindow native_window) const override {
-    NSWindow* window = native_window.GetNativeNSWindow();
-    EnsureDisplaysValid();
+    UpdateDisplaysIfNeeded();
+
     if (displays_.size() == 1)
       return displays_[0];
 
+    NSWindow* window = native_window.GetNativeNSWindow();
     if (!window)
       return GetPrimaryDisplay();
 
@@ -277,31 +286,30 @@
   static void DisplayReconfigurationCallBack(CGDirectDisplayID display,
                                              CGDisplayChangeSummaryFlags flags,
                                              void* userInfo) {
-    if (flags & kCGDisplayBeginConfigurationFlag)
-      return;
-
     ScreenMac* screen_mac = static_cast<ScreenMac*>(userInfo);
-
-    // Timer::Reset() ensures at least another interval passes before the
-    // associated task runs, effectively coalescing these events.
-    screen_mac->configure_timer_.Reset();
-    screen_mac->displays_require_update_ = true;
+    screen_mac->OnNSScreensMayHaveChanged();
   }
 
  private:
   Display GetCachedDisplayForScreen(NSScreen* screen) const {
-    EnsureDisplaysValid();
+    UpdateDisplaysIfNeeded();
     const CGDirectDisplayID display_id = [[[screen deviceDescription]
         objectForKey:@"NSScreenNumber"] unsignedIntValue];
     for (const Display& display : displays_) {
       if (display_id == display.id())
         return display;
     }
-    NOTREACHED();  // Asked for a hidden/sleeping/mirrored screen?
+    // In theory, this should not be reached, because |displays_require_update_|
+    // should have been set prior to -[NSScreen screens] changing. In practice,
+    // on Catalina, it has been observed that -[NSScreen screens] changes before
+    // any notifications are received.
+    // https://crbug.com/1021340.
+    OnNSScreensMayHaveChanged();
+    DLOG(ERROR) << "Value of -[NSScreen screens] changed before notification.";
     return BuildDisplayForScreen(screen);
   }
 
-  void EnsureDisplaysValid() const {
+  void UpdateDisplaysIfNeeded() const {
     if (displays_require_update_) {
       displays_ = BuildDisplaysFromQuartz();
       displays_require_update_ = false;
@@ -309,7 +317,7 @@
   }
 
   void ConfigureTimerFired() {
-    EnsureDisplaysValid();
+    UpdateDisplaysIfNeeded();
     change_notifier_.NotifyDisplaysChanged(old_displays_, displays_);
     old_displays_ = displays_;
   }
@@ -323,7 +331,7 @@
     // It would be ridiculous to have this many displays connected, but
     // CGDirectDisplayID is just an integer, so supporting up to this many
     // doesn't hurt.
-    CGDirectDisplayID online_displays[128];
+    CGDirectDisplayID online_displays[1024];
     CGDisplayCount online_display_count = 0;
     if (CGGetOnlineDisplayList(base::size(online_displays), online_displays,
                                &online_display_count) != kCGErrorSuccess) {
@@ -359,21 +367,32 @@
                             : displays;
   }
 
-  // The displays currently attached to the device. Cached.
+  void OnNSScreensMayHaveChanged() const {
+    // Timer::Reset() ensures at least another interval passes before the
+    // associated task runs, effectively coalescing these events.
+    configure_timer_.Reset();
+    displays_require_update_ = true;
+  }
+
+  // The displays currently attached to the device. Updated by
+  // UpdateDisplaysIfNeeded.
   mutable std::vector<Display> displays_;
 
-  // Set whenever the CGDisplayRegisterReconfigurationCallback is invoked and
-  // cleared when |displays_| is updated by BuildDisplaysFromQuartz().
+  // Whether or not |displays_| might need to be upated. Set in
+  // OnNSScreensMayHaveChanged, and un-set by UpdateDisplaysIfNeeded.
   mutable bool displays_require_update_ = false;
 
-  // The displays last communicated to DisplayChangeNotifier.
+  // The timer to delay configuring outputs and notifying observers (to coalesce
+  // several updates into one update).
+  mutable base::RetainingOneShotTimer configure_timer_;
+
+  // The displays last communicated to the DisplayChangeNotifier.
   std::vector<Display> old_displays_;
 
-  // The timer to delay configuring outputs and notifying observers.
-  base::RetainingOneShotTimer configure_timer_;
-
-  // The observer notified by NSScreenColorSpaceDidChangeNotification.
+  // The observers notified by NSScreenColorSpaceDidChangeNotification and
+  // NSApplicationDidChangeScreenParametersNotification.
   base::scoped_nsobject<id> screen_color_change_observer_;
+  base::scoped_nsobject<id> screen_params_change_observer_;
 
   DisplayChangeNotifier change_notifier_;
 
diff --git a/ui/file_manager/file_manager/foreground/js/toolbar_controller.js b/ui/file_manager/file_manager/foreground/js/toolbar_controller.js
index 492b0c9..674613a 100644
--- a/ui/file_manager/file_manager/foreground/js/toolbar_controller.js
+++ b/ui/file_manager/file_manager/foreground/js/toolbar_controller.js
@@ -68,7 +68,8 @@
      * @const
      */
     this.deleteCommand_ = assertInstanceof(
-        queryRequiredElement('#delete', assert(this.toolbar_.ownerDocument)),
+        queryRequiredElement(
+            '#delete', assert(this.toolbar_.ownerDocument.body)),
         cr.ui.Command);
 
     /**
@@ -76,7 +77,8 @@
      * @const
      */
     this.refreshCommand_ = assertInstanceof(
-        queryRequiredElement('#refresh', assert(this.toolbar_.ownerDocument)),
+        queryRequiredElement(
+            '#refresh', assert(this.toolbar_.ownerDocument.body)),
         cr.ui.Command);
 
     /**
@@ -85,7 +87,7 @@
      */
     this.newFolderCommand_ = assertInstanceof(
         queryRequiredElement(
-            '#new-folder', assert(this.toolbar_.ownerDocument)),
+            '#new-folder', assert(this.toolbar_.ownerDocument.body)),
         cr.ui.Command);
 
     /**
diff --git a/ui/file_manager/integration_tests/file_manager/zip_files.js b/ui/file_manager/integration_tests/file_manager/zip_files.js
index e3df0eb..576d3799 100644
--- a/ui/file_manager/integration_tests/file_manager/zip_files.js
+++ b/ui/file_manager/integration_tests/file_manager/zip_files.js
@@ -177,8 +177,10 @@
 
   const passphraseCloseScript = `
       function clickClose() {
-        let dialog = document.querySelector("passphrase-dialog");
-        dialog.shadowRoot.querySelector("#cancelButton").click();
+        HTMLImports.whenReady(() => {
+          let dialog = document.querySelector("passphrase-dialog");
+          dialog.shadowRoot.querySelector("#cancelButton").click();
+        });
       }
       if (document.readyState === "loading") {
         document.addEventListener("DOMContentLoaded", clickClose);
diff --git a/ui/gfx/font_fallback_unittest.cc b/ui/gfx/font_fallback_unittest.cc
index 19e9b264..86a29ffd9 100644
--- a/ui/gfx/font_fallback_unittest.cc
+++ b/ui/gfx/font_fallback_unittest.cc
@@ -135,7 +135,13 @@
 //
 // The previous checks can be activated or deactivated through the class
 // FallbackFontTestOption (e.g. test_option_).
-TEST_P(GetFallbackFontTest, GetFallbackFont) {
+#if defined(OS_MACOSX)
+// https://crbug.com/1022455
+#define MAYBE_GetFallbackFont DISABLED_GetFallbackFont
+#else
+#define MAYBE_GetFallbackFont GetFallbackFont
+#endif
+TEST_P(GetFallbackFontTest, MAYBE_GetFallbackFont) {
   // Default system font.
   const Font base_font;
 
diff --git a/ui/gfx/render_text.cc b/ui/gfx/render_text.cc
index 0c9caab..25212c6 100644
--- a/ui/gfx/render_text.cc
+++ b/ui/gfx/render_text.cc
@@ -316,23 +316,29 @@
   // Control Pictures block (see: https://unicode.org/charts/PDF/U2400.pdf).
   constexpr base::char16 kSymbolsCodepoint = 0x2400;
 
-  size_t next_offset = 0;
-  while (next_offset < text->length()) {
-    size_t offset = next_offset;
+  size_t offset = 0;
+  while (offset < text->length()) {
     UChar32 codepoint;
-    U16_NEXT(text->c_str(), next_offset, text->length(), codepoint);
+    U16_GET(text->c_str(), 0, offset, text->length(), codepoint);
 
     if (codepoint >= 0 && codepoint <= 0x1F) {
       // The newline character should be kept as-is when rendertext is
       // multiline.
-      if (codepoint == '\n' && multiline)
-        continue;
-      // Replace codepoints with their visual symbols, which are at the same
-      // offset from kSymbolsCodepoint.
-      (*text)[offset] = kSymbolsCodepoint + codepoint;
+      if (codepoint != '\n' || !multiline) {
+        // Replace codepoints with their visual symbols, which are at the same
+        // offset from kSymbolsCodepoint.
+        (*text)[offset] = kSymbolsCodepoint + codepoint;
+      }
     } else if (codepoint == 0x7F) {
       // Replace the 'del' codepoint by its symbol (u2421).
       (*text)[offset] = kSymbolsCodepoint + 0x21;
+    } else if (!U_IS_UNICODE_CHAR(codepoint)) {
+      // Unicode codepoint that can't be assigned a character. This handles:
+      // - single surrogate codepoints,
+      // - last two codepoints on each plane,
+      // - invalid characters (e.g. u+fdd0..u+fdef)
+      // - codepoints above u+10ffff
+      ReplaceCodepointAtIndex(offset, kReplacementCodepoint, text);
     } else if (codepoint > 0x7F) {
       // Private use codepoints are working with a pair of font and codepoint,
       // but they are not used in Chrome.
@@ -342,6 +348,10 @@
         ReplaceCodepointAtIndex(offset, kReplacementCodepoint, text);
       }
     }
+
+    // Move offset to the index of the next codepoint. This must be computed
+    // after any rewriting steps above since codepoint size may differ.
+    U16_NEXT(text->c_str(), offset, text->length(), codepoint);
   }
 }
 
diff --git a/ui/gfx/render_text_unittest.cc b/ui/gfx/render_text_unittest.cc
index 193e8a361..1e26c23e 100644
--- a/ui/gfx/render_text_unittest.cc
+++ b/ui/gfx/render_text_unittest.cc
@@ -863,14 +863,15 @@
   render_text->SetText(UTF8ToUTF16("new longer"));
   EXPECT_EQ(GetObscuredString(10), render_text->GetDisplayText());
 
-  // Text with invalid surrogates.
+  // Text with invalid surrogates (surrogates low 0xDC00 and high 0xD800).
+  // Invalid surrogates are replaced by replacement character (e.g. 0xFFFD).
   const base::char16 invalid_surrogates[] = {0xDC00, 0xD800, 'h', 'o', 'p', 0};
   render_text->SetText(invalid_surrogates);
   EXPECT_EQ(GetObscuredString(5), render_text->GetDisplayText());
   render_text->RenderText::SetObscuredRevealIndex(0);
-  EXPECT_EQ(GetObscuredString(5, 0, 0xDC00), render_text->GetDisplayText());
+  EXPECT_EQ(GetObscuredString(5, 0, 0xFFFD), render_text->GetDisplayText());
   render_text->RenderText::SetObscuredRevealIndex(1);
-  EXPECT_EQ(GetObscuredString(5, 1, 0xD800), render_text->GetDisplayText());
+  EXPECT_EQ(GetObscuredString(5, 1, 0xFFFD), render_text->GetDisplayText());
   render_text->RenderText::SetObscuredRevealIndex(2);
   EXPECT_EQ(GetObscuredString(5, 2, 'h'), render_text->GetDisplayText());
 
@@ -4785,12 +4786,24 @@
 
 TEST_F(RenderTextTest, PrivateUseCharacterReplacement) {
   RenderText* render_text = GetRenderText();
-  render_text->SetText(UTF8ToUTF16("xx\ue78d\ue78fa\U00100042z"));
+  render_text->SetText(WideToUTF16(L"xx\ue78d\ue78fa\U00100042z"));
 
   // The private use characters should have been replaced. If the code point is
   // a surrogate pair, it needs to be replaced by two characters.
   EXPECT_EQ(WideToUTF16(L"xx\ufffd\ufffda\ufffdz"),
             render_text->GetDisplayText());
+
+  // The private use characters from Area-B must be replaced. The rewrite step
+  // replaced 2 characters by 1 character.
+  render_text->SetText(WideToUTF16(L"x\U00100000\U00100001\U00100002"));
+  EXPECT_EQ(WideToUTF16(L"x\ufffd\ufffd\ufffd"), render_text->GetDisplayText());
+}
+
+TEST_F(RenderTextTest, InvalidSurrogateCharacterReplacement) {
+  // Text with invalid surrogates (surrogates low 0xDC00 and high 0xD800).
+  RenderText* render_text = GetRenderText();
+  render_text->SetText(WideToUTF16(L"\xDC00\xD800"));
+  EXPECT_EQ(WideToUTF16(L"\ufffd\ufffd"), render_text->GetDisplayText());
 }
 
 // Make sure the horizontal positions of runs in a line (left-to-right for
diff --git a/ui/views/bubble/bubble_frame_view.cc b/ui/views/bubble/bubble_frame_view.cc
index 40e3408..2ae016c282 100644
--- a/ui/views/bubble/bubble_frame_view.cc
+++ b/ui/views/bubble/bubble_frame_view.cc
@@ -495,6 +495,14 @@
       footnote_margins_, std::move(view), radius));
 }
 
+View* BubbleFrameView::GetFootnoteView() const {
+  if (!footnote_container_)
+    return nullptr;
+
+  DCHECK_EQ(1u, footnote_container_->children().size());
+  return footnote_container_->children()[0];
+}
+
 void BubbleFrameView::SetCornerRadius(int radius) {
   bubble_border_->SetCornerRadius(radius);
 }
diff --git a/ui/views/bubble/bubble_frame_view.h b/ui/views/bubble/bubble_frame_view.h
index f94f4a7..871782f 100644
--- a/ui/views/bubble/bubble_frame_view.h
+++ b/ui/views/bubble/bubble_frame_view.h
@@ -108,6 +108,7 @@
   // line and has a solid background by being embedded in a
   // FootnoteContainerView. An example footnote would be some help text.
   void SetFootnoteView(std::unique_ptr<View> view);
+  View* GetFootnoteView() const;
   void set_footnote_margins(const gfx::Insets& footnote_margins) {
     footnote_margins_ = footnote_margins;
   }
diff --git a/ui/views/win/hwnd_message_handler.cc b/ui/views/win/hwnd_message_handler.cc
index 00971fc..95b35bef 100644
--- a/ui/views/win/hwnd_message_handler.cc
+++ b/ui/views/win/hwnd_message_handler.cc
@@ -471,8 +471,7 @@
   // to element B's window.
   // So we create the fragment root now to ensure it's ready if asked for.
   if (::switches::IsExperimentalAccessibilityPlatformUIAEnabled())
-    ax_fragment_root_ =
-        std::make_unique<ui::AXFragmentRootWin>(hwnd(), this, true);
+    ax_fragment_root_ = std::make_unique<ui::AXFragmentRootWin>(hwnd(), this);
 
   // Disable pen flicks (http://crbug.com/506977)
   base::win::DisableFlicks(hwnd());
diff --git a/ui/views/window/dialog_delegate.cc b/ui/views/window/dialog_delegate.cc
index 1df02fe5..6e5ebf4 100644
--- a/ui/views/window/dialog_delegate.cc
+++ b/ui/views/window/dialog_delegate.cc
@@ -261,6 +261,21 @@
   return client ? client->extra_view() : nullptr;
 }
 
+views::View* DialogDelegate::GetFootnoteViewForTesting() {
+  if (!GetWidget())
+    return footnote_view_.get();
+
+  NonClientFrameView* frame = GetWidget()->non_client_view()->frame_view();
+
+  // CreateDialogFrameView above always uses BubbleFrameView. There are
+  // subclasses that override CreateDialogFrameView, but none of them override
+  // it to create anything other than a BubbleFrameView.
+  // TODO(https://crbug.com/1011446): Make CreateDialogFrameView final, then
+  // remove this DCHECK.
+  DCHECK_EQ(frame->GetClassName(), BubbleFrameView::kViewClassName);
+  return static_cast<BubbleFrameView*>(frame)->GetFootnoteView();
+}
+
 void DialogDelegate::AddObserver(DialogObserver* observer) {
   observer_list_.AddObserver(observer);
 }
diff --git a/ui/views/window/dialog_delegate.h b/ui/views/window/dialog_delegate.h
index e9e0893..706f9f2 100644
--- a/ui/views/window/dialog_delegate.h
+++ b/ui/views/window/dialog_delegate.h
@@ -169,6 +169,10 @@
   views::LabelButton* GetCancelButton();
   views::View* GetExtraView();
 
+  // Helper for accessing the footnote view. Unlike the three methods just
+  // above, this *is* safe to call before OnDialogInitialized.
+  views::View* GetFootnoteViewForTesting();
+
   // Add or remove an observer notified by calls to DialogModelChanged().
   void AddObserver(DialogObserver* observer);
   void RemoveObserver(DialogObserver* observer);
diff --git a/ui/webui/resources/js/cr/ui/menu_item.js b/ui/webui/resources/js/cr/ui/menu_item.js
index fe32ef6..91e8ee3 100644
--- a/ui/webui/resources/js/cr/ui/menu_item.js
+++ b/ui/webui/resources/js/cr/ui/menu_item.js
@@ -74,7 +74,7 @@
       }
 
       if (typeof command == 'string' && command[0] == '#') {
-        command = assert(this.ownerDocument.getElementById(command.slice(1)));
+        command = assert(this.ownerDocument.body.querySelector(command));
         cr.ui.decorate(command, Command);
       }
 
diff --git a/weblayer/BUILD.gn b/weblayer/BUILD.gn
index d6d637a..f41abce 100644
--- a/weblayer/BUILD.gn
+++ b/weblayer/BUILD.gn
@@ -169,6 +169,7 @@
       "//components/crash/android:crashpad_main",
       "//components/safe_browsing",
       "//components/safe_browsing/android:remote_database_manager",
+      "//components/safe_browsing/android:safe_browsing_api_handler",
       "//components/safe_browsing/browser",
       "//components/safe_browsing/browser:network_context",
       "//components/safe_browsing/db:database_manager",
diff --git a/weblayer/browser/java/BUILD.gn b/weblayer/browser/java/BUILD.gn
index a8ecc9f..86884bd 100644
--- a/weblayer/browser/java/BUILD.gn
+++ b/weblayer/browser/java/BUILD.gn
@@ -11,8 +11,8 @@
   custom_package = "org.chromium.weblayer_private"
 }
 
-generate_locale_config_srcjar("weblayer_locale_config") {
-  java_package = weblayer_locale_config_java_package
+generate_product_config_srcjar("weblayer_product_config") {
+  java_package = weblayer_product_config_java_package
 }
 
 java_cpp_enum("generated_enums") {
@@ -65,9 +65,9 @@
   ]
   srcjar_deps = [
     ":generated_enums",
-    ":weblayer_locale_config",
+    ":weblayer_product_config",
   ]
-  jar_excluded_patterns = [ "*/LocaleConfig.class" ]
+  jar_excluded_patterns = [ "*/ProductConfig.class" ]
   annotation_processor_deps = [ "//base/android/jni_generator:jni_processor" ]
 
   # Needed for android.webkit.WebView(Delegate|Factory)
@@ -141,6 +141,7 @@
   import_include = [ "org/chromium/weblayer_private/interfaces" ]
   sources = [
     "org/chromium/weblayer_private/interfaces/IBrowser.aidl",
+    "org/chromium/weblayer_private/interfaces/IBrowserClient.aidl",
     "org/chromium/weblayer_private/interfaces/IBrowserFragment.aidl",
     "org/chromium/weblayer_private/interfaces/IChildProcessService.aidl",
     "org/chromium/weblayer_private/interfaces/IClientNavigation.aidl",
diff --git a/weblayer/browser/java/org/chromium/weblayer_private/BrowserImpl.java b/weblayer/browser/java/org/chromium/weblayer_private/BrowserImpl.java
index e3717b2..172c074 100644
--- a/weblayer/browser/java/org/chromium/weblayer_private/BrowserImpl.java
+++ b/weblayer/browser/java/org/chromium/weblayer_private/BrowserImpl.java
@@ -7,12 +7,15 @@
 import android.content.Context;
 import android.content.Intent;
 import android.os.Bundle;
+import android.os.RemoteException;
 import android.view.View;
 import android.view.ViewGroup;
 import android.webkit.ValueCallback;
 
 import org.chromium.ui.base.WindowAndroid;
+import org.chromium.weblayer_private.interfaces.APICallException;
 import org.chromium.weblayer_private.interfaces.IBrowser;
+import org.chromium.weblayer_private.interfaces.IBrowserClient;
 import org.chromium.weblayer_private.interfaces.IObjectWrapper;
 import org.chromium.weblayer_private.interfaces.IProfile;
 import org.chromium.weblayer_private.interfaces.ITab;
@@ -29,6 +32,7 @@
     private BrowserViewController mViewController;
     private FragmentWindowAndroid mWindowAndroid;
     private ArrayList<TabImpl> mTabs = new ArrayList<TabImpl>();
+    private IBrowserClient mClient;
 
     public BrowserImpl(ProfileImpl profile, Bundle savedInstanceState) {
         mProfile = profile;
@@ -47,7 +51,7 @@
         mWindowAndroid = windowAndroid;
         mViewController = new BrowserViewController(context, windowAndroid);
         TabImpl tab = new TabImpl(mProfile, windowAndroid);
-        attachTab(tab);
+        addTab(tab);
         boolean set_active_result = setActiveTab(tab);
         assert set_active_result;
     }
@@ -93,19 +97,25 @@
         return mProfile;
     }
 
-    public void attachTab(ITab iTab) {
+    @Override
+    public void addTab(ITab iTab) {
         TabImpl tab = (TabImpl) iTab;
         if (tab.getBrowser() == this) return;
-        attachTabImpl(tab);
+        addTabImpl(tab);
     }
 
-    private void attachTabImpl(TabImpl tab) {
+    private void addTabImpl(TabImpl tab) {
         // Null case is only during initial creation.
         if (tab.getBrowser() != this && tab.getBrowser() != null) {
             tab.getBrowser().detachTab(tab);
         }
         mTabs.add(tab);
         tab.attachToBrowser(this);
+        try {
+            if (mClient != null) mClient.onTabAdded(tab);
+        } catch (RemoteException e) {
+            throw new APICallException(e);
+        }
     }
 
     public void detachTab(ITab iTab) {
@@ -113,6 +123,11 @@
         if (tab.getBrowser() != this) return;
         if (getActiveTab() == tab) setActiveTab(null);
         mTabs.remove(tab);
+        try {
+            if (mClient != null) mClient.onTabRemoved(tab.getId());
+        } catch (RemoteException e) {
+            throw new APICallException(e);
+        }
         // This doesn't reset state on TabImpl as |browser| is either about to be
         // destroyed, or switching to a different fragment.
     }
@@ -120,8 +135,15 @@
     @Override
     public boolean setActiveTab(ITab controller) {
         TabImpl tab = (TabImpl) controller;
-        if (tab.getBrowser() != this) return false;
+        if (tab != null && tab.getBrowser() != this) return false;
         mViewController.setActiveTab(tab);
+        try {
+            if (mClient != null) {
+                mClient.onActiveTabChanged(tab != null ? tab.getId() : 0);
+            }
+        } catch (RemoteException e) {
+            throw new APICallException(e);
+        }
         return true;
     }
 
@@ -139,6 +161,17 @@
         return getActiveTab() != null ? getActiveTab().getId() : 0;
     }
 
+    @Override
+    public void setClient(IBrowserClient client) {
+        mClient = client;
+    }
+
+    @Override
+    public void destroyTab(ITab iTab) {
+        detachTab(iTab);
+        ((TabImpl) iTab).destroy();
+    }
+
     public View getFragmentView() {
         return getViewController().getView();
     }
diff --git a/weblayer/browser/java/org/chromium/weblayer_private/ContentView.java b/weblayer/browser/java/org/chromium/weblayer_private/ContentView.java
index fa2fb4a..cce3656 100644
--- a/weblayer/browser/java/org/chromium/weblayer_private/ContentView.java
+++ b/weblayer/browser/java/org/chromium/weblayer_private/ContentView.java
@@ -245,8 +245,10 @@
         try {
             TraceEvent.begin("ContentView.onFocusChanged");
             super.onFocusChanged(gainFocus, direction, previouslyFocusedRect);
-            getViewEventSink().setHideKeyboardOnBlur(true);
-            getViewEventSink().onViewFocusChanged(gainFocus);
+            if (mWebContents != null) {
+                getViewEventSink().setHideKeyboardOnBlur(true);
+                getViewEventSink().onViewFocusChanged(gainFocus);
+            }
         } finally {
             TraceEvent.end("ContentView.onFocusChanged");
         }
@@ -255,7 +257,9 @@
     @Override
     public void onWindowFocusChanged(boolean hasWindowFocus) {
         super.onWindowFocusChanged(hasWindowFocus);
-        getViewEventSink().onWindowFocusChanged(hasWindowFocus);
+        if (mWebContents != null) {
+            getViewEventSink().onWindowFocusChanged(hasWindowFocus);
+        }
     }
 
     @Override
@@ -335,7 +339,9 @@
 
     @Override
     protected void onConfigurationChanged(Configuration newConfig) {
-        getViewEventSink().onConfigurationChanged(newConfig);
+        if (mWebContents != null) {
+            getViewEventSink().onConfigurationChanged(newConfig);
+        }
         super.onConfigurationChanged(newConfig);
     }
 
@@ -409,13 +415,17 @@
     @Override
     protected void onAttachedToWindow() {
         super.onAttachedToWindow();
-        getViewEventSink().onAttachedToWindow();
+        if (mWebContents != null) {
+            getViewEventSink().onAttachedToWindow();
+        }
     }
 
     @Override
     protected void onDetachedFromWindow() {
         super.onDetachedFromWindow();
-        getViewEventSink().onDetachedFromWindow();
+        if (mWebContents != null) {
+            getViewEventSink().onDetachedFromWindow();
+        }
     }
 
     // Implements SmartClipProvider
diff --git a/weblayer/browser/java/org/chromium/weblayer_private/NewTabCallbackProxy.java b/weblayer/browser/java/org/chromium/weblayer_private/NewTabCallbackProxy.java
index bc9bd4a0..2a73263ee 100644
--- a/weblayer/browser/java/org/chromium/weblayer_private/NewTabCallbackProxy.java
+++ b/weblayer/browser/java/org/chromium/weblayer_private/NewTabCallbackProxy.java
@@ -36,8 +36,8 @@
         assert mTab.getBrowser() != null;
         TabImpl tab =
                 new TabImpl(mTab.getProfile(), mTab.getBrowser().getWindowAndroid(), nativeTab);
-        mTab.getBrowser().attachTab(tab);
-        mTab.getClient().onNewTab(tab, mode);
+        mTab.getBrowser().addTab(tab);
+        mTab.getClient().onNewTab(tab.getId(), mode);
     }
 
     @NativeMethods
diff --git a/weblayer/browser/java/org/chromium/weblayer_private/WebLayerImpl.java b/weblayer/browser/java/org/chromium/weblayer_private/WebLayerImpl.java
index b67a606..d43ca1aa 100644
--- a/weblayer/browser/java/org/chromium/weblayer_private/WebLayerImpl.java
+++ b/weblayer/browser/java/org/chromium/weblayer_private/WebLayerImpl.java
@@ -99,7 +99,7 @@
         // other expensive startup tasks.
         R.onResourcesLoaded(resourcesPackageId);
 
-        ResourceBundle.setAvailablePakLocales(new String[] {}, LocaleConfig.UNCOMPRESSED_LOCALES);
+        ResourceBundle.setAvailablePakLocales(new String[] {}, ProductConfig.UNCOMPRESSED_LOCALES);
         PathUtils.setPrivateDataDirectorySuffix(PRIVATE_DATA_DIRECTORY_SUFFIX);
 
         ChildProcessCreationParams.set(appContext.getPackageName(), false /* isExternalService */,
diff --git a/weblayer/browser/java/org/chromium/weblayer_private/interfaces/IBrowser.aidl b/weblayer/browser/java/org/chromium/weblayer_private/interfaces/IBrowser.aidl
index 6ca9ffb..03b9e55b 100644
--- a/weblayer/browser/java/org/chromium/weblayer_private/interfaces/IBrowser.aidl
+++ b/weblayer/browser/java/org/chromium/weblayer_private/interfaces/IBrowser.aidl
@@ -4,6 +4,7 @@
 
 package org.chromium.weblayer_private.interfaces;
 
+import org.chromium.weblayer_private.interfaces.IBrowserClient;
 import org.chromium.weblayer_private.interfaces.IObjectWrapper;
 import org.chromium.weblayer_private.interfaces.ITab;
 
@@ -23,4 +24,9 @@
 
   int getActiveTabId() = 4;
   List getTabs() = 5;
+
+  void setClient(in IBrowserClient client) = 6;
+
+  void addTab(in ITab tab) = 7;
+  void destroyTab(in ITab tab) = 8;
 }
diff --git a/weblayer/browser/java/org/chromium/weblayer_private/interfaces/IBrowserClient.aidl b/weblayer/browser/java/org/chromium/weblayer_private/interfaces/IBrowserClient.aidl
new file mode 100644
index 0000000..c23221ef
--- /dev/null
+++ b/weblayer/browser/java/org/chromium/weblayer_private/interfaces/IBrowserClient.aidl
@@ -0,0 +1,13 @@
+// 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.
+
+package org.chromium.weblayer_private.interfaces;
+
+import org.chromium.weblayer_private.interfaces.ITab;
+
+interface IBrowserClient {
+  void onActiveTabChanged(in int activeTabId) = 0;
+  void onTabAdded(in ITab tab) = 1;
+  void onTabRemoved(in int tabId) = 2;
+}
diff --git a/weblayer/browser/java/org/chromium/weblayer_private/interfaces/ITabClient.aidl b/weblayer/browser/java/org/chromium/weblayer_private/interfaces/ITabClient.aidl
index 19573d3b..793f48cd 100644
--- a/weblayer/browser/java/org/chromium/weblayer_private/interfaces/ITabClient.aidl
+++ b/weblayer/browser/java/org/chromium/weblayer_private/interfaces/ITabClient.aidl
@@ -11,5 +11,5 @@
 interface ITabClient {
   void visibleUrlChanged(in String url) = 0;
 
-  void onNewTab(in ITab tab, in int mode) = 1;
+  void onNewTab(in int tabId, in int mode) = 1;
 }
diff --git a/weblayer/browser/java/org/chromium/weblayer_private/interfaces/WebLayerVersion.java b/weblayer/browser/java/org/chromium/weblayer_private/interfaces/WebLayerVersion.java
index e89c342..85513e3f 100644
--- a/weblayer/browser/java/org/chromium/weblayer_private/interfaces/WebLayerVersion.java
+++ b/weblayer/browser/java/org/chromium/weblayer_private/interfaces/WebLayerVersion.java
@@ -9,4 +9,4 @@
  *
  * Whenever any AIDL file is changed, sVersionNumber must be incremented.
  * */
-public final class WebLayerVersion { public static final int sVersionNumber = 10; }
+public final class WebLayerVersion { public static final int sVersionNumber = 11; }
diff --git a/weblayer/browser/safe_browsing/safe_browsing_service.cc b/weblayer/browser/safe_browsing/safe_browsing_service.cc
index df7e92a7..e6a8d8b 100644
--- a/weblayer/browser/safe_browsing/safe_browsing_service.cc
+++ b/weblayer/browser/safe_browsing/safe_browsing_service.cc
@@ -8,6 +8,7 @@
 #include "base/path_service.h"
 #include "base/task/post_task.h"
 #include "components/safe_browsing/android/remote_database_manager.h"
+#include "components/safe_browsing/android/safe_browsing_api_handler_bridge.h"
 #include "components/safe_browsing/browser/browser_url_loader_throttle.h"
 #include "components/safe_browsing/browser/mojo_safe_browsing_impl.h"
 #include "components/safe_browsing/browser/safe_browsing_network_context.h"
@@ -45,6 +46,11 @@
     return;
   }
 
+  safe_browsing_api_handler_.reset(
+      new safe_browsing::SafeBrowsingApiHandlerBridge());
+  safe_browsing::SafeBrowsingApiHandler::SetInstance(
+      safe_browsing_api_handler_.get());
+
   base::FilePath user_data_dir;
   bool result =
       base::PathService::Get(base::DIR_ANDROID_APP_DATA, &user_data_dir);
diff --git a/weblayer/browser/safe_browsing/safe_browsing_service.h b/weblayer/browser/safe_browsing/safe_browsing_service.h
index b9e029e5..b817bca 100644
--- a/weblayer/browser/safe_browsing/safe_browsing_service.h
+++ b/weblayer/browser/safe_browsing/safe_browsing_service.h
@@ -26,6 +26,7 @@
 namespace safe_browsing {
 class UrlCheckerDelegate;
 class RemoteSafeBrowsingDatabaseManager;
+class SafeBrowsingApiHandler;
 class SafeBrowsingNetworkContext;
 }  // namespace safe_browsing
 
@@ -81,6 +82,9 @@
   scoped_refptr<safe_browsing::UrlCheckerDelegate>
       safe_browsing_url_checker_delegate_;
 
+  std::unique_ptr<safe_browsing::SafeBrowsingApiHandler>
+      safe_browsing_api_handler_;
+
   std::string user_agent_;
 
   DISALLOW_COPY_AND_ASSIGN(SafeBrowsingService);
diff --git a/weblayer/browser/safe_browsing/url_checker_delegate_impl.cc b/weblayer/browser/safe_browsing/url_checker_delegate_impl.cc
index 93ad4ae7..6a4bc76 100644
--- a/weblayer/browser/safe_browsing/url_checker_delegate_impl.cc
+++ b/weblayer/browser/safe_browsing/url_checker_delegate_impl.cc
@@ -13,7 +13,12 @@
     scoped_refptr<safe_browsing::SafeBrowsingDatabaseManager> database_manager,
     scoped_refptr<SafeBrowsingUIManager> ui_manager)
     : database_manager_(std::move(database_manager)),
-      ui_manager_(std::move(ui_manager)) {}
+      ui_manager_(std::move(ui_manager)),
+      threat_types_(safe_browsing::CreateSBThreatTypeSet(
+          {safe_browsing::SB_THREAT_TYPE_URL_MALWARE,
+           safe_browsing::SB_THREAT_TYPE_URL_PHISHING,
+           safe_browsing::SB_THREAT_TYPE_URL_UNWANTED,
+           safe_browsing::SB_THREAT_TYPE_BILLING})) {}
 
 UrlCheckerDelegateImpl::~UrlCheckerDelegateImpl() = default;
 
diff --git a/weblayer/public/java/BUILD.gn b/weblayer/public/java/BUILD.gn
index 928152f..fef1b8c 100644
--- a/weblayer/public/java/BUILD.gn
+++ b/weblayer/public/java/BUILD.gn
@@ -39,6 +39,7 @@
     "org/chromium/weblayer/Profile.java",
     "org/chromium/weblayer/Tab.java",
     "org/chromium/weblayer/TabCallback.java",
+    "org/chromium/weblayer/TabListCallback.java",
     "org/chromium/weblayer/ThreadCheck.java",
     "org/chromium/weblayer/UnsupportedVersionException.java",
     "org/chromium/weblayer/WebLayer.java",
diff --git a/weblayer/public/java/org/chromium/weblayer/Browser.java b/weblayer/public/java/org/chromium/weblayer/Browser.java
index 26cef370..deca88bc 100644
--- a/weblayer/public/java/org/chromium/weblayer/Browser.java
+++ b/weblayer/public/java/org/chromium/weblayer/Browser.java
@@ -14,13 +14,11 @@
 
 import org.chromium.weblayer_private.interfaces.APICallException;
 import org.chromium.weblayer_private.interfaces.IBrowser;
+import org.chromium.weblayer_private.interfaces.IBrowserClient;
 import org.chromium.weblayer_private.interfaces.ITab;
 import org.chromium.weblayer_private.interfaces.ObjectWrapper;
 
-import java.util.ArrayList;
-import java.util.HashMap;
 import java.util.List;
-import java.util.Map;
 
 /**
  * Browser contains any number of Tabs, with one active Tab. The active Tab is visible to the user,
@@ -30,12 +28,16 @@
  */
 public final class Browser {
     private final IBrowser mImpl;
-    // Maps from id (as returned from ITab.getId() to Tab).
-    private final Map<Integer, Tab> mTabMap;
+    private final ObserverList<TabListCallback> mTabListCallbacks;
 
     Browser(IBrowser impl) {
         mImpl = impl;
-        mTabMap = new HashMap<Integer, Tab>();
+        mTabListCallbacks = new ObserverList<TabListCallback>();
+        try {
+            mImpl.setClient(new BrowserClientImpl());
+        } catch (RemoteException e) {
+            throw new APICallException(e);
+        }
         try {
             for (Object tab : impl.getTabs()) {
                 // getTabs() returns List<TabImpl>, which isn't accessible from the client library.
@@ -48,17 +50,6 @@
         }
     }
 
-    // This is called when a new Tab is created.
-    void registerTab(Tab tab) {
-        try {
-            int id = tab.getITab().getId();
-            assert !mTabMap.containsKey(id);
-            mTabMap.put(id, tab);
-        } catch (RemoteException e) {
-            throw new APICallException(e);
-        }
-    }
-
     /**
      * Returns the Browser for the supplied Fragment; null if
      * {@link fragment} was not created by WebLayer.
@@ -71,6 +62,13 @@
                                                    : null;
     }
 
+    // Called prior to notifying IBrowser of destroy().
+    void prepareForDestroy() {
+        for (Tab tab : getTabs()) {
+            Tab.unregisterTab(tab);
+        }
+    }
+
     /**
      * Sets the active (visible) Tab. Only one Tab is visible at a time.
      *
@@ -84,7 +82,7 @@
     public void setActiveTab(@NonNull Tab tab) {
         ThreadCheck.ensureOnUiThread();
         try {
-            if (!mImpl.setActiveTab(tab.getITab())) {
+            if (getActiveTab() != tab && !mImpl.setActiveTab(tab.getITab())) {
                 throw new IllegalStateException("attachTab() must be called before "
                         + "setActiveTab");
             }
@@ -94,6 +92,23 @@
     }
 
     /**
+     * Adds a tab to this Browser. If {link tab} is the active Tab of another Browser, then the
+     * other Browser's active tab is set to null. This does nothing if {@link tab} is already
+     * contained in this Browser.
+     *
+     * @param tab The Tab to add.
+     */
+    public void addTab(@NonNull Tab tab) {
+        ThreadCheck.ensureOnUiThread();
+        if (tab.getBrowser() == this) return;
+        try {
+            mImpl.addTab(tab.getITab());
+        } catch (RemoteException e) {
+            throw new APICallException(e);
+        }
+    }
+
+    /**
      * Returns the active (visible) Tab associated with this
      * Browser.
      *
@@ -103,7 +118,9 @@
     public Tab getActiveTab() {
         ThreadCheck.ensureOnUiThread();
         try {
-            return mTabMap.get(mImpl.getActiveTabId());
+            Tab tab = Tab.getTabById(mImpl.getActiveTabId());
+            assert tab == null || tab.getBrowser() == this;
+            return tab;
         } catch (RemoteException e) {
             throw new APICallException(e);
         }
@@ -117,21 +134,47 @@
     @NonNull
     public List<Tab> getTabs() {
         ThreadCheck.ensureOnUiThread();
-        return new ArrayList<Tab>(mTabMap.values());
+        return Tab.getTabsInBrowser(this);
     }
 
     /**
-     * Disposes a Tab. If {@link tabl} is the active Tab,
-     * no Tab is made active. After this call {@link tabl} should not be
-     * used.
+     * Disposes a Tab. If {@link tab} is the active Tab, no Tab is made active. After this call
+     *  {@link tab} should not be used.
      *
      * @param tab The Tab to dispose.
      *
      * @throws IllegalStateException is {@link tab} is not in this Browser.
      */
-    public void disposeTab(Tab tab) {
+    public void destroyTab(@NonNull Tab tab) {
         ThreadCheck.ensureOnUiThread();
-        // TODO(sky): implement this.
+        if (tab.getBrowser() != this) {
+            throw new IllegalStateException("destroyTab() must be called on a Tab in the Browser");
+        }
+        try {
+            mImpl.destroyTab(tab.getITab());
+        } catch (RemoteException e) {
+            throw new APICallException(e);
+        }
+    }
+
+    /**
+     * Adds a TabListCallback.
+     *
+     * @param callback The TabListCallback.
+     */
+    public void addTabListCallback(@NonNull TabListCallback callback) {
+        ThreadCheck.ensureOnUiThread();
+        mTabListCallbacks.addObserver(callback);
+    }
+
+    /**
+     * Removes a TabListCallback.
+     *
+     * @param callback The TabListCallback.
+     */
+    public void removeTabListCallback(@NonNull TabListCallback callback) {
+        ThreadCheck.ensureOnUiThread();
+        mTabListCallbacks.removeObserver(callback);
     }
 
     /**
@@ -188,4 +231,47 @@
             throw new APICallException(e);
         }
     }
+
+    private final class BrowserClientImpl extends IBrowserClient.Stub {
+        @Override
+        public void onActiveTabChanged(int activeTabId) {
+            Tab tab = Tab.getTabById(activeTabId);
+            for (TabListCallback callback : mTabListCallbacks) {
+                callback.onActiveTabChanged(tab);
+            }
+        }
+
+        @Override
+        public void onTabAdded(ITab iTab) {
+            int id = 0;
+            try {
+                id = iTab.getId();
+            } catch (RemoteException e) {
+                throw new APICallException(e);
+            }
+            Tab tab = Tab.getTabById(id);
+            if (tab == null) {
+                tab = new Tab(iTab, Browser.this);
+            } else {
+                tab.setBrowser(Browser.this);
+            }
+            for (TabListCallback callback : mTabListCallbacks) {
+                callback.onTabAdded(tab);
+            }
+        }
+
+        @Override
+        public void onTabRemoved(int tabId) {
+            Tab tab = Tab.getTabById(tabId);
+            // This should only be called with a previously created tab.
+            assert tab != null;
+            // And this should only be called for tabs attached to this browser.
+            assert tab.getBrowser() == Browser.this;
+
+            tab.setBrowser(null);
+            for (TabListCallback callback : mTabListCallbacks) {
+                callback.onTabRemoved(tab);
+            }
+        }
+    }
 }
diff --git a/weblayer/public/java/org/chromium/weblayer/BrowserFragment.java b/weblayer/public/java/org/chromium/weblayer/BrowserFragment.java
index a4225d53..5228bad 100644
--- a/weblayer/public/java/org/chromium/weblayer/BrowserFragment.java
+++ b/weblayer/public/java/org/chromium/weblayer/BrowserFragment.java
@@ -340,6 +340,7 @@
     @Override
     public void onDestroy() {
         ThreadCheck.ensureOnUiThread();
+        mBrowser.prepareForDestroy();
         try {
             mRemoteFragment.handleOnDestroy();
             // The other side does the clean up automatically in handleOnDestroy()
diff --git a/weblayer/public/java/org/chromium/weblayer/Tab.java b/weblayer/public/java/org/chromium/weblayer/Tab.java
index 7d8f41a..60b6055 100644
--- a/weblayer/public/java/org/chromium/weblayer/Tab.java
+++ b/weblayer/public/java/org/chromium/weblayer/Tab.java
@@ -22,6 +22,11 @@
 import org.chromium.weblayer_private.interfaces.ITabClient;
 import org.chromium.weblayer_private.interfaces.ObjectWrapper;
 
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
 /**
  * Represents a single tab in a browser. More specifically, owns a NavigationController, and allows
  * configuring state of the tab, such as delegates and callbacks.
@@ -30,6 +35,9 @@
     /** The top level key of the JSON object returned by executeScript(). */
     public static final String SCRIPT_RESULT_KEY = "result";
 
+    // Maps from id (as returned from ITab.getId()) to Tab.
+    private static final Map<Integer, Tab> sTabMap = new HashMap<Integer, Tab>();
+
     private final ITab mImpl;
     private FullscreenCallbackClientImpl mFullscreenCallbackClient;
     private final NavigationController mNavigationController;
@@ -37,11 +45,14 @@
     private Browser mBrowser;
     private DownloadCallbackClientImpl mDownloadCallbackClient;
     private NewTabCallback mNewTabCallback;
+    // Id from the remote side.
+    private final int mId;
 
     Tab(ITab impl, Browser browser) {
         mImpl = impl;
         mBrowser = browser;
         try {
+            mId = impl.getId();
             mImpl.setClient(new TabClientImpl());
         } catch (RemoteException e) {
             throw new APICallException(e);
@@ -49,7 +60,37 @@
 
         mCallbacks = new ObserverList<TabCallback>();
         mNavigationController = NavigationController.create(mImpl);
-        mBrowser.registerTab(this);
+        registerTab(this);
+    }
+
+    static void registerTab(Tab tab) {
+        assert getTabById(tab.getId()) == null;
+        sTabMap.put(tab.getId(), tab);
+    }
+
+    static void unregisterTab(Tab tab) {
+        assert getTabById(tab.getId()) != null;
+        sTabMap.remove(tab.getId());
+    }
+
+    static Tab getTabById(int id) {
+        return sTabMap.get(id);
+    }
+
+    static List<Tab> getTabsInBrowser(Browser browser) {
+        List<Tab> tabs = new ArrayList<Tab>();
+        for (Tab tab : sTabMap.values()) {
+            if (tab.getBrowser() == browser) tabs.add(tab);
+        }
+        return tabs;
+    }
+
+    int getId() {
+        return mId;
+    }
+
+    void setBrowser(Browser browser) {
+        mBrowser = browser;
     }
 
     public Browser getBrowser() {
@@ -171,11 +212,14 @@
         }
 
         @Override
-        public void onNewTab(ITab iTab, int mode) {
+        public void onNewTab(int tabId, int mode) {
             // This should only be hit if setNewTabCallback() has been called with a non-null
             // value.
             assert mNewTabCallback != null;
-            Tab tab = new Tab(iTab, mBrowser);
+            Tab tab = getTabById(tabId);
+            // Tab should have already been created by way of BrowserClient.
+            assert tab != null;
+            assert tab.getBrowser() == getBrowser();
             mNewTabCallback.onNewTab(tab, mode);
         }
     }
diff --git a/weblayer/public/java/org/chromium/weblayer/TabListCallback.java b/weblayer/public/java/org/chromium/weblayer/TabListCallback.java
new file mode 100644
index 0000000..058c9ca
--- /dev/null
+++ b/weblayer/public/java/org/chromium/weblayer/TabListCallback.java
@@ -0,0 +1,34 @@
+// 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.
+
+package org.chromium.weblayer;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+/**
+ * An interface for observing changes to the set of tabs in a browser.
+ */
+public abstract class TabListCallback {
+    /**
+     * The active tab has changed.
+     *
+     * @param activeTab The newly active tab.
+     */
+    public void onActiveTabChanged(@Nullable Tab activeTab) {}
+
+    /**
+     * A tab was added to the Browser.
+     *
+     * @param tab The tab that was added.
+     */
+    public void onTabAdded(@NonNull Tab tab) {}
+
+    /**
+     * A tab was removed from the Browser.
+     *
+     * @param tab The tab that was removed.
+     */
+    public void onTabRemoved(@NonNull Tab tab) {}
+}
diff --git a/weblayer/shell/android/BUILD.gn b/weblayer/shell/android/BUILD.gn
index c561c7e..be8f8e5 100644
--- a/weblayer/shell/android/BUILD.gn
+++ b/weblayer/shell/android/BUILD.gn
@@ -172,7 +172,7 @@
   android_manifest_dep = ":weblayer_support_manifest"
   shared_resources = true
 
-  locale_config_java_packages = [ weblayer_locale_config_java_package ]
+  product_config_java_packages = [ weblayer_product_config_java_package ]
 
   native_lib_version_rule = "//build/util:chrome_version_json"
   _native_lib_file =
@@ -204,12 +204,14 @@
     "javatests/src/org/chromium/weblayer/test/InputTypesTest.java",
     "javatests/src/org/chromium/weblayer/test/InstrumentationActivityTestRule.java",
     "javatests/src/org/chromium/weblayer/test/NavigationTest.java",
+    "javatests/src/org/chromium/weblayer/test/NewTabCallbackImpl.java",
     "javatests/src/org/chromium/weblayer/test/NewTabCallbackTest.java",
     "javatests/src/org/chromium/weblayer/test/RenderingTest.java",
     "javatests/src/org/chromium/weblayer/test/ProfileTest.java",
     "javatests/src/org/chromium/weblayer/test/SmokeTest.java",
     "javatests/src/org/chromium/weblayer/test/TabCallbackTest.java",
     "javatests/src/org/chromium/weblayer/test/TopControlsTest.java",
+    "javatests/src/org/chromium/weblayer/test/TabListCallbackTest.java",
   ]
   additional_apks = [
     "//weblayer/shell/android:weblayer_support_apk",
diff --git a/weblayer/shell/android/javatests/src/org/chromium/weblayer/test/NewTabCallbackImpl.java b/weblayer/shell/android/javatests/src/org/chromium/weblayer/test/NewTabCallbackImpl.java
new file mode 100644
index 0000000..84e209f
--- /dev/null
+++ b/weblayer/shell/android/javatests/src/org/chromium/weblayer/test/NewTabCallbackImpl.java
@@ -0,0 +1,32 @@
+// 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.
+
+package org.chromium.weblayer.test;
+
+import org.chromium.base.test.util.CallbackHelper;
+import org.chromium.weblayer.NewTabCallback;
+import org.chromium.weblayer.Tab;
+
+/**
+ * NewTabCallback test helper. Primarily used to wait for a new tab to be created.
+ */
+public class NewTabCallbackImpl extends NewTabCallback {
+    private final CallbackHelper mCallbackHelper = new CallbackHelper();
+
+    @Override
+    public void onNewTab(Tab tab, int mode) {
+        mCallbackHelper.notifyCalled();
+        tab.getBrowser().setActiveTab(tab);
+    }
+
+    public void waitForNewTab() {
+        try {
+            // waitForFirst() only handles a single call. If you need more convert from
+            // waitForFirst().
+            mCallbackHelper.waitForFirst();
+        } catch (Exception e) {
+            throw new RuntimeException(e);
+        }
+    }
+}
diff --git a/weblayer/shell/android/javatests/src/org/chromium/weblayer/test/NewTabCallbackTest.java b/weblayer/shell/android/javatests/src/org/chromium/weblayer/test/NewTabCallbackTest.java
index 87ac783..76fdeb8 100644
--- a/weblayer/shell/android/javatests/src/org/chromium/weblayer/test/NewTabCallbackTest.java
+++ b/weblayer/shell/android/javatests/src/org/chromium/weblayer/test/NewTabCallbackTest.java
@@ -15,10 +15,8 @@
 import org.junit.runner.RunWith;
 
 import org.chromium.base.test.BaseJUnit4ClassRunner;
-import org.chromium.base.test.util.CallbackHelper;
 import org.chromium.content_public.browser.test.util.TestThreadUtils;
 import org.chromium.net.test.EmbeddedTestServer;
-import org.chromium.weblayer.NewTabCallback;
 import org.chromium.weblayer.Tab;
 import org.chromium.weblayer.shell.InstrumentationActivity;
 
@@ -34,24 +32,6 @@
     private EmbeddedTestServer mTestServer;
     private InstrumentationActivity mActivity;
 
-    private static class NewTabCallbackImpl extends NewTabCallback {
-        private CallbackHelper mCallbackHelper = new CallbackHelper();
-
-        @Override
-        public void onNewTab(Tab tab, int mode) {
-            mCallbackHelper.notifyCalled();
-            tab.getBrowser().setActiveTab(tab);
-        }
-
-        public void waitForNewTab() {
-            try {
-                mCallbackHelper.waitForCallback(0);
-            } catch (Exception e) {
-                throw new RuntimeException(e);
-            }
-        }
-    }
-
     @Before
     public void setUp() {
         mTestServer = new EmbeddedTestServer();
diff --git a/weblayer/shell/android/javatests/src/org/chromium/weblayer/test/TabListCallbackTest.java b/weblayer/shell/android/javatests/src/org/chromium/weblayer/test/TabListCallbackTest.java
new file mode 100644
index 0000000..7355230b
--- /dev/null
+++ b/weblayer/shell/android/javatests/src/org/chromium/weblayer/test/TabListCallbackTest.java
@@ -0,0 +1,166 @@
+// 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.
+
+package org.chromium.weblayer.test;
+
+import android.os.Bundle;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.SmallTest;
+
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import org.chromium.base.test.BaseJUnit4ClassRunner;
+import org.chromium.content_public.browser.test.util.TestThreadUtils;
+import org.chromium.net.test.EmbeddedTestServer;
+import org.chromium.weblayer.Browser;
+import org.chromium.weblayer.Tab;
+import org.chromium.weblayer.TabListCallback;
+import org.chromium.weblayer.shell.InstrumentationActivity;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * Tests that NewTabCallback methods are invoked as expected.
+ */
+@RunWith(BaseJUnit4ClassRunner.class)
+public class TabListCallbackTest {
+    @Rule
+    public InstrumentationActivityTestRule mActivityTestRule =
+            new InstrumentationActivityTestRule();
+
+    private EmbeddedTestServer mTestServer;
+    private InstrumentationActivity mActivity;
+    private Tab mFirstTab;
+    private Tab mSecondTab;
+
+    private static class TabListCallbackImpl extends TabListCallback {
+        public static final String ADDED = "added";
+        public static final String ACTIVE = "active";
+        public static final String REMOVED = "removed";
+
+        private List<String> mObservedValues =
+                Collections.synchronizedList(new ArrayList<String>());
+
+        @Override
+        public void onActiveTabChanged(Tab activeTab) {
+            recordValue(ACTIVE);
+        }
+
+        @Override
+        public void onTabAdded(Tab tab) {
+            recordValue(ADDED);
+        }
+
+        @Override
+        public void onTabRemoved(Tab tab) {
+            recordValue(REMOVED);
+        }
+
+        private void recordValue(String parameter) {
+            mObservedValues.add(parameter);
+        }
+
+        public List<String> getObservedValues() {
+            return mObservedValues;
+        }
+    }
+
+    @Before
+    public void setUp() {
+        mTestServer = new EmbeddedTestServer();
+        mTestServer.initializeNative(InstrumentationRegistry.getInstrumentation().getContext(),
+                EmbeddedTestServer.ServerHTTPSSetting.USE_HTTP);
+        mTestServer.addDefaultHandlers("weblayer/test/data");
+        Assert.assertTrue(mTestServer.start(0));
+
+        String url = mTestServer.getURL("/new_browser.html");
+        mActivity = mActivityTestRule.launchShellWithUrl(url);
+        Assert.assertNotNull(mActivity);
+        NewTabCallbackImpl callback = new NewTabCallbackImpl();
+        mFirstTab = TestThreadUtils.runOnUiThreadBlockingNoException(() -> {
+            Tab tab = mActivity.getBrowser().getActiveTab();
+            tab.setNewTabCallback(callback);
+            return tab;
+        });
+
+        EventUtils.simulateTouchCenterOfView(mActivity.getWindow().getDecorView());
+        callback.waitForNewTab();
+        TestThreadUtils.runOnUiThreadBlocking(() -> {
+            Assert.assertEquals(2, mActivity.getBrowser().getTabs().size());
+            mSecondTab = mActivity.getBrowser().getActiveTab();
+            Assert.assertNotSame(mFirstTab, mSecondTab);
+        });
+    }
+
+    @After
+    public void tearDown() {
+        mTestServer.stopAndDestroyServer();
+    }
+
+    @Test
+    @SmallTest
+    public void testActiveTabChanged() {
+        TestThreadUtils.runOnUiThreadBlocking(() -> {
+            TabListCallbackImpl callback = new TabListCallbackImpl();
+            mActivity.getBrowser().addTabListCallback(callback);
+            mActivity.getBrowser().setActiveTab(mFirstTab);
+            Assert.assertTrue(callback.getObservedValues().contains(TabListCallbackImpl.ACTIVE));
+        });
+    }
+
+    @Test
+    @SmallTest
+    public void testMoveToDifferentFragment() {
+        TestThreadUtils.runOnUiThreadBlocking(() -> {
+            Browser browser2 =
+                    Browser.fromFragment(mActivity.createBrowserFragment(0, new Bundle()));
+            Browser browser1 = mActivity.getBrowser();
+            TabListCallbackImpl callback1 = new TabListCallbackImpl();
+            browser1.addTabListCallback(callback1);
+
+            TabListCallbackImpl callback2 = new TabListCallbackImpl();
+            browser2.addTabListCallback(callback2);
+
+            // Move the active tab from browser1 to browser2.
+            Tab tabToMove = browser1.getActiveTab();
+            browser2.addTab(tabToMove);
+            // This should notify callback1 the active tab changed and a tab was removed.
+            int browser1ActiveIndex =
+                    callback1.getObservedValues().indexOf(TabListCallbackImpl.ACTIVE);
+            Assert.assertNotSame(-1, browser1ActiveIndex);
+            int browser1RemoveIndex =
+                    callback1.getObservedValues().indexOf(TabListCallbackImpl.REMOVED);
+            Assert.assertNotSame(-1, browser1RemoveIndex);
+            Assert.assertTrue(browser1ActiveIndex < browser1RemoveIndex);
+            Assert.assertSame(null, browser1.getActiveTab());
+            Assert.assertEquals(1, browser1.getTabs().size());
+            Assert.assertFalse(browser1.getTabs().contains(tabToMove));
+
+            // callback2 should be notified of the insert.
+            Assert.assertTrue(callback2.getObservedValues().contains(TabListCallbackImpl.ADDED));
+            Assert.assertEquals(2, browser2.getTabs().size());
+            Assert.assertTrue(browser2.getTabs().contains(tabToMove));
+        });
+    }
+
+    @Test
+    @SmallTest
+    public void testDispose() {
+        TestThreadUtils.runOnUiThreadBlocking(() -> {
+            TabListCallbackImpl callback = new TabListCallbackImpl();
+            Browser browser = mActivity.getBrowser();
+            browser.addTabListCallback(callback);
+            browser.destroyTab(mActivity.getBrowser().getActiveTab());
+            Assert.assertTrue(callback.getObservedValues().contains(TabListCallbackImpl.ACTIVE));
+            Assert.assertEquals(1, browser.getTabs().size());
+        });
+    }
+}
diff --git a/weblayer/shell/android/shell_apk/src/org/chromium/weblayer/shell/InstrumentationActivity.java b/weblayer/shell/android/shell_apk/src/org/chromium/weblayer/shell/InstrumentationActivity.java
index 8aac82a..d427b53 100644
--- a/weblayer/shell/android/shell_apk/src/org/chromium/weblayer/shell/InstrumentationActivity.java
+++ b/weblayer/shell/android/shell_apk/src/org/chromium/weblayer/shell/InstrumentationActivity.java
@@ -153,7 +153,11 @@
                 return fragments.get(0);
             }
         }
+        return createBrowserFragment(mMainViewId, savedInstanceState);
+    }
 
+    public Fragment createBrowserFragment(int viewId, Bundle savedInstanceState) {
+        FragmentManager fragmentManager = getSupportFragmentManager();
         String profileName = getIntent().hasExtra(EXTRA_PROFILE_NAME)
                 ? getIntent().getStringExtra(EXTRA_PROFILE_NAME)
                 : "DefaultProfile";
@@ -164,7 +168,7 @@
 
         Fragment fragment = WebLayer.createBrowserFragment(profilePath);
         FragmentTransaction transaction = fragmentManager.beginTransaction();
-        transaction.add(mMainViewId, fragment);
+        transaction.add(viewId, fragment);
 
         // Note the commitNow() instead of commit(). We want the fragment to get attached to
         // activity synchronously, so we can use all the functionality immediately. Otherwise we'd
diff --git a/weblayer/test/BUILD.gn b/weblayer/test/BUILD.gn
index 20c1715..262ab3c 100644
--- a/weblayer/test/BUILD.gn
+++ b/weblayer/test/BUILD.gn
@@ -126,6 +126,6 @@
     android_manifest_dep = ":weblayer_browsertests_manifest"
     use_default_launcher = false
     shared_resources = true
-    locale_config_java_packages = [ weblayer_locale_config_java_package ]
+    product_config_java_packages = [ weblayer_product_config_java_package ]
   }
 }
diff --git a/weblayer/variables.gni b/weblayer/variables.gni
index ea0cbdb1..b703e68 100644
--- a/weblayer/variables.gni
+++ b/weblayer/variables.gni
@@ -2,4 +2,4 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
-weblayer_locale_config_java_package = "org.chromium.weblayer_private"
+weblayer_product_config_java_package = "org.chromium.weblayer_private"