diff --git a/BUILD.gn b/BUILD.gn
index 588e9a15..163bd31 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -1677,7 +1677,6 @@
       "components/neterror/resources:closure_compile",
       "components/security_interstitials:closure_compile",
       "components/sync/driver/resources:closure_compile",
-      "components/ukm/debug:closure_compile",
       "mojo/public/tools/bindings/generators/js_templates/lite/test:closure_compile",
       "ui/webui/resources:closure_compile",
     ]
diff --git a/DEPS b/DEPS
index 9689792c..bc8c7634 100644
--- a/DEPS
+++ b/DEPS
@@ -359,7 +359,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': 'e639d04ea5b82c15f8eb25f1bc64b0db735bdb8a',
+  'devtools_frontend_revision': 'c41a367319be563fa942a1cfa315de32b877388d',
   # 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.
@@ -780,7 +780,7 @@
   },
 
   'src/ios/third_party/material_components_ios/src': {
-      'url': Var('chromium_git') + '/external/github.com/material-components/material-components-ios.git' + '@' + 'c6d4682d323ed098f4a5265a1f264531f4a251f6',
+      'url': Var('chromium_git') + '/external/github.com/material-components/material-components-ios.git' + '@' + '3a4ccf8e3c9a4422efe6b9af0c641e09b4afae2c',
       'condition': 'checkout_ios',
   },
 
@@ -1145,7 +1145,7 @@
   },
 
   'src/third_party/depot_tools':
-    Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + '7ff520f0262b298e98ea29efefa5146b9fe5734d',
+    Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + 'c5c48533cfdfe58302eb233f8d57e05aa559cd2a',
 
   'src/third_party/devtools-frontend/src':
     Var('chromium_git') + '/devtools/devtools-frontend' + '@' + Var('devtools_frontend_revision'),
@@ -1542,7 +1542,7 @@
   },
 
   'src/third_party/perfetto':
-    Var('android_git') + '/platform/external/perfetto.git' + '@' + '6007fadd7cb1b558e89799b409d20529abb8c37f',
+    Var('android_git') + '/platform/external/perfetto.git' + '@' + '9fcbb7415a3717c30e656811bc6509eee4d13040',
 
   'src/third_party/perl': {
       'url': Var('chromium_git') + '/chromium/deps/perl.git' + '@' + '6f3e5028eb65d0b4c5fdd792106ac4c84eee1eb3',
@@ -1815,7 +1815,7 @@
     'packages': [
       {
         'package': 'chromeos_internal/apps/help_app/app',
-        'version': 'J98AlfpF1UP6dn-WUiCIyYmt4jPVhKO20Ezm7AEeURQC',
+        'version': 'eowIAxAh6WwOyfMECimDHmVKUYeuqIRwmt8gDLotcTUC',
       },
     ],
     'condition': 'checkout_chromeos and checkout_src_internal',
@@ -1837,7 +1837,7 @@
     'packages': [
       {
         'package': 'chromeos_internal/apps/projector_app/app',
-        'version': 'OqbadMsBFiUCD5WGb0d6iYYxq_dQh7b2r6RztHuYCkgC',
+        'version': 'Z4JT8aUmwfwFtY_0xMTBtD677pZSXpy2AOy-rqQApIMC',
       },
     ],
     'condition': 'checkout_chromeos and checkout_src_internal',
diff --git a/PRESUBMIT.py b/PRESUBMIT.py
index b76b956..36e66f0 100644
--- a/PRESUBMIT.py
+++ b/PRESUBMIT.py
@@ -6014,3 +6014,56 @@
                 "Please use '#!/usr/bin/env python/2/3' as the shebang of %s" %
                 file))
     return result
+
+
+def CheckBatchAnnotation(input_api, output_api):
+    """Checks that tests have either @Batch or @DoNotBatch annotation. If this
+    is not an instrumentation test, disregard."""
+
+    batch_annotation = input_api.re.compile(r'^\s*@Batch')
+    do_not_batch_annotation = input_api.re.compile(r'^\s*@DoNotBatch')
+    robolectric_test = input_api.re.compile(r'[rR]obolectric')
+    test_class_declaration = input_api.re.compile(r'^\s*public\sclass.*Test')
+    uiautomator_test = input_api.re.compile(r'[uU]i[aA]utomator')
+
+    errors = []
+
+    def _FilterFile(affected_file):
+        return input_api.FilterSourceFile(
+            affected_file,
+            files_to_skip=input_api.DEFAULT_FILES_TO_SKIP,
+            files_to_check=[r'.*Test\.java$'])
+
+    for f in input_api.AffectedSourceFiles(_FilterFile):
+        batch_matched = None
+        do_not_batch_matched = None
+        is_instrumentation_test = True
+        for line in f.NewContents():
+            if robolectric_test.search(line) or uiautomator_test.search(line):
+                # Skip Robolectric and UiAutomator tests.
+                is_instrumentation_test = False
+                break
+            if not batch_matched:
+                batch_matched = batch_annotation.search(line)
+            if not do_not_batch_matched:
+                do_not_batch_matched = do_not_batch_annotation.search(line)
+            test_class_declaration_matched = test_class_declaration.search(
+                line)
+            if test_class_declaration_matched:
+                break
+        if (is_instrumentation_test and
+            not batch_matched and
+            not do_not_batch_matched):
+          errors.append(str(f.LocalPath()))
+
+    results = []
+
+    if errors:
+        results.append(
+            output_api.PresubmitPromptWarning(
+                """
+Instrumentation tests should use either @Batch or @DoNotBatch. If tests are not
+safe to run in batch, please use @DoNotBatch with reasons.
+""", errors))
+
+    return results
diff --git a/PRESUBMIT_test.py b/PRESUBMIT_test.py
index 9eb7349..12a7f3b 100755
--- a/PRESUBMIT_test.py
+++ b/PRESUBMIT_test.py
@@ -4546,5 +4546,68 @@
     for error in errors:
       self.assertRegex(error.message, r'DCHECK_IS_ON().+parentheses')
 
+class CheckBatchAnnotation(unittest.TestCase):
+  """Test the CheckBatchAnnotation presubmit check."""
+
+  def testTruePositives(self):
+    """Examples of when there is no @Batch or @DoNotBatch is correctly flagged.
+"""
+    mock_input = MockInputApi()
+    mock_input.files = [
+        MockFile('path/OneTest.java', ['public class OneTest']),
+        MockFile('path/TwoTest.java', ['public class TwoTest']),
+    ]
+    errors = PRESUBMIT.CheckBatchAnnotation(mock_input, MockOutputApi())
+    self.assertEqual(1, len(errors))
+    self.assertEqual(2, len(errors[0].items))
+    self.assertIn('OneTest.java', errors[0].items[0])
+    self.assertIn('TwoTest.java', errors[0].items[1])
+
+  def testAnnotationsPresent(self):
+    """Examples of when there is @Batch or @DoNotBatch is correctly flagged."""
+    mock_input = MockInputApi()
+    mock_input.files = [
+        MockFile('path/OneTest.java',
+                 ['@Batch(Batch.PER_CLASS)', 'public class One {']),
+        MockFile('path/TwoTest.java',
+                 ['@DoNotBatch(reason = "dummy reasons.")', 'public class Two {'
+                 ]),
+        MockFile('path/ThreeTest.java',
+                 ['@Batch(Batch.PER_CLASS)',
+                  'public class Three extends BaseTestA {'],
+                 ['@Batch(Batch.PER_CLASS)',
+                  'public class Three extends BaseTestB {']),
+        MockFile('path/FourTest.java',
+                 ['@DoNotBatch(reason = "dummy reason 1")',
+                  'public class Four extends BaseTestA {'],
+                 ['@DoNotBatch(reason = "dummy reason 2")',
+                  'public class Four extends BaseTestB {']),
+        MockFile('path/FiveTest.java',
+                 ['import androidx.test.uiautomator.UiDevice;',
+                  'public class Five extends BaseTestA {'],
+                 ['import androidx.test.uiautomator.UiDevice;',
+                  'public class Five extends BaseTestB {']),
+        MockFile('path/SixTest.java',
+                 ['import org.chromium.base.test.BaseRobolectricTestRunner;',
+                  'public class Six extends BaseTestA {'],
+                 ['import org.chromium.base.test.BaseRobolectricTestRunner;',
+                  'public class Six extends BaseTestB {']),
+        MockFile('path/SevenTest.java',
+                 ['import org.robolectric.annotation.Config;',
+                  'public class Seven extends BaseTestA {'],
+                 ['import org.robolectric.annotation.Config;',
+                  'public class Seven extends BaseTestB {']),
+        MockFile(
+            'path/OtherClass.java',
+            ['public class OtherClass {'],
+        ),
+        MockFile('path/PRESUBMIT.py',
+                 ['@Batch(Batch.PER_CLASS)',
+                  '@DoNotBatch(reason = "dummy reason)']),
+    ]
+    errors = PRESUBMIT.CheckBatchAnnotation(mock_input, MockOutputApi())
+    self.assertEqual(0, len(errors))
+
+
 if __name__ == '__main__':
   unittest.main()
diff --git a/android_webview/java/src/org/chromium/android_webview/common/ProductionSupportedFlagList.java b/android_webview/java/src/org/chromium/android_webview/common/ProductionSupportedFlagList.java
index 9c2a946..078450c4 100644
--- a/android_webview/java/src/org/chromium/android_webview/common/ProductionSupportedFlagList.java
+++ b/android_webview/java/src/org/chromium/android_webview/common/ProductionSupportedFlagList.java
@@ -337,6 +337,8 @@
             Flag.baseFeature(BlinkFeatures.EARLY_EXIT_ON_NOOP_CLASS_OR_STYLE_CHANGE,
                     "Early exit when the style or class attribute of a DOM element is set to the"
                             + " same value as before."),
+            Flag.baseFeature(BlinkFeatures.THREADED_PRELOAD_SCANNER,
+                    "If enabled, the HTMLPreloadScanner will run on a worker thread."),
             // Add new commandline switches and features above. The final entry should have a
             // trailing comma for cleaner diffs.
     };
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/devui/ComponentsListFragmentTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/devui/ComponentsListFragmentTest.java
index bdc63b66..ea871b17 100644
--- a/android_webview/javatests/src/org/chromium/android_webview/test/devui/ComponentsListFragmentTest.java
+++ b/android_webview/javatests/src/org/chromium/android_webview/test/devui/ComponentsListFragmentTest.java
@@ -15,12 +15,15 @@
 
 import static org.chromium.android_webview.test.devui.DeveloperUiTestUtils.withCount;
 
+import android.content.Context;
 import android.content.Intent;
+import android.support.test.InstrumentationRegistry;
 
 import androidx.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;
@@ -28,6 +31,7 @@
 import org.chromium.android_webview.devui.ComponentsListFragment;
 import org.chromium.android_webview.devui.MainActivity;
 import org.chromium.android_webview.devui.R;
+import org.chromium.android_webview.nonembedded_util.WebViewPackageHelper;
 import org.chromium.android_webview.services.ComponentsProviderPathUtil;
 import org.chromium.android_webview.test.AwJUnit4ClassRunner;
 import org.chromium.base.ContextUtils;
@@ -53,6 +57,13 @@
     private static File sComponentsDownloadDir =
             new File(ComponentsProviderPathUtil.getComponentUpdateServiceDirectoryPath());
 
+    @Before
+    public void setUp() {
+        Context context = InstrumentationRegistry.getTargetContext();
+        WebViewPackageHelper.setCurrentWebViewPackageForTesting(
+                WebViewPackageHelper.getContextPackageInfo(context));
+    }
+
     @After
     public void tearDown() {
         if (sComponentsDownloadDir.exists()) {
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/devui/ComponentsListFragmentUpdateButtonTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/devui/ComponentsListFragmentUpdateButtonTest.java
index 48253fa..d3c5341 100644
--- a/android_webview/javatests/src/org/chromium/android_webview/test/devui/ComponentsListFragmentUpdateButtonTest.java
+++ b/android_webview/javatests/src/org/chromium/android_webview/test/devui/ComponentsListFragmentUpdateButtonTest.java
@@ -14,12 +14,15 @@
 
 import static org.hamcrest.Matchers.anything;
 
+import android.content.Context;
 import android.content.Intent;
+import android.support.test.InstrumentationRegistry;
 
 import androidx.test.filters.MediumTest;
 
 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;
@@ -27,6 +30,7 @@
 import org.chromium.android_webview.devui.ComponentsListFragment;
 import org.chromium.android_webview.devui.MainActivity;
 import org.chromium.android_webview.devui.R;
+import org.chromium.android_webview.nonembedded_util.WebViewPackageHelper;
 import org.chromium.android_webview.services.ComponentsProviderPathUtil;
 import org.chromium.android_webview.test.AwJUnit4ClassRunner;
 import org.chromium.android_webview.test.services.MockAwComponentUpdateService;
@@ -51,6 +55,13 @@
     private static File sComponentsDownloadDir =
             new File(ComponentsProviderPathUtil.getComponentUpdateServiceDirectoryPath());
 
+    @Before
+    public void setUp() {
+        Context context = InstrumentationRegistry.getTargetContext();
+        WebViewPackageHelper.setCurrentWebViewPackageForTesting(
+                WebViewPackageHelper.getContextPackageInfo(context));
+    }
+
     @After
     public void tearDown() {
         if (sComponentsDownloadDir.exists()) {
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/devui/CrashesListFragmentTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/devui/CrashesListFragmentTest.java
index 8d79892..21ae1d6 100644
--- a/android_webview/javatests/src/org/chromium/android_webview/test/devui/CrashesListFragmentTest.java
+++ b/android_webview/javatests/src/org/chromium/android_webview/test/devui/CrashesListFragmentTest.java
@@ -56,6 +56,7 @@
 import org.hamcrest.TypeSafeMatcher;
 import org.junit.After;
 import org.junit.Assume;
+import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -70,8 +71,8 @@
 import org.chromium.android_webview.devui.MainActivity;
 import org.chromium.android_webview.devui.R;
 import org.chromium.android_webview.devui.WebViewPackageError;
-import org.chromium.android_webview.nonembedded_util.WebViewPackageHelper;
 import org.chromium.android_webview.devui.util.CrashBugUrlFactory;
+import org.chromium.android_webview.nonembedded_util.WebViewPackageHelper;
 import org.chromium.android_webview.test.AwJUnit4ClassRunner;
 import org.chromium.base.Callback;
 import org.chromium.base.ContextUtils;
@@ -106,6 +107,13 @@
     @Rule
     public BaseActivityTestRule mRule = new BaseActivityTestRule<MainActivity>(MainActivity.class);
 
+    @Before
+    public void setUp() {
+        Context context = InstrumentationRegistry.getTargetContext();
+        WebViewPackageHelper.setCurrentWebViewPackageForTesting(
+                WebViewPackageHelper.getContextPackageInfo(context));
+    }
+
     @After
     public void tearDown() {
         FileUtils.recursivelyDeleteFile(SystemWideCrashDirectories.getWebViewCrashDir(), null);
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/devui/DeveloperUiTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/devui/DeveloperUiTest.java
index ae28995..0238c15a 100644
--- a/android_webview/javatests/src/org/chromium/android_webview/test/devui/DeveloperUiTest.java
+++ b/android_webview/javatests/src/org/chromium/android_webview/test/devui/DeveloperUiTest.java
@@ -48,12 +48,14 @@
 import org.hamcrest.Matcher;
 import org.junit.After;
 import org.junit.Assume;
+import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
 import org.chromium.android_webview.devui.MainActivity;
 import org.chromium.android_webview.devui.R;
+import org.chromium.android_webview.nonembedded_util.WebViewPackageHelper;
 import org.chromium.android_webview.test.AwJUnit4ClassRunner;
 import org.chromium.base.test.BaseActivityTestRule;
 import org.chromium.base.test.util.Feature;
@@ -85,6 +87,13 @@
                 .respondWith(new ActivityResult(Activity.RESULT_OK, null));
     }
 
+    @Before
+    public void setUp() {
+        Context context = InstrumentationRegistry.getTargetContext();
+        WebViewPackageHelper.setCurrentWebViewPackageForTesting(
+                WebViewPackageHelper.getContextPackageInfo(context));
+    }
+
     @After
     public void tearDown() throws Exception {
         // Activity is launched, i.e the test is not skipped.
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/devui/FlagsFragmentTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/devui/FlagsFragmentTest.java
index 338db25..8e6234f 100644
--- a/android_webview/javatests/src/org/chromium/android_webview/test/devui/FlagsFragmentTest.java
+++ b/android_webview/javatests/src/org/chromium/android_webview/test/devui/FlagsFragmentTest.java
@@ -29,6 +29,7 @@
 
 import static org.chromium.android_webview.test.devui.DeveloperUiTestUtils.withCount;
 
+import android.content.Context;
 import android.content.Intent;
 import android.graphics.Color;
 import android.graphics.drawable.Drawable;
@@ -63,6 +64,7 @@
 import org.chromium.android_webview.devui.FlagsFragment;
 import org.chromium.android_webview.devui.MainActivity;
 import org.chromium.android_webview.devui.R;
+import org.chromium.android_webview.nonembedded_util.WebViewPackageHelper;
 import org.chromium.android_webview.services.DeveloperUiService;
 import org.chromium.android_webview.test.AwJUnit4ClassRunner;
 import org.chromium.base.ContextUtils;
@@ -109,6 +111,9 @@
             FlagsFragment.setFlagListForTesting(sMockFlagList);
             DeveloperUiService.setFlagListForTesting(sMockFlagList);
         });
+        Context context = InstrumentationRegistry.getTargetContext();
+        WebViewPackageHelper.setCurrentWebViewPackageForTesting(
+                WebViewPackageHelper.getContextPackageInfo(context));
         intent.putExtra(MainActivity.FRAGMENT_ID_INTENT_EXTRA, MainActivity.FRAGMENT_ID_FLAGS);
         mRule.launchActivity(intent);
         // Always close the soft keyboard when the activity is launched which is sometimes shown
diff --git a/ash/components/arc/session/arc_container_client_adapter.cc b/ash/components/arc/session/arc_container_client_adapter.cc
index 1a038b74..bfaacce 100644
--- a/ash/components/arc/session/arc_container_client_adapter.cc
+++ b/ash/components/arc/session/arc_container_client_adapter.cc
@@ -167,6 +167,7 @@
     request.set_packages_cache_mode(
         ToLoginManagerPackageCacheMode(params.packages_cache_mode));
     request.set_skip_gms_core_cache(params.skip_gms_core_cache);
+    request.set_skip_tts_cache(params.skip_tts_cache);
     request.set_is_demo_session(params.is_demo_session);
     request.set_demo_session_apps_path(params.demo_session_apps_path.value());
     request.set_locale(params.locale);
diff --git a/ash/components/arc/session/arc_container_client_adapter_unittest.cc b/ash/components/arc/session/arc_container_client_adapter_unittest.cc
index d7c4a5c..7f6ff379 100644
--- a/ash/components/arc/session/arc_container_client_adapter_unittest.cc
+++ b/ash/components/arc/session/arc_container_client_adapter_unittest.cc
@@ -196,6 +196,26 @@
   EXPECT_TRUE(request.enable_tts_caching());
 }
 
+TEST_F(ArcContainerClientAdapterTest, ConvertUpgradeParams_SkipTtsCacheSetup) {
+  UpgradeParams upgrade_params;
+  upgrade_params.skip_tts_cache = true;
+  client_adapter()->UpgradeArc(std::move(upgrade_params),
+                               base::BindOnce(&OnMiniInstanceStarted));
+  const auto& upgrade_request =
+      chromeos::FakeSessionManagerClient::Get()->last_upgrade_arc_request();
+  EXPECT_TRUE(upgrade_request.skip_tts_cache());
+}
+
+TEST_F(ArcContainerClientAdapterTest,
+       ConvertUpgradeParams_EnableTtsCacheSetup) {
+  UpgradeParams upgrade_params;
+  client_adapter()->UpgradeArc(std::move(upgrade_params),
+                               base::BindOnce(&OnMiniInstanceStarted));
+  const auto& upgrade_request =
+      chromeos::FakeSessionManagerClient::Get()->last_upgrade_arc_request();
+  EXPECT_FALSE(upgrade_request.skip_tts_cache());
+}
+
 struct DalvikMemoryProfileTestParam {
   // Requested profile.
   StartParams::DalvikMemoryProfile profile;
diff --git a/ash/components/arc/session/arc_upgrade_params.cc b/ash/components/arc/session/arc_upgrade_params.cc
index db9b3be..980dba98 100644
--- a/ash/components/arc/session/arc_upgrade_params.cc
+++ b/ash/components/arc/session/arc_upgrade_params.cc
@@ -36,6 +36,8 @@
       packages_cache_mode(GetPackagesCacheMode()),
       skip_gms_core_cache(base::CommandLine::ForCurrentProcess()->HasSwitch(
           ash::switches::kArcDisableGmsCoreCache)),
+      skip_tts_cache(base::CommandLine::ForCurrentProcess()->HasSwitch(
+          ash::switches::kArcDisableTtsCache)),
       enable_arc_nearby_share(
           base::FeatureList::IsEnabled(arc::kEnableArcNearbyShare)) {}
 
diff --git a/ash/components/arc/session/arc_upgrade_params.h b/ash/components/arc/session/arc_upgrade_params.h
index 4dc07d9..191b4c6 100644
--- a/ash/components/arc/session/arc_upgrade_params.h
+++ b/ash/components/arc/session/arc_upgrade_params.h
@@ -67,6 +67,10 @@
   // The constructor automatically populates this from command-line.
   bool skip_gms_core_cache;
 
+  // Option to disable TTS cache.
+  // The constructor automatically populates this from command-line.
+  bool skip_tts_cache;
+
   // The supervision transition state for this account. Indicates whether
   // child account should become regular, regular account should become child
   // or neither.
diff --git a/ash/components/arc/session/arc_vm_client_adapter.cc b/ash/components/arc/session/arc_vm_client_adapter.cc
index 00c3413..f09f575c 100644
--- a/ash/components/arc/session/arc_vm_client_adapter.cc
+++ b/ash/components/arc/session/arc_vm_client_adapter.cc
@@ -160,6 +160,8 @@
           static_cast<int>(upgrade_params.management_transition)),
       base::StringPrintf("%s.serialno=%s", prefix.c_str(),
                          serial_number.c_str()),
+      base::StringPrintf("%s.skip_tts_cache=%d", prefix.c_str(),
+                         upgrade_params.skip_tts_cache),
   };
   // Conditionally sets more properties based on |upgrade_params|.
   if (!upgrade_params.locale.empty()) {
diff --git a/ash/components/arc/session/arc_vm_client_adapter_unittest.cc b/ash/components/arc/session/arc_vm_client_adapter_unittest.cc
index a8d6a6e2..29d3b82 100644
--- a/ash/components/arc/session/arc_vm_client_adapter_unittest.cc
+++ b/ash/components/arc/session/arc_vm_client_adapter_unittest.cc
@@ -2544,5 +2544,22 @@
       base::Contains(request.params(), "androidboot.arc.tts.caching=1"));
 }
 
+TEST_F(ArcVmClientAdapterTest, ConvertUpgradeParams_SkipTtsCacheSetup) {
+  StartMiniArc();
+  UpgradeParams upgrade_params = GetPopulatedUpgradeParams();
+  upgrade_params.skip_tts_cache = true;
+  UpgradeArcWithParams(true, std::move(upgrade_params));
+  EXPECT_TRUE(base::Contains(boot_notification_server()->received_data(),
+                             "ro.boot.skip_tts_cache=1"));
+}
+
+TEST_F(ArcVmClientAdapterTest, ConvertUpgradeParams_EnableTtsCacheSetup) {
+  StartMiniArc();
+  UpgradeParams upgrade_params = GetPopulatedUpgradeParams();
+  UpgradeArcWithParams(true, std::move(upgrade_params));
+  EXPECT_TRUE(base::Contains(boot_notification_server()->received_data(),
+                             "ro.boot.skip_tts_cache=0"));
+}
+
 }  // namespace
 }  // namespace arc
diff --git a/ash/constants/ash_switches.cc b/ash/constants/ash_switches.cc
index def3c9a..a0df38c5 100644
--- a/ash/constants/ash_switches.cc
+++ b/ash/constants/ash_switches.cc
@@ -101,6 +101,9 @@
 // apps silently. Used in autotests to resolve racy conditions.
 const char kArcDisablePlayAutoInstall[] = "arc-disable-play-auto-install";
 
+// Used in autotest to disable TTS cache which is on by default.
+const char kArcDisableTtsCache[] = "arc-disable-tts-cache";
+
 // Flag that disables ureadahead completely, including host and guest parts.
 // See also |kArcVmUreadaheadMode|.
 const char kArcDisableUreadahead[] = "arc-disable-ureadahead";
diff --git a/ash/constants/ash_switches.h b/ash/constants/ash_switches.h
index ab02961..06f18bd 100644
--- a/ash/constants/ash_switches.h
+++ b/ash/constants/ash_switches.h
@@ -40,6 +40,7 @@
 COMPONENT_EXPORT(ASH_CONSTANTS)
 extern const char kArcDisableMediaStoreMaintenance[];
 COMPONENT_EXPORT(ASH_CONSTANTS) extern const char kArcDisablePlayAutoInstall[];
+COMPONENT_EXPORT(ASH_CONSTANTS) extern const char kArcDisableTtsCache[];
 COMPONENT_EXPORT(ASH_CONSTANTS) extern const char kArcDisableUreadahead[];
 COMPONENT_EXPORT(ASH_CONSTANTS) extern const char kArcForceShowOptInUi[];
 COMPONENT_EXPORT(ASH_CONSTANTS) extern const char kArcGeneratePlayAutoInstall[];
diff --git a/ash/system/ime_menu/ime_menu_tray.cc b/ash/system/ime_menu/ime_menu_tray.cc
index d15778f..6b992ae 100644
--- a/ash/system/ime_menu/ime_menu_tray.cc
+++ b/ash/system/ime_menu/ime_menu_tray.cc
@@ -279,10 +279,6 @@
     // DetailedViewDelegate:
     void TransitionToMainView(bool restore_focus) override {}
     void CloseBubble() override {}
-
-    gfx::Insets GetInsetsForDetailedView() const override {
-      return gfx::Insets();
-    }
   };
 
   explicit ImeMenuListView(std::unique_ptr<Delegate> delegate)
diff --git a/ash/system/message_center/ash_notification_view.cc b/ash/system/message_center/ash_notification_view.cc
index 6c8ce9f..8dd8691 100644
--- a/ash/system/message_center/ash_notification_view.cc
+++ b/ash/system/message_center/ash_notification_view.cc
@@ -112,9 +112,9 @@
 constexpr int kTitleCharacterLimit =
     message_center::kNotificationWidth * message_center::kMaxTitleLines /
     message_center::kMinPixelsPerTitleCharacter;
-constexpr int kTitleLabelSize = 14;
+constexpr int kTitleLabelSize = 13;
 constexpr int kTimestampInCollapsedViewSize = 12;
-constexpr int kMessageLabelSize = 13;
+constexpr int kMessageLabelSize = 12;
 // The size for `icon_view_`, which is the icon within right content (between
 // title/message view and expand button).
 constexpr int kIconViewSize = 48;
diff --git a/ash/system/tray/detailed_view_delegate.cc b/ash/system/tray/detailed_view_delegate.cc
index 47b1fd02..e6783b6 100644
--- a/ash/system/tray/detailed_view_delegate.cc
+++ b/ash/system/tray/detailed_view_delegate.cc
@@ -104,10 +104,6 @@
   return absl::nullopt;
 }
 
-gfx::Insets DetailedViewDelegate::GetInsetsForDetailedView() const {
-  return kUnifiedDetailedViewPadding;
-}
-
 bool DetailedViewDelegate::IsOverflowIndicatorEnabled() const {
   return false;
 }
diff --git a/ash/system/tray/detailed_view_delegate.h b/ash/system/tray/detailed_view_delegate.h
index ee1b663..7c4ed23 100644
--- a/ash/system/tray/detailed_view_delegate.h
+++ b/ash/system/tray/detailed_view_delegate.h
@@ -51,9 +51,6 @@
   // Get the background color of the detailed view.
   virtual absl::optional<SkColor> GetBackgroundColor();
 
-  // Get the padding of the detailed view.
-  virtual gfx::Insets GetInsetsForDetailedView() const;
-
   // Return true if overflow indicator of ScrollView is enabled.
   virtual bool IsOverflowIndicatorEnabled() const;
 
diff --git a/ash/system/tray/tray_constants.h b/ash/system/tray/tray_constants.h
index 544b41e..90f271a1 100644
--- a/ash/system/tray/tray_constants.h
+++ b/ash/system/tray/tray_constants.h
@@ -219,7 +219,6 @@
 constexpr int kUnifiedTopShortcutButtonMinSpacing = 4;
 
 // Constants used in the detailed view in UnifiedSystemTray.
-constexpr auto kUnifiedDetailedViewPadding = gfx::Insets::TLBR(0, 0, 8, 0);
 constexpr auto kUnifiedDetailedViewTitlePadding =
     gfx::Insets::TLBR(0, 0, 0, 16);
 constexpr int kUnifiedDetailedViewTitleRowHeight = 64;
diff --git a/ash/system/tray/tray_detailed_view.cc b/ash/system/tray/tray_detailed_view.cc
index c1aa5bb..390bd87 100644
--- a/ash/system/tray/tray_detailed_view.cc
+++ b/ash/system/tray/tray_detailed_view.cc
@@ -275,8 +275,7 @@
 TrayDetailedView::TrayDetailedView(DetailedViewDelegate* delegate)
     : delegate_(delegate) {
   box_layout_ = SetLayoutManager(std::make_unique<views::BoxLayout>(
-      views::BoxLayout::Orientation::kVertical,
-      delegate->GetInsetsForDetailedView()));
+      views::BoxLayout::Orientation::kVertical));
   SetBackground(views::CreateSolidBackground(
       delegate_->GetBackgroundColor().value_or(SK_ColorTRANSPARENT)));
 }
diff --git a/ash/webui/diagnostics_ui/backend/session_log_handler_unittest.cc b/ash/webui/diagnostics_ui/backend/session_log_handler_unittest.cc
index 2f3c46b..ea8fa7b 100644
--- a/ash/webui/diagnostics_ui/backend/session_log_handler_unittest.cc
+++ b/ash/webui/diagnostics_ui/backend/session_log_handler_unittest.cc
@@ -235,7 +235,8 @@
   testing::NiceMock<ash::MockHoldingSpaceClient> holding_space_client_;
 };
 
-TEST_F(SessionLogHandlerTest, SaveSessionLog) {
+// Flaky; see crbug.com/1336726
+TEST_F(SessionLogHandlerTest, DISABLED_SaveSessionLog) {
   base::RunLoop run_loop;
   // Populate routine log
   routine_log_->LogRoutineStarted(mojom::RoutineType::kCpuStress);
diff --git a/ash/webui/os_feedback_ui/mojom/os_feedback_ui.mojom b/ash/webui/os_feedback_ui/mojom/os_feedback_ui.mojom
index 0c36070..c1faf303 100644
--- a/ash/webui/os_feedback_ui/mojom/os_feedback_ui.mojom
+++ b/ash/webui/os_feedback_ui/mojom/os_feedback_ui.mojom
@@ -113,6 +113,9 @@
   // Whether or not to include the screenshot with this report. The screenshot
   // data should have been cached in C++ side.
   bool include_screenshot;
+  // Whether or not consent has been granted to Google for to contact user in
+  // reference to report. See (go/feedback-user-consent-faq).
+  bool contact_user_consent_granted;
 };
 
 // Provides services needed by the feedback UI to display data and send reports.
diff --git a/ash/webui/os_feedback_ui/os_feedback_ui.cc b/ash/webui/os_feedback_ui/os_feedback_ui.cc
index 46f4d083..13695c15 100644
--- a/ash/webui/os_feedback_ui/os_feedback_ui.cc
+++ b/ash/webui/os_feedback_ui/os_feedback_ui.cc
@@ -41,6 +41,7 @@
 
 void AddLocalizedStrings(content::WebUIDataSource* source) {
   static constexpr webui::LocalizedString kLocalizedStrings[] = {
+      {"backButtonLabel", IDS_FEEDBACK_TOOL_BACK_BUTTON_LABEL},
       {"continueButtonLabel", IDS_FEEDBACK_TOOL_CONTINUE_BUTTON_LABEL},
       {"descriptionHint", IDS_FEEDBACK_TOOL_DESCRIPTION_HINT},
       {"descriptionLabel", IDS_FEEDBACK_TOOL_DESCRIPTION_LABEL},
@@ -60,6 +61,7 @@
       {"confirmationTitleOnline", IDS_FEEDBACK_TOOL_PAGE_TITLE_AFTER_SENT},
       {"diagnosticsAppLabel",
        IDS_FEEDBACK_TOOL_RESOURCES_DIAGNOSTICS_APP_LABEL},
+      {"userConsentLabel", IDS_FEEDBACK_TOOL_USER_CONSENT_LABEL},
   };
 
   source->AddLocalizedStrings(kLocalizedStrings);
diff --git a/ash/webui/os_feedback_ui/resources/share_data_page.html b/ash/webui/os_feedback_ui/resources/share_data_page.html
index bd2191cc..6a8a431 100644
--- a/ash/webui/os_feedback_ui/resources/share_data_page.html
+++ b/ash/webui/os_feedback_ui/resources/share_data_page.html
@@ -67,6 +67,9 @@
     margin-bottom: 24px;
   }
 
+  .disabled-input-text {
+    color: var(--cros-text-color-disabled);
+  }
 </style>
 <div id="container">
   <div id="header">
@@ -101,6 +104,12 @@
         <option value="">Don't include email address</option>
       </select>
     </div>
+    <!-- User consent -->
+    <div id="userConsent" class="checkbox-field-container">
+      <input id="userConsentCheckbox" type="checkbox"
+          aria-labelledby="userConsentLabel">
+      <label id="userConsentLabel">[[i18n('userConsentLabel')]]</label>
+    </div>
     <!-- Diagnostic data -->
     <div id="shareDiagnosticData">
       <h2 id="shareDiagnosticDataLabel">[[i18n('shareDiagnosticDataLabel')]]</h2>
@@ -130,7 +139,7 @@
   <div id="navButtons">
     <cr-button id="buttonBack" class="cancel-button"
         on-click="handleBackButtonClicked_">
-      Back
+      [[i18n('backButtonLabel')]]
     </cr-button>
     <cr-button id="buttonSend" class="action-button"
         on-click="handleSendButtonClicked_">
diff --git a/ash/webui/os_feedback_ui/resources/share_data_page.js b/ash/webui/os_feedback_ui/resources/share_data_page.js
index 4b382e6..a24d875 100644
--- a/ash/webui/os_feedback_ui/resources/share_data_page.js
+++ b/ash/webui/os_feedback_ui/resources/share_data_page.js
@@ -56,6 +56,15 @@
     this.screenshotUrl;
   }
 
+  ready() {
+    super.ready();
+
+    // Set up event listener for email change to retarget |this| to be the
+    // ShareDataPageElement's context.
+    this.$.userEmailDropDown.addEventListener(
+        'change', this.handleUserEmailDropDownChanged_.bind(this));
+  }
+
   /**
    * @return {boolean}
    * @protected
@@ -72,6 +81,23 @@
     return !!this.screenshotUrl;
   }
 
+  /** @protected */
+  handleUserEmailDropDownChanged_() {
+    const email = this.$.userEmailDropDown.value;
+    const consentCheckbox = this.$.userConsentCheckbox;
+
+    // Update UI and state of #userConsentCheckbox base on if report will be
+    // anonymous.
+    if (email === '') {
+      consentCheckbox.disabled = true;
+      consentCheckbox.checked = false;
+      this.$.userConsentLabel.classList.add('disabled-input-text');
+    } else {
+      consentCheckbox.disabled = false;
+      this.$.userConsentLabel.classList.remove('disabled-input-text');
+    }
+  }
+
   /**
    * @param {!Event} e
    * @protected
@@ -125,7 +151,9 @@
       includeSystemLogsAndHistograms:
           this.getElement_('#sysInfoCheckbox').checked,
       includeScreenshot: this.getElement_('#screenshotCheckbox').checked &&
-          !!this.getElement_('#screenshotImage').src
+          !!this.getElement_('#screenshotImage').src,
+      contactUserConsentGranted:
+          this.getElement_('#userConsentCheckbox').checked,
     });
 
     report.attachedFile =
@@ -136,6 +164,11 @@
       report.feedbackContext.email = email;
     }
 
+    // Ensure consent granted is false when email not provided.
+    if (!email) {
+      report.contactUserConsentGranted = false;
+    }
+
     if (this.getElement_('#pageUrlCheckbox').checked) {
       report.feedbackContext.pageUrl = {
         url: this.getElement_('#pageUrlText').value
diff --git a/ash/webui/personalization_app/resources/common/common_style.css b/ash/webui/personalization_app/resources/common/common_style.css
index 4f922df..0a9b764 100644
--- a/ash/webui/personalization_app/resources/common/common_style.css
+++ b/ash/webui/personalization_app/resources/common/common_style.css
@@ -109,7 +109,6 @@
 
 .photo-images-container {
   background-color: var(--personalization-app-grid-item-background-color);
-  border: 1px solid rgba(0, 0, 0, 0.08);
   border-radius: 12px;
   box-sizing: border-box;
   display: flex;
@@ -182,6 +181,9 @@
   animation-fill-mode: forwards;
   animation-name: resize;
   animation-timing-function: cubic-bezier(0.40, 0.00, 0.20, 1.00);
+}
+
+.photo-inner-container[aria-selected='true'] .photo-images-border {
   border: 0;
 }
 
@@ -233,13 +235,29 @@
 }
 
 .preview-image-container {
-  border: 1px solid rgba(0, 0, 0, 0.08);
   border-radius: 12px;
   box-sizing: border-box;
   overflow: hidden;
   position: relative;
 }
 
+/**
+ * Hover a border over the image container to avoid sub pixel rounding issues
+ * with chrome scaling images.
+ */
+.preview-image-border,
+.photo-images-border {
+  border: 1px solid rgba(0, 0, 0, 0.08);
+  border-radius: 12px;
+  bottom: 0;
+  box-sizing: border-box;
+  left: 0;
+  position: absolute;
+  right: 0;
+  top: 0;
+  z-index: 2;
+}
+
 .preview-image {
   height: 100%;
   object-fit: cover;
diff --git a/ash/webui/personalization_app/resources/trusted/ambient/ambient_preview_element.html b/ash/webui/personalization_app/resources/trusted/ambient/ambient_preview_element.html
index a9aece8..416e32c 100644
--- a/ash/webui/personalization_app/resources/trusted/ambient/ambient_preview_element.html
+++ b/ash/webui/personalization_app/resources/trusted/ambient/ambient_preview_element.html
@@ -173,6 +173,7 @@
   <template is="dom-if" if="[[!loading_]]">
     <template is="dom-if" if="[[!ambientModeEnabled_]]">
       <div id="imageContainer" class="preview-image-container" aria-hidden="true">
+        <div class="preview-image-border"></div>
         <img class="preview-image disabled" src="//personalization/common/slideshow.png">
       </div>
       <div id="messageContainer">
@@ -190,6 +191,7 @@
            Currently, we show blank containers -->
       <template is="dom-if" if="[[previewAlbums_]]">
         <div id="imageContainer" class="preview-image-container">
+          <div class="preview-image-border"></div>
           <template is="dom-if" if="[[clickable]]">
             <img class="preview-image clickable" is="cr-auto-img"
                 on-click="onClickPreviewImage_"
diff --git a/ash/webui/personalization_app/resources/trusted/ambient/ambient_subpage_element.html b/ash/webui/personalization_app/resources/trusted/ambient/ambient_subpage_element.html
index 41ac52b8..2b2aa16 100644
--- a/ash/webui/personalization_app/resources/trusted/ambient/ambient_subpage_element.html
+++ b/ash/webui/personalization_app/resources/trusted/ambient/ambient_subpage_element.html
@@ -119,7 +119,8 @@
   }
 </style>
 <div id="container">
-  <template is="dom-if" if="[[shouldShowMainSettings_(path)]]">
+  <!-- restamp to avoid layout issues with iron-list resizing while hidden -->
+  <template is="dom-if" if="[[shouldShowMainSettings_(path)]]" restamp>
     <div id="mainSettings">
       <template is="dom-if" if="[[loadingAmbientMode_(ambientModeEnabled_)]]">
         <div id="toggleRowPlaceholder" class="ambient-toggle-row-container">
@@ -195,7 +196,8 @@
       </template>
     </div>
   </template>
-  <template is="dom-if" if="[[shouldShowAlbums_(path)]]">
+  <!-- restamp to avoid layout issues with iron-list resizing while hidden -->
+  <template is="dom-if" if="[[shouldShowAlbums_(path)]]" restamp>
     <div id="albumsSubpage">
       <albums-subpage topic-source="[[getTopicSource_(queryParams)]]"
           albums="[[getAlbums_(albums_, queryParams)]]">
diff --git a/ash/webui/personalization_app/resources/trusted/wallpaper/local_images_element.html b/ash/webui/personalization_app/resources/trusted/wallpaper/local_images_element.html
index 376aa75..c6dc19a1 100644
--- a/ash/webui/personalization_app/resources/trusted/wallpaper/local_images_element.html
+++ b/ash/webui/personalization_app/resources/trusted/wallpaper/local_images_element.html
@@ -33,6 +33,7 @@
               aria-selected$="[[getAriaSelected_(item, currentSelected_, pendingSelected_)]]"
               aria-label$="[[getAriaLabel_(item)]]">
             <div class="photo-images-container">
+              <div class="photo-images-border"></div>
               <img src="[[getImageData_(item, imageData_)]]" aria-hidden="true">
               <iron-icon icon="personalization:checkmark"></iron-icon>
             </div>
diff --git a/ash/webui/personalization_app/resources/trusted/wallpaper/wallpaper_preview_element.html b/ash/webui/personalization_app/resources/trusted/wallpaper/wallpaper_preview_element.html
index 3754a36..79dee42 100644
--- a/ash/webui/personalization_app/resources/trusted/wallpaper/wallpaper_preview_element.html
+++ b/ash/webui/personalization_app/resources/trusted/wallpaper/wallpaper_preview_element.html
@@ -91,6 +91,7 @@
     <template is="dom-if" if="[[!isPolicyControlled_(image_)]]">
       <div id="imageContainer" class="photo-images-container clickable"
           on-click="onClickWallpaper_" on-keypress="onClickWallpaper_">
+        <div class="photo-images-border"></div>
         <img src$="[[getImageSrc_(image_)]]"
             alt$="[[getImageAltDescription_(image_)]]">
         <div id="shelf"></div>
@@ -98,6 +99,7 @@
     </template>
     <template is="dom-if" if="[[isPolicyControlled_(image_)]]">
       <div id="imageContainer" class="photo-images-container">
+        <div class="photo-images-border"></div>
         <img src$="[[getImageSrc_(image_)]]"
             aria-description="$i18n{managedSetting}"
             alt$="[[getImageAltDescription_(image_)]]">
diff --git a/ash/webui/personalization_app/resources/trusted/wallpaper/wallpaper_selected_element.html b/ash/webui/personalization_app/resources/trusted/wallpaper/wallpaper_selected_element.html
index 7de93a3..cce9290a 100644
--- a/ash/webui/personalization_app/resources/trusted/wallpaper/wallpaper_selected_element.html
+++ b/ash/webui/personalization_app/resources/trusted/wallpaper/wallpaper_selected_element.html
@@ -61,6 +61,7 @@
   </template>
   <template is="dom-if" if="[[showImage_]]">
     <div id="imageContainer" class="preview-image-container">
+      <div class="preview-image-border"></div>
       <img class="preview-image" src$="[[getImageSrc_(image_)]]" aria-hidden="true">
     </div>
     <h2 id="textContainer" class="preview-text-container" aria-live="polite" aria-label$="[[getAriaLabel_(image_, dailyRefreshState_)]]">
diff --git a/ash/webui/personalization_app/resources/untrusted/collections_grid.html b/ash/webui/personalization_app/resources/untrusted/collections_grid.html
index cb061d5e2..07e86866 100644
--- a/ash/webui/personalization_app/resources/untrusted/collections_grid.html
+++ b/ash/webui/personalization_app/resources/untrusted/collections_grid.html
@@ -161,6 +161,7 @@
             aria-disabled="true"
             class="photo-inner-container photo-loading-failure">
           <div class$="[[getClassForImagesContainer_(item)]]">
+            <div class="photo-images-border"></div>
             <template is="dom-repeat" items="[[item.preview]]" as="preview">
               <img is="cr-auto-img" auto-src="[[preview.url]]"
                   aria-hidden="true" clear-src>
@@ -185,6 +186,7 @@
             on-click="onCollectionSelected_"
             on-keypress="onCollectionSelected_">
           <div class$="[[getClassForImagesContainer_(item)]]">
+            <div class="photo-images-border"></div>
             <img is="cr-auto-img" auto-src="[[getImageUrlForEmptyTile_(item)]]"
                 aria-hidden="true" clear-src>
           </div>
@@ -208,6 +210,7 @@
             on-keypress="onCollectionSelected_"
             tabindex$="[[tabIndex]]">
           <div class$="[[getClassForImagesContainer_(item)]]">
+            <div class="photo-images-border"></div>
             <template is="dom-repeat" items="[[item.preview]]" as="preview">
               <img is="cr-auto-img" auto-src="[[preview.url]]"
                   class$="[[getClassForImg_(index, item)]]"
diff --git a/ash/webui/personalization_app/resources/untrusted/images_grid.html b/ash/webui/personalization_app/resources/untrusted/images_grid.html
index 51308fe7..16fda71 100644
--- a/ash/webui/personalization_app/resources/untrusted/images_grid.html
+++ b/ash/webui/personalization_app/resources/untrusted/images_grid.html
@@ -26,6 +26,7 @@
               aria-selected$="[[getAriaSelected_(item, selectedAssetId_, pendingSelectedAssetId_)]]"
               aria-label$="[[getAriaLabel_(item)]]">
             <div class="photo-images-container">
+              <div class="photo-images-border"></div>
               <template is="dom-repeat" items="[[item.preview]]" as="preview">
                 <img is="cr-auto-img" class$="[[getClassForImg_(index, item)]]"
                     auto-src="[[preview.url]]" aria-hidden="true" clear-src>
diff --git a/base/json/json_parser.cc b/base/json/json_parser.cc
index c011e14..b632a47 100644
--- a/base/json/json_parser.cc
+++ b/base/json/json_parser.cc
@@ -53,8 +53,6 @@
       return JSONParser::kUnsupportedEncoding;
     case JSONParser::JSON_UNQUOTED_DICTIONARY_KEY:
       return JSONParser::kUnquotedDictionaryKey;
-    case JSONParser::JSON_TOO_LARGE:
-      return JSONParser::kInputTooLarge;
     case JSONParser::JSON_UNREPRESENTABLE_NUMBER:
       return JSONParser::kUnrepresentableNumber;
     case JSONParser::JSON_PARSE_ERROR_COUNT:
@@ -109,7 +107,6 @@
     "Unsupported encoding. JSON must be UTF-8.";
 const char JSONParser::kUnquotedDictionaryKey[] =
     "Dictionary keys must be quoted.";
-const char JSONParser::kInputTooLarge[] = "Input string is too large (>2GB).";
 const char JSONParser::kUnrepresentableNumber[] =
     "Number cannot be represented.";
 
@@ -143,19 +140,12 @@
   // index of the imaginary '\n' immediately before the start of the string:
   // 'A' is in column (0 - -1) = 1.
   line_number_ = 1;
-  index_last_line_ = -1;
+  index_last_line_ = static_cast<size_t>(-1);
 
   error_code_ = JSON_NO_ERROR;
   error_line_ = 0;
   error_column_ = 0;
 
-  // ICU and ReadUnicodeCharacter() use int32_t for lengths, so ensure
-  // that the index_ will not overflow when parsing.
-  if (!base::IsValueInRangeForNumericType<int32_t>(input.length())) {
-    ReportError(JSON_TOO_LARGE, -1);
-    return absl::nullopt;
-  }
-
   // When the input JSON string starts with a UTF-8 Byte-Order-Mark,
   // advance the start position to avoid the ParseNextToken function mis-
   // treating a Unicode BOM as an invalid character and returning NULL.
@@ -215,7 +205,7 @@
     if (UNLIKELY(point == kUnicodeReplacementPoint)) {
       string_->append(kUnicodeReplacementString);
     } else {
-      WriteUnicodeCharacter(point, &*string_);
+      WriteUnicodeCharacter(static_cast<base_icu::UChar32>(point), &*string_);
     }
   }
 }
@@ -264,7 +254,7 @@
 }
 
 const char* JSONParser::pos() {
-  CHECK_LE(static_cast<size_t>(index_), input_.length());
+  CHECK_LE(index_, input_.length());
   return input_.data() + index_;
 }
 
@@ -541,8 +531,7 @@
 
   while (PeekChar()) {
     base_icu::UChar32 next_char = 0;
-    if (!ReadUnicodeCharacter(input_.data(),
-                              static_cast<int32_t>(input_.length()), &index_,
+    if (!ReadUnicodeCharacter(input_.data(), input_.length(), &index_,
                               &next_char) ||
         !IsValidCodepoint(next_char)) {
       if ((options_ & JSON_REPLACE_INVALID_CHARACTERS) == 0) {
@@ -747,8 +736,8 @@
 
 absl::optional<Value> JSONParser::ConsumeNumber() {
   const char* num_start = pos();
-  const int start_index = index_;
-  int end_index = start_index;
+  const size_t start_index = index_;
+  size_t end_index = start_index;
 
   if (PeekChar() == '-')
     ConsumeChar();
@@ -787,7 +776,7 @@
   // so save off where the parser should be on exit (see Consume invariant at
   // the top of the header), then make sure the next token is one which is
   // valid.
-  int exit_index = index_;
+  size_t exit_index = index_;
 
   switch (GetNextToken()) {
     case T_OBJECT_END:
diff --git a/base/json/json_parser.h b/base/json/json_parser.h
index 6cdd1e08..aad1b3a 100644
--- a/base/json/json_parser.h
+++ b/base/json/json_parser.h
@@ -54,7 +54,6 @@
     JSON_UNEXPECTED_DATA_AFTER_ROOT,
     JSON_UNSUPPORTED_ENCODING,
     JSON_UNQUOTED_DICTIONARY_KEY,
-    JSON_TOO_LARGE,
     JSON_UNREPRESENTABLE_NUMBER,
     JSON_PARSE_ERROR_COUNT
   };
@@ -68,7 +67,6 @@
   static const char kUnexpectedDataAfterRoot[];
   static const char kUnsupportedEncoding[];
   static const char kUnquotedDictionaryKey[];
-  static const char kInputTooLarge[];
   static const char kUnrepresentableNumber[];
 
   explicit JSONParser(int options, size_t max_depth = kAbsoluteMaxDepth);
@@ -253,7 +251,7 @@
   StringPiece input_;
 
   // The index in the input stream to which the parser is wound.
-  int index_;
+  size_t index_;
 
   // The number of times the parser has recursed (current stack depth).
   size_t stack_depth_;
@@ -262,7 +260,7 @@
   int line_number_;
 
   // The last value of |index_| on the previous line.
-  int index_last_line_;
+  size_t index_last_line_;
 
   // Error information.
   JsonParseError error_code_;
diff --git a/base/json/string_escape.cc b/base/json/string_escape.cc
index 4c1dbeb5..69362f7 100644
--- a/base/json/string_escape.cc
+++ b/base/json/string_escape.cc
@@ -85,12 +85,8 @@
   if (put_in_quotes)
     dest->push_back('"');
 
-  // Casting is necessary because ICU uses int32_t. Try and do so safely.
-  CHECK_LE(str.length(),
-           static_cast<size_t>(std::numeric_limits<int32_t>::max()));
-  const int32_t length = static_cast<int32_t>(str.length());
-
-  for (int32_t i = 0; i < length; ++i) {
+  const size_t length = str.length();
+  for (size_t i = 0; i < length; ++i) {
     base_icu::UChar32 code_point;
     if (!ReadUnicodeCharacter(str.data(), length, &i, &code_point) ||
         code_point == CBU_SENTINEL) {
diff --git a/base/strings/escape.cc b/base/strings/escape.cc
index e9b9afc..8c79bcd 100644
--- a/base/strings/escape.cc
+++ b/base/strings/escape.cc
@@ -61,7 +61,7 @@
       escaped.push_back(IntToHex(c >> 4));
       escaped.push_back(IntToHex(c & 0xf));
     } else {
-      escaped.push_back(c);
+      escaped.push_back(static_cast<char>(c));
     }
   }
   return escaped;
@@ -198,8 +198,8 @@
   char most_sig_digit(escaped_text[index + 1]);
   char least_sig_digit(escaped_text[index + 2]);
   if (IsHexDigit(most_sig_digit) && IsHexDigit(least_sig_digit)) {
-    *value =
-        HexDigitToInt(most_sig_digit) * 16 + HexDigitToInt(least_sig_digit);
+    *value = static_cast<unsigned char>(HexDigitToInt(most_sig_digit) * 16 +
+                                        HexDigitToInt(least_sig_digit));
     return true;
   }
   return false;
@@ -236,7 +236,7 @@
     }
   }
 
-  int32_t char_index = 0;
+  size_t char_index = 0;
   // Check if the unicode "character" that was just unescaped is valid.
   if (!ReadUnicodeCharacter(reinterpret_cast<char*>(bytes), num_bytes,
                             &char_index, code_point_out)) {
@@ -253,10 +253,11 @@
 
 // This method takes a Unicode code point and returns true if it should be
 // unescaped, based on |rules|.
-bool ShouldUnescapeCodePoint(UnescapeRule::Type rules, uint32_t code_point) {
+bool ShouldUnescapeCodePoint(UnescapeRule::Type rules,
+                             base_icu::UChar32 code_point) {
   // If this is an ASCII character, use the lookup table.
-  if (code_point < 0x80) {
-    return kUrlUnescape[code_point] ||
+  if (code_point >= 0 && code_point < 0x80) {
+    return kUrlUnescape[static_cast<size_t>(code_point)] ||
            // Allow some additional unescaping when flags are set.
            (code_point == ' ' && (rules & UnescapeRule::SPACES)) ||
            // Allow any of the prohibited but non-control characters when doing
@@ -418,7 +419,7 @@
       // sequences.
       unsigned char non_utf8_byte;
       if (UnescapeUnsignedByteAtIndex(escaped_text, i, &non_utf8_byte)) {
-        result.push_back(non_utf8_byte);
+        result.push_back(static_cast<char>(non_utf8_byte));
         if (adjustments)
           adjustments->push_back(OffsetAdjuster::Adjustment(i, 3, 1));
         i += 3;
@@ -569,7 +570,7 @@
     // UnescapeUnsignedByteAtIndex does bounds checking, so this is always safe
     // to call.
     if (UnescapeUnsignedByteAtIndex(escaped_text, i, &byte)) {
-      unescaped_text[output_index++] = byte;
+      unescaped_text[output_index++] = static_cast<char>(byte);
       i += 3;
       continue;
     }
@@ -595,7 +596,7 @@
   unescaped_text->clear();
 
   std::set<unsigned char> illegal_encoded_bytes;
-  for (char c = '\x00'; c < '\x20'; ++c) {
+  for (unsigned char c = '\x00'; c < '\x20'; ++c) {
     illegal_encoded_bytes.insert(c);
   }
   if (fail_on_path_separators) {
@@ -632,7 +633,7 @@
 std::u16string UnescapeForHTML(StringPiece16 input) {
   static const struct {
     const char* ampersand_code;
-    const char replacement;
+    const char16_t replacement;
   } kEscapeToChars[] = {
       {"&lt;", '<'},   {"&gt;", '>'},   {"&amp;", '&'},
       {"&quot;", '"'}, {"&#39;", '\''},
@@ -648,14 +649,15 @@
        ++iter) {
     if (*iter == '&') {
       // Potential ampersand encode char.
-      size_t index = iter - text.begin();
+      size_t index = static_cast<size_t>(iter - text.begin());
       for (size_t i = 0; i < std::size(kEscapeToChars); i++) {
         if (ampersand_chars[i].empty()) {
           ampersand_chars[i] = ASCIIToUTF16(kEscapeToChars[i].ampersand_code);
         }
         if (text.find(ampersand_chars[i], index) == index) {
-          text.replace(iter, iter + ampersand_chars[i].length(), 1,
-                       kEscapeToChars[i].replacement);
+          text.replace(
+              iter, iter + static_cast<ptrdiff_t>(ampersand_chars[i].length()),
+              1, kEscapeToChars[i].replacement);
           break;
         }
       }
diff --git a/base/strings/escape.h b/base/strings/escape.h
index 151993c..9eb6258 100644
--- a/base/strings/escape.h
+++ b/base/strings/escape.h
@@ -74,41 +74,39 @@
   // functions.
   typedef uint32_t Type;
 
-  enum {
-    // Don't unescape anything at all.
-    NONE = 0,
+  // Don't unescape anything at all.
+  static constexpr Type NONE = 0;
 
-    // Don't unescape anything special, but all normal unescaping will happen.
-    // This is a placeholder and can't be combined with other flags (since it's
-    // just the absence of them). All other unescape rules imply "normal" in
-    // addition to their special meaning. Things like escaped letters, digits,
-    // and most symbols will get unescaped with this mode.
-    NORMAL = 1 << 0,
+  // Don't unescape anything special, but all normal unescaping will happen.
+  // This is a placeholder and can't be combined with other flags (since it's
+  // just the absence of them). All other unescape rules imply "normal" in
+  // addition to their special meaning. Things like escaped letters, digits,
+  // and most symbols will get unescaped with this mode.
+  static constexpr Type NORMAL = 1 << 0;
 
-    // Convert %20 to spaces. In some places where we're showing URLs, we may
-    // want this. In places where the URL may be copied and pasted out, then
-    // you wouldn't want this since it might not be interpreted in one piece
-    // by other applications.  Other UTF-8 spaces will not be unescaped.
-    SPACES = 1 << 1,
+  // Convert %20 to spaces. In some places where we're showing URLs, we may
+  // want this. In places where the URL may be copied and pasted out, then
+  // you wouldn't want this since it might not be interpreted in one piece
+  // by other applications.  Other UTF-8 spaces will not be unescaped.
+  static constexpr Type SPACES = 1 << 1;
 
-    // Unescapes '/' and '\\'. If these characters were unescaped, the resulting
-    // URL won't be the same as the source one. Moreover, they are dangerous to
-    // unescape in strings that will be used as file paths or names. This value
-    // should only be used when slashes don't have special meaning, like data
-    // URLs.
-    PATH_SEPARATORS = 1 << 2,
+  // Unescapes '/' and '\\'. If these characters were unescaped, the resulting
+  // URL won't be the same as the source one. Moreover, they are dangerous to
+  // unescape in strings that will be used as file paths or names. This value
+  // should only be used when slashes don't have special meaning, like data
+  // URLs.
+  static constexpr Type PATH_SEPARATORS = 1 << 2;
 
-    // Unescapes various characters that will change the meaning of URLs,
-    // including '%', '+', '&', '#'. Does not unescape path separators.
-    // If these characters were unescaped, the resulting URL won't be the same
-    // as the source one. This flag is used when generating final output like
-    // filenames for URLs where we won't be interpreting as a URL and want to do
-    // as much unescaping as possible.
-    URL_SPECIAL_CHARS_EXCEPT_PATH_SEPARATORS = 1 << 3,
+  // Unescapes various characters that will change the meaning of URLs,
+  // including '%', '+', '&', '#'. Does not unescape path separators.
+  // If these characters were unescaped, the resulting URL won't be the same
+  // as the source one. This flag is used when generating final output like
+  // filenames for URLs where we won't be interpreting as a URL and want to do
+  // as much unescaping as possible.
+  static constexpr Type URL_SPECIAL_CHARS_EXCEPT_PATH_SEPARATORS = 1 << 3;
 
-    // URL queries use "+" for space. This flag controls that replacement.
-    REPLACE_PLUS_WITH_SPACE = 1 << 4,
-  };
+  // URL queries use "+" for space. This flag controls that replacement.
+  static constexpr Type REPLACE_PLUS_WITH_SPACE = 1 << 4;
 };
 
 // Unescapes |escaped_text| and returns the result.
diff --git a/base/strings/pattern.cc b/base/strings/pattern.cc
index 613ddc28..9b01b2a3 100644
--- a/base/strings/pattern.cc
+++ b/base/strings/pattern.cc
@@ -124,7 +124,7 @@
   base_icu::UChar32 operator()(const char** p, const char* end) {
     base_icu::UChar32 c;
     int offset = 0;
-    CBU8_NEXT(*p, offset, end - *p, c);
+    CBU8_NEXT(reinterpret_cast<const uint8_t*>(*p), offset, end - *p, c);
     *p += offset;
     return c;
   }
diff --git a/base/strings/safe_sprintf.cc b/base/strings/safe_sprintf.cc
index 12621c8..dbb0e3a4 100644
--- a/base/strings/safe_sprintf.cc
+++ b/base/strings/safe_sprintf.cc
@@ -223,8 +223,13 @@
   // if |pad| is ' '.
   //
   // Returns "false", if the |buffer_| overflowed at any time.
-  bool IToASCII(bool sign, bool upcase, int64_t i, int base,
-                char pad, size_t padding, const char* prefix);
+  bool IToASCII(bool sign,
+                bool upcase,
+                int64_t i,
+                size_t base,
+                char pad,
+                size_t padding,
+                const char* prefix);
 
  private:
   // Increments |count_| by |inc| unless this would cause |count_| to
@@ -275,9 +280,13 @@
   size_t count_;
 };
 
-
-bool Buffer::IToASCII(bool sign, bool upcase, int64_t i, int base,
-                      char pad, size_t padding, const char* prefix) {
+bool Buffer::IToASCII(bool sign,
+                      bool upcase,
+                      int64_t i,
+                      size_t base,
+                      char pad,
+                      size_t padding,
+                      const char* prefix) {
   // Sanity check for parameters. None of these should ever fail, but see
   // above for the rationale why we can't call CHECK().
   DEBUG_CHECK(base >= 2);
@@ -295,7 +304,7 @@
   //   if (sign && i < 0)
   //     prefix = "-";
   //   num = abs(i);
-  int minint = 0;
+  size_t minint = 0;
   uint64_t num;
   if (sign && i < 0) {
     prefix = "-";
@@ -335,7 +344,7 @@
     }
   } else
     prefix = nullptr;
-  const size_t prefix_length = reverse_prefix - prefix;
+  const size_t prefix_length = static_cast<size_t>(reverse_prefix - prefix);
 
   // Loop until we have converted the entire number. Output at least one
   // character (i.e. '0').
@@ -384,7 +393,8 @@
       }
     } else {
       started = true;
-      Out((upcase ? kUpCaseHexDigits : kDownCaseHexDigits)[num%base + minint]);
+      Out((upcase ? kUpCaseHexDigits
+                  : kDownCaseHexDigits)[num % base + minint]);
     }
 
     minint = 0;
@@ -457,13 +467,14 @@
         // character from a space ' ' to a zero '0'.
         pad = ch == '0' ? '0' : ' ';
         for (;;) {
+          const size_t digit = static_cast<size_t>(ch - '0');
           // The maximum allowed padding fills all the available address
           // space and leaves just enough space to insert the trailing NUL.
           const size_t max_padding = kSSizeMax - 1;
-          if (padding > max_padding/10 ||
-              10*padding > max_padding - (ch - '0')) {
-            DEBUG_CHECK(padding <= max_padding/10 &&
-                        10*padding <= max_padding - (ch - '0'));
+          if (padding > max_padding / 10 ||
+              10 * padding > max_padding - digit) {
+            DEBUG_CHECK(padding <= max_padding / 10 &&
+                        10 * padding <= max_padding - digit);
             // Integer overflow detected. Skip the rest of the width until
             // we find the format character, then do the normal error handling.
           padding_overflow:
@@ -475,7 +486,7 @@
             }
             goto fail_to_expand;
           }
-          padding = 10*padding + ch - '0';
+          padding = 10 * padding + digit;
           if (padding > max_padding) {
             // This doesn't happen for "sane" values of kSSizeMax. But once
             // kSSizeMax gets smaller than about 10, our earlier range checks
@@ -552,9 +563,9 @@
         } else {
           // Pointer values require an actual pointer or a string.
           if (arg.type == Arg::POINTER) {
-            i = reinterpret_cast<uintptr_t>(arg.ptr);
+            i = reinterpret_cast<intptr_t>(arg.ptr);
           } else if (arg.type == Arg::STRING) {
-            i = reinterpret_cast<uintptr_t>(arg.str);
+            i = reinterpret_cast<intptr_t>(arg.str);
           } else if (arg.type == Arg::INT &&
                      arg.integer.width == sizeof(NULL) &&
                      arg.integer.i == 0) {  // Allow C++'s version of NULL
diff --git a/base/strings/string_piece.cc b/base/strings/string_piece.cc
index 95170da..28aabe4 100644
--- a/base/strings/string_piece.cc
+++ b/base/strings/string_piece.cc
@@ -129,7 +129,7 @@
                                          s.begin(), s.end());
   if (found == self.end())
     return BasicStringPiece<CharT>::npos;
-  return found - self.begin();
+  return static_cast<size_t>(found - self.begin());
 }
 
 size_t find_first_of(StringPiece16 self, StringPiece16 s, size_t pos) {
diff --git a/base/strings/string_util.cc b/base/strings/string_util.cc
index 3bbc6e9..26a3b36 100644
--- a/base/strings/string_util.cc
+++ b/base/strings/string_util.cc
@@ -174,7 +174,8 @@
   while (char_index >= 0) {
     int32_t prev = char_index;
     base_icu::UChar32 code_point = 0;
-    CBU8_NEXT(data, char_index, truncation_length, code_point);
+    CBU8_NEXT(reinterpret_cast<const uint8_t*>(data), char_index,
+              truncation_length, code_point);
     if (!IsValidCharacter(code_point)) {
       char_index = prev - 1;
     } else {
@@ -183,7 +184,7 @@
   }
 
   if (char_index >= 0 )
-    *output = input.substr(0, char_index);
+    *output = input.substr(0, static_cast<size_t>(char_index));
   else
     output->clear();
 }
diff --git a/base/strings/stringprintf.cc b/base/strings/stringprintf.cc
index bcace27..aee227c6 100644
--- a/base/strings/stringprintf.cc
+++ b/base/strings/stringprintf.cc
@@ -65,14 +65,14 @@
   int result = vsnprintfT(stack_buf, std::size(stack_buf), format, ap_copy);
   va_end(ap_copy);
 
-  if (result >= 0 && result < static_cast<int>(std::size(stack_buf))) {
+  if (result >= 0 && static_cast<size_t>(result) < std::size(stack_buf)) {
     // It fit.
-    dst->append(stack_buf, result);
+    dst->append(stack_buf, static_cast<size_t>(result));
     return;
   }
 
   // Repeatedly increase buffer size until it fits.
-  int mem_length = std::size(stack_buf);
+  size_t mem_length = std::size(stack_buf);
   while (true) {
     if (result < 0) {
 #if BUILDFLAG(IS_WIN)
@@ -88,7 +88,7 @@
 #endif
     } else {
       // We need exactly "result + 1" characters.
-      mem_length = result + 1;
+      mem_length = static_cast<size_t>(result) + 1;
     }
 
     if (mem_length > 32 * 1024 * 1024) {
@@ -107,9 +107,9 @@
     result = vsnprintfT(&mem_buf[0], mem_length, format, ap_copy);
     va_end(ap_copy);
 
-    if ((result >= 0) && (result < mem_length)) {
+    if ((result >= 0) && (static_cast<size_t>(result) < mem_length)) {
       // It fit.
-      dst->append(&mem_buf[0], result);
+      dst->append(&mem_buf[0], static_cast<size_t>(result));
       return;
     }
   }
diff --git a/base/strings/sys_string_conversions_win.cc b/base/strings/sys_string_conversions_win.cc
index 356064f..340b25d 100644
--- a/base/strings/sys_string_conversions_win.cc
+++ b/base/strings/sys_string_conversions_win.cc
@@ -42,7 +42,7 @@
     return std::wstring();
 
   std::wstring wide;
-  wide.resize(charcount);
+  wide.resize(static_cast<size_t>(charcount));
   MultiByteToWideChar(code_page, 0, mb.data(), mb_length, &wide[0], charcount);
 
   return wide;
@@ -61,7 +61,7 @@
     return std::string();
 
   std::string mb;
-  mb.resize(charcount);
+  mb.resize(static_cast<size_t>(charcount));
   WideCharToMultiByte(code_page, 0, wide.data(), wide_length,
                       &mb[0], charcount, NULL, NULL);
 
diff --git a/base/strings/utf_offset_string_conversions.cc b/base/strings/utf_offset_string_conversions.cc
index f243e58..b4eb0e8f 100644
--- a/base/strings/utf_offset_string_conversions.cc
+++ b/base/strings/utf_offset_string_conversions.cc
@@ -39,7 +39,8 @@
   DCHECK(offset);
   if (*offset == std::u16string::npos)
     return;
-  int adjustment = 0;
+  size_t original_lengths = 0;
+  size_t output_lengths = 0;
   for (const auto& i : adjustments) {
     if (*offset <= i.original_offset)
       break;
@@ -47,9 +48,10 @@
       *offset = std::u16string::npos;
       return;
     }
-    adjustment += static_cast<int>(i.original_length - i.output_length);
+    original_lengths += i.original_length;
+    output_lengths += i.output_length;
   }
-  *offset -= adjustment;
+  *offset += output_lengths - original_lengths;
 
   if (*offset > limit)
     *offset = std::u16string::npos;
@@ -70,17 +72,20 @@
                                     size_t* offset) {
   if (*offset == std::u16string::npos)
     return;
-  int adjustment = 0;
+  size_t original_lengths = 0;
+  size_t output_lengths = 0;
   for (const auto& i : adjustments) {
-    if (*offset + adjustment <= i.original_offset)
+    if (*offset + original_lengths - output_lengths <= i.original_offset)
       break;
-    adjustment += static_cast<int>(i.original_length - i.output_length);
-    if ((*offset + adjustment) < (i.original_offset + i.original_length)) {
+    original_lengths += i.original_length;
+    output_lengths += i.output_length;
+    if ((*offset + original_lengths - output_lengths) <
+        (i.original_offset + i.original_length)) {
       *offset = std::u16string::npos;
       return;
     }
   }
-  *offset += adjustment;
+  *offset += original_lengths - output_lengths;
 }
 
 // static
@@ -149,15 +154,15 @@
       //   <=
       //   adjusted_iter->original_offset + shift +
       //       adjusted_iter->original_length
-
       // Modify the current |adjusted_iter| to include whatever collapsing
       // happened in |first_iter|, then advance to the next |first_adjustments|
       // because we dealt with the current one.
-      const int collapse = static_cast<int>(first_iter->original_length) -
-          static_cast<int>(first_iter->output_length);
+
       // This function does not know how to deal with a string that expands and
       // then gets modified, only strings that collapse and then get modified.
-      DCHECK_GT(collapse, 0);
+      DCHECK_GT(first_iter->original_length, first_iter->output_length);
+      const size_t collapse =
+          first_iter->original_length - first_iter->output_length;
       adjusted_iter->original_length += collapse;
       currently_collapsing += collapse;
       ++first_iter;
@@ -188,14 +193,12 @@
                     OffsetAdjuster::Adjustments* adjustments) {
   if (adjustments)
     adjustments->clear();
-  // ICU requires 32-bit numbers.
   bool success = true;
-  int32_t src_len32 = static_cast<int32_t>(src_len);
-  for (int32_t i = 0; i < src_len32; i++) {
+  for (size_t i = 0; i < src_len; i++) {
     base_icu::UChar32 code_point;
     size_t original_i = i;
     size_t chars_written = 0;
-    if (ReadUnicodeCharacter(src, src_len32, &i, &code_point)) {
+    if (ReadUnicodeCharacter(src, src_len, &i, &code_point)) {
       chars_written = WriteUnicodeCharacter(code_point, output);
     } else {
       chars_written = WriteUnicodeCharacter(0xFFFD, output);
diff --git a/base/strings/utf_string_conversion_utils.cc b/base/strings/utf_string_conversion_utils.cc
index c530637..4b900bd 100644
--- a/base/strings/utf_string_conversion_utils.cc
+++ b/base/strings/utf_string_conversion_utils.cc
@@ -12,11 +12,12 @@
 // ReadUnicodeCharacter --------------------------------------------------------
 
 bool ReadUnicodeCharacter(const char* src,
-                          int32_t src_len,
-                          int32_t* char_index,
+                          size_t src_len,
+                          size_t* char_index,
                           base_icu::UChar32* code_point_out) {
   base_icu::UChar32 code_point;
-  CBU8_NEXT(src, *char_index, src_len, code_point);
+  CBU8_NEXT(reinterpret_cast<const uint8_t*>(src), *char_index, src_len,
+            code_point);
   *code_point_out = code_point;
 
   // The ICU macro above moves to the next char, we want to point to the last
@@ -28,13 +29,12 @@
 }
 
 bool ReadUnicodeCharacter(const char16_t* src,
-                          int32_t src_len,
-                          int32_t* char_index,
+                          size_t src_len,
+                          size_t* char_index,
                           base_icu::UChar32* code_point) {
   if (CBU16_IS_SURROGATE(src[*char_index])) {
-    if (!CBU16_IS_SURROGATE_LEAD(src[*char_index]) ||
-        *char_index + 1 >= src_len ||
-        !CBU16_IS_TRAIL(src[*char_index + 1])) {
+    if (!CBU16_IS_SURROGATE_LEAD(src[*char_index]) || !src_len ||
+        *char_index >= src_len - 1 || !CBU16_IS_TRAIL(src[*char_index + 1])) {
       // Invalid surrogate pair.
       return false;
     }
@@ -53,8 +53,8 @@
 
 #if defined(WCHAR_T_IS_UTF32)
 bool ReadUnicodeCharacter(const wchar_t* src,
-                          int32_t src_len,
-                          int32_t* char_index,
+                          size_t src_len,
+                          size_t* char_index,
                           base_icu::UChar32* code_point) {
   // Conversion is easy since the source is 32-bit.
   *code_point = src[*char_index];
@@ -66,20 +66,21 @@
 
 // WriteUnicodeCharacter -------------------------------------------------------
 
-size_t WriteUnicodeCharacter(uint32_t code_point, std::string* output) {
-  if (code_point <= 0x7f) {
+size_t WriteUnicodeCharacter(base_icu::UChar32 code_point,
+                             std::string* output) {
+  if (code_point >= 0 && code_point <= 0x7f) {
     // Fast path the common case of one byte.
     output->push_back(static_cast<char>(code_point));
     return 1;
   }
 
-
   // CBU8_APPEND_UNSAFE can append up to 4 bytes.
   size_t char_offset = output->length();
   size_t original_char_offset = char_offset;
   output->resize(char_offset + CBU8_MAX_LENGTH);
 
-  CBU8_APPEND_UNSAFE(&(*output)[0], char_offset, code_point);
+  CBU8_APPEND_UNSAFE(reinterpret_cast<uint8_t*>(output->data()), char_offset,
+                     code_point);
 
   // CBU8_APPEND_UNSAFE will advance our pointer past the inserted character, so
   // it will represent the new length of the string.
@@ -87,9 +88,10 @@
   return char_offset - original_char_offset;
 }
 
-size_t WriteUnicodeCharacter(uint32_t code_point, std::u16string* output) {
+size_t WriteUnicodeCharacter(base_icu::UChar32 code_point,
+                             std::u16string* output) {
   if (CBU16_LENGTH(code_point) == 1) {
-    // Thie code point is in the Basic Multilingual Plane (BMP).
+    // The code point is in the Basic Multilingual Plane (BMP).
     output->push_back(static_cast<char16_t>(code_point));
     return 1;
   }
diff --git a/base/strings/utf_string_conversion_utils.h b/base/strings/utf_string_conversion_utils.h
index 2ca9262..1f6bc0e 100644
--- a/base/strings/utf_string_conversion_utils.h
+++ b/base/strings/utf_string_conversion_utils.h
@@ -49,21 +49,21 @@
 //
 // Returns true on success. On false, |*code_point| will be invalid.
 BASE_EXPORT bool ReadUnicodeCharacter(const char* src,
-                                      int32_t src_len,
-                                      int32_t* char_index,
+                                      size_t src_len,
+                                      size_t* char_index,
                                       base_icu::UChar32* code_point_out);
 
 // Reads a UTF-16 character. The usage is the same as the 8-bit version above.
 BASE_EXPORT bool ReadUnicodeCharacter(const char16_t* src,
-                                      int32_t src_len,
-                                      int32_t* char_index,
+                                      size_t src_len,
+                                      size_t* char_index,
                                       base_icu::UChar32* code_point);
 
 #if defined(WCHAR_T_IS_UTF32)
 // Reads UTF-32 character. The usage is the same as the 8-bit version above.
 BASE_EXPORT bool ReadUnicodeCharacter(const wchar_t* src,
-                                      int32_t src_len,
-                                      int32_t* char_index,
+                                      size_t src_len,
+                                      size_t* char_index,
                                       base_icu::UChar32* code_point);
 #endif  // defined(WCHAR_T_IS_UTF32)
 
@@ -71,20 +71,21 @@
 
 // Appends a UTF-8 character to the given 8-bit string.  Returns the number of
 // bytes written.
-BASE_EXPORT size_t WriteUnicodeCharacter(uint32_t code_point,
+BASE_EXPORT size_t WriteUnicodeCharacter(base_icu::UChar32 code_point,
                                          std::string* output);
 
 // Appends the given code point as a UTF-16 character to the given 16-bit
 // string.  Returns the number of 16-bit values written.
-BASE_EXPORT size_t WriteUnicodeCharacter(uint32_t code_point,
+BASE_EXPORT size_t WriteUnicodeCharacter(base_icu::UChar32 code_point,
                                          std::u16string* output);
 
 #if defined(WCHAR_T_IS_UTF32)
 // Appends the given UTF-32 character to the given 32-bit string.  Returns the
 // number of 32-bit values written.
-inline size_t WriteUnicodeCharacter(uint32_t code_point, std::wstring* output) {
+inline size_t WriteUnicodeCharacter(base_icu::UChar32 code_point,
+                                    std::wstring* output) {
   // This is the easy case, just append the character.
-  output->push_back(code_point);
+  output->push_back(static_cast<wchar_t>(code_point));
   return 1;
 }
 #endif  // defined(WCHAR_T_IS_UTF32)
diff --git a/base/test/launcher/unit_test_launcher.cc b/base/test/launcher/unit_test_launcher.cc
index 63d4600..ebb815c 100644
--- a/base/test/launcher/unit_test_launcher.cc
+++ b/base/test/launcher/unit_test_launcher.cc
@@ -38,6 +38,10 @@
 #include "base/files/file_descriptor_watcher_posix.h"
 #endif
 
+#if BUILDFLAG(IS_WIN)
+#include "base/debug/handle_hooks_win.h"
+#endif
+
 namespace base {
 
 namespace {
@@ -166,6 +170,9 @@
       force_single_process = true;
     }
   }
+#if BUILDFLAG(IS_WIN)
+  base::debug::HandleHooks::PatchLoadedModules();
+#endif  // BUILDFLAG(IS_WIN)
 
   if (CommandLine::ForCurrentProcess()->HasSwitch(kGTestHelpFlag) ||
       CommandLine::ForCurrentProcess()->HasSwitch(kGTestListTestsFlag) ||
diff --git a/base/third_party/icu/icu_utf.h b/base/third_party/icu/icu_utf.h
index 16792c4..6fa41010 100644
--- a/base/third_party/icu/icu_utf.h
+++ b/base/third_party/icu/icu_utf.h
@@ -278,25 +278,27 @@
  * @see U8_APPEND
  * @stable ICU 2.4
  */
-#define CBU8_APPEND_UNSAFE(s, i, c) CBUPRV_BLOCK_MACRO_BEGIN { \
-    uint32_t __uc=(c); \
-    if(__uc<=0x7f) { \
-        (s)[(i)++]=(uint8_t)__uc; \
-    } else { \
-        if(__uc<=0x7ff) { \
-            (s)[(i)++]=(uint8_t)((__uc>>6)|0xc0); \
-        } else { \
-            if(__uc<=0xffff) { \
-                (s)[(i)++]=(uint8_t)((__uc>>12)|0xe0); \
-            } else { \
-                (s)[(i)++]=(uint8_t)((__uc>>18)|0xf0); \
-                (s)[(i)++]=(uint8_t)(((__uc>>12)&0x3f)|0x80); \
-            } \
-            (s)[(i)++]=(uint8_t)(((__uc>>6)&0x3f)|0x80); \
-        } \
-        (s)[(i)++]=(uint8_t)((__uc&0x3f)|0x80); \
-    } \
-} CBUPRV_BLOCK_MACRO_END
+#define CBU8_APPEND_UNSAFE(s, i, c)                             \
+  CBUPRV_BLOCK_MACRO_BEGIN {                                    \
+    uint32_t __uc = (uint32_t)(c);                              \
+    if (__uc <= 0x7f) {                                         \
+      (s)[(i)++] = (uint8_t)__uc;                               \
+    } else {                                                    \
+      if (__uc <= 0x7ff) {                                      \
+        (s)[(i)++] = (uint8_t)((__uc >> 6) | 0xc0);             \
+      } else {                                                  \
+        if (__uc <= 0xffff) {                                   \
+          (s)[(i)++] = (uint8_t)((__uc >> 12) | 0xe0);          \
+        } else {                                                \
+          (s)[(i)++] = (uint8_t)((__uc >> 18) | 0xf0);          \
+          (s)[(i)++] = (uint8_t)(((__uc >> 12) & 0x3f) | 0x80); \
+        }                                                       \
+        (s)[(i)++] = (uint8_t)(((__uc >> 6) & 0x3f) | 0x80);    \
+      }                                                         \
+      (s)[(i)++] = (uint8_t)((__uc & 0x3f) | 0x80);             \
+    }                                                           \
+  }                                                             \
+  CBUPRV_BLOCK_MACRO_END
 
 // source/common/unicode/utf16.h
 
@@ -314,7 +316,7 @@
  * @return TRUE or FALSE
  * @stable ICU 2.4
  */
-#define CBU16_IS_LEAD(c) (((c)&0xfffffc00)==0xd800)
+#define CBU16_IS_LEAD(c) (((uint32_t)(c)&0xfffffc00) == 0xd800)
 
 /**
  * Is this code unit a trail surrogate (U+dc00..U+dfff)?
@@ -322,7 +324,7 @@
  * @return TRUE or FALSE
  * @stable ICU 2.4
  */
-#define CBU16_IS_TRAIL(c) (((c)&0xfffffc00)==0xdc00)
+#define CBU16_IS_TRAIL(c) (((uint32_t)(c)&0xfffffc00) == 0xdc00)
 
 /**
  * Is this code unit a surrogate (U+d800..U+dfff)?
diff --git a/base/trace_event/memory_infra_background_allowlist.cc b/base/trace_event/memory_infra_background_allowlist.cc
index 43b482d..b20ce7d1 100644
--- a/base/trace_event/memory_infra_background_allowlist.cc
+++ b/base/trace_event/memory_infra_background_allowlist.cc
@@ -287,6 +287,7 @@
     "sync/0x?/model_type/PASSWORD",
     "sync/0x?/model_type/PREFERENCE",
     "sync/0x?/model_type/PRINTER",
+    "sync/0x?/model_type/PRINTERS_AUTHORIZATION_SERVER",
     "sync/0x?/model_type/PRIORITY_PREFERENCE",
     "sync/0x?/model_type/READING_LIST",
     "sync/0x?/model_type/SEARCH_ENGINE",
diff --git a/base/win/scoped_handle_unittest.cc b/base/win/scoped_handle_unittest.cc
index fa1a073..1663f2c 100644
--- a/base/win/scoped_handle_unittest.cc
+++ b/base/win/scoped_handle_unittest.cc
@@ -11,7 +11,6 @@
 
 #include "base/base_switches.h"
 #include "base/command_line.h"
-#include "base/debug/handle_hooks_win.h"
 #include "base/files/file_path.h"
 #include "base/scoped_native_library.h"
 #include "base/test/multiprocess_test.h"
@@ -19,7 +18,6 @@
 #include "base/win/scoped_handle.h"
 #include "base/win/windows_version.h"
 #include "build/build_config.h"
-
 #include "testing/gtest/include/gtest/gtest.h"
 #include "testing/multiprocess_func_list.h"
 
@@ -38,36 +36,8 @@
 #endif  // !defined(DEBUG) && defined(OFFICIAL_BUILD)
 }
 
-}  // namespace
-
-namespace testing {
-extern "C" bool __declspec(dllexport) RunTest();
-}  // namespace testing
-
-class ScopedHandleTest : public ::testing::Test,
-                         public ::testing::WithParamInterface<bool> {
- public:
-  ScopedHandleTest(const ScopedHandleTest&) = delete;
-  ScopedHandleTest& operator=(const ScopedHandleTest&) = delete;
-
- protected:
-  ScopedHandleTest() {
-    if (HooksEnabled()) {
-#if defined(ARCH_CPU_32_BITS)
-      // EAT patch is only supported on 32-bit.
-      base::debug::HandleHooks::AddEATPatch();
-#endif
-      base::debug::HandleHooks::PatchLoadedModules();
-    }
-  }
-
-  static bool HooksEnabled() { return GetParam(); }
-  static bool DoDeathTestsWork() {
-    // Death tests don't seem to work on Windows 7 32-bit native with hooks
-    // enabled.
-    // TODO(crbug.com/1328022): Investigate why.
-    if (!HooksEnabled())
-      return true;
+// Death tests don't seem to work on Windows 7 32-bit native with hooks enabled.
+bool DoDeathTestsWork() {
 #if defined(ARCH_CPU_32_BITS)
     const auto* os_info = base::win::OSInfo::GetInstance();
     if (os_info->version() <= base::win::Version::WIN7 &&
@@ -76,12 +46,18 @@
     }
 #endif  // defined(ARCH_CPU_32_BITS)
     return true;
-  }
-};
+}
 
-using ScopedHandleDeathTest = ScopedHandleTest;
+}  // namespace
 
-TEST_P(ScopedHandleTest, ScopedHandle) {
+namespace testing {
+extern "C" bool __declspec(dllexport) RunTest();
+}  // namespace testing
+
+using ScopedHandleTest = ::testing::Test;
+using ScopedHandleDeathTest = ::testing::Test;
+
+TEST_F(ScopedHandleTest, ScopedHandle) {
   // Any illegal error code will do. We just need to test that it is preserved
   // by ScopedHandle to avoid https://crbug.com/528394.
   const DWORD magic_error = 0x12345678;
@@ -106,7 +82,10 @@
   EXPECT_EQ(magic_error, ::GetLastError());
 }
 
-TEST_P(ScopedHandleDeathTest, HandleVerifierTrackedHasBeenClosed) {
+TEST_F(ScopedHandleDeathTest, HandleVerifierTrackedHasBeenClosed) {
+  // This test is only valid if hooks are enabled.
+  if (!DoDeathTestsWork())
+    return;
   HANDLE handle = ::CreateMutex(nullptr, false, nullptr);
   ASSERT_NE(HANDLE(nullptr), handle);
   using NtCloseFunc = decltype(&::NtClose);
@@ -124,10 +103,8 @@
       FailureMessage("CloseHandle failed"));
 }
 
-TEST_P(ScopedHandleDeathTest, HandleVerifierCloseTrackedHandle) {
+TEST_F(ScopedHandleDeathTest, HandleVerifierCloseTrackedHandle) {
   // This test is only valid if hooks are enabled.
-  if (!HooksEnabled())
-    return;
   if (!DoDeathTestsWork())
     return;
 
@@ -152,7 +129,7 @@
       FailureMessage("CloseHandleHook validation failure"));
 }
 
-TEST_P(ScopedHandleDeathTest, HandleVerifierDoubleTracking) {
+TEST_F(ScopedHandleDeathTest, HandleVerifierDoubleTracking) {
   if (!DoDeathTestsWork())
     return;
 
@@ -165,7 +142,7 @@
                FailureMessage("Handle Already Tracked"));
 }
 
-TEST_P(ScopedHandleDeathTest, HandleVerifierWrongOwner) {
+TEST_F(ScopedHandleDeathTest, HandleVerifierWrongOwner) {
   if (!DoDeathTestsWork())
     return;
 
@@ -183,7 +160,7 @@
   handle_holder.Close();
 }
 
-TEST_P(ScopedHandleDeathTest, HandleVerifierUntrackedHandle) {
+TEST_F(ScopedHandleDeathTest, HandleVerifierUntrackedHandle) {
   if (!DoDeathTestsWork())
     return;
 
@@ -208,7 +185,7 @@
 #define MAYBE_MultiProcess MultiProcess
 #endif
 
-TEST_P(ScopedHandleTest, MAYBE_MultiProcess) {
+TEST_F(ScopedHandleTest, MAYBE_MultiProcess) {
   // Initializing ICU in the child process causes a scoped handle to be created
   // before the test gets a chance to test the race condition, so disable ICU
   // for the child process here.
@@ -240,18 +217,5 @@
   return 0;
 }
 
-INSTANTIATE_TEST_SUITE_P(HooksEnabled,
-                         ScopedHandleTest,
-                         ::testing::Values(true));
-INSTANTIATE_TEST_SUITE_P(HooksDisabled,
-                         ScopedHandleTest,
-                         ::testing::Values(false));
-INSTANTIATE_TEST_SUITE_P(HooksEnabled,
-                         ScopedHandleDeathTest,
-                         ::testing::Values(true));
-INSTANTIATE_TEST_SUITE_P(HooksDisabled,
-                         ScopedHandleDeathTest,
-                         ::testing::Values(false));
-
 }  // namespace win
 }  // namespace base
diff --git a/build/android/gyp/util/build_utils.py b/build/android/gyp/util/build_utils.py
index 7cb6451..839cb7d6 100644
--- a/build/android/gyp/util/build_utils.py
+++ b/build/android/gyp/util/build_utils.py
@@ -14,7 +14,6 @@
 import os
 import pipes
 import re
-import shlex
 import shutil
 import stat
 import subprocess
@@ -281,26 +280,18 @@
 
   has_stdout = print_stdout and stdout
   has_stderr = print_stderr and stderr
-  if has_stdout or has_stderr:
+  if fail_on_output and (has_stdout or has_stderr):
+    MSG = """\
+Command failed because it wrote to {}.
+You can often set treat_warnings_as_errors=false to not treat output as \
+failure (useful when developing locally)."""
     if has_stdout and has_stderr:
       stream_string = 'stdout and stderr'
     elif has_stdout:
       stream_string = 'stdout'
     else:
       stream_string = 'stderr'
-
-    if fail_on_output:
-      MSG = """
-Command failed because it wrote to {}.
-You can often set treat_warnings_as_errors=false to not treat output as \
-failure (useful when developing locally)."""
-      raise CalledProcessError(cwd, args, MSG.format(stream_string))
-
-    MSG = """
-The above {} output was from:
-{}
-"""
-    sys.stderr.write(MSG.format(stream_string, shlex.join(args)))
+    raise CalledProcessError(cwd, args, MSG.format(stream_string))
 
   return stdout
 
diff --git a/build/config/android/test/resource_overlay/BUILD.gn b/build/config/android/test/resource_overlay/BUILD.gn
index 4a063d22..4cf18ef 100644
--- a/build/config/android/test/resource_overlay/BUILD.gn
+++ b/build/config/android/test/resource_overlay/BUILD.gn
@@ -43,7 +43,7 @@
   deps = [ ":root_tagged_dependency_resources" ]
 }
 
-android_library("javatests") {
+android_library("unit_device_javatests") {
   testonly = true
   sources = [
     "java/src/org/chromium/build/resource_overlay/ResourceOverlayTest.java",
diff --git a/cc/paint/paint_image.cc b/cc/paint/paint_image.cc
index ed3a8f5..d8a27e3 100644
--- a/cc/paint/paint_image.cc
+++ b/cc/paint/paint_image.cc
@@ -71,6 +71,8 @@
     return false;
   if (paint_worklet_input_ != other.paint_worklet_input_)
     return false;
+  // Do not check may_be_lcp_candidate_ as it should not affect any rendering
+  // operation, only metrics collection.
   return true;
 }
 
@@ -397,6 +399,7 @@
       << " animation_type_: " << static_cast<int>(animation_type_)
       << " completion_state_: " << static_cast<int>(completion_state_)
       << " is_multipart_: " << is_multipart_
+      << " may_be_lcp_candidate_: " << may_be_lcp_candidate_
       << " is YUV: " << IsYuv(SkYUVAPixmapInfo::SupportedDataTypes::All());
   return str.str();
 }
diff --git a/cc/paint/paint_image.h b/cc/paint/paint_image.h
index 32cbb7f..859f448 100644
--- a/cc/paint/paint_image.h
+++ b/cc/paint/paint_image.h
@@ -264,6 +264,7 @@
   CompletionState completion_state() const { return completion_state_; }
   bool is_multipart() const { return is_multipart_; }
   bool is_high_bit_depth() const { return is_high_bit_depth_; }
+  bool may_be_lcp_candidate() const { return may_be_lcp_candidate_; }
   int repetition_count() const { return repetition_count_; }
   bool ShouldAnimate() const;
   AnimationSequenceId reset_animation_sequence_id() const {
@@ -389,6 +390,12 @@
   // Whether this image has more than 8 bits per color channel.
   bool is_high_bit_depth_ = false;
 
+  // Whether this image may untimately be a candidate for Largest Contentful
+  // Paint. The final LCP contribution of an image is unknown until we present
+  // it, but this flag is intended for metrics on when we do not present the
+  // image when the system claims.
+  bool may_be_lcp_candidate_ = false;
+
   // An incrementing sequence number maintained by the painter to indicate if
   // this animation should be reset in the compositor. Incrementing this number
   // will reset this animation in the compositor for the first frame which has a
diff --git a/cc/paint/paint_image_builder.h b/cc/paint/paint_image_builder.h
index 008d0fa5..f75c3ff 100644
--- a/cc/paint/paint_image_builder.h
+++ b/cc/paint/paint_image_builder.h
@@ -87,6 +87,10 @@
     paint_image_.is_high_bit_depth_ = is_high_bit_depth;
     return std::move(*this);
   }
+  PaintImageBuilder&& set_may_be_lcp_candidate(bool may_be_lcp_candidate) {
+    paint_image_.may_be_lcp_candidate_ = may_be_lcp_candidate;
+    return std::move(*this);
+  }
   PaintImageBuilder&& set_repetition_count(int count) {
     paint_image_.repetition_count_ = count;
     return std::move(*this);
diff --git a/cc/trees/property_tree.cc b/cc/trees/property_tree.cc
index 76bbece9..4e8eaad0 100644
--- a/cc/trees/property_tree.cc
+++ b/cc/trees/property_tree.cc
@@ -475,7 +475,7 @@
       gfx::ScaleVector2d(overscroll_offset, 1.f / page_scale_factor());
 
   ClipTree& clip_tree = property_trees()->clip_tree_mutable();
-  ClipNode* clip_node = clip_tree.Node(overscroll_node_id_);
+  ClipNode* clip_node = clip_tree.Node(clip_tree.overscroll_node_id());
 
   if (clip_node) {
     // Inflate the clip rect based on the overscroll direction.
@@ -1265,7 +1265,8 @@
 }
 
 ClipTree::ClipTree(PropertyTrees* property_trees)
-    : PropertyTree<ClipNode>(property_trees) {}
+    : PropertyTree<ClipNode>(property_trees),
+      overscroll_node_id_(kInvalidPropertyNodeId) {}
 
 void ClipTree::SetViewportClip(gfx::RectF viewport_rect) {
   if (size() < 2)
@@ -1285,7 +1286,8 @@
 
 #if DCHECK_IS_ON()
 bool ClipTree::operator==(const ClipTree& other) const {
-  return PropertyTree::operator==(other);
+  return PropertyTree::operator==(other) &&
+         overscroll_node_id_ == other.overscroll_node_id();
 }
 #endif
 
diff --git a/cc/trees/property_tree.h b/cc/trees/property_tree.h
index 352c3b5..12ecb6e 100644
--- a/cc/trees/property_tree.h
+++ b/cc/trees/property_tree.h
@@ -333,6 +333,14 @@
 
   void SetViewportClip(gfx::RectF viewport_rect);
   gfx::RectF ViewportClip() const;
+
+  void set_overscroll_node_id(int id) { overscroll_node_id_ = id; }
+  int overscroll_node_id() const { return overscroll_node_id_; }
+
+ private:
+  // Used to track the ClipNode that is corresponding to the overscroll
+  // TransformNode.
+  int overscroll_node_id_;
 };
 
 class CC_EXPORT EffectTree final : public PropertyTree<EffectNode> {
diff --git a/cc/trees/property_tree_unittest.cc b/cc/trees/property_tree_unittest.cc
index 53b242c..b2ec896d 100644
--- a/cc/trees/property_tree_unittest.cc
+++ b/cc/trees/property_tree_unittest.cc
@@ -203,18 +203,13 @@
   PropertyTrees property_trees(synchronizer);
 
   ClipTree& clip_tree = property_trees.clip_tree_mutable();
-  ClipNode root_clip;
-  root_clip.id = 1;
-  root_clip.parent_id = 0;
-  root_clip.clip = gfx::RectF(0, 0, 500, 500);
-  clip_tree.Insert(root_clip, 0);
-
   const gfx::RectF clip_rect(0, 0, 100, 100);
-  ClipNode overscroll_clip;
-  overscroll_clip.id = 2;
-  overscroll_clip.parent_id = 1;
-  overscroll_clip.clip = clip_rect;
-  clip_tree.Insert(overscroll_clip, 1);
+  ClipNode clip_node;
+  clip_node.id = 1;
+  clip_node.parent_id = 0;
+  clip_node.clip = clip_rect;
+  clip_tree.Insert(clip_node, 0);
+  clip_tree.set_overscroll_node_id(clip_node.id);
 
   TransformTree& transform_tree = property_trees.transform_tree_mutable();
   TransformNode contents_root;
@@ -245,7 +240,8 @@
 
   gfx::RectF expected_clip_rect(clip_rect);
   expected_clip_rect.set_height(clip_rect.height() + overscroll_offset.y());
-  EXPECT_EQ(clip_tree.Node(overscroll_node.id)->clip, expected_clip_rect);
+  EXPECT_EQ(clip_tree.Node(clip_tree.overscroll_node_id())->clip,
+            expected_clip_rect);
 }
 
 TEST(PropertyTreeTest, TransformsWithFlattening) {
diff --git a/chrome/android/BUILD.gn b/chrome/android/BUILD.gn
index 29493cb9..775733e 100644
--- a/chrome/android/BUILD.gn
+++ b/chrome/android/BUILD.gn
@@ -1109,7 +1109,6 @@
     "//components/commerce/core:proto_java",
     "//components/content_capture/android:java",
     "//components/content_settings/android:content_settings_enums_java",
-    "//components/crash/android:junit",
     "//components/digital_goods/mojom:mojom_java",
     "//components/dom_distiller/core/android:dom_distiller_core_java",
     "//components/embedder_support/android:content_view_java",
@@ -1366,8 +1365,6 @@
     "//components/security_state/core:security_state_enums_java",
     "//components/signin/public/android:java",
     "//components/signin/public/android:signin_java_test_support",
-    "//components/strictmode/android:javatests",
-    "//components/url_formatter/android:url_formatter_javatests",
     "//content/public/android:content_full_java",
     "//content/public/test/android:content_java_test_support",
     "//third_party/android_deps:espresso_java",
@@ -1549,7 +1546,6 @@
     "//components/autofill_assistant/android:public_java",
     "//components/background_task_scheduler:background_task_scheduler_java",
     "//components/background_task_scheduler:background_task_scheduler_task_ids_java",
-    "//components/background_task_scheduler/internal:background_task_scheduler_javatests",
     "//components/bookmarks/common/android:bookmarks_java",
     "//components/browser_ui/accessibility/android:java",
     "//components/browser_ui/bottomsheet/android:java",
@@ -1578,7 +1574,6 @@
     "//components/content_settings/android:content_settings_enums_java",
     "//components/content_settings/android:java",
     "//components/crash/android:java",
-    "//components/crash/android:javatests",
     "//components/digital_goods/mojom:mojom_java",
     "//components/dom_distiller/core/android:dom_distiller_core_java",
     "//components/dom_distiller/core/mojom:mojom_java",
@@ -3149,19 +3144,21 @@
   shared_libraries = [ ":libchromefortest" ]
   deps = [
     ":chrome_unit_test_java",
-    "//build/config/android/test/resource_overlay:javatests",
+    "//build/config/android/test/resource_overlay:unit_device_javatests",
     "//chrome/android/features/tab_ui:unit_device_javatests",
-    "//chrome/browser/back_press/android:javatests",
+    "//chrome/browser/back_press/android:unit_device_javatests",
     "//chrome/browser/loading_modal/android:unit_device_javatests",
-    "//chrome/browser/partnercustomizations:javatests",
+    "//chrome/browser/partnercustomizations:unit_device_javatests",
     "//chrome/browser/ui/android/appmenu/internal:unit_device_javatests",
     "//chrome/browser/ui/messages/android:unit_device_javatests",
-    "//chrome/browser/user_education:javatests",
-    "//chrome/browser/video_tutorials/internal:javatests",
+    "//chrome/browser/user_education:unit_device_javatests",
+    "//chrome/browser/video_tutorials/internal:unit_device_javatests",
     "//components/browser_ui/widget/android:unit_device_javatests",
     "//components/embedder_support/android:embedder_support_javatests",
     "//components/paint_preview/player/android:javatests",
-    "//components/signin/public/android:javatests",
+    "//components/signin/public/android:unit_device_javatests",
+    "//components/strictmode/android:unit_device_javatests",
+    "//components/url_formatter/android:unit_device_javatests",
   ]
 
   data_deps = [
diff --git a/chrome/android/chrome_test_java_sources.gni b/chrome/android/chrome_test_java_sources.gni
index ca1ab4e..0d95e72 100644
--- a/chrome/android/chrome_test_java_sources.gni
+++ b/chrome/android/chrome_test_java_sources.gni
@@ -327,10 +327,8 @@
   "javatests/src/org/chromium/chrome/browser/omnibox/suggestions/OmniboxPedalsRenderTest.java",
   "javatests/src/org/chromium/chrome/browser/omnibox/suggestions/OmniboxPedalsTest.java",
   "javatests/src/org/chromium/chrome/browser/omnibox/suggestions/SwitchToTabTest.java",
-  "javatests/src/org/chromium/chrome/browser/omnibox/suggestions/answer/AnswerSuggestionProcessorUnitTest.java",
   "javatests/src/org/chromium/chrome/browser/omnibox/suggestions/base/BaseSuggestionProcessorTest.java",
   "javatests/src/org/chromium/chrome/browser/omnibox/suggestions/editurl/EditUrlSuggestionUnitTest.java",
-  "javatests/src/org/chromium/chrome/browser/omnibox/suggestions/entity/EntitySuggestionProcessorUnitTest.java",
   "javatests/src/org/chromium/chrome/browser/omnibox/suggestions/mostvisited/MostVisitedTilesTest.java",
   "javatests/src/org/chromium/chrome/browser/omnibox/voice/AssistantVoiceSearchConsentBottomSheetTest.java",
   "javatests/src/org/chromium/chrome/browser/omnibox/voice/AssistantVoiceSearchConsentControllerTest.java",
diff --git a/chrome/android/features/start_surface/javatests/src/org/chromium/chrome/features/start_surface/StartSurfaceBackButtonTest.java b/chrome/android/features/start_surface/javatests/src/org/chromium/chrome/features/start_surface/StartSurfaceBackButtonTest.java
index 13dda83..ae830628b0 100644
--- a/chrome/android/features/start_surface/javatests/src/org/chromium/chrome/features/start_surface/StartSurfaceBackButtonTest.java
+++ b/chrome/android/features/start_surface/javatests/src/org/chromium/chrome/features/start_surface/StartSurfaceBackButtonTest.java
@@ -6,9 +6,6 @@
 
 import static androidx.test.espresso.Espresso.onView;
 import static androidx.test.espresso.action.ViewActions.click;
-import static androidx.test.espresso.action.ViewActions.pressKey;
-import static androidx.test.espresso.action.ViewActions.replaceText;
-import static androidx.test.espresso.assertion.ViewAssertions.matches;
 import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed;
 import static androidx.test.espresso.matcher.ViewMatchers.withId;
 import static androidx.test.espresso.matcher.ViewMatchers.withParent;
@@ -17,7 +14,6 @@
 
 import static org.chromium.chrome.features.start_surface.StartSurfaceTestUtils.START_SURFACE_TEST_BASE_PARAMS;
 import static org.chromium.chrome.features.start_surface.StartSurfaceTestUtils.sClassParamsForStartSurfaceTest;
-import static org.chromium.ui.test.util.ViewUtils.VIEW_GONE;
 import static org.chromium.ui.test.util.ViewUtils.onViewWaiting;
 import static org.chromium.ui.test.util.ViewUtils.waitForView;
 
@@ -25,14 +21,11 @@
 import android.support.test.InstrumentationRegistry;
 import android.support.test.runner.lifecycle.ActivityLifecycleMonitorRegistry;
 import android.support.test.runner.lifecycle.Stage;
-import android.view.KeyEvent;
 import android.view.View;
 
 import androidx.test.espresso.contrib.RecyclerViewActions;
-import androidx.test.filters.LargeTest;
 import androidx.test.filters.MediumTest;
 
-import org.hamcrest.Matchers;
 import org.junit.Assert;
 import org.junit.Assume;
 import org.junit.Before;
@@ -65,7 +58,6 @@
 import org.chromium.chrome.browser.tab.Tab;
 import org.chromium.chrome.browser.tab.TabLaunchType;
 import org.chromium.chrome.browser.tabmodel.TabModel;
-import org.chromium.chrome.browser.tasks.tab_management.TabSelectionEditorTestingRobot;
 import org.chromium.chrome.browser.tasks.tab_management.TabUiTestHelper;
 import org.chromium.chrome.test.ChromeJUnit4RunnerDelegate;
 import org.chromium.chrome.test.ChromeTabbedActivityTestRule;
@@ -290,198 +282,6 @@
     @Test
     @MediumTest
     @Feature({"StartSurface"})
-    @EnableFeatures(ChromeFeatureList.TAB_GROUPS_ANDROID)
-    // clang-format off
-    @CommandLineFlags.Add({START_SURFACE_TEST_BASE_PARAMS})
-    @DisableIf.
-        Build(sdk_is_less_than = Build.VERSION_CODES.N, message = "Flaky, see crbug.com/1246457")
-    public void testShow_SingleAsHomepage_BackButtonOnCarouselTabSwitcher() {
-        // clang-format on
-        if (!mImmediateReturn) {
-            StartSurfaceTestUtils.pressHomePageButton(mActivityTestRule.getActivity());
-        }
-
-        ChromeTabbedActivity cta = mActivityTestRule.getActivity();
-        StartSurfaceTestUtils.waitForOverviewVisible(
-                mLayoutChangedCallbackHelper, mCurrentlyActiveLayout);
-        StartSurfaceTestUtils.waitForTabModel(cta);
-        TabUiTestHelper.verifyTabModelTabCount(cta, 1, 0);
-
-        onViewWaiting(withId(R.id.search_box_text)).perform(replaceText("about:blank"));
-        onView(withId(R.id.url_bar)).perform(pressKey(KeyEvent.KEYCODE_ENTER));
-        LayoutTestUtils.waitForLayout(cta.getLayoutManager(), LayoutType.BROWSING);
-        TabUiTestHelper.verifyTabModelTabCount(cta, 2, 0);
-
-        TabUiTestHelper.mergeAllNormalTabsToAGroup(cta);
-        StartSurfaceTestUtils.pressHomePageButton(cta);
-        LayoutTestUtils.waitForLayout(cta.getLayoutManager(), LayoutType.TAB_SWITCHER);
-
-        StartSurfaceTestUtils.clickFirstTabInCarousel();
-        onViewWaiting(allOf(
-                withId(org.chromium.chrome.tab_ui.R.id.dialog_container_view), isDisplayed()));
-
-        StartSurfaceTestUtils.pressBack(mActivityTestRule);
-        waitForView(withId(org.chromium.chrome.tab_ui.R.id.dialog_container_view), VIEW_GONE);
-        onView(withId(R.id.primary_tasks_surface_view)).check(matches(isDisplayed()));
-    }
-
-    @Test
-    @LargeTest
-    @Feature({"StartSurface"})
-    @EnableFeatures(ChromeFeatureList.TAB_GROUPS_ANDROID)
-    @CommandLineFlags.Add({START_SURFACE_TEST_BASE_PARAMS})
-    public void testShow_SingleAsHomepage_BackButtonOnTabSwitcherWithDialogShowing() {
-        backButtonOnTabSwitcherWithDialogShowingImpl();
-    }
-
-    @Test
-    @LargeTest
-    @Feature({"StartSurface"})
-    @EnableFeatures(ChromeFeatureList.TAB_GROUPS_ANDROID)
-    @CommandLineFlags.Add({START_SURFACE_TEST_BASE_PARAMS + "/show_last_active_tab_only/true"})
-    public void testShow_SingleAsHomepageV2_BackButtonOnTabSwitcherWithDialogShowing() {
-        backButtonOnTabSwitcherWithDialogShowingImpl();
-    }
-
-    private void backButtonOnTabSwitcherWithDialogShowingImpl() {
-        if (!mImmediateReturn) {
-            StartSurfaceTestUtils.pressHomePageButton(mActivityTestRule.getActivity());
-        }
-
-        ChromeTabbedActivity cta = mActivityTestRule.getActivity();
-        StartSurfaceTestUtils.waitForOverviewVisible(
-                mLayoutChangedCallbackHelper, mCurrentlyActiveLayout);
-        StartSurfaceTestUtils.waitForTabModel(cta);
-        onViewWaiting(withId(R.id.logo));
-
-        // Launches the first site in mv tiles.
-        StartSurfaceTestUtils.launchFirstMVTile(cta, /* currentTabCount = */ 1);
-
-        List<Tab> tabs = getTabsInCurrentTabModel(cta.getCurrentTabModel());
-        TabSelectionEditorTestingRobot robot = new TabSelectionEditorTestingRobot();
-
-        if (isInstantReturn()) {
-            // TODO(crbug.com/1076274): fix toolbar to avoid wrongly focusing on the toolbar
-            // omnibox.
-            return;
-        }
-        onViewWaiting(withId(org.chromium.chrome.tab_ui.R.id.tab_switcher_button));
-        TabUiTestHelper.enterTabSwitcher(cta);
-
-        waitForView(withId(R.id.secondary_tasks_surface_view));
-        StartSurfaceCoordinator startSurfaceCoordinator =
-                StartSurfaceTestUtils.getStartSurfaceFromUIThread(cta);
-        TestThreadUtils.runOnUiThreadBlocking(
-                () -> startSurfaceCoordinator.showTabSelectionEditorForTesting(tabs));
-        robot.resultRobot.verifyTabSelectionEditorIsVisible()
-                .verifyToolbarActionButtonDisabled()
-                .verifyToolbarActionButtonWithResourceId(
-                        org.chromium.chrome.tab_ui.R.string.tab_selection_editor_group)
-                .verifyToolbarSelectionTextWithResourceId(
-                        org.chromium.chrome.tab_ui.R.string
-                                .tab_selection_editor_toolbar_select_tabs)
-                .verifyAdapterHasItemCount(tabs.size())
-                .verifyHasAtLeastNItemVisible(2);
-
-        // Verifies that tapping the back button will close the TabSelectionEditor.
-        StartSurfaceTestUtils.pressBack(mActivityTestRule);
-        robot.resultRobot.verifyTabSelectionEditorIsHidden();
-        onViewWaiting(withId(R.id.secondary_tasks_surface_view));
-
-        // Groups the two tabs.
-        TestThreadUtils.runOnUiThreadBlocking(
-                () -> startSurfaceCoordinator.showTabSelectionEditorForTesting(tabs));
-        robot.resultRobot.verifyToolbarActionButtonWithResourceId(
-                org.chromium.chrome.tab_ui.R.string.tab_selection_editor_group);
-        robot.actionRobot.clickItemAtAdapterPosition(0)
-                .clickItemAtAdapterPosition(1)
-                .clickToolbarActionButton();
-        robot.resultRobot.verifyTabSelectionEditorIsHidden();
-
-        // Opens the TabGridDialog by clicking the first group card.
-        onViewWaiting(Matchers.allOf(withParent(withId(R.id.tasks_surface_body)),
-                              withId(org.chromium.chrome.tab_ui.R.id.tab_list_view)))
-                .perform(RecyclerViewActions.actionOnItemAtPosition(0, click()));
-        CriteriaHelper.pollUiThread(() -> isTabGridDialogShown(cta));
-
-        // Verifies that the TabGridDialog is closed by tapping back button.
-        StartSurfaceTestUtils.pressBack(mActivityTestRule);
-        CriteriaHelper.pollUiThread(() -> isTabGridDialogHidden(cta));
-        onViewWaiting(withId(R.id.secondary_tasks_surface_view));
-    }
-
-    @Test
-    @LargeTest
-    @Feature({"StartSurface"})
-    @EnableFeatures(ChromeFeatureList.TAB_GROUPS_ANDROID)
-    @CommandLineFlags.Add({START_SURFACE_TEST_BASE_PARAMS})
-    public void testShow_SingleAsHomepage_BackButtonOnHomepageWithGroupTabsDialog() {
-        backButtonOnHomepageWithGroupTabsDialogImpl();
-    }
-
-    @Test
-    @LargeTest
-    @Feature({"StartSurface"})
-    @EnableFeatures(ChromeFeatureList.TAB_GROUPS_ANDROID)
-    @CommandLineFlags.Add({START_SURFACE_TEST_BASE_PARAMS + "/show_last_active_tab_only/true"})
-    public void testShow_SingleAsHomepageV2_BackButtonOnHomepageWithGroupTabsDialog() {
-        backButtonOnHomepageWithGroupTabsDialogImpl();
-    }
-
-    private void backButtonOnHomepageWithGroupTabsDialogImpl() {
-        if (!mImmediateReturn) {
-            StartSurfaceTestUtils.pressHomePageButton(mActivityTestRule.getActivity());
-        }
-
-        ChromeTabbedActivity cta = mActivityTestRule.getActivity();
-        StartSurfaceTestUtils.waitForOverviewVisible(
-                mLayoutChangedCallbackHelper, mCurrentlyActiveLayout);
-        StartSurfaceTestUtils.waitForTabModel(cta);
-        onViewWaiting(withId(R.id.logo));
-
-        // Launches the first site in MV tiles to create the second tab for grouping.
-        StartSurfaceTestUtils.launchFirstMVTile(cta, /* currentTabCount = */ 1);
-
-        // When show_last_active_tab_only is enabled, we need to enter the tab switcher first to
-        // initialize the secondary task surface which shows the TabSelectionEditor dialog.
-        onViewWaiting(withId(org.chromium.chrome.tab_ui.R.id.tab_switcher_button));
-        if (isInstantReturn()) {
-            // TODO(crbug.com/1076274): fix toolbar to avoid wrongly focusing on the toolbar
-            // omnibox.
-            return;
-        }
-        TabUiTestHelper.enterTabSwitcher(cta);
-        waitForView(withId(R.id.secondary_tasks_surface_view));
-        List<Tab> tabs = getTabsInCurrentTabModel(cta.getCurrentTabModel());
-        TabSelectionEditorTestingRobot robot = new TabSelectionEditorTestingRobot();
-
-        // Enters the homepage, and shows the TabSelectionEditor dialog.
-        StartSurfaceTestUtils.pressHomePageButton(cta);
-        waitForView(withId(R.id.primary_tasks_surface_view));
-
-        StartSurfaceCoordinator startSurfaceCoordinator =
-                StartSurfaceTestUtils.getStartSurfaceFromUIThread(cta);
-        TestThreadUtils.runOnUiThreadBlocking(
-                () -> startSurfaceCoordinator.showTabSelectionEditorForTesting(tabs));
-        robot.resultRobot.verifyTabSelectionEditorIsVisible()
-                .verifyToolbarActionButtonDisabled()
-                .verifyToolbarActionButtonWithResourceId(
-                        org.chromium.chrome.tab_ui.R.string.tab_selection_editor_group)
-                .verifyToolbarSelectionTextWithResourceId(
-                        org.chromium.chrome.tab_ui.R.string
-                                .tab_selection_editor_toolbar_select_tabs)
-                .verifyAdapterHasItemCount(tabs.size())
-                .verifyHasAtLeastNItemVisible(2);
-
-        // Verifies that tapping the back button will close the TabSelectionEditor.
-        StartSurfaceTestUtils.pressBack(mActivityTestRule);
-        robot.resultRobot.verifyTabSelectionEditorIsHidden();
-        onViewWaiting(withId(R.id.primary_tasks_surface_view));
-    }
-
-    @Test
-    @MediumTest
-    @Feature({"StartSurface"})
     @CommandLineFlags.Add({START_SURFACE_TEST_BASE_PARAMS})
     public void testOpenRecentTabOnStartAndTapBackButtonReturnToStartSurface()
             throws ExecutionException {
diff --git a/chrome/android/features/start_surface/javatests/src/org/chromium/chrome/features/start_surface/StartSurfaceTest.java b/chrome/android/features/start_surface/javatests/src/org/chromium/chrome/features/start_surface/StartSurfaceTest.java
index 2d516c7..d45021b 100644
--- a/chrome/android/features/start_surface/javatests/src/org/chromium/chrome/features/start_surface/StartSurfaceTest.java
+++ b/chrome/android/features/start_surface/javatests/src/org/chromium/chrome/features/start_surface/StartSurfaceTest.java
@@ -413,6 +413,7 @@
     @MediumTest
     @Feature({"StartSurface"})
     @CommandLineFlags.Add({START_SURFACE_TEST_BASE_PARAMS})
+    @DisabledTest(message = "crbug.com/1170673 - NoInstant_NoReturn version is flaky")
     public void testSearchInSingleSurface() {
         if (!mImmediateReturn) {
             StartSurfaceTestUtils.pressHomePageButton(mActivityTestRule.getActivity());
diff --git a/chrome/android/features/start_surface/javatests/src/org/chromium/chrome/features/start_surface/TabSwitcherAndStartSurfaceLayoutTest.java b/chrome/android/features/start_surface/javatests/src/org/chromium/chrome/features/start_surface/TabSwitcherAndStartSurfaceLayoutTest.java
index 532afe8337..4e97586 100644
--- a/chrome/android/features/start_surface/javatests/src/org/chromium/chrome/features/start_surface/TabSwitcherAndStartSurfaceLayoutTest.java
+++ b/chrome/android/features/start_surface/javatests/src/org/chromium/chrome/features/start_surface/TabSwitcherAndStartSurfaceLayoutTest.java
@@ -48,13 +48,11 @@
 
 import android.content.res.Configuration;
 import android.graphics.Bitmap;
-import android.graphics.drawable.BitmapDrawable;
 import android.graphics.drawable.Drawable;
 import android.os.Build;
 import android.os.Build.VERSION_CODES;
 import android.support.test.InstrumentationRegistry;
 import android.view.View;
-import android.widget.ImageView;
 
 import androidx.annotation.Nullable;
 import androidx.recyclerview.widget.GridLayoutManager;
@@ -65,6 +63,7 @@
 import androidx.test.espresso.ViewAssertion;
 import androidx.test.espresso.contrib.AccessibilityChecks;
 import androidx.test.espresso.contrib.RecyclerViewActions;
+import androidx.test.filters.LargeTest;
 import androidx.test.filters.MediumTest;
 import androidx.test.uiautomator.UiDevice;
 
@@ -104,6 +103,7 @@
 import org.chromium.chrome.browser.tab.Tab;
 import org.chromium.chrome.browser.tab.TabLaunchType;
 import org.chromium.chrome.browser.tab.TabStateExtractor;
+import org.chromium.chrome.browser.tab.TabUtils;
 import org.chromium.chrome.browser.tabmodel.IncognitoTabHostUtils;
 import org.chromium.chrome.browser.tabmodel.TabCreator;
 import org.chromium.chrome.browser.tabmodel.TabModel;
@@ -111,6 +111,7 @@
 import org.chromium.chrome.browser.tasks.pseudotab.PseudoTab;
 import org.chromium.chrome.browser.tasks.pseudotab.TabAttributeCache;
 import org.chromium.chrome.browser.tasks.tab_groups.TabGroupModelFilter;
+import org.chromium.chrome.browser.tasks.tab_management.TabGridThumbnailView;
 import org.chromium.chrome.browser.tasks.tab_management.TabProperties;
 import org.chromium.chrome.browser.tasks.tab_management.TabSelectionEditorTestingRobot;
 import org.chromium.chrome.browser.tasks.tab_management.TabSuggestionMessageService;
@@ -1106,26 +1107,33 @@
     }
 
     @Test
-    @MediumTest
-    @EnableFeatures({ChromeFeatureList.TAB_TO_GTS_ANIMATION + "<Study"})
-    @CommandLineFlags.Add({BASE_PARAMS})
+    @LargeTest
+    // clang-format off
+    @EnableFeatures({ChromeFeatureList.TAB_TO_GTS_ANIMATION + "<Study",
+            ChromeFeatureList.TAB_GROUPS_CONTINUATION_ANDROID + "<Study"})
+    @CommandLineFlags.Add({BASE_PARAMS + "/enable_launch_polish/true"})
     @DisabledTest(message = "https://crbug.com/1122657")
     public void testThumbnailAspectRatio_default() {
-        prepareTabs(2, 0, mUrl);
+        // clang-format on
+        prepareTabs(2, 0, "about:blank");
         enterTabSwitcher(mActivityTestRule.getActivity());
-        onView(tabSwitcherViewMatcher())
-                .check(ThumbnailAspectRatioAssertion.havingAspectRatio(1.0));
+        onViewWaiting(tabSwitcherViewMatcher())
+                .check(ThumbnailAspectRatioAssertion.havingAspectRatio(
+                        TabUtils.getTabThumbnailAspectRatio(mActivityTestRule.getActivity())));
     }
 
     @Test
-    @MediumTest
-    @EnableFeatures({ChromeFeatureList.TAB_TO_GTS_ANIMATION + "<Study"})
-    @CommandLineFlags.Add({BASE_PARAMS + "/thumbnail_aspect_ratio/0.75"})
+    @LargeTest
+    // clang-format off
+    @EnableFeatures({ChromeFeatureList.TAB_TO_GTS_ANIMATION + "<Study",
+            ChromeFeatureList.TAB_GROUPS_CONTINUATION_ANDROID + "<Study"})
+    @CommandLineFlags.Add({BASE_PARAMS + "/thumbnail_aspect_ratio/0.75/enable_launch_polish/true"})
     @DisabledTest(message = "https://crbug.com/1122657")
     public void testThumbnailAspectRatio_point75() {
-        prepareTabs(2, 0, mUrl);
+        // clang-format on
+        prepareTabs(2, 0, "about:blank");
         enterTabSwitcher(mActivityTestRule.getActivity());
-        onView(tabSwitcherViewMatcher())
+        onViewWaiting(tabSwitcherViewMatcher())
                 .check(ThumbnailAspectRatioAssertion.havingAspectRatio(0.75));
         leaveGTSAndVerifyThumbnailsAreReleased();
 
@@ -1133,19 +1141,25 @@
         mActivityTestRule.loadUrlInTab(
                 NTP_URL, PageTransition.TYPED | PageTransition.FROM_ADDRESS_BAR, tab);
         enterTabSwitcher(mActivityTestRule.getActivity());
-        onView(tabSwitcherViewMatcher())
+        onViewWaiting(tabSwitcherViewMatcher())
                 .check(ThumbnailAspectRatioAssertion.havingAspectRatio(0.75));
     }
 
     @Test
-    @MediumTest
-    @EnableFeatures({ChromeFeatureList.TAB_TO_GTS_ANIMATION + "<Study"})
-    @CommandLineFlags.Add({BASE_PARAMS + "/thumbnail_aspect_ratio/2.0/allow_to_refetch/true"})
+    @LargeTest
+    // clang-format off
+    @EnableFeatures({ChromeFeatureList.TAB_TO_GTS_ANIMATION + "<Study",
+            ChromeFeatureList.TAB_GROUPS_CONTINUATION_ANDROID + "<Study"})
+    @CommandLineFlags.Add({BASE_PARAMS
+            + "/thumbnail_aspect_ratio/2.0/allow_to_refetch/true/enable_launch_polish/true"})
     @DisabledTest(message = "Flaky - https://crbug.com/1124041")
     public void testThumbnailAspectRatio_fromTwoToPoint75() throws Exception {
-        prepareTabs(2, 0, mUrl);
+        // clang-format on
+        prepareTabs(2, 0, "about:blank");
+        // Select the first tab to ensure the second tab thumbnail is captured.
+        ChromeTabUtils.switchTabInCurrentTabModel(mActivityTestRule.getActivity(), 0);
         enterTabSwitcher(mActivityTestRule.getActivity());
-        onView(tabSwitcherViewMatcher())
+        onViewWaiting(tabSwitcherViewMatcher())
                 .check(ThumbnailAspectRatioAssertion.havingAspectRatio(2.0));
         TabModel currentTabModel =
                 mActivityTestRule.getActivity().getTabModelSelector().getCurrentModel();
@@ -1158,7 +1172,7 @@
         verifyAllThumbnailHasAspectRatio(0.75);
 
         enterTabSwitcher(mActivityTestRule.getActivity());
-        onView(tabSwitcherViewMatcher())
+        onViewWaiting(tabSwitcherViewMatcher())
                 .check(ThumbnailAspectRatioAssertion.havingAspectRatio(2.0));
         TabUiTestHelper.finishActivity(mActivityTestRule.getActivity());
     }
@@ -1591,22 +1605,25 @@
             RecyclerView recyclerView = (RecyclerView) view;
 
             RecyclerView.Adapter adapter = recyclerView.getAdapter();
+            boolean hasAtLeastOneValidViewHolder = false;
             for (int i = 0; i < adapter.getItemCount(); i++) {
                 RecyclerView.ViewHolder viewHolder =
                         recyclerView.findViewHolderForAdapterPosition(i);
                 if (viewHolder != null) {
+                    hasAtLeastOneValidViewHolder = true;
                     ViewLookupCachingFrameLayout tabView =
                             (ViewLookupCachingFrameLayout) viewHolder.itemView;
-                    ImageView thumbnail = (ImageView) tabView.fastFindViewById(R.id.tab_thumbnail);
-                    BitmapDrawable drawable = (BitmapDrawable) thumbnail.getDrawable();
-                    Bitmap bitmap = drawable.getBitmap();
-                    double bitmapRatio = bitmap.getWidth() * 1.0 / bitmap.getHeight();
-                    assertTrue(
-                            "Actual ratio: " + bitmapRatio + "; Expected ratio: " + mExpectedRatio,
-                            Math.abs(bitmapRatio - mExpectedRatio)
+                    TabGridThumbnailView thumbnail =
+                            (TabGridThumbnailView) tabView.fastFindViewById(R.id.tab_thumbnail);
+                    double thumbnailViewRatio = thumbnail.getWidth() * 1.0 / thumbnail.getHeight();
+
+                    assertTrue("Actual ratio: " + thumbnailViewRatio
+                                    + "; Expected ratio: " + mExpectedRatio,
+                            Math.abs(thumbnailViewRatio - mExpectedRatio)
                                     <= TabContentManager.ASPECT_RATIO_PRECISION);
                 }
             }
+            assertTrue("should have at least one valid ViewHolder", hasAtLeastOneValidViewHolder);
         }
     }
 
diff --git a/chrome/android/features/start_surface/junit/src/org/chromium/chrome/features/start_surface/StartSurfaceMediatorUnitTest.java b/chrome/android/features/start_surface/junit/src/org/chromium/chrome/features/start_surface/StartSurfaceMediatorUnitTest.java
index fc05c3f5..a627b2c5b 100644
--- a/chrome/android/features/start_surface/junit/src/org/chromium/chrome/features/start_surface/StartSurfaceMediatorUnitTest.java
+++ b/chrome/android/features/start_surface/junit/src/org/chromium/chrome/features/start_surface/StartSurfaceMediatorUnitTest.java
@@ -1443,6 +1443,106 @@
                 StartSurfaceState.SHOWN_HOMEPAGE, mediator.getStartSurfaceState());
     }
 
+    /**
+     * Tests the logic of StartSurfaceMediator#onBackPressedInternal() when the Start surface is
+     * showing but Tab switcher hasn't been created yet.
+     */
+    @Test
+    public void testBackPressHandlerOnStartSurfaceWithoutTabSwitcherCreated() {
+        doReturn(false).when(mTabModelSelector).isIncognitoSelected();
+        doReturn(false).when(mTabModelSelector).isIncognitoSelected();
+        doReturn(mVoiceRecognitionHandler).when(mOmniboxStub).getVoiceRecognitionHandler();
+        doReturn(true).when(mVoiceRecognitionHandler).isVoiceSearchEnabled();
+        StartSurfaceMediator mediator =
+                createStartSurfaceMediator(/*isStartSurfaceEnabled=*/true, false);
+
+        mediator.setStartSurfaceState(StartSurfaceState.SHOWN_HOMEPAGE);
+        Assert.assertEquals(StartSurfaceState.SHOWN_HOMEPAGE, mediator.getStartSurfaceState());
+
+        doReturn(true).when(mMainTabGridController).isDialogVisible();
+        mediator.onBackPressed();
+        verify(mMainTabGridController).onBackPressed(true);
+
+        doReturn(false).when(mMainTabGridController).isDialogVisible();
+        mediator.onBackPressed();
+        verify(mMainTabGridController, times(2)).onBackPressed(true);
+    }
+
+    /**
+     * Tests the logic of StartSurfaceMediator#onBackPressedInternal() when the Start surface is
+     * showing and the Tab switcher has been created.
+     */
+    @Test
+    public void testBackPressHandlerOnStartSurfaceWithTabSwitcherCreated() {
+        doReturn(false).when(mTabModelSelector).isIncognitoSelected();
+        doReturn(false).when(mTabModelSelector).isIncognitoSelected();
+        doReturn(mVoiceRecognitionHandler).when(mOmniboxStub).getVoiceRecognitionHandler();
+        doReturn(true).when(mVoiceRecognitionHandler).isVoiceSearchEnabled();
+
+        StartSurfaceMediator mediator =
+                createStartSurfaceMediator(/*isStartSurfaceEnabled=*/true, false);
+        mediator.setSecondaryTasksSurfacePropertyModel(mSecondaryTasksSurfacePropertyModel);
+        mediator.setSecondaryTasksSurfaceController(mSecondaryTasksSurfaceController);
+
+        mediator.setStartSurfaceState(StartSurfaceState.SHOWN_HOMEPAGE);
+        Assert.assertEquals(StartSurfaceState.SHOWN_HOMEPAGE, mediator.getStartSurfaceState());
+
+        doReturn(true).when(mMainTabGridController).isDialogVisible();
+        doReturn(true).when(mSecondaryTasksSurfaceController).isDialogVisible();
+        mediator.onBackPressed();
+        verify(mMainTabGridController, never()).onBackPressed(true);
+        verify(mSecondaryTasksSurfaceController,
+                description("Secondary task surface has a higher priority of handling back press"))
+                .onBackPressed(true);
+
+        doReturn(true).when(mMainTabGridController).isDialogVisible();
+        doReturn(false).when(mSecondaryTasksSurfaceController).isDialogVisible();
+        mediator.onBackPressed();
+        verify(mMainTabGridController).onBackPressed(true);
+
+        doReturn(false).when(mMainTabGridController).isDialogVisible();
+        doReturn(false).when(mSecondaryTasksSurfaceController).isDialogVisible();
+        mediator.onBackPressed();
+        verify(mMainTabGridController, times(2)).onBackPressed(true);
+    }
+
+    /**
+     * Tests the logic of StartSurfaceMediator#onBackPressedInternal() when the Tab switcher is
+     * showing.
+     */
+    @Test
+    public void testBackPressHandlerOnTabSwitcher() {
+        doReturn(false).when(mTabModelSelector).isIncognitoSelected();
+        doReturn(false).when(mTabModelSelector).isIncognitoSelected();
+        doReturn(mVoiceRecognitionHandler).when(mOmniboxStub).getVoiceRecognitionHandler();
+        doReturn(true).when(mVoiceRecognitionHandler).isVoiceSearchEnabled();
+
+        StartSurfaceMediator mediator =
+                createStartSurfaceMediator(/*isStartSurfaceEnabled=*/true, false);
+        mediator.setSecondaryTasksSurfacePropertyModel(mSecondaryTasksSurfacePropertyModel);
+        mediator.setSecondaryTasksSurfaceController(mSecondaryTasksSurfaceController);
+
+        mediator.setStartSurfaceState(StartSurfaceState.SHOWN_TABSWITCHER);
+        Assert.assertEquals(StartSurfaceState.SHOWN_TABSWITCHER, mediator.getStartSurfaceState());
+        // The primary task surface is invisible when showing the Tab Switcher.
+        doReturn(false).when(mMainTabGridController).isDialogVisible();
+
+        doReturn(true).when(mSecondaryTasksSurfaceController).isDialogVisible();
+        mediator.onBackPressed();
+        verify(mMainTabGridController, never()).onBackPressed(false);
+        verify(mSecondaryTasksSurfaceController).onBackPressed(false);
+
+        doReturn(false).when(mSecondaryTasksSurfaceController).isDialogVisible();
+        verify(mSecondaryTasksSurfaceController).onBackPressed(false);
+
+        mediator.setStartSurfaceState(StartSurfaceState.SHOWN_HOMEPAGE);
+        mediator.setStartSurfaceState(StartSurfaceState.SHOWN_TABSWITCHER);
+        Assert.assertEquals(StartSurfaceState.SHOWN_TABSWITCHER, mediator.getStartSurfaceState());
+        mediator.onBackPressed();
+        Assert.assertEquals("Should return to home page on back press.",
+                StartSurfaceState.SHOWN_HOMEPAGE, mediator.getStartSurfaceState());
+    }
+
     private StartSurfaceMediator createStartSurfaceMediator(
             boolean isStartSurfaceEnabled, boolean excludeMVTiles) {
         return createStartSurfaceMediator(
diff --git a/chrome/android/features/tab_ui/BUILD.gn b/chrome/android/features/tab_ui/BUILD.gn
index b429175..802453e 100644
--- a/chrome/android/features/tab_ui/BUILD.gn
+++ b/chrome/android/features/tab_ui/BUILD.gn
@@ -237,7 +237,10 @@
 
 android_library("unit_device_javatests") {
   testonly = true
-  sources = [ "javatests/src/org/chromium/chrome/browser/tasks/tab_management/LargeMessageCardViewBinderTest.java" ]
+  sources = [
+    "javatests/src/org/chromium/chrome/browser/tasks/tab_management/LargeMessageCardViewBinderTest.java",
+    "javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabSelectionEditorLayoutBinderTest.java",
+  ]
 
   deps = [
     ":java",
@@ -246,8 +249,11 @@
     "//chrome/android/features/tab_ui:java",
     "//chrome/browser/tab:java",
     "//chrome/test/android:chrome_java_test_support_common",
+    "//components/browser_ui/widget/android:java",
     "//content/public/test/android:content_java_test_support",
+    "//third_party/android_sdk:android_test_base_java",
     "//third_party/androidx:androidx_appcompat_appcompat_resources_java",
+    "//third_party/androidx:androidx_recyclerview_recyclerview_java",
     "//third_party/androidx:androidx_test_runner_java",
     "//third_party/junit:junit",
     "//ui/android:ui_java_test_support",
diff --git a/chrome/android/features/tab_ui/DEPS b/chrome/android/features/tab_ui/DEPS
index 79eedc5..8f96d0f 100644
--- a/chrome/android/features/tab_ui/DEPS
+++ b/chrome/android/features/tab_ui/DEPS
@@ -18,4 +18,5 @@
  "+components/search_engines/android/java/src/org/chromium/components/search_engines",
  "+components/module_installer",
  "+content/public/android/java/src/org/chromium/content_public/browser",
+ "+content/public/android/java/src/org/chromium/content_public/common",
 ]
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 c9eb47e..62cd832 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
@@ -305,7 +305,7 @@
 
         // Add 400px top margin to the recyclerView.
         RecyclerView recyclerView = cta.findViewById(R.id.tab_list_view);
-        float tabGridCardPadding = TabUiThemeProvider.getTabCardPaddingDimension(cta);
+        float tabGridCardPadding = TabUiThemeProvider.getTabGridCardMargin(cta);
         int deltaTopMargin = 400;
         ViewGroup.MarginLayoutParams params =
                 (ViewGroup.MarginLayoutParams) recyclerView.getLayoutParams();
@@ -1132,19 +1132,20 @@
                     if (noMatchException != null) throw noMatchException;
                     Assert.assertTrue(v instanceof ListView);
                     ListView listView = (ListView) v;
+                    int menuItemCount = 1;
                     verifyTabGridDialogToolbarMenuItem(listView, 0,
                             cta.getString(R.string.tab_grid_dialog_toolbar_remove_from_group));
                     if (TabUiFeatureUtilities.ENABLE_TAB_GROUP_SHARING.getValue()) {
-                        verifyTabGridDialogToolbarMenuItem(listView, 1,
+                        menuItemCount += 1;
+                        verifyTabGridDialogToolbarMenuItem(listView, menuItemCount - 1,
                                 cta.getString(R.string.tab_grid_dialog_toolbar_share_group));
                     }
                     if (TabUiFeatureUtilities.isLaunchPolishEnabled()) {
-                        assertEquals(3, listView.getCount());
-                        verifyTabGridDialogToolbarMenuItem(listView, 2,
+                        menuItemCount += 1;
+                        verifyTabGridDialogToolbarMenuItem(listView, menuItemCount - 1,
                                 cta.getString(R.string.tab_grid_dialog_toolbar_edit_group_name));
-                    } else {
-                        assertEquals(2, listView.getCount());
                     }
+                    assertEquals(menuItemCount, listView.getCount());
                 });
     }
 
diff --git a/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupUiTest.java b/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupUiTest.java
index 357c7026..2a036c4 100644
--- a/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupUiTest.java
+++ b/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupUiTest.java
@@ -44,6 +44,7 @@
 
 import androidx.recyclerview.widget.RecyclerView;
 import androidx.test.espresso.NoMatchingRootException;
+import androidx.test.filters.LargeTest;
 import androidx.test.filters.MediumTest;
 
 import org.junit.Before;
@@ -55,7 +56,6 @@
 import org.chromium.base.test.util.CriteriaHelper;
 import org.chromium.base.test.util.DisabledTest;
 import org.chromium.base.test.util.Feature;
-import org.chromium.base.test.util.FlakyTest;
 import org.chromium.base.test.util.Restriction;
 import org.chromium.chrome.browser.ChromeTabbedActivity;
 import org.chromium.chrome.browser.compositor.layouts.Layout;
@@ -100,6 +100,7 @@
     public ChromeRenderTestRule mRenderTestRule =
             ChromeRenderTestRule.Builder.withPublicCorpus()
                     .setBugComponent(ChromeRenderTestRule.Component.UI_BROWSER_MOBILE_TAB_GROUPS)
+                    .setRevision(1)
                     .build();
 
     @Before
@@ -130,13 +131,13 @@
     }
 
     @Test
-    @MediumTest
+    @LargeTest
     @Feature({"RenderTest"})
-    @FlakyTest(message = "https://crbug.com/1208386")
+    @DisabledTest(message = "https://crbug.com/1208386")
     public void testRenderStrip_Select5thTabIn10Tabs() throws IOException {
         final ChromeTabbedActivity cta = mActivityTestRule.getActivity();
         AtomicReference<RecyclerView> recyclerViewReference = new AtomicReference<>();
-        createTabs(cta, false, 10);
+        TabUiTestHelper.addBlankTabs(cta, false, 9);
         enterTabSwitcher(cta);
         verifyTabSwitcherCardCount(cta, 10);
         mergeAllNormalTabsToAGroup(cta);
@@ -155,13 +156,13 @@
     }
 
     @Test
-    @MediumTest
+    @LargeTest
     @Feature({"RenderTest"})
-    @FlakyTest(message = "https://crbug.com/1208386")
+    @DisabledTest(message = "https://crbug.com/1208386")
     public void testRenderStrip_Select10thTabIn10Tabs() throws IOException {
         final ChromeTabbedActivity cta = mActivityTestRule.getActivity();
         AtomicReference<RecyclerView> recyclerViewReference = new AtomicReference<>();
-        createTabs(cta, false, 10);
+        TabUiTestHelper.addBlankTabs(cta, false, 9);
         enterTabSwitcher(cta);
         verifyTabSwitcherCardCount(cta, 10);
         mergeAllNormalTabsToAGroup(cta);
@@ -180,13 +181,13 @@
     }
 
     @Test
-    @MediumTest
+    @LargeTest
     @Feature({"RenderTest"})
-    @FlakyTest(message = "https://crbug.com/1208386")
+    @DisabledTest(message = "https://crbug.com/1208386")
     public void testRenderStrip_AddTab() throws IOException {
         final ChromeTabbedActivity cta = mActivityTestRule.getActivity();
         AtomicReference<RecyclerView> recyclerViewReference = new AtomicReference<>();
-        createTabs(cta, false, 10);
+        TabUiTestHelper.addBlankTabs(cta, false, 9);
         enterTabSwitcher(cta);
         verifyTabSwitcherCardCount(cta, 10);
         mergeAllNormalTabsToAGroup(cta);
diff --git a/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherMultiWindowTest.java b/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherMultiWindowTest.java
index 2e84f97..d7b5059 100644
--- a/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherMultiWindowTest.java
+++ b/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherMultiWindowTest.java
@@ -13,7 +13,6 @@
 import static org.chromium.chrome.browser.multiwindow.MultiWindowTestHelper.waitForSecondChromeTabbedActivity;
 import static org.chromium.chrome.browser.tasks.tab_management.TabUiTestHelper.clickFirstCardFromTabSwitcher;
 import static org.chromium.chrome.browser.tasks.tab_management.TabUiTestHelper.clickFirstTabInDialog;
-import static org.chromium.chrome.browser.tasks.tab_management.TabUiTestHelper.createTabs;
 import static org.chromium.chrome.browser.tasks.tab_management.TabUiTestHelper.enterTabSwitcher;
 import static org.chromium.chrome.browser.tasks.tab_management.TabUiTestHelper.mergeAllIncognitoTabsToAGroup;
 import static org.chromium.chrome.browser.tasks.tab_management.TabUiTestHelper.mergeAllNormalTabsToAGroup;
@@ -26,6 +25,7 @@
 import android.support.test.InstrumentationRegistry;
 
 import androidx.annotation.RequiresApi;
+import androidx.test.filters.LargeTest;
 import androidx.test.filters.MediumTest;
 
 import org.junit.Before;
@@ -35,7 +35,6 @@
 
 import org.chromium.base.test.util.CommandLineFlags;
 import org.chromium.base.test.util.CriteriaHelper;
-import org.chromium.base.test.util.DisabledTest;
 import org.chromium.base.test.util.MinAndroidSdkLevel;
 import org.chromium.base.test.util.Restriction;
 import org.chromium.chrome.browser.ChromeTabbedActivity;
@@ -65,7 +64,7 @@
 
     @Before
     public void setUp() {
-        mActivityTestRule.startMainActivityFromLauncher();
+        mActivityTestRule.startMainActivityOnBlankPage();
         Layout layout = mActivityTestRule.getActivity().getLayoutManager().getOverviewLayout();
         assertTrue(layout instanceof TabSwitcherAndStartSurfaceLayout);
         CriteriaHelper.pollUiThread(
@@ -73,14 +72,14 @@
     }
 
     @Test
-    @MediumTest
+    @LargeTest
     @RequiresApi(Build.VERSION_CODES.N)
-    @DisabledTest(message = "https://crbug.com/1231616")
     public void testMoveTabsAcrossWindow_GTS_WithoutGroup() {
         final ChromeTabbedActivity cta1 = mActivityTestRule.getActivity();
-        // Initially, we have 4 normal tabs and 3 incognito tabs in cta1.
-        createTabs(cta1, false, 4);
-        createTabs(cta1, true, 3);
+        // Initially, we have 4 normal tabs (including the one created at activity start) and 3
+        // incognito tabs in cta1.
+        TabUiTestHelper.addBlankTabs(cta1, false, 3);
+        TabUiTestHelper.addBlankTabs(cta1, true, 3);
         verifyTabModelTabCount(cta1, 4, 3);
 
         // Enter tab switcher in cta1 in incognito mode.
@@ -156,16 +155,16 @@
     }
 
     @Test
-    @MediumTest
+    @LargeTest
     @RequiresApi(Build.VERSION_CODES.N)
     @Features.
     EnableFeatures({ChromeFeatureList.TAB_GROUPS_ANDROID, ChromeFeatureList.TAB_REPARENTING})
-    @DisabledTest(message = "https://crbug.com/1163569")
     public void testMoveTabsAcrossWindow_GTS_WithGroup() {
-        // Initially, we have 5 normal tabs and 5 incognito tabs in cta1.
+        // Initially, we have 5 normal tabs (including the one created at activity start) and 5
+        // incognito tabs in cta1.
         final ChromeTabbedActivity cta1 = mActivityTestRule.getActivity();
-        createTabs(cta1, false, 5);
-        createTabs(cta1, true, 5);
+        TabUiTestHelper.addBlankTabs(cta1, false, 4);
+        TabUiTestHelper.addBlankTabs(cta1, true, 5);
         verifyTabModelTabCount(cta1, 5, 5);
 
         // Enter tab switcher in cta1 in incognito mode.
@@ -249,10 +248,9 @@
         ChromeFeatureList.TAB_REPARENTING})
     public void testMoveLastIncognitoTab() {
         // clang-format on
-        // Initially, we have 1 normal tab and 1 incognito tab in cta1.
+        // Initially, we have 1 normal tab (created in #setup()) and 1 incognito tab in cta1.
         final ChromeTabbedActivity cta1 = mActivityTestRule.getActivity();
-        createTabs(cta1, false, 1);
-        createTabs(cta1, true, 1);
+        TabUiTestHelper.addBlankTabs(cta1, true, 1);
         verifyTabModelTabCount(cta1, 1, 1);
 
         // Move the incognito tab to cta2.
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 bc46bdc..aed34e5 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
@@ -60,6 +60,7 @@
 import org.chromium.chrome.browser.layouts.LayoutTestUtils;
 import org.chromium.chrome.browser.layouts.LayoutType;
 import org.chromium.chrome.browser.tab.Tab;
+import org.chromium.chrome.browser.tab.TabLaunchType;
 import org.chromium.chrome.browser.tab.TabSelectionType;
 import org.chromium.chrome.browser.tabmodel.TabModel;
 import org.chromium.chrome.browser.tasks.pseudotab.PseudoTab;
@@ -67,7 +68,9 @@
 import org.chromium.chrome.tab_ui.R;
 import org.chromium.chrome.test.ChromeTabbedActivityTestRule;
 import org.chromium.chrome.test.util.ChromeTabUtils;
+import org.chromium.content_public.browser.LoadUrlParams;
 import org.chromium.content_public.browser.test.util.TestThreadUtils;
+import org.chromium.content_public.common.ContentUrlConstants;
 
 import java.io.File;
 import java.lang.annotation.Retention;
@@ -104,6 +107,23 @@
     }
 
     /**
+     * Open additional tabs for the provided activity. The added tabs will be opened to
+     * "about:blank" and will not wait for the page to finish loading.
+     * @param cta The activity to add the tabs to.
+     * @param incognito Whether the tabs should be incognito.
+     * @param count The number of tabs to create.
+     */
+    public static void addBlankTabs(ChromeTabbedActivity cta, boolean incognito, int count) {
+        for (int i = 0; i < count; i++) {
+            TestThreadUtils.runOnUiThreadBlocking(() -> {
+                cta.getTabCreator(incognito).createNewTab(
+                        new LoadUrlParams(ContentUrlConstants.ABOUT_BLANK_DISPLAY_URL),
+                        TabLaunchType.FROM_CHROME_UI, null);
+            });
+        }
+    }
+
+    /**
      * Enter tab switcher from a tab page.
      * @param cta  The current running activity.
      */
diff --git a/chrome/android/features/tab_ui/tab_management_java_sources.gni b/chrome/android/features/tab_ui/tab_management_java_sources.gni
index 898b42a..550520d 100644
--- a/chrome/android/features/tab_ui/tab_management_java_sources.gni
+++ b/chrome/android/features/tab_ui/tab_management_java_sources.gni
@@ -43,7 +43,6 @@
   "//chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupUiViewBinderTest.java",
   "//chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabListContainerViewBinderTest.java",
   "//chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabListViewHolderTest.java",
-  "//chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabSelectionEditorLayoutBinderTest.java",
   "//chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabSelectionEditorTest.java",
   "//chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabSelectionEditorTestingRobot.java",
   "//chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabSuggestionMessageCardTest.java",
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/app/flags/ChromeCachedFlags.java b/chrome/android/java/src/org/chromium/chrome/browser/app/flags/ChromeCachedFlags.java
index a86c788..6b911908 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/app/flags/ChromeCachedFlags.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/app/flags/ChromeCachedFlags.java
@@ -94,6 +94,7 @@
                 add(ChromeFeatureList.FEED_LOADING_PLACEHOLDER);
                 add(ChromeFeatureList.GRID_TAB_SWITCHER_FOR_TABLETS);
                 add(ChromeFeatureList.IMMERSIVE_UI_MODE);
+                add(ChromeFeatureList.INCOGNITO_REAUTHENTICATION_FOR_ANDROID);
                 add(ChromeFeatureList.INSTANT_START);
                 add(ChromeFeatureList.INSTANCE_SWITCHER);
                 add(ChromeFeatureList.INTEREST_FEED_V2);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/PartialCustomTabHeightStrategy.java b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/PartialCustomTabHeightStrategy.java
index 0ee12c7..89fd504 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/PartialCustomTabHeightStrategy.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/PartialCustomTabHeightStrategy.java
@@ -624,7 +624,7 @@
                 mNavbar.animate().alpha(1.f).setDuration(NAVBAR_FADE_DURATION_MS);
             }
         } else {
-            mNavbar.animate().alpha(0.f).setDuration(NAVBAR_FADE_DURATION_MS);
+            if (mNavbar != null) mNavbar.animate().alpha(0.f).setDuration(NAVBAR_FADE_DURATION_MS);
         }
         showNavbarButtons(show);
     }
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/download/DownloadTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/download/DownloadTest.java
index 0e55075a1..aeab8a3b 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/download/DownloadTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/download/DownloadTest.java
@@ -22,9 +22,6 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
-import org.chromium.base.test.params.ParameterAnnotations;
-import org.chromium.base.test.params.ParameterSet;
-import org.chromium.base.test.params.ParameterizedRunner;
 import org.chromium.base.test.util.CommandLineFlags;
 import org.chromium.base.test.util.Criteria;
 import org.chromium.base.test.util.CriteriaHelper;
@@ -32,18 +29,15 @@
 import org.chromium.base.test.util.Feature;
 import org.chromium.base.test.util.FlakyTest;
 import org.chromium.base.test.util.UrlUtils;
-import org.chromium.chrome.R;
 import org.chromium.chrome.browser.download.DownloadTestRule.CustomMainActivityStart;
-import org.chromium.chrome.browser.flags.ChromeFeatureList;
 import org.chromium.chrome.browser.flags.ChromeSwitches;
 import org.chromium.chrome.browser.profiles.OTRProfileID;
 import org.chromium.chrome.browser.tab.Tab;
 import org.chromium.chrome.browser.tab.TabLaunchType;
 import org.chromium.chrome.browser.tabmodel.TabCreator;
 import org.chromium.chrome.browser.tabmodel.TabModel;
-import org.chromium.chrome.test.ChromeJUnit4RunnerDelegate;
+import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
 import org.chromium.chrome.test.util.ChromeTabUtils;
-import org.chromium.chrome.test.util.browser.Features;
 import org.chromium.components.download.DownloadState;
 import org.chromium.components.offline_items_collection.ContentId;
 import org.chromium.components.offline_items_collection.FailState;
@@ -51,6 +45,7 @@
 import org.chromium.components.offline_items_collection.OfflineItem.Progress;
 import org.chromium.components.offline_items_collection.PendingState;
 import org.chromium.components.offline_items_collection.UpdateDelta;
+import org.chromium.components.policy.test.annotations.Policies;
 import org.chromium.content_public.browser.LoadUrlParams;
 import org.chromium.content_public.browser.test.util.DOMUtils;
 import org.chromium.content_public.browser.test.util.TestThreadUtils;
@@ -61,21 +56,14 @@
 import org.chromium.url.GURL;
 
 import java.util.ArrayList;
-import java.util.Arrays;
 import java.util.List;
 
 /**
  * Tests Chrome download feature by attempting to download some files.
  */
-@RunWith(ParameterizedRunner.class)
-@ParameterAnnotations.UseRunnerDelegate(ChromeJUnit4RunnerDelegate.class)
+@RunWith(ChromeJUnit4ClassRunner.class)
 @CommandLineFlags.Add({ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE})
 public class DownloadTest implements CustomMainActivityStart {
-    @ParameterAnnotations.ClassParameter
-    private static List<ParameterSet> sClassParams = Arrays.asList(
-            new ParameterSet().value(true).name("UseDownloadOfflineContentProviderEnabled"),
-            new ParameterSet().value(false).name("UseDownloadOfflineContentProviderDisabled"));
-
     @Rule
     public DownloadTestRule mDownloadTestRule = new DownloadTestRule(this);
 
@@ -99,8 +87,6 @@
         FILENAME_GZIP
     };
 
-    private boolean mUseDownloadOfflineContentProvider;
-
     static class DownloadManagerRequestInterceptorForTest
             implements DownloadManagerService.DownloadManagerRequestInterceptor {
         public DownloadItem mDownloadItem;
@@ -180,10 +166,6 @@
         void resumeDownload(Intent intent) {}
     }
 
-    public DownloadTest(boolean useDownloadOfflineContentProvider) {
-        mUseDownloadOfflineContentProvider = useDownloadOfflineContentProvider;
-    }
-
     @Before
     public void setUp() {
         deleteTestFiles();
@@ -201,11 +183,6 @@
 
     @Override
     public void customMainActivityStart() throws InterruptedException {
-        if (mUseDownloadOfflineContentProvider) {
-            Features.getInstance().enable(ChromeFeatureList.DOWNLOAD_OFFLINE_CONTENT_PROVIDER);
-        } else {
-            Features.getInstance().disable(ChromeFeatureList.DOWNLOAD_OFFLINE_CONTENT_PROVIDER);
-        }
         mDownloadTestRule.startMainActivityOnBlankPage();
     }
 
@@ -266,19 +243,15 @@
     @Test
     @MediumTest
     @Feature({"Downloads"})
-    @DisabledTest(message = "crbug.com/147904")
+    @Policies.Add({ @Policies.Item(key = "PromptForDownloadLocation", string = "false") })
     public void testCloseEmptyDownloadTab() throws Exception {
         mDownloadTestRule.loadUrl(mTestServer.getURL(TEST_DOWNLOAD_DIRECTORY + "get.html"));
         waitForFocus();
         final int initialTabCount = mDownloadTestRule.getActivity().getCurrentTabModel().getCount();
+        int currentCallCount = mDownloadTestRule.getChromeDownloadCallCount();
         View currentView = mDownloadTestRule.getActivity().getActivityTab().getView();
-        TouchCommon.longPressView(currentView);
-
-        int callCount = mDownloadTestRule.getChromeDownloadCallCount();
-        InstrumentationRegistry.getInstrumentation().invokeContextMenuAction(
-                mDownloadTestRule.getActivity(), R.id.contextmenu_open_in_new_tab, 0);
-        Assert.assertTrue(mDownloadTestRule.waitForChromeDownloadToFinish(callCount));
-        Assert.assertTrue(mDownloadTestRule.hasDownload(FILENAME_GZIP, null));
+        TouchCommon.singleClickView(currentView);
+        Assert.assertTrue(mDownloadTestRule.waitForChromeDownloadToFinish(currentCallCount));
 
         CriteriaHelper.pollUiThread(() -> {
             Criteria.checkThat(mDownloadTestRule.getActivity().getCurrentTabModel().getCount(),
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/omnibox/suggestions/base/BaseSuggestionProcessorTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/omnibox/suggestions/base/BaseSuggestionProcessorTest.java
index d9f8dac2..d111e007 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/omnibox/suggestions/base/BaseSuggestionProcessorTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/omnibox/suggestions/base/BaseSuggestionProcessorTest.java
@@ -4,7 +4,6 @@
 
 package org.chromium.chrome.browser.omnibox.suggestions.base;
 
-import static org.mockito.Mockito.anyInt;
 import static org.mockito.Mockito.eq;
 import static org.mockito.Mockito.verify;
 
@@ -27,10 +26,11 @@
 import org.chromium.base.test.BaseJUnit4ClassRunner;
 import org.chromium.base.test.util.Batch;
 import org.chromium.chrome.browser.omnibox.OmniboxSuggestionType;
+import org.chromium.chrome.browser.omnibox.suggestions.FaviconFetcher;
+import org.chromium.chrome.browser.omnibox.suggestions.FaviconFetcher.FaviconFetchCompleteListener;
+import org.chromium.chrome.browser.omnibox.suggestions.FaviconFetcher.FaviconType;
 import org.chromium.chrome.browser.omnibox.suggestions.OmniboxSuggestionUiType;
 import org.chromium.chrome.browser.omnibox.suggestions.SuggestionHost;
-import org.chromium.components.favicon.LargeIconBridge;
-import org.chromium.components.favicon.LargeIconBridge.LargeIconCallback;
 import org.chromium.components.omnibox.AutocompleteMatch;
 import org.chromium.components.omnibox.AutocompleteMatchBuilder;
 import org.chromium.ui.modelutil.PropertyModel;
@@ -44,14 +44,10 @@
 public class BaseSuggestionProcessorTest {
     private class TestBaseSuggestionProcessor extends BaseSuggestionViewProcessor {
         private final Context mContext;
-        private final LargeIconBridge mLargeIconBridge;
-        private final Runnable mRunable;
-        public TestBaseSuggestionProcessor(Context context, SuggestionHost suggestionHost,
-                LargeIconBridge largeIconBridge, Runnable runable) {
-            super(context, suggestionHost);
+        public TestBaseSuggestionProcessor(
+                Context context, SuggestionHost suggestionHost, FaviconFetcher faviconFetcher) {
+            super(context, suggestionHost, faviconFetcher);
             mContext = context;
-            mLargeIconBridge = largeIconBridge;
-            mRunable = runable;
         }
 
         @Override
@@ -74,16 +70,12 @@
             super.populateModel(suggestion, model, position);
             setSuggestionDrawableState(model,
                     SuggestionDrawableState.Builder.forBitmap(mContext, mDefaultBitmap).build());
-            fetchSuggestionFavicon(model, suggestion.getUrl(), mLargeIconBridge, mRunable);
+            fetchSuggestionFavicon(model, suggestion.getUrl());
         }
     }
 
-    @Mock
-    SuggestionHost mSuggestionHost;
-    @Mock
-    LargeIconBridge mIconBridge;
-    @Mock
-    Runnable mRunnable;
+    private @Mock SuggestionHost mSuggestionHost;
+    private @Mock FaviconFetcher mFaviconFetcher;
 
     private TestBaseSuggestionProcessor mProcessor;
     private AutocompleteMatch mSuggestion;
@@ -96,7 +88,7 @@
         MockitoAnnotations.initMocks(this);
         mBitmap = Bitmap.createBitmap(1, 1, Config.ALPHA_8);
         mProcessor = new TestBaseSuggestionProcessor(
-                ContextUtils.getApplicationContext(), mSuggestionHost, mIconBridge, mRunnable);
+                ContextUtils.getApplicationContext(), mSuggestionHost, mFaviconFetcher);
     }
 
     /**
@@ -111,15 +103,15 @@
     @Test
     @SmallTest
     public void suggestionFavicons_showFaviconWhenAvailable() {
-        final ArgumentCaptor<LargeIconCallback> callback =
-                ArgumentCaptor.forClass(LargeIconCallback.class);
+        final ArgumentCaptor<FaviconFetchCompleteListener> callback =
+                ArgumentCaptor.forClass(FaviconFetchCompleteListener.class);
         final GURL url = new GURL("http://url");
         createSuggestion(OmniboxSuggestionType.URL_WHAT_YOU_TYPED, url);
         SuggestionDrawableState icon1 = mModel.get(BaseSuggestionViewProperties.ICON);
         Assert.assertNotNull(icon1);
 
-        verify(mIconBridge).getLargeIconForUrl(eq(url), anyInt(), callback.capture());
-        callback.getValue().onLargeIconAvailable(mBitmap, 0, false, 0);
+        verify(mFaviconFetcher).fetchFaviconWithBackoff(eq(url), eq(false), callback.capture());
+        callback.getValue().onFaviconFetchComplete(mBitmap, FaviconType.REGULAR);
         SuggestionDrawableState icon2 = mModel.get(BaseSuggestionViewProperties.ICON);
         Assert.assertNotNull(icon2);
 
@@ -130,15 +122,15 @@
     @Test
     @SmallTest
     public void suggestionFavicons_doNotReplaceFallbackIconWhenNoFaviconIsAvailable() {
-        final ArgumentCaptor<LargeIconCallback> callback =
-                ArgumentCaptor.forClass(LargeIconCallback.class);
+        final ArgumentCaptor<FaviconFetchCompleteListener> callback =
+                ArgumentCaptor.forClass(FaviconFetchCompleteListener.class);
         final GURL url = new GURL("http://url");
         createSuggestion(OmniboxSuggestionType.URL_WHAT_YOU_TYPED, url);
         SuggestionDrawableState icon1 = mModel.get(BaseSuggestionViewProperties.ICON);
         Assert.assertNotNull(icon1);
 
-        verify(mIconBridge).getLargeIconForUrl(eq(url), anyInt(), callback.capture());
-        callback.getValue().onLargeIconAvailable(null, 0, false, 0);
+        verify(mFaviconFetcher).fetchFaviconWithBackoff(eq(url), eq(false), callback.capture());
+        callback.getValue().onFaviconFetchComplete(null, FaviconType.NONE);
         SuggestionDrawableState icon2 = mModel.get(BaseSuggestionViewProperties.ICON);
         Assert.assertNotNull(icon2);
 
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/omnibox/suggestions/editurl/EditUrlSuggestionUnitTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/omnibox/suggestions/editurl/EditUrlSuggestionUnitTest.java
index ba2536d..a2b12fd 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/omnibox/suggestions/editurl/EditUrlSuggestionUnitTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/omnibox/suggestions/editurl/EditUrlSuggestionUnitTest.java
@@ -32,6 +32,7 @@
 import org.chromium.base.test.UiThreadTest;
 import org.chromium.base.test.util.Batch;
 import org.chromium.chrome.browser.omnibox.OmniboxSuggestionType;
+import org.chromium.chrome.browser.omnibox.suggestions.FaviconFetcher;
 import org.chromium.chrome.browser.omnibox.suggestions.SuggestionHost;
 import org.chromium.chrome.browser.omnibox.suggestions.UrlBarDelegate;
 import org.chromium.chrome.browser.omnibox.suggestions.base.BaseSuggestionViewProperties;
@@ -43,7 +44,6 @@
 import org.chromium.chrome.browser.tab.SadTab;
 import org.chromium.chrome.browser.tab.Tab;
 import org.chromium.chrome.test.util.browser.Features;
-import org.chromium.components.favicon.LargeIconBridge;
 import org.chromium.components.omnibox.AutocompleteMatch;
 import org.chromium.components.search_engines.TemplateUrlService;
 import org.chromium.content_public.browser.test.NativeLibraryTestUtils;
@@ -108,7 +108,7 @@
     private View mSuggestionView;
 
     @Mock
-    private LargeIconBridge mIconBridge;
+    private FaviconFetcher mIconFetcher;
 
     @Mock
     private TemplateUrlService mTemplateUrlService;
@@ -140,8 +140,8 @@
             mModel = new PropertyModel.Builder(SuggestionViewProperties.ALL_KEYS).build();
 
             mProcessor = new EditUrlSuggestionProcessor(ContextUtils.getApplicationContext(),
-                    mSuggestionHost, mUrlBarDelegate,
-                    () -> mIconBridge, () -> mTab, () -> mShareDelegate);
+                    mSuggestionHost, mUrlBarDelegate, mIconFetcher,
+                    () -> mTab, () -> mShareDelegate);
         });
 
         doReturn(mTestUrl).when(mTab).getUrl();
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/suggestions/tile/TileGroupUnitTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/suggestions/tile/TileGroupUnitTest.java
index 119b326..1c4dc664 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/suggestions/tile/TileGroupUnitTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/suggestions/tile/TileGroupUnitTest.java
@@ -184,7 +184,11 @@
     @Test
     @UiThreadTest
     @SmallTest
-    public void testReceiveNewTilesWithDataChanges() {
+    @DisabledTest(
+            message =
+                    "https://crbug.com/1330627, https://crbug.com/1293208, https://crbug.com/1336742")
+    public void
+    testReceiveNewTilesWithDataChanges() {
         TileGroup tileGroup = initialiseTileGroup(URLS);
 
         // Notify the about different URLs, but the same number. #onTileCountChanged() should not be
diff --git a/chrome/app/chrome_exe_main_win.cc b/chrome/app/chrome_exe_main_win.cc
index 5ba728c0..b3672a15 100644
--- a/chrome/app/chrome_exe_main_win.cc
+++ b/chrome/app/chrome_exe_main_win.cc
@@ -19,6 +19,7 @@
 #include "base/command_line.h"
 #include "base/containers/cxx20_erase.h"
 #include "base/debug/alias.h"
+#include "base/debug/handle_hooks_win.h"
 #include "base/feature_list.h"
 #include "base/files/file_path.h"
 #include "base/files/file_util.h"
@@ -31,6 +32,7 @@
 #include "base/strings/string_util.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/time/time.h"
+#include "base/win/current_module.h"
 #include "base/win/registry.h"
 #include "base/win/win_util.h"
 #include "base/win/windows_version.h"
@@ -300,12 +302,20 @@
   const std::string process_type =
       command_line->GetSwitchValueASCII(switches::kProcessType);
 
+#if !defined(COMPONENT_BUILD) && DCHECK_IS_ON()
   // In non-component mode, chrome.exe contains a separate instance of
   // base::FeatureList. Prevent accidental use of this here by forbidding use of
   // the one that's linked with chrome.exe.
-#if !defined(COMPONENT_BUILD) && DCHECK_IS_ON()
   base::FeatureList::ForbidUseForCurrentModule();
-#endif
+
+  // Patch the main EXE on non-component builds when DCHECKs are enabled.
+  // This allows detection of third party code that might attempt to meddle with
+  // Chrome's handles. This must be done when single-threaded to avoid other
+  // threads attempting to make calls through the hooks while they are being
+  // emplaced.
+  // Note: The DLL is patched separately, in chrome/app/chrome_main.cc.
+  base::debug::HandleHooks::AddIATPatch(CURRENT_MODULE());
+#endif  // !defined(COMPONENT_BUILD) && !DCHECK_IS_ON()
 
   // Confirm that an explicit prefetch profile is used for all process types
   // except for the browser process. Any new process type will have to assign
diff --git a/chrome/app/chrome_main.cc b/chrome/app/chrome_main.cc
index 2a517628..e67ea60 100644
--- a/chrome/app/chrome_main.cc
+++ b/chrome/app/chrome_main.cc
@@ -32,6 +32,9 @@
 
 #if BUILDFLAG(IS_WIN)
 #include "base/allocator/buildflags.h"
+#include "base/dcheck_is_on.h"
+#include "base/debug/handle_hooks_win.h"
+#include "base/win/current_module.h"
 #if BUILDFLAG(USE_ALLOCATOR_SHIM)
 #include "base/allocator/allocator_shim.h"
 #endif
@@ -90,7 +93,16 @@
   base::UmaHistogramEnumeration("Windows.ChromeDllPrefetchResult",
                                 prefetch_result_code);
   install_static::InitializeFromPrimaryModule();
-#endif
+#if !defined(COMPONENT_BUILD) && DCHECK_IS_ON()
+  // Patch the main EXE on non-component builds when DCHECKs are enabled.
+  // This allows detection of third party code that might attempt to meddle with
+  // Chrome's handles. This must be done when single-threaded to avoid other
+  // threads attempting to make calls through the hooks while they are being
+  // emplaced.
+  // Note: The EXE is patched separately, in chrome/app/chrome_exe_main_win.cc.
+  base::debug::HandleHooks::AddIATPatch(CURRENT_MODULE());
+#endif  // !defined(COMPONENT_BUILD) && DCHECK_IS_ON()
+#endif  // BUILDFLAG(IS_WIN)
 
   ChromeMainDelegate chrome_main_delegate(
       base::TimeTicks::FromInternalValue(exe_entry_point_ticks));
diff --git a/chrome/app/generated_resources.grd b/chrome/app/generated_resources.grd
index 34f6e3d..e958565 100644
--- a/chrome/app/generated_resources.grd
+++ b/chrome/app/generated_resources.grd
@@ -2517,13 +2517,13 @@
                desc="Footer link in the Download Bubble">
         Show all downloads
       </message>
-      <message name="IDS_DOWNLOAD_BUBBLE_DOWNLOAD_SEPERATOR"
-               desc="A separator to place between download size and download time. A symbol, in this case a bullet •, with a white space before it and a white space after it, is used to separate them. If the bullet • resembles another symbol in the language, please translate the bullet • as a symbol (A) that is not easily mistaken for a number or letter in the language and (B) that is typically used to separate elements.">
-        ''' • '''
+      <message name="IDS_DOWNLOAD_BUBBLE_DOWNLOAD_STATUS_MESSAGE_WITH_SEPARATOR"
+               desc="The download status and message are separated by a symbol, in this case bullet •, with a white space before it and a white space after it. If the bullet • resembles another symbol in the language, please translate the bullet • as a symbol (A) that is not easily mistaken for a number or letter in the language and (B) that is typically used to separate elements.">
+        <ph name="STATUS">$1<ex>100/120 MB</ex></ph> • <ph name="MESSAGE">$2<ex>Opening in 10 seconds...</ex></ph>
       </message>
-      <message name="IDS_DOWNLOAD_BUBBLE_DOWNLOAD_SYMBOL"
-               desc="A symbol for active downloads, in this case downward arrow ↓. If the downward arrow ↓ resembles another symbol in the language, please translate the downward arrow ↓ as a symbol that indicates a downward arrow">
-        '''↓ '''
+      <message name="IDS_DOWNLOAD_BUBBLE_DOWNLOAD_STATUS_WITH_SYMBOL"
+               desc="Active downloads have a symbol associated with them, in this case a downward arrow ↓. If the downward arrow ↓ resembles another symbol in the language, please translate the downward arrow ↓ as a symbol that indicates a downward arrow">
+        ↓ <ph name="STATUS">$1<ex>100/120 MB</ex></ph>
       </message>
       <message name="IDS_DOWNLOAD_BUBBLE_STATUS_RESUMING"
                desc="When resuming a download, let the user know we're resuming the download.">
diff --git a/chrome/app/generated_resources_grd/IDS_DOWNLOAD_BUBBLE_DOWNLOAD_SEPERATOR.png.sha1 b/chrome/app/generated_resources_grd/IDS_DOWNLOAD_BUBBLE_DOWNLOAD_STATUS_MESSAGE_WITH_SEPARATOR.png.sha1
similarity index 100%
rename from chrome/app/generated_resources_grd/IDS_DOWNLOAD_BUBBLE_DOWNLOAD_SEPERATOR.png.sha1
rename to chrome/app/generated_resources_grd/IDS_DOWNLOAD_BUBBLE_DOWNLOAD_STATUS_MESSAGE_WITH_SEPARATOR.png.sha1
diff --git a/chrome/app/generated_resources_grd/IDS_DOWNLOAD_BUBBLE_DOWNLOAD_SYMBOL.png.sha1 b/chrome/app/generated_resources_grd/IDS_DOWNLOAD_BUBBLE_DOWNLOAD_STATUS_WITH_SYMBOL.png.sha1
similarity index 100%
rename from chrome/app/generated_resources_grd/IDS_DOWNLOAD_BUBBLE_DOWNLOAD_SYMBOL.png.sha1
rename to chrome/app/generated_resources_grd/IDS_DOWNLOAD_BUBBLE_DOWNLOAD_STATUS_WITH_SYMBOL.png.sha1
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn
index 1493ebf..74aa267 100644
--- a/chrome/browser/BUILD.gn
+++ b/chrome/browser/BUILD.gn
@@ -4147,14 +4147,8 @@
       "sharing/click_to_call/click_to_call_ui_controller.h",
       "sharing/shared_clipboard/remote_copy_message_handler.cc",
       "sharing/shared_clipboard/remote_copy_message_handler.h",
-      "sharing/shared_clipboard/shared_clipboard_context_menu_observer.cc",
-      "sharing/shared_clipboard/shared_clipboard_context_menu_observer.h",
       "sharing/shared_clipboard/shared_clipboard_message_handler_desktop.cc",
       "sharing/shared_clipboard/shared_clipboard_message_handler_desktop.h",
-      "sharing/shared_clipboard/shared_clipboard_ui_controller.cc",
-      "sharing/shared_clipboard/shared_clipboard_ui_controller.h",
-      "sharing/shared_clipboard/shared_clipboard_utils.cc",
-      "sharing/shared_clipboard/shared_clipboard_utils.h",
       "sharing/sharing_app.cc",
       "sharing/sharing_app.h",
       "sharing/sharing_dialog.h",
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc
index 85de9ef..990f7433 100644
--- a/chrome/browser/about_flags.cc
+++ b/chrome/browser/about_flags.cc
@@ -6443,6 +6443,12 @@
      kOsMac | kOsLinux | kOsLacros | kOsWin | kOsFuchsia,
      SINGLE_VALUE_TYPE(switches::kEnableUnsafeWebGPU)},
 
+    {"enable-webgpu-developer-features",
+     flag_descriptions::kWebGpuDeveloperFeaturesName,
+     flag_descriptions::kWebGpuDeveloperFeaturesDescription,
+     kOsMac | kOsLinux | kOsLacros | kOsWin | kOsFuchsia,
+     SINGLE_VALUE_TYPE(switches::kEnableWebGPUDeveloperFeatures)},
+
 #if BUILDFLAG(IS_ANDROID)
     {"autofill-use-mobile-label-disambiguation",
      flag_descriptions::kAutofillUseMobileLabelDisambiguationName,
diff --git a/chrome/browser/accessibility/live_caption_controller_browsertest.cc b/chrome/browser/accessibility/live_caption_controller_browsertest.cc
index 53210ab..56838e6c 100644
--- a/chrome/browser/accessibility/live_caption_controller_browsertest.cc
+++ b/chrome/browser/accessibility/live_caption_controller_browsertest.cc
@@ -109,7 +109,7 @@
 
   void OnErrorOnProfile(Profile* profile) {
     GetControllerForProfile(profile)->OnError(
-        GetCaptionBubbleContextBrowser(), CaptionBubbleErrorType::GENERIC,
+        GetCaptionBubbleContextBrowser(), CaptionBubbleErrorType::kGeneric,
         base::RepeatingClosure(),
         base::BindRepeating(
             [](CaptionBubbleErrorType error_type, bool checked) {}));
diff --git a/chrome/browser/accessibility/live_caption_speech_recognition_host.cc b/chrome/browser/accessibility/live_caption_speech_recognition_host.cc
index d85cba76..09109800 100644
--- a/chrome/browser/accessibility/live_caption_speech_recognition_host.cc
+++ b/chrome/browser/accessibility/live_caption_speech_recognition_host.cc
@@ -75,7 +75,7 @@
   LiveCaptionController* live_caption_controller = GetLiveCaptionController();
   if (live_caption_controller)
     live_caption_controller->OnError(
-        context_.get(), CaptionBubbleErrorType::GENERIC,
+        context_.get(), CaptionBubbleErrorType::kGeneric,
         base::RepeatingClosure(),
         base::BindRepeating(
             [](CaptionBubbleErrorType error_type, bool checked) {}));
diff --git a/chrome/browser/accessibility/live_caption_unavailability_notifier.cc b/chrome/browser/accessibility/live_caption_unavailability_notifier.cc
index a0e813a..cca178ea 100644
--- a/chrome/browser/accessibility/live_caption_unavailability_notifier.cc
+++ b/chrome/browser/accessibility/live_caption_unavailability_notifier.cc
@@ -133,7 +133,7 @@
   // received from another audio stream.
   live_caption_controller->OnError(
       context_.get(),
-      CaptionBubbleErrorType::MEDIA_FOUNDATION_RENDERER_UNSUPPORTED,
+      CaptionBubbleErrorType::kMediaFoundationRendererUnsupported,
       base::BindRepeating(&LiveCaptionUnavailabilityNotifier::
                               OnMediaFoundationRendererErrorClicked,
                           weak_factory_.GetWeakPtr()),
diff --git a/chrome/browser/accessibility/live_caption_unavailability_notifier_unittest.cc b/chrome/browser/accessibility/live_caption_unavailability_notifier_unittest.cc
index 45a39d48..e18fa2b 100644
--- a/chrome/browser/accessibility/live_caption_unavailability_notifier_unittest.cc
+++ b/chrome/browser/accessibility/live_caption_unavailability_notifier_unittest.cc
@@ -33,7 +33,7 @@
   void OnMediaFoundationRendererErrorDoNotShowAgainCheckboxClicked(
       bool checked) {
     notifier_->OnMediaFoundationRendererErrorDoNotShowAgainCheckboxClicked(
-        CaptionBubbleErrorType::MEDIA_FOUNDATION_RENDERER_UNSUPPORTED, checked);
+        CaptionBubbleErrorType::kMediaFoundationRendererUnsupported, checked);
   }
 
  protected:
diff --git a/chrome/browser/android/chrome_backup_agent.cc b/chrome/browser/android/chrome_backup_agent.cc
index 9850236c..37f44b3 100644
--- a/chrome/browser/android/chrome_backup_agent.cc
+++ b/chrome/browser/android/chrome_backup_agent.cc
@@ -20,7 +20,7 @@
 
 // TODO(crbug.com/1305213): The data type toggles shouldn't be individually
 // listed here.
-static_assert(39 == syncer::GetNumModelTypes(),
+static_assert(40 == syncer::GetNumModelTypes(),
               "If the new type has a corresponding pref, add it here");
 const char* backed_up_preferences_[] = {
     autofill::prefs::kAutofillWalletImportEnabled,
diff --git a/chrome/browser/ash/BUILD.gn b/chrome/browser/ash/BUILD.gn
index 6d2eefe..12402bfbb 100644
--- a/chrome/browser/ash/BUILD.gn
+++ b/chrome/browser/ash/BUILD.gn
@@ -1597,6 +1597,7 @@
     "//chromeos/dbus/userdataauth",
     "//chromeos/dbus/util",
     "//chromeos/dbus/virtual_file_provider",
+    "//chromeos/dbus/vm_plugin_dispatcher",
     "//chromeos/ime:gencode",
     "//chromeos/login/login_state",
     "//chromeos/services/assistant/public/cpp",
diff --git a/chrome/browser/ash/accessibility/dictation_browsertest.cc b/chrome/browser/ash/accessibility/dictation_browsertest.cc
index ebfc53f4..b57a1bc 100644
--- a/chrome/browser/ash/accessibility/dictation_browsertest.cc
+++ b/chrome/browser/ash/accessibility/dictation_browsertest.cc
@@ -1309,11 +1309,11 @@
       "Hello, world! Good afternoon; good evening? Goodnight, world.",
       "Hello, world! Good afternoon; good evening? Goodnight, world.");
   RunHiddenMacro(/*DELETE_PREV_SENT*/ 18);
-  WaitForTextAreaValue("Hello, world! Good afternoon; good evening? ");
+  WaitForTextAreaValue("Hello, world! Good afternoon; good evening?");
   RunHiddenMacro(/*DELETE_PREV_SENT*/ 18);
-  WaitForTextAreaValue("Hello, world! Good afternoon; ");
+  WaitForTextAreaValue("Hello, world! Good afternoon;");
   RunHiddenMacro(/*DELETE_PREV_SENT*/ 18);
-  WaitForTextAreaValue("Hello, world! ");
+  WaitForTextAreaValue("Hello, world!");
 }
 
 IN_PROC_BROWSER_TEST_P(DictationHiddenMacrosTest, DeletePrevSentTwoSentences) {
@@ -1322,7 +1322,7 @@
   SendFinalResultAndWaitForTextAreaValue("Hello, world. Goodnight, world.",
                                          "Hello, world. Goodnight, world.");
   RunHiddenMacro(/*DELETE_PREV_SENT*/ 18);
-  WaitForTextAreaValue("Hello, world. ");
+  WaitForTextAreaValue("Hello, world.");
 }
 
 IN_PROC_BROWSER_TEST_P(DictationHiddenMacrosTest,
@@ -1335,7 +1335,7 @@
   SendFinalResultAndWaitForCaretBoundsChanged("Move to the Previous character");
   SendFinalResultAndWaitForCaretBoundsChanged("Move to the Previous character");
   RunHiddenMacro(/*DELETE_PREV_SENT*/ 18);
-  WaitForTextAreaValue("Hello, world. d.");
+  WaitForTextAreaValue("Hello, world.d.");
 }
 
 IN_PROC_BROWSER_TEST_P(DictationHiddenMacrosTest, MoveByWord) {
@@ -1431,6 +1431,20 @@
   SendFinalResultAndWaitForTextAreaValue("delete", "This .");
 }
 
+IN_PROC_BROWSER_TEST_P(DictationHiddenMacrosTest, MoveBySentence) {
+  ToggleDictationWithKeystroke();
+  WaitForRecognitionStarted();
+  SendFinalResultAndWaitForTextAreaValue("Hello world! Goodnight world?",
+                                         "Hello world! Goodnight world?");
+  RunMacroAndWaitForCaretBoundsChanged(/*NAV_PREV_SENT*/ 26);
+  SendFinalResultAndWaitForTextAreaValue(
+      "Good evening.", "Hello world! Good evening. Goodnight world?");
+  RunMacroAndWaitForCaretBoundsChanged(/*NAV_NEXT_SENT*/ 25);
+  SendFinalResultAndWaitForTextAreaValue(
+      "Time for a midnight snack",
+      "Hello world! Good evening. Goodnight world? Time for a midnight snack");
+}
+
 // Tests behavior of Dictation and installation of Pumpkin.
 class DictationPumpkinInstallTest : public DictationTest {
  protected:
diff --git a/chrome/browser/ash/crosapi/browser_data_migrator_util.h b/chrome/browser/ash/crosapi/browser_data_migrator_util.h
index 50d2ef3..315665ff5 100644
--- a/chrome/browser/ash/crosapi/browser_data_migrator_util.h
+++ b/chrome/browser/ash/crosapi/browser_data_migrator_util.h
@@ -295,7 +295,7 @@
 };
 
 // List of data types in Sync Data that have to stay in Ash and Ash only.
-static_assert(39 == syncer::GetNumModelTypes(),
+static_assert(40 == syncer::GetNumModelTypes(),
               "If adding a new sync data type, update the lists below if"
               " you want to keep the new data type in Ash only.");
 constexpr syncer::ModelType kAshOnlySyncDataTypes[] = {
@@ -304,6 +304,7 @@
     syncer::ModelType::OS_PREFERENCES,
     syncer::ModelType::OS_PRIORITY_PREFERENCES,
     syncer::ModelType::PRINTERS,
+    syncer::ModelType::PRINTERS_AUTHORIZATION_SERVERS,
     syncer::ModelType::WIFI_CONFIGURATIONS,
     syncer::ModelType::WORKSPACE_DESK,
 };
diff --git a/chrome/browser/ash/crostini/ansible/ansible_management_service.cc b/chrome/browser/ash/crostini/ansible/ansible_management_service.cc
index 5a776eef..bd2184c 100644
--- a/chrome/browser/ash/crostini/ansible/ansible_management_service.cc
+++ b/chrome/browser/ash/crostini/ansible/ansible_management_service.cc
@@ -58,7 +58,7 @@
     std::move(callback).Run(false);
     return;
   }
-  if (container_id == ContainerId::GetDefault() &&
+  if (container_id == DefaultContainerId() &&
       !ShouldConfigureDefaultContainer(profile_)) {
     LOG(ERROR) << "Trying to configure default Crostini container when it "
                << "should not be configured";
@@ -251,7 +251,7 @@
     const ContainerId& container_id,
     bool success) {
   DCHECK_GT(configuration_tasks_.count(container_id), 0);
-  if (success && container_id == ContainerId::GetDefault()) {
+  if (success && container_id == DefaultContainerId()) {
     profile_->GetPrefs()->SetBoolean(prefs::kCrostiniDefaultContainerConfigured,
                                      true);
   }
diff --git a/chrome/browser/ash/crostini/ansible/ansible_management_service_unittest.cc b/chrome/browser/ash/crostini/ansible/ansible_management_service_unittest.cc
index 54db9ae..3b7211e 100644
--- a/chrome/browser/ash/crostini/ansible/ansible_management_service_unittest.cc
+++ b/chrome/browser/ash/crostini/ansible/ansible_management_service_unittest.cc
@@ -147,7 +147,7 @@
       vm_tools::cicerone::ApplyAnsiblePlaybookResponse::STARTED);
 
   ansible_management_service()->ConfigureContainer(
-      ContainerId::GetDefault(),
+      DefaultContainerId(),
       profile_->GetPrefs()->GetFilePath(
           prefs::kCrostiniAnsiblePlaybookFilePath),
       base::BindOnce(&AnsibleManagementServiceTest::ExpectTrueResult,
@@ -160,7 +160,7 @@
       vm_tools::cicerone::InstallLinuxPackageResponse::FAILED);
 
   ansible_management_service()->ConfigureContainer(
-      ContainerId::GetDefault(),
+      DefaultContainerId(),
       profile_->GetPrefs()->GetFilePath(
           prefs::kCrostiniAnsiblePlaybookFilePath),
       base::BindOnce(&AnsibleManagementServiceTest::ExpectFalseResult,
@@ -174,7 +174,7 @@
   SetInstallAnsibleStatus(false);
 
   ansible_management_service()->ConfigureContainer(
-      ContainerId::GetDefault(),
+      DefaultContainerId(),
       profile_->GetPrefs()->GetFilePath(
           prefs::kCrostiniAnsiblePlaybookFilePath),
       base::BindOnce(&AnsibleManagementServiceTest::ExpectFalseResult,
@@ -189,7 +189,7 @@
       vm_tools::cicerone::ApplyAnsiblePlaybookResponse::FAILED);
 
   ansible_management_service()->ConfigureContainer(
-      ContainerId::GetDefault(),
+      DefaultContainerId(),
       profile_->GetPrefs()->GetFilePath(
           prefs::kCrostiniAnsiblePlaybookFilePath),
       base::BindOnce(&AnsibleManagementServiceTest::ExpectFalseResult,
@@ -205,7 +205,7 @@
   SetApplyAnsibleStatus(false);
 
   ansible_management_service()->ConfigureContainer(
-      ContainerId::GetDefault(),
+      DefaultContainerId(),
       profile_->GetPrefs()->GetFilePath(
           prefs::kCrostiniAnsiblePlaybookFilePath),
       base::BindOnce(&AnsibleManagementServiceTest::ExpectFalseResult,
@@ -221,12 +221,12 @@
       vm_tools::cicerone::ApplyAnsiblePlaybookResponse::STARTED);
 
   ansible_management_service()->ConfigureContainer(
-      ContainerId::GetDefault(),
+      DefaultContainerId(),
       profile_->GetPrefs()->GetFilePath(
           prefs::kCrostiniAnsiblePlaybookFilePath),
       base::BindOnce(&ExpectResult, base::BindLambdaForTesting([&]() {
         ansible_management_service()->ConfigureContainer(
-            ContainerId::GetDefault(),
+            DefaultContainerId(),
             profile_->GetPrefs()->GetFilePath(
                 prefs::kCrostiniAnsiblePlaybookFilePath),
             base::BindOnce(&AnsibleManagementServiceTest::ExpectFalseResult,
@@ -243,7 +243,7 @@
 
   // Unsuccessful sequence of events.
   ansible_management_service()->ConfigureContainer(
-      ContainerId::GetDefault(),
+      DefaultContainerId(),
       profile_->GetPrefs()->GetFilePath(
           prefs::kCrostiniAnsiblePlaybookFilePath),
       base::BindOnce(&ExpectResult, base::BindLambdaForTesting([&]() {
@@ -253,7 +253,7 @@
         test_helper_->SetUpPlaybookApplication(
             vm_tools::cicerone::ApplyAnsiblePlaybookResponse::STARTED);
         ansible_management_service()->ConfigureContainer(
-            ContainerId::GetDefault(),
+            DefaultContainerId(),
             profile_->GetPrefs()->GetFilePath(
                 prefs::kCrostiniAnsiblePlaybookFilePath),
             base::BindOnce(&AnsibleManagementServiceTest::ExpectTrueResult,
diff --git a/chrome/browser/ash/crostini/crostini_export_import.cc b/chrome/browser/ash/crostini/crostini_export_import.cc
index bece978..90284cac 100644
--- a/chrome/browser/ash/crostini/crostini_export_import.cc
+++ b/chrome/browser/ash/crostini/crostini_export_import.cc
@@ -128,7 +128,7 @@
 
 CrostiniExportImport::OperationData* CrostiniExportImport::NewOperationData(
     ExportImportType type) {
-  return NewOperationData(type, ContainerId::GetDefault());
+  return NewOperationData(type, DefaultContainerId());
 }
 
 void CrostiniExportImport::ExportContainer(ContainerId container_id,
diff --git a/chrome/browser/ash/crostini/crostini_installer.cc b/chrome/browser/ash/crostini/crostini_installer.cc
index 1f4b5157..cc575f6 100644
--- a/chrome/browser/ash/crostini/crostini_installer.cc
+++ b/chrome/browser/ash/crostini/crostini_installer.cc
@@ -626,7 +626,7 @@
   if (!skip_launching_terminal_for_testing_) {
     // kInvalidDisplayId will launch terminal on the current active display.
     crostini::LaunchTerminal(profile_, display::kInvalidDisplayId,
-                             crostini::ContainerId::GetDefault());
+                             crostini::DefaultContainerId());
   }
 }
 
@@ -665,7 +665,7 @@
   restart_id_ =
       crostini::CrostiniManager::GetForProfile(profile_)
           ->RestartCrostiniWithOptions(
-              crostini::ContainerId::GetDefault(), std::move(restart_options_),
+              crostini::DefaultContainerId(), std::move(restart_options_),
               base::BindOnce(&CrostiniInstaller::OnCrostiniRestartFinished,
                              weak_ptr_factory_.GetWeakPtr()),
               this);
diff --git a/chrome/browser/ash/crostini/crostini_manager.cc b/chrome/browser/ash/crostini/crostini_manager.cc
index 52fecc8..a34a8ea 100644
--- a/chrome/browser/ash/crostini/crostini_manager.cc
+++ b/chrome/browser/ash/crostini/crostini_manager.cc
@@ -535,7 +535,7 @@
   // Additional setup might be required in case of default Crostini container
   // such as installing Ansible in default container and applying
   // pre-determined configuration to the default container.
-  if (container_id_ == ContainerId::GetDefault() &&
+  if (container_id_ == DefaultContainerId() &&
       ShouldConfigureDefaultContainer(profile_)) {
     requests_[0].options.ansible_playbook = profile_->GetPrefs()->GetFilePath(
         prefs::kCrostiniAnsiblePlaybookFilePath);
@@ -555,7 +555,7 @@
   // it's possible in tests to end up here without a running container. Don't
   // try mounting sshfs in that case.
   auto info = crostini_manager_->GetContainerInfo(container_id_);
-  if (container_id_ == ContainerId::GetDefault() && info) {
+  if (container_id_ == DefaultContainerId() && info) {
     crostini_manager_->MountCrostiniFiles(container_id_, base::DoNothing(),
                                           true);
   }
@@ -1168,7 +1168,7 @@
     // Already shown the upgrade dialog.
     return false;
   }
-  if (container_id != ContainerId::GetDefault()) {
+  if (container_id != DefaultContainerId()) {
     return false;
   }
   bool upgradable = IsContainerUpgradeable(container_id);
@@ -2561,7 +2561,7 @@
   // The UI can only resize the default VM, so only (maybe) show the
   // notification for the default VM, if we got a value, and if the value isn't
   // an error (the API we call for space returns -1 on error).
-  if (vm_name == ContainerId::GetDefault().vm_name &&
+  if (vm_name == DefaultContainerId().vm_name &&
       response->free_bytes_has_value() && response->free_bytes() >= 0) {
     low_disk_notifier_->ShowNotificationIfAppropriate(response->free_bytes());
   }
@@ -3866,8 +3866,8 @@
 
 void CrostiniManager::SuspendImminent(
     power_manager::SuspendImminent::Reason reason) {
-  auto info = GetContainerInfo(ContainerId::GetDefault());
-  if (!crostini_sshfs_->IsSshfsMounted(ContainerId::GetDefault())) {
+  auto info = GetContainerInfo(DefaultContainerId());
+  if (!crostini_sshfs_->IsSshfsMounted(DefaultContainerId())) {
     return;
   }
 
@@ -3875,7 +3875,7 @@
   auto token = base::UnguessableToken::Create();
   chromeos::PowerManagerClient::Get()->BlockSuspend(token, "CrostiniManager");
   crostini_sshfs_->UnmountCrostiniFiles(
-      ContainerId::GetDefault(),
+      DefaultContainerId(),
       base::BindOnce(&CrostiniManager::OnRemoveSshfsCrostiniVolume,
                      weak_ptr_factory_.GetWeakPtr(), token));
 }
@@ -3883,7 +3883,7 @@
 void CrostiniManager::SuspendDone(base::TimeDelta sleep_duration) {
   // https://crbug.com/968060.  Sshfs is unmounted before suspend,
   // call RestartCrostini to force remount if container is running.
-  ContainerId container_id = ContainerId::GetDefault();
+  ContainerId container_id = DefaultContainerId();
   if (GetContainerInfo(container_id)) {
     // TODO(crbug/1142321): Double-check if anything breaks if we change this
     // to just remount the sshfs mounts, in particular check 9p mounts.
@@ -3903,7 +3903,7 @@
 
 void CrostiniManager::RemoveUncleanSshfsMounts() {
   // TODO(crbug/1142321): Success metrics
-  crostini_sshfs_->UnmountCrostiniFiles(ContainerId::GetDefault(),
+  crostini_sshfs_->UnmountCrostiniFiles(DefaultContainerId(),
                                         base::DoNothing());
 }
 
diff --git a/chrome/browser/ash/crostini/crostini_manager_unittest.cc b/chrome/browser/ash/crostini/crostini_manager_unittest.cc
index 0de6899..56714d2 100644
--- a/chrome/browser/ash/crostini/crostini_manager_unittest.cc
+++ b/chrome/browser/ash/crostini/crostini_manager_unittest.cc
@@ -523,7 +523,7 @@
 
   EnsureTerminaInstalled();
   crostini_manager()->StartTerminaVm(
-      ContainerId::GetDefault().vm_name, disk_path, {}, 0,
+      DefaultContainerId().vm_name, disk_path, {}, 0,
       base::BindOnce(&ExpectSuccess, run_loop()->QuitClosure()));
   run_loop()->Run();
 
@@ -545,7 +545,7 @@
 
   EnsureTerminaInstalled();
   crostini_manager()->StartTerminaVm(
-      ContainerId::GetDefault().vm_name, disk_path, {}, 0,
+      DefaultContainerId().vm_name, disk_path, {}, 0,
       base::BindOnce(&ExpectSuccess, run_loop()->QuitClosure()));
   run_loop()->Run();
 
@@ -1165,7 +1165,7 @@
 TEST_F(CrostiniManagerRestartTest, CancelOnContainerCreated) {
   cancel_on_container_created_ = true;
   restart_id_ = crostini_manager()->RestartCrostini(
-      ContainerId::GetDefault(),
+      DefaultContainerId(),
       base::BindOnce(&CrostiniManagerRestartTest::RestartCrostiniCallback,
                      base::Unretained(this), run_loop()->QuitClosure()),
       this);
@@ -1180,7 +1180,7 @@
   fake_cicerone_client_->set_send_create_lxd_container_response_delay(
       base::TimeDelta::Max());
   restart_id_ = crostini_manager()->RestartCrostini(
-      ContainerId::GetDefault(),
+      DefaultContainerId(),
       base::BindOnce(&CrostiniManagerRestartTest::RestartCrostiniCallback,
                      base::Unretained(this), run_loop()->QuitClosure()),
       this);
@@ -1194,7 +1194,7 @@
   fake_cicerone_client_->set_send_notify_lxd_container_created_signal_delay(
       base::TimeDelta::Max());
   restart_id_ = crostini_manager()->RestartCrostini(
-      ContainerId::GetDefault(),
+      DefaultContainerId(),
       base::BindOnce(&CrostiniManagerRestartTest::RestartCrostiniCallback,
                      base::Unretained(this), run_loop()->QuitClosure()),
       this);
@@ -1210,12 +1210,12 @@
   fake_cicerone_client_->set_send_notify_lxd_container_created_signal_delay(
       base::TimeDelta::Max());
   vm_tools::cicerone::LxdContainerDownloadingSignal signal;
-  signal.set_container_name(ContainerId::GetDefault().container_name);
-  signal.set_vm_name(ContainerId::GetDefault().vm_name);
+  signal.set_container_name(DefaultContainerId().container_name);
+  signal.set_vm_name(DefaultContainerId().vm_name);
   signal.set_owner_id(CryptohomeIdForProfile(profile()));
 
   restart_id_ = crostini_manager()->RestartCrostini(
-      ContainerId::GetDefault(),
+      DefaultContainerId(),
       base::BindOnce(&CrostiniManagerRestartTest::RestartCrostiniCallback,
                      base::Unretained(this), run_loop()->QuitClosure()),
       this);
@@ -1239,7 +1239,7 @@
   fake_cicerone_client_->set_lxd_container_created_signal_status(
       vm_tools::cicerone::LxdContainerCreatedSignal::UNKNOWN);
   restart_id_ = crostini_manager()->RestartCrostini(
-      ContainerId::GetDefault(),
+      DefaultContainerId(),
       base::BindOnce(&ExpectCrostiniResult, run_loop()->QuitClosure(),
                      CrostiniResult::UNKNOWN_ERROR),
       this);
@@ -1254,7 +1254,7 @@
 TEST_F(CrostiniManagerRestartTest, CancelOnContainerStarted) {
   cancel_on_container_started_ = true;
   restart_id_ = crostini_manager()->RestartCrostini(
-      ContainerId::GetDefault(),
+      DefaultContainerId(),
       base::BindOnce(&CrostiniManagerRestartTest::RestartCrostiniCallback,
                      base::Unretained(this), run_loop()->QuitClosure()),
       this);
@@ -1268,7 +1268,7 @@
 TEST_F(CrostiniManagerRestartTest, CancelOnContainerSetup) {
   cancel_on_container_setup_ = true;
   restart_id_ = crostini_manager()->RestartCrostini(
-      ContainerId::GetDefault(),
+      DefaultContainerId(),
       base::BindOnce(&CrostiniManagerRestartTest::RestartCrostiniCallback,
                      base::Unretained(this), run_loop()->QuitClosure()),
       this);
@@ -1283,7 +1283,7 @@
   fake_cicerone_client_->set_send_set_up_lxd_container_user_response_delay(
       base::TimeDelta::Max());
   restart_id_ = crostini_manager()->RestartCrostini(
-      ContainerId::GetDefault(),
+      DefaultContainerId(),
       base::BindOnce(&CrostiniManagerRestartTest::RestartCrostiniCallback,
                      base::Unretained(this), run_loop()->QuitClosure()),
       this);
@@ -1299,7 +1299,7 @@
   fake_cicerone_client_->set_send_start_lxd_container_response_delay(
       base::TimeDelta::Max());
   restart_id_ = crostini_manager()->RestartCrostini(
-      ContainerId::GetDefault(),
+      DefaultContainerId(),
       base::BindOnce(&CrostiniManagerRestartTest::RestartCrostiniCallback,
                      base::Unretained(this), run_loop()->QuitClosure()),
       this);
@@ -1313,7 +1313,7 @@
   fake_cicerone_client_->set_send_container_started_signal_delay(
       base::TimeDelta::Max());
   restart_id_ = crostini_manager()->RestartCrostini(
-      ContainerId::GetDefault(),
+      DefaultContainerId(),
       base::BindOnce(&CrostiniManagerRestartTest::RestartCrostiniCallback,
                      base::Unretained(this), run_loop()->QuitClosure()),
       this);
@@ -1330,13 +1330,13 @@
   fake_cicerone_client_->set_send_container_started_signal_delay(
       base::TimeDelta::Max());
   vm_tools::cicerone::LxdContainerStartingSignal signal;
-  signal.set_container_name(ContainerId::GetDefault().container_name);
-  signal.set_vm_name(ContainerId::GetDefault().vm_name);
+  signal.set_container_name(DefaultContainerId().container_name);
+  signal.set_vm_name(DefaultContainerId().vm_name);
   signal.set_owner_id(CryptohomeIdForProfile(profile()));
   signal.set_status(vm_tools::cicerone::LxdContainerStartingSignal::STARTING);
 
   restart_id_ = crostini_manager()->RestartCrostini(
-      ContainerId::GetDefault(),
+      DefaultContainerId(),
       base::BindOnce(&CrostiniManagerRestartTest::RestartCrostiniCallback,
                      base::Unretained(this), run_loop()->QuitClosure()),
       this);
@@ -2036,7 +2036,7 @@
       response);
 
   restart_id_ = crostini_manager()->RestartCrostini(
-      ContainerId::GetDefault(),
+      DefaultContainerId(),
       base::BindOnce(&CrostiniManagerRestartTest::RestartCrostiniCallback,
                      base::Unretained(this), run_loop()->QuitClosure()),
       this);
@@ -2062,7 +2062,7 @@
       response);
 
   restart_id_ = crostini_manager()->RestartCrostini(
-      ContainerId::GetDefault(),
+      DefaultContainerId(),
       base::BindOnce(&CrostiniManagerRestartTest::RestartCrostiniCallback,
                      base::Unretained(this), run_loop()->QuitClosure()),
       this);
@@ -2424,7 +2424,7 @@
       vm_tools::cicerone::InstallLinuxPackageResponse::FAILED);
 
   crostini_manager()->RestartCrostini(
-      ContainerId::GetDefault(),
+      DefaultContainerId(),
       base::BindOnce(&ExpectCrostiniResult, run_loop()->QuitClosure(),
                      CrostiniResult::CONTAINER_CONFIGURATION_FAILED),
       this);
@@ -2438,7 +2438,7 @@
   SetInstallAnsibleStatus(false);
 
   crostini_manager()->RestartCrostini(
-      ContainerId::GetDefault(),
+      DefaultContainerId(),
       base::BindOnce(&ExpectCrostiniResult, run_loop()->QuitClosure(),
                      CrostiniResult::CONTAINER_CONFIGURATION_FAILED),
       this);
@@ -2453,7 +2453,7 @@
       vm_tools::cicerone::ApplyAnsiblePlaybookResponse::FAILED);
 
   crostini_manager()->RestartCrostini(
-      ContainerId::GetDefault(),
+      DefaultContainerId(),
       base::BindOnce(&ExpectCrostiniResult, run_loop()->QuitClosure(),
                      CrostiniResult::CONTAINER_CONFIGURATION_FAILED),
       this);
@@ -2470,7 +2470,7 @@
   SetApplyAnsibleStatus(false);
 
   crostini_manager()->RestartCrostini(
-      ContainerId::GetDefault(),
+      DefaultContainerId(),
       base::BindOnce(&ExpectCrostiniResult, run_loop()->QuitClosure(),
                      CrostiniResult::CONTAINER_CONFIGURATION_FAILED),
       this);
@@ -2485,7 +2485,7 @@
       vm_tools::cicerone::ApplyAnsiblePlaybookResponse::STARTED);
 
   crostini_manager()->RestartCrostini(
-      ContainerId::GetDefault(),
+      DefaultContainerId(),
       base::BindOnce(&ExpectCrostiniResult, run_loop()->QuitClosure(),
                      CrostiniResult::SUCCESS),
       this);
diff --git a/chrome/browser/ash/crostini/crostini_package_notification_unittest.cc b/chrome/browser/ash/crostini/crostini_package_notification_unittest.cc
index 344685e..5f7afc21 100644
--- a/chrome/browser/ash/crostini/crostini_package_notification_unittest.cc
+++ b/chrome/browser/ash/crostini/crostini_package_notification_unittest.cc
@@ -71,8 +71,8 @@
   CrostiniPackageNotification notification(
       profile_.get(),
       CrostiniPackageNotification::NotificationType::PACKAGE_INSTALL,
-      PackageOperationStatus::RUNNING, ContainerId::GetDefault(),
-      std::u16string(), kNotificationId, service_.get());
+      PackageOperationStatus::RUNNING, DefaultContainerId(), std::u16string(),
+      kNotificationId, service_.get());
 
   notification.UpdateProgress(PackageOperationStatus::SUCCEEDED, 100);
   EXPECT_EQ(notification.GetButtonCountForTesting(), 0);
@@ -82,8 +82,8 @@
   CrostiniPackageNotification notification(
       profile_.get(),
       CrostiniPackageNotification::NotificationType::PACKAGE_INSTALL,
-      PackageOperationStatus::RUNNING, ContainerId::GetDefault(),
-      std::u16string(), kNotificationId, service_.get());
+      PackageOperationStatus::RUNNING, DefaultContainerId(), std::u16string(),
+      kNotificationId, service_.get());
 
   auto app = CrostiniTestHelper::BasicApp(kDefaultAppFileId);
   crostini_test_helper_->AddApp(app);
@@ -96,8 +96,8 @@
   CrostiniPackageNotification notification(
       profile_.get(),
       CrostiniPackageNotification::NotificationType::PACKAGE_INSTALL,
-      PackageOperationStatus::RUNNING, ContainerId::GetDefault(),
-      std::u16string(), kNotificationId, service_.get());
+      PackageOperationStatus::RUNNING, DefaultContainerId(), std::u16string(),
+      kNotificationId, service_.get());
 
   auto app = CrostiniTestHelper::BasicApp(kDefaultAppFileId);
   crostini_test_helper_->AddApp(app);
diff --git a/chrome/browser/ash/crostini/crostini_package_service_unittest.cc b/chrome/browser/ash/crostini/crostini_package_service_unittest.cc
index 854e3a3..122f21f 100644
--- a/chrome/browser/ash/crostini/crostini_package_service_unittest.cc
+++ b/chrome/browser/ash/crostini/crostini_package_service_unittest.cc
@@ -1023,7 +1023,7 @@
 TEST_F(CrostiniPackageServiceTest, UninstallNotificationWaitsForAppListUpdate) {
   service_->QueueUninstallApplication(kDefaultAppId);
 
-  SendAppListUpdateSignal(ContainerId::GetDefault(), 1);
+  SendAppListUpdateSignal(DefaultContainerId(), 1);
 
   StartAndSignalUninstall(UninstallPackageProgressSignal::SUCCEEDED);
 
@@ -1033,7 +1033,7 @@
       UnorderedElementsAre(
           IsUninstallWaitingForAppListNotification(DEFAULT_APP)));
 
-  SendAppListUpdateSignal(ContainerId::GetDefault(), 0);
+  SendAppListUpdateSignal(DefaultContainerId(), 0);
 
   EXPECT_THAT(
       Printable(notification_display_service_->GetDisplayedNotificationsForType(
@@ -1045,7 +1045,7 @@
        UninstallNotificationDoesntWaitForAppListUpdate) {
   service_->QueueUninstallApplication(kDefaultAppId);
 
-  SendAppListUpdateSignal(ContainerId::GetDefault(), 0);
+  SendAppListUpdateSignal(DefaultContainerId(), 0);
 
   StartAndSignalUninstall(UninstallPackageProgressSignal::SUCCEEDED);
 
@@ -1054,7 +1054,7 @@
           NotificationHandler::Type::TRANSIENT)),
       UnorderedElementsAre(IsUninstallSuccessNotification(DEFAULT_APP)));
 
-  SendAppListUpdateSignal(ContainerId::GetDefault(), 1);
+  SendAppListUpdateSignal(DefaultContainerId(), 1);
 
   EXPECT_THAT(
       Printable(notification_display_service_->GetDisplayedNotificationsForType(
@@ -1710,7 +1710,7 @@
 TEST_F(CrostiniPackageServiceTest, InstallDisplaysProgressNotificationOnStart) {
   base::RunLoop run_loop;
   service_->QueueInstallLinuxPackage(
-      ContainerId::GetDefault(), package_file_url_,
+      DefaultContainerId(), package_file_url_,
       base::BindOnce(&ExpectedCrostiniResult, run_loop.QuitClosure(),
                      CrostiniResult::SUCCESS));
   run_loop.Run();
@@ -1723,8 +1723,8 @@
 
 TEST_F(CrostiniPackageServiceTest,
        InstallUpdatesProgressNotificationOnDownloadingSignal) {
-  service_->QueueInstallLinuxPackage(ContainerId::GetDefault(),
-                                     package_file_url_, base::DoNothing());
+  service_->QueueInstallLinuxPackage(DefaultContainerId(), package_file_url_,
+                                     base::DoNothing());
   StartAndSignalInstall(InstallLinuxPackageProgressSignal::DOWNLOADING,
                         44 /*progress_percent*/);
 
@@ -1737,8 +1737,8 @@
 
 TEST_F(CrostiniPackageServiceTest,
        InstallUpdatesProgressNotificationOnInstallingSignal) {
-  service_->QueueInstallLinuxPackage(ContainerId::GetDefault(),
-                                     package_file_url_, base::DoNothing());
+  service_->QueueInstallLinuxPackage(DefaultContainerId(), package_file_url_,
+                                     base::DoNothing());
   StartAndSignalInstall(InstallLinuxPackageProgressSignal::INSTALLING,
                         44 /*progress_percent*/);
 
@@ -1751,8 +1751,8 @@
 
 TEST_F(CrostiniPackageServiceTest,
        InstallDisplaysSuccessNotificationOnSuccessSignal) {
-  service_->QueueInstallLinuxPackage(ContainerId::GetDefault(),
-                                     package_file_url_, base::DoNothing());
+  service_->QueueInstallLinuxPackage(DefaultContainerId(), package_file_url_,
+                                     base::DoNothing());
   StartAndSignalInstall(InstallLinuxPackageProgressSignal::SUCCEEDED);
 
   EXPECT_THAT(
@@ -1763,8 +1763,8 @@
 
 TEST_F(CrostiniPackageServiceTest,
        InstallDisplaysFailureNotificationOnFailedSignal) {
-  service_->QueueInstallLinuxPackage(ContainerId::GetDefault(),
-                                     package_file_url_, base::DoNothing());
+  service_->QueueInstallLinuxPackage(DefaultContainerId(), package_file_url_,
+                                     base::DoNothing());
   StartAndSignalInstall(InstallLinuxPackageProgressSignal::FAILED);
 
   EXPECT_THAT(
@@ -1774,11 +1774,11 @@
 }
 
 TEST_F(CrostiniPackageServiceTest, InstallNotificationWaitsForAppListUpdate) {
-  service_->QueueInstallLinuxPackage(ContainerId::GetDefault(),
-                                     package_file_url_, base::DoNothing());
+  service_->QueueInstallLinuxPackage(DefaultContainerId(), package_file_url_,
+                                     base::DoNothing());
   base::RunLoop().RunUntilIdle();
 
-  SendAppListUpdateSignal(ContainerId::GetDefault(), 1);
+  SendAppListUpdateSignal(DefaultContainerId(), 1);
 
   StartAndSignalInstall(InstallLinuxPackageProgressSignal::SUCCEEDED);
 
@@ -1787,7 +1787,7 @@
           NotificationHandler::Type::TRANSIENT)),
       UnorderedElementsAre(IsInstallWaitingForAppListNotification()));
 
-  SendAppListUpdateSignal(ContainerId::GetDefault(), 0);
+  SendAppListUpdateSignal(DefaultContainerId(), 0);
 
   EXPECT_THAT(
       Printable(notification_display_service_->GetDisplayedNotificationsForType(
@@ -1797,11 +1797,11 @@
 
 TEST_F(CrostiniPackageServiceTest,
        InstallNotificationDoesntWaitForAppListUpdate) {
-  service_->QueueInstallLinuxPackage(ContainerId::GetDefault(),
-                                     package_file_url_, base::DoNothing());
+  service_->QueueInstallLinuxPackage(DefaultContainerId(), package_file_url_,
+                                     base::DoNothing());
   base::RunLoop().RunUntilIdle();
 
-  SendAppListUpdateSignal(ContainerId::GetDefault(), 0);
+  SendAppListUpdateSignal(DefaultContainerId(), 0);
 
   StartAndSignalInstall(InstallLinuxPackageProgressSignal::SUCCEEDED);
 
@@ -1810,7 +1810,7 @@
           NotificationHandler::Type::TRANSIENT)),
       UnorderedElementsAre(IsInstallSuccessNotification()));
 
-  SendAppListUpdateSignal(ContainerId::GetDefault(), 1);
+  SendAppListUpdateSignal(DefaultContainerId(), 1);
 
   EXPECT_THAT(
       Printable(notification_display_service_->GetDisplayedNotificationsForType(
@@ -1822,8 +1822,8 @@
        InstallNotificationAppListUpdatesAreVmSpecific) {
   InstallLinuxPackageRequest request;
 
-  service_->QueueInstallLinuxPackage(ContainerId::GetDefault(),
-                                     package_file_url_, base::DoNothing());
+  service_->QueueInstallLinuxPackage(DefaultContainerId(), package_file_url_,
+                                     base::DoNothing());
   request =
       fake_cicerone_client_->get_most_recent_install_linux_package_request();
   InstallLinuxPackageProgressSignal signal_progress =
@@ -1855,8 +1855,8 @@
 
 TEST_F(CrostiniPackageServiceTest,
        InstallNotificationAppListUpdatesFromUnknownContainersAreIgnored) {
-  service_->QueueInstallLinuxPackage(ContainerId::GetDefault(),
-                                     package_file_url_, base::DoNothing());
+  service_->QueueInstallLinuxPackage(DefaultContainerId(), package_file_url_,
+                                     base::DoNothing());
 
   base::RunLoop().RunUntilIdle();
 
@@ -1872,8 +1872,8 @@
 }
 
 TEST_F(CrostiniPackageServiceTest, InstallNotificationFailsOnVmShutdown) {
-  service_->QueueInstallLinuxPackage(ContainerId::GetDefault(),
-                                     package_file_url_, base::DoNothing());
+  service_->QueueInstallLinuxPackage(DefaultContainerId(), package_file_url_,
+                                     base::DoNothing());
 
   base::RunLoop().RunUntilIdle();
 
@@ -1897,8 +1897,8 @@
 }
 
 TEST_F(CrostiniPackageServiceTest, UninstallsQueuesBehindStartingUpInstall) {
-  service_->QueueInstallLinuxPackage(ContainerId::GetDefault(),
-                                     package_file_url_, base::DoNothing());
+  service_->QueueInstallLinuxPackage(DefaultContainerId(), package_file_url_,
+                                     base::DoNothing());
   service_->QueueUninstallApplication(kDefaultAppId);
 
   // Install doesn't show a notification until it gets a response, but uninstall
@@ -1913,7 +1913,7 @@
 TEST_F(CrostiniPackageServiceTest, InstallRunsInFrontOfQueuedUninstall) {
   base::RunLoop run_loop;
   service_->QueueInstallLinuxPackage(
-      ContainerId::GetDefault(), package_file_url_,
+      DefaultContainerId(), package_file_url_,
       base::BindOnce(&ExpectedCrostiniResult, run_loop.QuitClosure(),
                      CrostiniResult::SUCCESS));
   service_->QueueUninstallApplication(kDefaultAppId);
@@ -1932,8 +1932,8 @@
 }
 
 TEST_F(CrostiniPackageServiceTest, QueuedUninstallRunsAfterCompletedInstall) {
-  service_->QueueInstallLinuxPackage(ContainerId::GetDefault(),
-                                     package_file_url_, base::DoNothing());
+  service_->QueueInstallLinuxPackage(DefaultContainerId(), package_file_url_,
+                                     base::DoNothing());
   service_->QueueUninstallApplication(kDefaultAppId);
   StartAndSignalInstall(InstallLinuxPackageProgressSignal::SUCCEEDED);
 
@@ -1957,8 +1957,8 @@
   response.set_status(InstallLinuxPackageResponse::FAILED);
   response.set_failure_reason("No such file");
   fake_cicerone_client_->set_install_linux_package_response(response);
-  service_->QueueInstallLinuxPackage(ContainerId::GetDefault(),
-                                     package_file_url_, base::DoNothing());
+  service_->QueueInstallLinuxPackage(DefaultContainerId(), package_file_url_,
+                                     base::DoNothing());
   service_->QueueUninstallApplication(kDefaultAppId);
 
   UninstallPackageOwningFileRequest request;
@@ -1978,8 +1978,8 @@
 
 TEST_F(CrostiniPackageServiceTest,
        QueuedUninstallRunsAfterFailedInstallSignal) {
-  service_->QueueInstallLinuxPackage(ContainerId::GetDefault(),
-                                     package_file_url_, base::DoNothing());
+  service_->QueueInstallLinuxPackage(DefaultContainerId(), package_file_url_,
+                                     base::DoNothing());
   service_->QueueUninstallApplication(kDefaultAppId);
   StartAndSignalInstall(InstallLinuxPackageProgressSignal::FAILED);
 
diff --git a/chrome/browser/ash/crostini/crostini_port_forwarder_unittest.cc b/chrome/browser/ash/crostini/crostini_port_forwarder_unittest.cc
index 909d694..fa29a5f4 100644
--- a/chrome/browser/ash/crostini/crostini_port_forwarder_unittest.cc
+++ b/chrome/browser/ash/crostini/crostini_port_forwarder_unittest.cc
@@ -31,7 +31,7 @@
 class CrostiniPortForwarderTest : public testing::Test {
  public:
   CrostiniPortForwarderTest()
-      : default_container_id_(ContainerId::GetDefault()),
+      : default_container_id_(DefaultContainerId()),
         other_container_id_(ContainerId("other", "other")),
         inactive_container_id_(ContainerId("inactive", "inactive")) {}
 
diff --git a/chrome/browser/ash/crostini/crostini_sshfs.cc b/chrome/browser/ash/crostini/crostini_sshfs.cc
index 34a4067..2871348fa 100644
--- a/chrome/browser/ash/crostini/crostini_sshfs.cc
+++ b/chrome/browser/ash/crostini/crostini_sshfs.cc
@@ -97,7 +97,7 @@
     return;
   }
 
-  if (container_id != ContainerId::GetDefault()) {
+  if (container_id != DefaultContainerId()) {
     LOG(ERROR) << "Unable to mount files for non-default container";
     Finish(CrostiniSshfsResult::kNotDefaultContainer);
     return;
diff --git a/chrome/browser/ash/crostini/crostini_sshfs_unittest.cc b/chrome/browser/ash/crostini/crostini_sshfs_unittest.cc
index 5639234..ec14b228 100644
--- a/chrome/browser/ash/crostini/crostini_sshfs_unittest.cc
+++ b/chrome/browser/ash/crostini/crostini_sshfs_unittest.cc
@@ -167,12 +167,12 @@
 };
 
 TEST_F(CrostiniSshfsHelperTest, MountDiskMountsDisk) {
-  SetContainerRunning(ContainerId::GetDefault());
+  SetContainerRunning(DefaultContainerId());
   ExpectMountCalls(1);
   bool result = false;
 
   crostini_sshfs_->MountCrostiniFiles(
-      ContainerId::GetDefault(),
+      DefaultContainerId(),
       base::BindLambdaForTesting([&result](bool res) { result = res; }), true);
   task_environment_.RunUntilIdle();
 
@@ -193,7 +193,7 @@
   EXPECT_CALL(*disk_manager_, MountPath).Times(0);
 
   crostini_sshfs_->MountCrostiniFiles(
-      ContainerId::GetDefault(),
+      DefaultContainerId(),
       base::BindLambdaForTesting([&result](bool res) { result = res; }), false);
   task_environment_.RunUntilIdle();
 
@@ -240,17 +240,17 @@
 }
 
 TEST_F(CrostiniSshfsHelperTest, MultipleCallsAreQueuedAndOnlyMountOnce) {
-  SetContainerRunning(ContainerId::GetDefault());
+  SetContainerRunning(DefaultContainerId());
 
   ExpectMountCalls(1);
   int successes = 0;
   crostini_sshfs_->MountCrostiniFiles(
-      ContainerId::GetDefault(),
+      DefaultContainerId(),
       base::BindLambdaForTesting(
           [&successes](bool result) { successes += result; }),
       false);
   crostini_sshfs_->MountCrostiniFiles(
-      ContainerId::GetDefault(),
+      DefaultContainerId(),
       base::BindLambdaForTesting(
           [&successes](bool result) { successes += result; }),
       false);
@@ -270,7 +270,7 @@
 }
 
 TEST_F(CrostiniSshfsHelperTest, CanRemountAfterUnmount) {
-  SetContainerRunning(ContainerId::GetDefault());
+  SetContainerRunning(DefaultContainerId());
   ExpectMountCalls(2);
   EXPECT_CALL(*disk_manager_, UnmountPath)
       .WillOnce(testing::Invoke(
@@ -281,15 +281,15 @@
           }));
 
   crostini_sshfs_->MountCrostiniFiles(
-      ContainerId::GetDefault(),
+      DefaultContainerId(),
       base::BindLambdaForTesting([](bool res) { EXPECT_TRUE(res); }), false);
   task_environment_.RunUntilIdle();
   crostini_sshfs_->UnmountCrostiniFiles(
-      ContainerId::GetDefault(),
+      DefaultContainerId(),
       base::BindLambdaForTesting([](bool res) { EXPECT_TRUE(res); }));
   task_environment_.RunUntilIdle();
   crostini_sshfs_->MountCrostiniFiles(
-      ContainerId::GetDefault(),
+      DefaultContainerId(),
       base::BindLambdaForTesting([](bool res) { EXPECT_TRUE(res); }), false);
   task_environment_.RunUntilIdle();
 
@@ -308,16 +308,16 @@
 }
 
 TEST_F(CrostiniSshfsHelperTest, ContainerShutdownClearsMountStatus) {
-  SetContainerRunning(ContainerId::GetDefault());
+  SetContainerRunning(DefaultContainerId());
   ExpectMountCalls(2);
   crostini_sshfs_->MountCrostiniFiles(
-      ContainerId::GetDefault(),
+      DefaultContainerId(),
       base::BindLambdaForTesting([](bool res) { EXPECT_TRUE(res); }), false);
   task_environment_.RunUntilIdle();
-  crostini_sshfs_->OnContainerShutdown(ContainerId::GetDefault());
+  crostini_sshfs_->OnContainerShutdown(DefaultContainerId());
   task_environment_.RunUntilIdle();
   crostini_sshfs_->MountCrostiniFiles(
-      ContainerId::GetDefault(),
+      DefaultContainerId(),
       base::BindLambdaForTesting([](bool res) { EXPECT_TRUE(res); }), true);
   task_environment_.RunUntilIdle();
 
diff --git a/chrome/browser/ash/crostini/crostini_terminal.cc b/chrome/browser/ash/crostini/crostini_terminal.cc
index dbbcf16..20665c3 100644
--- a/chrome/browser/ash/crostini/crostini_terminal.cc
+++ b/chrome/browser/ash/crostini/crostini_terminal.cc
@@ -256,7 +256,7 @@
   }
 
   // Look for vm_name and container_name in intent->extras.
-  ContainerId container_id = ContainerId::GetDefault();
+  ContainerId container_id = DefaultContainerId();
   std::string settings_profile;
   if (intent && intent->extras.has_value()) {
     for (const auto& extra : intent->extras.value()) {
diff --git a/chrome/browser/ash/crostini/crostini_terminal.h b/chrome/browser/ash/crostini/crostini_terminal.h
index 924570b..9fd81d0 100644
--- a/chrome/browser/ash/crostini/crostini_terminal.h
+++ b/chrome/browser/ash/crostini/crostini_terminal.h
@@ -113,17 +113,16 @@
 const std::string& GetTerminalHomeUrl();
 
 // Generate URL to launch terminal.
-GURL GenerateTerminalURL(
-    Profile* profile,
-    const std::string& setings_profile,
-    const ContainerId& container_id = ContainerId::GetDefault(),
-    const std::string& cwd = "",
-    const std::vector<std::string>& terminal_args = {});
+GURL GenerateTerminalURL(Profile* profile,
+                         const std::string& setings_profile,
+                         const ContainerId& container_id = DefaultContainerId(),
+                         const std::string& cwd = "",
+                         const std::vector<std::string>& terminal_args = {});
 
 // Launches the terminal tabbed app.
 void LaunchTerminal(Profile* profile,
                     int64_t display_id = display::kInvalidDisplayId,
-                    const ContainerId& container_id = ContainerId::GetDefault(),
+                    const ContainerId& container_id = DefaultContainerId(),
                     const std::string& cwd = "",
                     const std::vector<std::string>& terminal_args = {});
 
diff --git a/chrome/browser/ash/crostini/crostini_terminal_unittest.cc b/chrome/browser/ash/crostini/crostini_terminal_unittest.cc
index 3c3f47f..79a383d 100644
--- a/chrome/browser/ash/crostini/crostini_terminal_unittest.cc
+++ b/chrome/browser/ash/crostini/crostini_terminal_unittest.cc
@@ -21,13 +21,12 @@
 TEST_F(CrostiniTerminalTest, GenerateTerminalURL) {
   content::BrowserTaskEnvironment task_environment;
   TestingProfile profile;
-  EXPECT_EQ(
-      GenerateTerminalURL(&profile, "", ContainerId::GetDefault(), "", {}),
-      "chrome-untrusted://terminal/html/terminal.html"
-      "?command=vmshell"
-      "&args[]=--vm_name%3Dtermina"
-      "&args[]=--target_container%3Dpenguin"
-      "&args[]=--owner_id%3Dtest");
+  EXPECT_EQ(GenerateTerminalURL(&profile, "", DefaultContainerId(), "", {}),
+            "chrome-untrusted://terminal/html/terminal.html"
+            "?command=vmshell"
+            "&args[]=--vm_name%3Dtermina"
+            "&args[]=--target_container%3Dpenguin"
+            "&args[]=--owner_id%3Dtest");
   EXPECT_EQ(GenerateTerminalURL(&profile, "red",
                                 ContainerId("test-vm", "test-container"),
                                 "/home/user", {"arg1"}),
diff --git a/chrome/browser/ash/crostini/crostini_upgrade_available_notification.cc b/chrome/browser/ash/crostini/crostini_upgrade_available_notification.cc
index 6634e60a..24959286 100644
--- a/chrome/browser/ash/crostini/crostini_upgrade_available_notification.cc
+++ b/chrome/browser/ash/crostini/crostini_upgrade_available_notification.cc
@@ -33,7 +33,7 @@
         notification_(notification),
         closure_(std::move(closure)) {
     CrostiniManager::GetForProfile(profile_)->UpgradePromptShown(
-        ContainerId::GetDefault());
+        DefaultContainerId());
   }
 
   CrostiniUpgradeAvailableNotificationDelegate(
diff --git a/chrome/browser/ash/crostini/crostini_util.cc b/chrome/browser/ash/crostini/crostini_util.cc
index 962fb11f..56677d4 100644
--- a/chrome/browser/ash/crostini/crostini_util.cc
+++ b/chrome/browser/ash/crostini/crostini_util.cc
@@ -224,10 +224,6 @@
                  << container_id.container_name << "\")";
 }
 
-ContainerId ContainerId::GetDefault() {
-  return ContainerId(kCrostiniDefaultVmName, kCrostiniDefaultContainerName);
-}
-
 bool IsUninstallable(Profile* profile, const std::string& app_id) {
   if (!CrostiniFeatures::Get()->IsEnabled(profile)) {
     return false;
@@ -259,8 +255,7 @@
 bool ShouldAllowContainerUpgrade(Profile* profile) {
   return CrostiniFeatures::Get()->IsContainerUpgradeUIAllowed(profile) &&
          crostini::CrostiniManager::GetForProfile(profile)
-             ->IsContainerUpgradeable(ContainerId(
-                 kCrostiniDefaultVmName, kCrostiniDefaultContainerName));
+             ->IsContainerUpgradeable(DefaultContainerId());
 }
 
 void AddSpinner(crostini::CrostiniManager::RestartId restart_id,
@@ -557,7 +552,7 @@
   if (!CrostiniFeatures::Get()->IsContainerUpgradeUIAllowed(profile)) {
     return false;
   }
-  if (container_id != ContainerId::GetDefault()) {
+  if (container_id != DefaultContainerId()) {
     return false;
   }
   // If the warning dialog is already open we can add more callbacks to it, but
diff --git a/chrome/browser/ash/crostini/crostini_util.h b/chrome/browser/ash/crostini/crostini_util.h
index 603ebc9..b6909f7 100644
--- a/chrome/browser/ash/crostini/crostini_util.h
+++ b/chrome/browser/ash/crostini/crostini_util.h
@@ -74,8 +74,6 @@
   base::flat_map<std::string, std::string> ToMap() const;
   base::Value::Dict ToDictValue() const;
 
-  static ContainerId GetDefault();
-
   std::string vm_name;
   std::string container_name;
 };
diff --git a/chrome/browser/ash/dbus/ash_dbus_helper.cc b/chrome/browser/ash/dbus/ash_dbus_helper.cc
index 4b8f5973..6ac94d5 100644
--- a/chrome/browser/ash/dbus/ash_dbus_helper.cc
+++ b/chrome/browser/ash/dbus/ash_dbus_helper.cc
@@ -67,6 +67,7 @@
 #include "chromeos/dbus/userdataauth/cryptohome_pkcs11_client.h"
 #include "chromeos/dbus/userdataauth/install_attributes_client.h"
 #include "chromeos/dbus/userdataauth/userdataauth_client.h"
+#include "chromeos/dbus/vm_plugin_dispatcher/vm_plugin_dispatcher_client.h"
 #include "device/bluetooth/dbus/bluez_dbus_manager.h"
 #include "device/bluetooth/floss/floss_dbus_manager.h"
 #include "device/bluetooth/floss/floss_features.h"
@@ -160,6 +161,7 @@
   InitializeDBusClient<chromeos::U2FClient>(bus);
   InitializeDBusClient<chromeos::UserDataAuthClient>(bus);
   InitializeDBusClient<UpstartClient>(bus);
+  InitializeDBusClient<chromeos::VmPluginDispatcherClient>(bus);
 
   // Initialize the device settings service so that we'll take actions per
   // signals sent from the session manager. This needs to happen before
@@ -215,6 +217,7 @@
     bluez::BluezDBusManager::Shutdown();
   }
   // Other D-Bus clients are shut down, also in reverse order of initialization.
+  chromeos::VmPluginDispatcherClient::Shutdown();
   UpstartClient::Shutdown();
   chromeos::UserDataAuthClient::Shutdown();
   chromeos::U2FClient::Shutdown();
diff --git a/chrome/browser/ash/file_manager/file_manager_browsertest_base.cc b/chrome/browser/ash/file_manager/file_manager_browsertest_base.cc
index f14899a5..7dd01145 100644
--- a/chrome/browser/ash/file_manager/file_manager_browsertest_base.cc
+++ b/chrome/browser/ash/file_manager/file_manager_browsertest_base.cc
@@ -1704,7 +1704,7 @@
   std::string DisplayName() override { return name_; }
   Profile* profile() override { return profile_; }
   crostini::ContainerId ContainerId() override {
-    return crostini::ContainerId::GetDefault();
+    return crostini::DefaultContainerId();
   }
 
   guest_os::VmType vm_type() override {
@@ -1724,9 +1724,9 @@
  public:
   explicit GuestOsTestVolume(Profile* profile,
                              MockGuestOsMountProvider* provider)
-      : LocalTestVolume(util::GetGuestOsMountPointName(
-            profile,
-            crostini::ContainerId::GetDefault())),
+      : LocalTestVolume(
+            util::GetGuestOsMountPointName(profile,
+                                           crostini::DefaultContainerId())),
         provider_(provider) {}
 
   GuestOsTestVolume(const GuestOsTestVolume&) = delete;
diff --git a/chrome/browser/ash/file_manager/file_watcher.cc b/chrome/browser/ash/file_manager/file_watcher.cc
index c0ac0747..639ff3a 100644
--- a/chrome/browser/ash/file_manager/file_watcher.cc
+++ b/chrome/browser/ash/file_manager/file_watcher.cc
@@ -66,7 +66,7 @@
       : crostini_manager_(crostini_manager),
         crostini_mount_(std::move(crostini_mount)),
         crostini_path_(std::move(crostini_path)),
-        container_id_(crostini::ContainerId::GetDefault()) {}
+        container_id_(crostini::DefaultContainerId()) {}
 
   ~CrostiniFileWatcher() override {
     if (file_watcher_callback_) {
diff --git a/chrome/browser/ash/file_manager/path_util.cc b/chrome/browser/ash/file_manager/path_util.cc
index 69205db2..8762e05 100644
--- a/chrome/browser/ash/file_manager/path_util.cc
+++ b/chrome/browser/ash/file_manager/path_util.cc
@@ -491,7 +491,7 @@
     if (map_crostini_home) {
       absl::optional<crostini::ContainerInfo> container_info =
           crostini::CrostiniManager::GetForProfile(profile)->GetContainerInfo(
-              crostini::ContainerId::GetDefault());
+              crostini::DefaultContainerId());
       if (!container_info) {
         return false;
       }
@@ -546,7 +546,7 @@
   if (map_crostini_home) {
     absl::optional<crostini::ContainerInfo> container_info =
         crostini::CrostiniManager::GetForProfile(profile)->GetContainerInfo(
-            crostini::ContainerId::GetDefault());
+            crostini::DefaultContainerId());
     if (container_info &&
         AppendRelativePath(container_info->homedir, inside, &relative_path)) {
       *file_system_url = mount_points->CreateExternalFileSystemURL(
diff --git a/chrome/browser/ash/file_manager/volume_manager.cc b/chrome/browser/ash/file_manager/volume_manager.cc
index 72f76261..17fc537 100644
--- a/chrome/browser/ash/file_manager/volume_manager.cc
+++ b/chrome/browser/ash/file_manager/volume_manager.cc
@@ -799,7 +799,7 @@
   // Listen for crostini container shutdown and remove volume.
   crostini::CrostiniManager::GetForProfile(profile_)
       ->AddShutdownContainerCallback(
-          crostini::ContainerId::GetDefault(),
+          crostini::DefaultContainerId(),
           base::BindOnce(&VolumeManager::RemoveSshfsCrostiniVolume,
                          weak_ptr_factory_.GetWeakPtr(), sshfs_mount_path,
                          base::BindOnce([](bool result) {
diff --git a/chrome/browser/ash/guest_os/guest_os_launcher.cc b/chrome/browser/ash/guest_os/guest_os_launcher.cc
index c546f86..207b137 100644
--- a/chrome/browser/ash/guest_os/guest_os_launcher.cc
+++ b/chrome/browser/ash/guest_os/guest_os_launcher.cc
@@ -61,7 +61,7 @@
                     LaunchCallback callback) {
   crostini::CrostiniManager::RestartOptions options;
   options.start_vm_only = just_termina;
-  auto container_id = crostini::ContainerId::GetDefault();
+  auto container_id = crostini::DefaultContainerId();
   crostini::CrostiniManager::GetForProfile(profile)->RestartCrostiniWithOptions(
       container_id, std::move(options),
       base::BindOnce(
diff --git a/chrome/browser/ash/guest_os/guest_os_share_path_unittest.cc b/chrome/browser/ash/guest_os/guest_os_share_path_unittest.cc
index fe88a18..ee91a18e 100644
--- a/chrome/browser/ash/guest_os/guest_os_share_path_unittest.cc
+++ b/chrome/browser/ash/guest_os/guest_os_share_path_unittest.cc
@@ -41,6 +41,7 @@
 #include "chromeos/ash/components/dbus/seneschal/seneschal_service.pb.h"
 #include "chromeos/dbus/dbus_thread_manager.h"
 #include "chromeos/dbus/dlcservice/dlcservice_client.h"
+#include "chromeos/dbus/vm_plugin_dispatcher/vm_plugin_dispatcher_client.h"
 #include "components/account_id/account_id.h"
 #include "components/drive/drive_pref_names.h"
 #include "components/prefs/pref_service.h"
@@ -227,6 +228,7 @@
     ash::CiceroneClient::InitializeFake();
     ash::ConciergeClient::InitializeFake();
     ash::SeneschalClient::InitializeFake();
+    chromeos::VmPluginDispatcherClient::InitializeFake();
 
     fake_concierge_client_ = ash::FakeConciergeClient::Get();
     fake_seneschal_client_ = ash::FakeSeneschalClient::Get();
@@ -236,6 +238,7 @@
   GuestOsSharePathTest& operator=(const GuestOsSharePathTest&) = delete;
 
   ~GuestOsSharePathTest() override {
+    chromeos::VmPluginDispatcherClient::Shutdown();
     ash::SeneschalClient::Shutdown();
     ash::ConciergeClient::Shutdown();
     ash::CiceroneClient::Shutdown();
diff --git a/chrome/browser/ash/guest_os/guest_os_test_helpers.cc b/chrome/browser/ash/guest_os/guest_os_test_helpers.cc
index fd552d2..131037d 100644
--- a/chrome/browser/ash/guest_os/guest_os_test_helpers.cc
+++ b/chrome/browser/ash/guest_os/guest_os_test_helpers.cc
@@ -11,7 +11,7 @@
 namespace guest_os {
 
 MockMountProvider::MockMountProvider()
-    : profile_(nullptr), container_id_(crostini::ContainerId::GetDefault()) {}
+    : profile_(nullptr), container_id_(crostini::DefaultContainerId()) {}
 
 MockMountProvider::MockMountProvider(Profile* profile,
                                      crostini::ContainerId container_id)
diff --git a/chrome/browser/ash/input_method/native_input_method_engine_observer.cc b/chrome/browser/ash/input_method/native_input_method_engine_observer.cc
index 231705e..d50bcf4 100644
--- a/chrome/browser/ash/input_method/native_input_method_engine_observer.cc
+++ b/chrome/browser/ash/input_method/native_input_method_engine_observer.cc
@@ -399,13 +399,12 @@
 }
 
 uint32_t Utf16ToCodepoint(const std::u16string& str) {
-  int32_t index = 0;
+  size_t index = 0;
   base_icu::UChar32 codepoint = 0;
   base::ReadUnicodeCharacter(str.data(), str.length(), &index, &codepoint);
 
   // Should only contain a single codepoint.
-  DCHECK_GE(index, 0);
-  DCHECK_EQ(static_cast<size_t>(index), str.length() - 1);
+  DCHECK_EQ(index, str.length() - 1);
   return codepoint;
 }
 
diff --git a/chrome/browser/ash/login/chrome_restart_request.cc b/chrome/browser/ash/login/chrome_restart_request.cc
index 3541546d..2475995 100644
--- a/chrome/browser/ash/login/chrome_restart_request.cc
+++ b/chrome/browser/ash/login/chrome_restart_request.cc
@@ -153,6 +153,7 @@
     ::switches::kDisableWebGLImageChromium,
     ::switches::kEnableWebGLImageChromium,
     ::switches::kEnableUnsafeWebGPU,
+    ::switches::kEnableWebGPUDeveloperFeatures,
     ::switches::kDisableWebRtcHWDecoding,
     ::switches::kDisableWebRtcHWEncoding,
     ::switches::kOzonePlatform,
diff --git a/chrome/browser/ash/os_feedback/chrome_os_feedback_delegate.cc b/chrome/browser/ash/os_feedback/chrome_os_feedback_delegate.cc
index 0258817..9d43e78 100644
--- a/chrome/browser/ash/os_feedback/chrome_os_feedback_delegate.cc
+++ b/chrome/browser/ash/os_feedback/chrome_os_feedback_delegate.cc
@@ -85,6 +85,16 @@
   return true;
 }
 
+// Key-value pair to be added to FeedbackData when user grants consent to Google
+// to follow-up on feedback report. See (go/feedback-user-consent-faq) for more
+// information.
+// Consent key matches cross-platform key.
+constexpr char kFeedbackUserConsentKey[] = "feedbackUserCtlConsent";
+// Consent value matches JavaScript: `String(true)`.
+constexpr char kFeedbackUserConsentGrantedValue[] = "true";
+// Consent value matches JavaScript: `String(false)`.
+constexpr char kFeedbackUserConsentDeniedValue[] = "false";
+
 }  // namespace
 
 ChromeOsFeedbackDelegate::ChromeOsFeedbackDelegate(Profile* profile)
@@ -174,6 +184,16 @@
         std::string(png_data->front_as<char>(), png_data->size()));
   }
 
+  // Append consent value to report. For cross platform implementations see:
+  // extensions/browser/api/feedback_private/feedback_private_api.cc
+  if (report->contact_user_consent_granted) {
+    feedback_data->AddLog(kFeedbackUserConsentKey,
+                          kFeedbackUserConsentGrantedValue);
+  } else {
+    feedback_data->AddLog(kFeedbackUserConsentKey,
+                          kFeedbackUserConsentDeniedValue);
+  }
+
   const AttachedFilePtr& attached_file = report->attached_file;
   if (ShouldAddAttachment(attached_file)) {
     feedback_data->set_attached_filename(
diff --git a/chrome/browser/ash/os_feedback/chrome_os_feedback_delegate_browsertest.cc b/chrome/browser/ash/os_feedback/chrome_os_feedback_delegate_browsertest.cc
index 7fd57eec..bf1d292 100644
--- a/chrome/browser/ash/os_feedback/chrome_os_feedback_delegate_browsertest.cc
+++ b/chrome/browser/ash/os_feedback/chrome_os_feedback_delegate_browsertest.cc
@@ -55,6 +55,9 @@
 
 constexpr char kPageUrl[] = "https://www.google.com/?q=123";
 constexpr char kSignedInUserEmail[] = "test_user_email@gmail.com";
+constexpr char kFeedbackUserConsentKey[] = "feedbackUserCtlConsent";
+constexpr char kFeedbackUserConsentGrantedValue[] = "true";
+constexpr char kFeedbackUserConsentDeniedValue[] = "false";
 const std::u16string kDescription = u"This is a fake description";
 
 }  // namespace
@@ -157,6 +160,7 @@
 // passed to SendFeedback method of the feedback service.
 // - System logs and histograms are included.
 // - Screenshot is included.
+// - Consent granted.
 // TODO(xiangdongkong): Add tests for other flags once they are supported.
 // Currently, only load_system_info and send_histograms flags are implemented.
 IN_PROC_BROWSER_TEST_F(ChromeOsFeedbackDelegateTest,
@@ -165,6 +169,7 @@
   report->feedback_context = FeedbackContext::New();
   report->description = kDescription;
   report->include_screenshot = true;
+  report->contact_user_consent_granted = true;
 
   report->include_system_logs_and_histograms = true;
   const FeedbackParams expected_params{/*is_internal_email=*/false,
@@ -181,12 +186,19 @@
   EXPECT_EQ(base::UTF16ToUTF8(kDescription), feedback_data->description());
   // Verify screenshot is added to feedback data.
   EXPECT_GT(feedback_data->image().size(), 0);
+  // Verify consent data appended to sys_info map.
+  auto consent_granted =
+      feedback_data->sys_info()->find(kFeedbackUserConsentKey);
+  EXPECT_NE(feedback_data->sys_info()->end(), consent_granted);
+  EXPECT_EQ(kFeedbackUserConsentKey, consent_granted->first);
+  EXPECT_EQ(kFeedbackUserConsentGrantedValue, consent_granted->second);
 }
 
 // Test that feedback params and data are populated with correct data before
 // passed to SendFeedback method of the feedback service.
 // - System logs and histograms are not included.
 // - Screenshot is not included.
+// - Consent not granted.
 IN_PROC_BROWSER_TEST_F(ChromeOsFeedbackDelegateTest,
                        FeedbackDataPopulatedNotIncludeSysLogsOrScreenshot) {
   ReportPtr report = Report::New();
@@ -195,6 +207,7 @@
   report->feedback_context->page_url = GURL(kPageUrl);
   report->description = kDescription;
   report->include_screenshot = false;
+  report->contact_user_consent_granted = false;
 
   report->include_system_logs_and_histograms = false;
   const FeedbackParams expected_params{/*is_internal_email=*/false,
@@ -211,6 +224,12 @@
   EXPECT_EQ(base::UTF16ToUTF8(kDescription), feedback_data->description());
   // Verify no screenshot is added to feedback data.
   EXPECT_EQ("", feedback_data->image());
+  // Verify consent data appended to sys_info map.
+  auto consent_denied =
+      feedback_data->sys_info()->find(kFeedbackUserConsentKey);
+  EXPECT_NE(feedback_data->sys_info()->end(), consent_denied);
+  EXPECT_EQ(kFeedbackUserConsentKey, consent_denied->first);
+  EXPECT_EQ(kFeedbackUserConsentDeniedValue, consent_denied->second);
 }
 
 // Test GetScreenshot returns correct data when there is a screenshot.
diff --git a/chrome/browser/ash/plugin_vm/plugin_vm_files_unittest.cc b/chrome/browser/ash/plugin_vm/plugin_vm_files_unittest.cc
index cdab9ee..7e05452 100644
--- a/chrome/browser/ash/plugin_vm/plugin_vm_files_unittest.cc
+++ b/chrome/browser/ash/plugin_vm/plugin_vm_files_unittest.cc
@@ -28,6 +28,7 @@
 #include "chromeos/ash/components/dbus/seneschal/seneschal_client.h"
 #include "chromeos/dbus/dbus_thread_manager.h"
 #include "chromeos/dbus/vm_applications/apps.pb.h"
+#include "chromeos/dbus/vm_plugin_dispatcher/vm_plugin_dispatcher_client.h"
 #include "content/public/test/browser_task_environment.h"
 #include "storage/browser/file_system/external_mount_points.h"
 #include "storage/common/file_system/file_system_types.h"
@@ -101,8 +102,10 @@
       ash::CiceroneClient::InitializeFake();
       ash::ConciergeClient::InitializeFake();
       ash::SeneschalClient::InitializeFake();
+      chromeos::VmPluginDispatcherClient::InitializeFake();
     }
     ~ScopedDBusThreadManager() {
+      chromeos::VmPluginDispatcherClient::Shutdown();
       ash::SeneschalClient::Shutdown();
       ash::ConciergeClient::Shutdown();
       ash::CiceroneClient::Shutdown();
diff --git a/chrome/browser/ash/plugin_vm/plugin_vm_installer_unittest.cc b/chrome/browser/ash/plugin_vm/plugin_vm_installer_unittest.cc
index fa1190b6..f4dc605 100644
--- a/chrome/browser/ash/plugin_vm/plugin_vm_installer_unittest.cc
+++ b/chrome/browser/ash/plugin_vm/plugin_vm_installer_unittest.cc
@@ -30,6 +30,7 @@
 #include "chromeos/ash/components/dbus/concierge/fake_concierge_client.h"
 #include "chromeos/dbus/dbus_thread_manager.h"
 #include "chromeos/dbus/dlcservice/fake_dlcservice_client.h"
+#include "chromeos/dbus/vm_plugin_dispatcher/vm_plugin_dispatcher_client.h"
 #include "components/account_id/account_id.h"
 #include "components/download/public/background_service/test/test_download_service.h"
 #include "components/drive/service/dummy_drive_service.h"
@@ -166,6 +167,7 @@
   void SetUp() override {
     chromeos::DBusThreadManager::Initialize();
     ash::ConciergeClient::InitializeFake(/*fake_cicerone_client=*/nullptr);
+    chromeos::VmPluginDispatcherClient::InitializeFake();
 
     ASSERT_TRUE(profiles_dir_.CreateUniqueTempDir());
     CreateProfile();
@@ -197,6 +199,7 @@
     profile_.reset();
     observer_.reset();
 
+    chromeos::VmPluginDispatcherClient::Shutdown();
     ash::ConciergeClient::Shutdown();
     chromeos::DBusThreadManager::Shutdown();
     chromeos::DlcserviceClient::Shutdown();
diff --git a/chrome/browser/ash/plugin_vm/plugin_vm_manager_impl.cc b/chrome/browser/ash/plugin_vm/plugin_vm_manager_impl.cc
index 3f31a88..278e260f 100644
--- a/chrome/browser/ash/plugin_vm/plugin_vm_manager_impl.cc
+++ b/chrome/browser/ash/plugin_vm/plugin_vm_manager_impl.cc
@@ -122,15 +122,11 @@
 PluginVmManagerImpl::PluginVmManagerImpl(Profile* profile)
     : profile_(profile),
       owner_id_(ash::ProfileHelper::GetUserIdHashFromProfile(profile)) {
-  chromeos::DBusThreadManager::Get()
-      ->GetVmPluginDispatcherClient()
-      ->AddObserver(this);
+  chromeos::VmPluginDispatcherClient::Get()->AddObserver(this);
 }
 
 PluginVmManagerImpl::~PluginVmManagerImpl() {
-  chromeos::DBusThreadManager::Get()
-      ->GetVmPluginDispatcherClient()
-      ->RemoveObserver(this);
+  chromeos::VmPluginDispatcherClient::Get()->RemoveObserver(this);
 }
 
 void PluginVmManagerImpl::OnPrimaryUserSessionStarted() {
@@ -139,7 +135,7 @@
   request.set_vm_name_uuid(kPluginVmName);
 
   // Probe the dispatcher.
-  chromeos::DBusThreadManager::Get()->GetVmPluginDispatcherClient()->ListVms(
+  chromeos::VmPluginDispatcherClient::Get()->ListVms(
       std::move(request),
       base::BindOnce(
           [](absl::optional<vm_tools::plugin_dispatcher::ListVmResponse>
@@ -230,8 +226,8 @@
   }
 
   // TODO(juwa): This may not work if the vm is STARTING|CONTINUING|RESUMING.
-  chromeos::DBusThreadManager::Get()->GetVmPluginDispatcherClient()->StopVm(
-      std::move(request), base::DoNothing());
+  chromeos::VmPluginDispatcherClient::Get()->StopVm(std::move(request),
+                                                    base::DoNothing());
 }
 
 void PluginVmManagerImpl::RelaunchPluginVm() {
@@ -249,7 +245,7 @@
   request.set_vm_name_uuid(kPluginVmName);
 
   // TODO(dtor): This may not work if the vm is STARTING|CONTINUING|RESUMING.
-  chromeos::DBusThreadManager::Get()->GetVmPluginDispatcherClient()->SuspendVm(
+  chromeos::VmPluginDispatcherClient::Get()->SuspendVm(
       std::move(request),
       base::BindOnce(&PluginVmManagerImpl::OnSuspendVmForRelaunch,
                      weak_ptr_factory_.GetWeakPtr()));
@@ -460,7 +456,7 @@
   request.set_owner_id(owner_id_);
   request.set_vm_name_uuid(kPluginVmName);
 
-  chromeos::DBusThreadManager::Get()->GetVmPluginDispatcherClient()->ListVms(
+  chromeos::VmPluginDispatcherClient::Get()->ListVms(
       std::move(request),
       base::BindOnce(&PluginVmManagerImpl::OnListVms,
                      weak_ptr_factory_.GetWeakPtr(),
@@ -540,7 +536,7 @@
   request.set_owner_id(owner_id_);
   request.set_vm_name_uuid(kPluginVmName);
 
-  chromeos::DBusThreadManager::Get()->GetVmPluginDispatcherClient()->StartVm(
+  chromeos::VmPluginDispatcherClient::Get()->StartVm(
       std::move(request), base::BindOnce(&PluginVmManagerImpl::OnStartVm,
                                          weak_ptr_factory_.GetWeakPtr()));
 }
@@ -582,7 +578,7 @@
   request.set_owner_id(owner_id_);
   request.set_vm_name_uuid(kPluginVmName);
 
-  chromeos::DBusThreadManager::Get()->GetVmPluginDispatcherClient()->ShowVm(
+  chromeos::VmPluginDispatcherClient::Get()->ShowVm(
       std::move(request), base::BindOnce(&PluginVmManagerImpl::OnShowVm,
                                          weak_ptr_factory_.GetWeakPtr()));
 }
@@ -722,7 +718,7 @@
   request.set_stop_mode(
       vm_tools::plugin_dispatcher::VmStopMode::VM_STOP_MODE_SHUTDOWN);
 
-  chromeos::DBusThreadManager::Get()->GetVmPluginDispatcherClient()->StopVm(
+  chromeos::VmPluginDispatcherClient::Get()->StopVm(
       std::move(request),
       base::BindOnce(&PluginVmManagerImpl::OnStopVmForUninstall,
                      weak_ptr_factory_.GetWeakPtr()));
diff --git a/chrome/browser/ash/plugin_vm/plugin_vm_manager_impl_unittest.cc b/chrome/browser/ash/plugin_vm/plugin_vm_manager_impl_unittest.cc
index 41788d43..35bdc889 100644
--- a/chrome/browser/ash/plugin_vm/plugin_vm_manager_impl_unittest.cc
+++ b/chrome/browser/ash/plugin_vm/plugin_vm_manager_impl_unittest.cc
@@ -31,6 +31,7 @@
 #include "chromeos/dbus/dbus_thread_manager.h"
 #include "chromeos/dbus/dlcservice/fake_dlcservice_client.h"
 #include "chromeos/dbus/vm_plugin_dispatcher/fake_vm_plugin_dispatcher_client.h"
+#include "chromeos/dbus/vm_plugin_dispatcher/vm_plugin_dispatcher_client.h"
 #include "components/prefs/pref_service.h"
 #include "content/public/test/browser_task_environment.h"
 #include "testing/gmock/include/gmock/gmock.h"
@@ -52,6 +53,7 @@
     ash::CiceroneClient::InitializeFake();
     ash::ConciergeClient::InitializeFake();
     ash::SeneschalClient::InitializeFake();
+    chromeos::VmPluginDispatcherClient::InitializeFake();
     testing_profile_ = std::make_unique<TestingProfile>();
     test_helper_ = std::make_unique<PluginVmTestHelper>(testing_profile_.get());
     plugin_vm_manager_ = static_cast<PluginVmManagerImpl*>(
@@ -89,6 +91,7 @@
     display_service_.reset();
     test_helper_.reset();
     testing_profile_.reset();
+    chromeos::VmPluginDispatcherClient::Shutdown();
     ash::SeneschalClient::Shutdown();
     ash::ConciergeClient::Shutdown();
     ash::CiceroneClient::Shutdown();
@@ -98,7 +101,7 @@
  protected:
   chromeos::FakeVmPluginDispatcherClient& VmPluginDispatcherClient() {
     return *static_cast<chromeos::FakeVmPluginDispatcherClient*>(
-        chromeos::DBusThreadManager::Get()->GetVmPluginDispatcherClient());
+        chromeos::VmPluginDispatcherClient::Get());
   }
   ash::FakeConciergeClient& ConciergeClient() {
     return *ash::FakeConciergeClient::Get();
diff --git a/chrome/browser/ash/policy/handlers/lock_to_single_user_manager_unittest.cc b/chrome/browser/ash/policy/handlers/lock_to_single_user_manager_unittest.cc
index f4997cec..95d36d6 100644
--- a/chrome/browser/ash/policy/handlers/lock_to_single_user_manager_unittest.cc
+++ b/chrome/browser/ash/policy/handlers/lock_to_single_user_manager_unittest.cc
@@ -29,6 +29,7 @@
 #include "chromeos/dbus/anomaly_detector/anomaly_detector_client.h"
 #include "chromeos/dbus/dbus_thread_manager.h"
 #include "chromeos/dbus/userdataauth/fake_cryptohome_misc_client.h"
+#include "chromeos/dbus/vm_plugin_dispatcher/vm_plugin_dispatcher_client.h"
 #include "components/account_id/account_id.h"
 #include "components/policy/proto/chrome_device_policy.pb.h"
 #include "components/prefs/testing_pref_service.h"
@@ -48,7 +49,7 @@
 
   void SetUp() override {
     // This is required before Concierge tests start calling
-    // DBusThreadManager::Get() for GetAnomalyDetectorClient.
+    // DBusThreadManager::Get() for GuestOsStabilityMonitor.
     chromeos::DBusThreadManager::Initialize();
 
     ash::CiceroneClient::InitializeFake();
@@ -59,6 +60,7 @@
         base::CommandLine::ForCurrentProcess());
     chromeos::AnomalyDetectorClient::InitializeFake();
     chromeos::CryptohomeMiscClient::InitializeFake();
+    chromeos::VmPluginDispatcherClient::InitializeFake();
     lock_to_single_user_manager_ = std::make_unique<LockToSingleUserManager>();
 
     BrowserWithTestWindowTest::SetUp();
@@ -101,6 +103,7 @@
 
     arc_service_manager_->set_browser_context(nullptr);
     arc_service_manager_.reset();
+    chromeos::VmPluginDispatcherClient::Shutdown();
     chromeos::CryptohomeMiscClient::Shutdown();
     chromeos::AnomalyDetectorClient::Shutdown();
     ash::SeneschalClient::Shutdown();
diff --git a/chrome/browser/ash/printing/oauth2/profile_auth_servers_sync_bridge.cc b/chrome/browser/ash/printing/oauth2/profile_auth_servers_sync_bridge.cc
new file mode 100644
index 0000000..5ad4dc6
--- /dev/null
+++ b/chrome/browser/ash/printing/oauth2/profile_auth_servers_sync_bridge.cc
@@ -0,0 +1,154 @@
+// Copyright 2022 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/ash/printing/oauth2/profile_auth_servers_sync_bridge.h"
+
+#include <memory>
+#include <set>
+#include <string>
+#include <vector>
+
+#include "base/bind.h"
+#include "base/check.h"
+#include "base/memory/ptr_util.h"
+#include "chrome/common/channel_info.h"
+#include "components/sync/base/report_unrecoverable_error.h"
+#include "components/sync/model/client_tag_based_model_type_processor.h"
+#include "components/sync/model/entity_change.h"
+#include "components/sync/model/metadata_change_list.h"
+#include "components/sync/model/model_error.h"
+#include "components/sync/model/model_type_change_processor.h"
+#include "components/sync/model/model_type_store.h"
+#include "components/sync/model/model_type_store_base.h"
+#include "components/sync/model/model_type_sync_bridge.h"
+#include "components/sync/protocol/entity_data.h"
+#include "components/sync/protocol/entity_specifics.pb.h"
+#include "components/sync/protocol/printers_authorization_server_specifics.pb.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
+
+namespace ash::printing::oauth2 {
+
+std::unique_ptr<ProfileAuthServersSyncBridge>
+ProfileAuthServersSyncBridge::Create(
+    Observer* observer,
+    syncer::OnceModelTypeStoreFactory store_factory) {
+  DCHECK(observer);
+  return base::WrapUnique(new ProfileAuthServersSyncBridge(
+      std::make_unique<syncer::ClientTagBasedModelTypeProcessor>(
+          syncer::PRINTERS_AUTHORIZATION_SERVERS,
+          base::BindRepeating(&syncer::ReportUnrecoverableError,
+                              chrome::GetChannel())),
+      std::move(store_factory), observer));
+}
+
+std::unique_ptr<ProfileAuthServersSyncBridge>
+ProfileAuthServersSyncBridge::CreateForTesting(
+    Observer* observer,
+    std::unique_ptr<syncer::ModelTypeChangeProcessor> change_processor,
+    syncer::OnceModelTypeStoreFactory store_factory) {
+  DCHECK(observer);
+  DCHECK(change_processor);
+  return base::WrapUnique(new ProfileAuthServersSyncBridge(
+      std::move(change_processor), std::move(store_factory), observer));
+}
+
+ProfileAuthServersSyncBridge::~ProfileAuthServersSyncBridge() = default;
+
+ProfileAuthServersSyncBridge::ProfileAuthServersSyncBridge(
+    std::unique_ptr<syncer::ModelTypeChangeProcessor> change_processor,
+    syncer::OnceModelTypeStoreFactory store_factory,
+    Observer* observer)
+    : syncer::ModelTypeSyncBridge(std::move(change_processor)),
+      observer_(observer) {
+  std::move(store_factory)
+      .Run(syncer::PRINTERS_AUTHORIZATION_SERVERS,
+           base::BindOnce(&ProfileAuthServersSyncBridge::OnStoreCreated,
+                          weak_ptr_factory_.GetWeakPtr()));
+}
+
+void ProfileAuthServersSyncBridge::OnStoreCreated(
+    const absl::optional<syncer::ModelError>& error,
+    std::unique_ptr<syncer::ModelTypeStore> store) {
+  if (error) {
+    change_processor()->ReportError(*error);
+    return;
+  }
+
+  store_ = std::move(store);
+  store_->ReadAllData(
+      base::BindOnce(&ProfileAuthServersSyncBridge::OnReadAllData,
+                     weak_ptr_factory_.GetWeakPtr()));
+}
+
+void ProfileAuthServersSyncBridge::OnReadAllData(
+    const absl::optional<syncer::ModelError>& error,
+    std::unique_ptr<syncer::ModelTypeStore::RecordList> record_list) {
+  if (error) {
+    change_processor()->ReportError(*error);
+    return;
+  }
+
+  for (const syncer::ModelTypeStore::Record& r : *record_list) {
+    sync_pb::PrintersAuthorizationServerSpecifics specifics;
+    if (!specifics.ParseFromString(r.value)) {
+      change_processor()->ReportError(
+          {FROM_HERE, "Failed to deserialize all specifics."});
+      return;
+    }
+    servers_uris_.insert(specifics.uri());
+  }
+
+  // Data loaded. Load metadata.
+  store_->ReadAllMetadata(
+      base::BindOnce(&ProfileAuthServersSyncBridge::OnReadAllMetadata,
+                     weak_ptr_factory_.GetWeakPtr()));
+}
+
+void ProfileAuthServersSyncBridge::OnReadAllMetadata(
+    const absl::optional<syncer::ModelError>& error,
+    std::unique_ptr<syncer::MetadataBatch> metadata_batch) {
+  if (error) {
+    change_processor()->ReportError(*error);
+    return;
+  }
+  change_processor()->ModelReadyToSync(std::move(metadata_batch));
+  observer_->OnProfileAuthorizationServersInitialized();
+}
+
+std::unique_ptr<syncer::MetadataChangeList>
+ProfileAuthServersSyncBridge::CreateMetadataChangeList() {
+  return syncer::ModelTypeStore::WriteBatch::CreateMetadataChangeList();
+}
+
+absl::optional<syncer::ModelError> ProfileAuthServersSyncBridge::MergeSyncData(
+    std::unique_ptr<syncer::MetadataChangeList> metadata_change_list,
+    syncer::EntityChangeList entity_data) {
+  return absl::nullopt;
+}
+
+absl::optional<syncer::ModelError>
+ProfileAuthServersSyncBridge::ApplySyncChanges(
+    std::unique_ptr<syncer::MetadataChangeList> metadata_change_list,
+    syncer::EntityChangeList entity_changes) {
+  return absl::nullopt;
+}
+
+void ProfileAuthServersSyncBridge::GetData(StorageKeyList storage_keys,
+                                           DataCallback callback) {}
+
+void ProfileAuthServersSyncBridge::GetAllDataForDebugging(
+    DataCallback callback) {}
+
+std::string ProfileAuthServersSyncBridge::GetClientTag(
+    const syncer::EntityData& entity_data) {
+  return GetStorageKey(entity_data);
+}
+
+std::string ProfileAuthServersSyncBridge::GetStorageKey(
+    const syncer::EntityData& entity_data) {
+  DCHECK(entity_data.specifics.has_printers_authorization_server());
+  return entity_data.specifics.printers_authorization_server().uri();
+}
+
+}  // namespace ash::printing::oauth2
diff --git a/chrome/browser/ash/printing/oauth2/profile_auth_servers_sync_bridge.h b/chrome/browser/ash/printing/oauth2/profile_auth_servers_sync_bridge.h
new file mode 100644
index 0000000..3b1d96bea
--- /dev/null
+++ b/chrome/browser/ash/printing/oauth2/profile_auth_servers_sync_bridge.h
@@ -0,0 +1,105 @@
+// Copyright 2022 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_ASH_PRINTING_OAUTH2_PROFILE_AUTH_SERVERS_SYNC_BRIDGE_H_
+#define CHROME_BROWSER_ASH_PRINTING_OAUTH2_PROFILE_AUTH_SERVERS_SYNC_BRIDGE_H_
+
+#include <memory>
+#include <set>
+#include <string>
+
+#include "base/memory/raw_ptr.h"
+#include "base/memory/weak_ptr.h"
+#include "components/sync/model/entity_change.h"
+#include "components/sync/model/model_error.h"
+#include "components/sync/model/model_type_store.h"
+#include "components/sync/model/model_type_sync_bridge.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
+
+namespace syncer {
+struct EntityData;
+class ModelTypeChangeProcessor;
+class MetadataChangeList;
+}  // namespace syncer
+
+namespace ash::printing::oauth2 {
+
+// This class is the bridge responsible for the synchronization of the list of
+// trusted Authorization Servers between the user's profile and local storage.
+class ProfileAuthServersSyncBridge : public syncer::ModelTypeSyncBridge {
+ public:
+  class Observer {
+   public:
+    // This method is called when the sync bridge is ready to process calls to
+    // AddAuthorizationServer(...). This method is called only once and it is
+    // always the first method called on the observer by the sync bridge.
+    virtual void OnProfileAuthorizationServersInitialized() = 0;
+
+   protected:
+    virtual ~Observer() = default;
+  };
+
+  // Factory function. |observer| must not be nullptr.
+  static std::unique_ptr<ProfileAuthServersSyncBridge> Create(
+      Observer* observer,
+      syncer::OnceModelTypeStoreFactory store_factory);
+
+  // Factory function for testing. |observer| and |change_processor| must not be
+  // nullptr.
+  static std::unique_ptr<ProfileAuthServersSyncBridge> CreateForTesting(
+      Observer* observer,
+      std::unique_ptr<syncer::ModelTypeChangeProcessor> change_processor,
+      syncer::OnceModelTypeStoreFactory store_factory);
+
+  ProfileAuthServersSyncBridge(const ProfileAuthServersSyncBridge&) = delete;
+  ProfileAuthServersSyncBridge& operator=(const ProfileAuthServersSyncBridge&) =
+      delete;
+
+  ~ProfileAuthServersSyncBridge() override;
+
+ private:
+  ProfileAuthServersSyncBridge(
+      std::unique_ptr<syncer::ModelTypeChangeProcessor> change_processor,
+      syncer::OnceModelTypeStoreFactory store_factory,
+      Observer* observer);
+
+  // Callback for ModelTypeStore initialization.
+  void OnStoreCreated(const absl::optional<syncer::ModelError>& error,
+                      std::unique_ptr<syncer::ModelTypeStore> store);
+
+  // Callback from the store when all data are loaded.
+  void OnReadAllData(
+      const absl::optional<syncer::ModelError>& error,
+      std::unique_ptr<syncer::ModelTypeStore::RecordList> record_list);
+
+  // Callback from the store when all metadata are loaded.
+  void OnReadAllMetadata(const absl::optional<syncer::ModelError>& error,
+                         std::unique_ptr<syncer::MetadataBatch> metadata_batch);
+
+  // Implementation of ModelTypeSyncBridge interface.
+  std::unique_ptr<syncer::MetadataChangeList> CreateMetadataChangeList()
+      override;
+  absl::optional<syncer::ModelError> MergeSyncData(
+      std::unique_ptr<syncer::MetadataChangeList> metadata_change_list,
+      syncer::EntityChangeList entity_data) override;
+  absl::optional<syncer::ModelError> ApplySyncChanges(
+      std::unique_ptr<syncer::MetadataChangeList> metadata_change_list,
+      syncer::EntityChangeList entity_changes) override;
+  void GetData(StorageKeyList storage_keys, DataCallback callback) override;
+  void GetAllDataForDebugging(DataCallback callback) override;
+  std::string GetClientTag(const syncer::EntityData& entity_data) override;
+  std::string GetStorageKey(const syncer::EntityData& entity_data) override;
+
+  // The current trusted list of Authorization Servers URIs.
+  std::set<std::string> servers_uris_;
+  // The local storage.
+  std::unique_ptr<syncer::ModelTypeStore> store_;
+
+  raw_ptr<Observer> const observer_;
+  base::WeakPtrFactory<ProfileAuthServersSyncBridge> weak_ptr_factory_{this};
+};
+
+}  // namespace ash::printing::oauth2
+
+#endif  // CHROME_BROWSER_ASH_PRINTING_OAUTH2_PROFILE_AUTH_SERVERS_SYNC_BRIDGE_H_
diff --git a/chrome/browser/ash/printing/oauth2/profile_auth_servers_sync_bridge_unittest.cc b/chrome/browser/ash/printing/oauth2/profile_auth_servers_sync_bridge_unittest.cc
new file mode 100644
index 0000000..ab73d239
--- /dev/null
+++ b/chrome/browser/ash/printing/oauth2/profile_auth_servers_sync_bridge_unittest.cc
@@ -0,0 +1,58 @@
+// Copyright 2022 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/ash/printing/oauth2/profile_auth_servers_sync_bridge.h"
+
+#include <memory>
+
+#include "base/run_loop.h"
+#include "base/test/task_environment.h"
+#include "components/sync/model/model_type_store.h"
+#include "components/sync/test/model/mock_model_type_change_processor.h"
+#include "components/sync/test/model/model_type_store_test_util.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace ash::printing::oauth2 {
+namespace {
+
+class MockProfileAuthServersSyncBridgeObserver
+    : public ProfileAuthServersSyncBridge::Observer {
+ public:
+  MOCK_METHOD(void, OnProfileAuthorizationServersInitialized, (), (override));
+};
+
+class PrintingOAuth2ProfileAuthServersSyncBridgeTest : public testing::Test {
+ protected:
+  PrintingOAuth2ProfileAuthServersSyncBridgeTest() = default;
+
+  void CreateBridge() {
+    ON_CALL(mock_processor_, IsTrackingMetadata())
+        .WillByDefault(testing::Return(false));
+    bridge_ = ProfileAuthServersSyncBridge::CreateForTesting(
+        &mock_observer_, mock_processor_.CreateForwardingProcessor(),
+        syncer::ModelTypeStoreTestUtil::FactoryForForwardingStore(
+            store_.get()));
+  }
+
+  // In memory model type store needs to be able to post tasks.
+  base::test::SingleThreadTaskEnvironment task_environment_;
+  std::unique_ptr<syncer::ModelTypeStore> store_ =
+      syncer::ModelTypeStoreTestUtil::CreateInMemoryStoreForTest();
+  testing::StrictMock<MockProfileAuthServersSyncBridgeObserver> mock_observer_;
+  testing::NiceMock<syncer::MockModelTypeChangeProcessor> mock_processor_;
+  std::unique_ptr<ProfileAuthServersSyncBridge> bridge_;
+};
+
+TEST_F(PrintingOAuth2ProfileAuthServersSyncBridgeTest, Initialization) {
+  base::RunLoop loop;
+  CreateBridge();
+  EXPECT_CALL(mock_observer_, OnProfileAuthorizationServersInitialized())
+      .Times(1)
+      .WillOnce([&loop] { loop.Quit(); });
+  loop.Run();
+}
+
+}  // namespace
+}  // namespace ash::printing::oauth2
diff --git a/chrome/browser/ash/usb/cros_usb_detector.cc b/chrome/browser/ash/usb/cros_usb_detector.cc
index 93f4a67c..f6ce174d 100644
--- a/chrome/browser/ash/usb/cros_usb_detector.cc
+++ b/chrome/browser/ash/usb/cros_usb_detector.cc
@@ -37,7 +37,6 @@
 #include "chrome/common/webui_url_constants.h"
 #include "chrome/grit/generated_resources.h"
 #include "chromeos/ash/components/dbus/concierge/concierge_client.h"
-#include "chromeos/dbus/dbus_thread_manager.h"
 #include "components/prefs/scoped_user_pref_update.h"
 #include "components/vector_icons/vector_icons.h"
 #include "content/public/browser/device_service.h"
@@ -449,9 +448,7 @@
   fastboot_device_filter_->protocol_code = kFastbootProtocol;
 
   ConciergeClient::Get()->AddVmObserver(this);
-  chromeos::DBusThreadManager::Get()
-      ->GetVmPluginDispatcherClient()
-      ->AddObserver(this);
+  chromeos::VmPluginDispatcherClient::Get()->AddObserver(this);
   disks::DiskMountManager::GetInstance()->AddObserver(this);
 }
 
@@ -459,9 +456,7 @@
   DCHECK_EQ(this, g_cros_usb_detector);
   disks::DiskMountManager::GetInstance()->RemoveObserver(this);
   ConciergeClient::Get()->RemoveVmObserver(this);
-  chromeos::DBusThreadManager::Get()
-      ->GetVmPluginDispatcherClient()
-      ->RemoveObserver(this);
+  chromeos::VmPluginDispatcherClient::Get()->RemoveObserver(this);
   g_cros_usb_detector = nullptr;
 }
 
diff --git a/chrome/browser/ash/usb/cros_usb_detector_unittest.cc b/chrome/browser/ash/usb/cros_usb_detector_unittest.cc
index bbf637c..1c9ccb4a 100644
--- a/chrome/browser/ash/usb/cros_usb_detector_unittest.cc
+++ b/chrome/browser/ash/usb/cros_usb_detector_unittest.cc
@@ -134,15 +134,17 @@
 class CrosUsbDetectorTest : public BrowserWithTestWindowTest {
  public:
   CrosUsbDetectorTest() {
+    // Needed for ChunneldClient via GuestOsStabilityMonitor.
     chromeos::DBusThreadManager::Initialize();
     ash::CiceroneClient::InitializeFake();
     ConciergeClient::InitializeFake();
     SeneschalClient::InitializeFake();
+    chromeos::VmPluginDispatcherClient::InitializeFake();
     fake_cicerone_client_ = ash::FakeCiceroneClient::Get();
     fake_concierge_client_ = FakeConciergeClient::Get();
     fake_vm_plugin_dispatcher_client_ =
         static_cast<chromeos::FakeVmPluginDispatcherClient*>(
-            chromeos::DBusThreadManager::Get()->GetVmPluginDispatcherClient());
+            chromeos::VmPluginDispatcherClient::Get());
 
     mock_disk_mount_manager_ =
         new testing::NiceMock<disks::MockDiskMountManager>;
@@ -154,6 +156,7 @@
 
   ~CrosUsbDetectorTest() override {
     disks::DiskMountManager::Shutdown();
+    chromeos::VmPluginDispatcherClient::Shutdown();
     SeneschalClient::Shutdown();
     ConciergeClient::Shutdown();
     ash::CiceroneClient::Shutdown();
@@ -288,7 +291,6 @@
 
   ash::FakeCiceroneClient* fake_cicerone_client_;
   FakeConciergeClient* fake_concierge_client_;
-  // Owned by chromeos::DBusThreadManager
   chromeos::FakeVmPluginDispatcherClient* fake_vm_plugin_dispatcher_client_;
 
   TestCrosUsbDeviceObserver usb_device_observer_;
diff --git a/chrome/browser/back_press/android/BUILD.gn b/chrome/browser/back_press/android/BUILD.gn
index e79a1aa..d286fa4f 100644
--- a/chrome/browser/back_press/android/BUILD.gn
+++ b/chrome/browser/back_press/android/BUILD.gn
@@ -42,7 +42,7 @@
 }
 
 # On-device unit tests.
-android_library("javatests") {
+android_library("unit_device_javatests") {
   testonly = true
 
   sources = [
diff --git a/chrome/browser/browsing_data/browsing_data_remover_browsertest.cc b/chrome/browser/browsing_data/browsing_data_remover_browsertest.cc
index 2b3e10ea..c01b6c2 100644
--- a/chrome/browser/browsing_data/browsing_data_remover_browsertest.cc
+++ b/chrome/browser/browsing_data/browsing_data_remover_browsertest.cc
@@ -41,7 +41,6 @@
 #include "content/public/browser/browsing_data_filter_builder.h"
 #include "content/public/browser/storage_partition.h"
 #include "content/public/browser/storage_usage_info.h"
-#include "content/public/common/content_features.h"
 #include "content/public/common/content_paths.h"
 #include "content/public/common/content_switches.h"
 #include "content/public/common/network_service_util.h"
@@ -1077,9 +1076,8 @@
   // quota system. GetMediaLicenseCount() is expected to always return 0 using
   // the new backend.
   // TODO(crbug.com/1307796): Fix GetCookiesTreeModelCount() to include quota
-  // nodes.
-  int count =
-      base::FeatureList::IsEnabled(features::kMediaLicenseBackend) ? 0 : 1;
+  // nodes. `count` should be 1 here.
+  int count = 0;
   SetDataForType(kMediaLicenseType);
   EXPECT_EQ(1, GetSiteDataCount());
   EXPECT_EQ(count, GetMediaLicenseCount());
@@ -1128,9 +1126,8 @@
   // quota system. GetMediaLicenseCount() is expected to always return 0 using
   // the new backend.
   // TODO(crbug.com/1307796): Fix GetCookiesTreeModelCount() to include quota
-  // nodes.
-  int count =
-      base::FeatureList::IsEnabled(features::kMediaLicenseBackend) ? 0 : 1;
+  // nodes. `count` should be 1 here.
+  int count = 0;
   SetDataForType(kMediaLicenseType);
   EXPECT_EQ(1, GetSiteDataCount());
   EXPECT_EQ(count, GetMediaLicenseCount());
@@ -1149,9 +1146,8 @@
   // quota system. GetMediaLicenseCount() is expected to always return 0 using
   // the new backend.
   // TODO(crbug.com/1307796): Fix GetCookiesTreeModelCount() to include quota
-  // nodes.
-  int count =
-      base::FeatureList::IsEnabled(features::kMediaLicenseBackend) ? 0 : 1;
+  // nodes. `count` should be 1 here.
+  int count = 0;
 
   // As the PRE_ test should run first, there should be one media license
   // still stored. The time of it's creation should be sometime before
@@ -1180,7 +1176,9 @@
   // http://crbug.com/909829.
   EXPECT_FALSE(HasDataForType(kMediaLicenseType));
 
-  count = base::FeatureList::IsEnabled(features::kMediaLicenseBackend) ? 0 : 2;
+  // TODO(crbug.com/1307796): Fix GetCookiesTreeModelCount() to include quota
+  // nodes. `count` should be 2 here.
+  count = 0;
   // Create a media license for this domain.
   SetDataForType(kMediaLicenseType);
   EXPECT_EQ(count, GetMediaLicenseCount());
@@ -1192,7 +1190,9 @@
   // media license, and leave the one created by the PRE_ test.
   RemoveAndWait(chrome_browsing_data_remover::DATA_TYPE_SITE_DATA,
                 TimeEnum::kStart);
-  count = base::FeatureList::IsEnabled(features::kMediaLicenseBackend) ? 0 : 1;
+  // TODO(crbug.com/1307796): Fix GetCookiesTreeModelCount() to include quota
+  // nodes. `count` should be 1 here.
+  count = 0;
   EXPECT_EQ(1, GetSiteDataCount());
   EXPECT_EQ(count, GetMediaLicenseCount());
   EXPECT_FALSE(HasDataForType(kMediaLicenseType));
@@ -1221,9 +1221,8 @@
   // quota system. GetMediaLicenseCount() is expected to always return 0 using
   // the new backend.
   // TODO(crbug.com/1307796): Fix GetCookiesTreeModelCount() to include quota
-  // nodes.
-  int count =
-      base::FeatureList::IsEnabled(features::kMediaLicenseBackend) ? 0 : 1;
+  // nodes. `count` should be 1 here.
+  int count = 0;
   SetDataForType(kMediaLicenseType);
   EXPECT_EQ(count, GetMediaLicenseCount());
   EXPECT_TRUE(HasDataForType(kMediaLicenseType));
@@ -1291,12 +1290,6 @@
   ASSERT_TRUE(ui_test_utils::NavigateToURL(GetBrowser(), url));
 
   for (const std::string& type : kStorageTypes) {
-    // TODO(crbug.com/1231162): This test was never run against the old media
-    // license backend (it fails), but we can run it against the new backend.
-    if (type == "MediaLicense" &&
-        !base::FeatureList::IsEnabled(features::kMediaLicenseBackend)) {
-      continue;
-    }
     SetDataForType(type);
     EXPECT_TRUE(HasDataForType(type));
   }
diff --git a/chrome/browser/browsing_data/incognito_browsing_data_browsertest.cc b/chrome/browser/browsing_data/incognito_browsing_data_browsertest.cc
index d4e3e910..958217f 100644
--- a/chrome/browser/browsing_data/incognito_browsing_data_browsertest.cc
+++ b/chrome/browser/browsing_data/incognito_browsing_data_browsertest.cc
@@ -11,7 +11,6 @@
 #include "base/path_service.h"
 #include "base/run_loop.h"
 #include "base/test/bind.h"
-#include "base/test/scoped_feature_list.h"
 #include "base/time/time.h"
 #include "build/build_config.h"
 #include "build/chromeos_buildflags.h"
@@ -29,7 +28,6 @@
 #include "components/prefs/pref_service.h"
 #include "content/public/browser/storage_partition.h"
 #include "content/public/browser/storage_usage_info.h"
-#include "content/public/common/content_features.h"
 #include "content/public/common/content_paths.h"
 #include "content/public/common/content_switches.h"
 #include "content/public/test/browser_test.h"
@@ -518,9 +516,8 @@
   // quota system. GetMediaLicenseCount() is expected to always return 0 using
   // the new backend.
   // TODO(crbug.com/1307796): Fix GetCookiesTreeModelCount() to include quota
-  // nodes.
-  int count =
-      base::FeatureList::IsEnabled(features::kMediaLicenseBackend) ? 0 : 1;
+  // nodes. `count` should be 1 here.
+  int count = 0;
   SetDataForType(kMediaLicenseType);
   EXPECT_EQ(1, GetSiteDataCount());
   EXPECT_EQ(count, GetMediaLicenseCount());
diff --git a/chrome/browser/chromeos/BUILD.gn b/chrome/browser/chromeos/BUILD.gn
index e684fa9..d0becae 100644
--- a/chrome/browser/chromeos/BUILD.gn
+++ b/chrome/browser/chromeos/BUILD.gn
@@ -1789,6 +1789,8 @@
     "../ash/printing/oauth2/http_exchange.h",
     "../ash/printing/oauth2/ipp_endpoint_token_fetcher.cc",
     "../ash/printing/oauth2/ipp_endpoint_token_fetcher.h",
+    "../ash/printing/oauth2/profile_auth_servers_sync_bridge.cc",
+    "../ash/printing/oauth2/profile_auth_servers_sync_bridge.h",
     "../ash/printing/oauth2/status_code.h",
     "../ash/printing/ppd_provider_factory.cc",
     "../ash/printing/ppd_provider_factory.h",
@@ -3495,6 +3497,7 @@
     "../ash/printing/oauth2/authorization_zone_unittest.cc",
     "../ash/printing/oauth2/http_exchange_unittest.cc",
     "../ash/printing/oauth2/ipp_endpoint_token_fetcher_unittest.cc",
+    "../ash/printing/oauth2/profile_auth_servers_sync_bridge_unittest.cc",
     "../ash/printing/oauth2/test_authorization_server.cc",
     "../ash/printing/oauth2/test_authorization_server.h",
     "../ash/printing/oauth2/test_authorization_server_unittest.cc",
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 e6b6166..5daacf4 100644
--- a/chrome/browser/chromeos/extensions/autotest_private/autotest_private_api.cc
+++ b/chrome/browser/chromeos/extensions/autotest_private/autotest_private_api.cc
@@ -2397,7 +2397,7 @@
         installer_ui->ClickInstallForTesting();
       }));
   crostini::CrostiniManager::GetForProfile(profile)->RestartCrostini(
-      crostini::ContainerId::GetDefault(),
+      crostini::DefaultContainerId(),
       base::BindOnce(
           &AutotestPrivateRunCrostiniInstallerFunction::CrostiniRestarted,
           this));
@@ -2473,7 +2473,7 @@
   }
 
   crostini::CrostiniExportImport::GetForProfile(profile)->ExportContainer(
-      crostini::ContainerId::GetDefault(),
+      crostini::DefaultContainerId(),
       file_manager::util::GetDownloadsFolderForProfile(profile).Append(path),
       base::BindOnce(&AutotestPrivateExportCrostiniFunction::CrostiniExported,
                      this));
@@ -2513,7 +2513,7 @@
     return RespondNow(Error("Invalid import path must not reference parent"));
   }
   crostini::CrostiniExportImport::GetForProfile(profile)->ImportContainer(
-      crostini::ContainerId::GetDefault(),
+      crostini::DefaultContainerId(),
       file_manager::util::GetDownloadsFolderForProfile(profile).Append(path),
       base::BindOnce(&AutotestPrivateImportCrostiniFunction::CrostiniImported,
                      this));
diff --git a/chrome/browser/chromeos/extensions/file_manager/private_api_misc.cc b/chrome/browser/chromeos/extensions/file_manager/private_api_misc.cc
index c82e706..6a3fdfb 100644
--- a/chrome/browser/chromeos/extensions/file_manager/private_api_misc.cc
+++ b/chrome/browser/chromeos/extensions/file_manager/private_api_misc.cc
@@ -828,7 +828,7 @@
       Profile::FromBrowserContext(browser_context())->GetOriginalProfile();
   DCHECK(crostini::CrostiniFeatures::Get()->IsEnabled(profile));
   crostini::CrostiniManager::GetForProfile(profile)->RestartCrostini(
-      crostini::ContainerId::GetDefault(),
+      crostini::DefaultContainerId(),
       base::BindOnce(&FileManagerPrivateMountCrostiniFunction::RestartCallback,
                      this));
   return RespondLater();
@@ -847,7 +847,7 @@
       Profile::FromBrowserContext(browser_context())->GetOriginalProfile();
   DCHECK(crostini::CrostiniFeatures::Get()->IsEnabled(profile));
   crostini::CrostiniManager::GetForProfile(profile)->MountCrostiniFiles(
-      crostini::ContainerId::GetDefault(),
+      crostini::DefaultContainerId(),
       base::BindOnce(&FileManagerPrivateMountCrostiniFunction::MountCallback,
                      this),
       false);
@@ -887,7 +887,7 @@
           .path();
 
   crostini::CrostiniExportImport::GetForProfile(profile)->ImportContainer(
-      crostini::ContainerId::GetDefault(), path,
+      crostini::DefaultContainerId(), path,
       base::BindOnce(
           [](base::FilePath path, crostini::CrostiniResult result) {
             if (result != crostini::CrostiniResult::SUCCESS) {
@@ -1014,7 +1014,7 @@
           profile, render_frame_host());
 
   crostini::CrostiniPackageService::GetForProfile(profile)->GetLinuxPackageInfo(
-      crostini::ContainerId::GetDefault(),
+      crostini::DefaultContainerId(),
       file_system_context->CrackURLInFirstPartyContext(GURL(params->url)),
       base::BindOnce(&FileManagerPrivateInternalGetLinuxPackageInfoFunction::
                          OnGetLinuxPackageInfo,
@@ -1055,7 +1055,7 @@
 
   crostini::CrostiniPackageService::GetForProfile(profile)
       ->QueueInstallLinuxPackage(
-          crostini::ContainerId::GetDefault(),
+          crostini::DefaultContainerId(),
           file_system_context->CrackURLInFirstPartyContext(GURL(params->url)),
           base::BindOnce(
               &FileManagerPrivateInternalInstallLinuxPackageFunction::
diff --git a/chrome/browser/commerce/android/shopping_service_factory_android.cc b/chrome/browser/commerce/android/shopping_service_factory_android.cc
index 229894e..ca9470b 100644
--- a/chrome/browser/commerce/android/shopping_service_factory_android.cc
+++ b/chrome/browser/commerce/android/shopping_service_factory_android.cc
@@ -6,6 +6,7 @@
 #include "base/android/scoped_java_ref.h"
 #include "chrome/browser/commerce/android/shopping_service_jni/ShoppingServiceFactory_jni.h"
 #include "chrome/browser/commerce/shopping_service_factory.h"
+#include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/profiles/profile_android.h"
 #include "components/commerce/core/android/shopping_service_android.h"
 #include "components/commerce/core/shopping_service.h"
@@ -22,9 +23,11 @@
 ScopedJavaLocalRef<jobject> JNI_ShoppingServiceFactory_GetForProfile(
     JNIEnv* env,
     const JavaParamRef<jobject>& j_profile) {
-  ShoppingService* service = ShoppingServiceFactory::GetForBrowserContext(
-      ProfileAndroid::FromProfileAndroid(j_profile));
+  Profile* profile = ProfileAndroid::FromProfileAndroid(j_profile);
+  CHECK(profile);
 
+  ShoppingService* service =
+      ShoppingServiceFactory::GetForBrowserContext(profile);
   CHECK(service);
 
   ShoppingServiceAndroid* bridge = static_cast<ShoppingServiceAndroid*>(
diff --git a/chrome/browser/commerce/merchant_viewer/android/java/src/org/chromium/chrome/browser/merchant_viewer/MerchantTrustSignalsDataProvider.java b/chrome/browser/commerce/merchant_viewer/android/java/src/org/chromium/chrome/browser/merchant_viewer/MerchantTrustSignalsDataProvider.java
index 8276f2c..7d9ce5b 100644
--- a/chrome/browser/commerce/merchant_viewer/android/java/src/org/chromium/chrome/browser/merchant_viewer/MerchantTrustSignalsDataProvider.java
+++ b/chrome/browser/commerce/merchant_viewer/android/java/src/org/chromium/chrome/browser/merchant_viewer/MerchantTrustSignalsDataProvider.java
@@ -19,6 +19,10 @@
      * Fetches {@link MerchantInfo} based on {@link GURL}.
      */
     public void getDataForUrl(Profile profile, GURL url, Callback<MerchantInfo> callback) {
+        if (profile == null || profile.isOffTheRecord()) {
+            callback.onResult(null);
+            return;
+        }
         ShoppingServiceFactory.getForProfile(profile).getMerchantInfoForUrl(url,
                 (gurl, info) -> callback.onResult(isValidMerchantTrustSignals(info) ? info : null));
     }
diff --git a/chrome/browser/commerce/merchant_viewer/android/javatests/src/org/chromium/chrome/browser/merchant_viewer/MerchantTrustSignalsDataProviderTest.java b/chrome/browser/commerce/merchant_viewer/android/javatests/src/org/chromium/chrome/browser/merchant_viewer/MerchantTrustSignalsDataProviderTest.java
index 2a22a61..fe200ed 100644
--- a/chrome/browser/commerce/merchant_viewer/android/javatests/src/org/chromium/chrome/browser/merchant_viewer/MerchantTrustSignalsDataProviderTest.java
+++ b/chrome/browser/commerce/merchant_viewer/android/javatests/src/org/chromium/chrome/browser/merchant_viewer/MerchantTrustSignalsDataProviderTest.java
@@ -99,6 +99,35 @@
     }
 
     @Test
+    public void testGetDataForNullProfile() throws TimeoutException {
+        MerchantTrustSignalsDataProvider instance = getDataProvider();
+
+        MerchantTrustSignalsCallbackHelper callbackHelper =
+                new MerchantTrustSignalsCallbackHelper();
+
+        int callCount = callbackHelper.getCallCount();
+        mockShoppingServiceResponse(mFakeMerchantTrustSignals);
+        instance.getDataForUrl(null, mMockDestinationGurl, callbackHelper::notifyCalled);
+        callbackHelper.waitForCallback(callCount);
+        Assert.assertNull(callbackHelper.getMerchantTrustSignalsResult());
+    }
+
+    @Test
+    public void testGetDataForIncognitoProfile() throws TimeoutException {
+        doReturn(true).when(mMockProfile).isOffTheRecord();
+        MerchantTrustSignalsDataProvider instance = getDataProvider();
+
+        MerchantTrustSignalsCallbackHelper callbackHelper =
+                new MerchantTrustSignalsCallbackHelper();
+
+        int callCount = callbackHelper.getCallCount();
+        mockShoppingServiceResponse(mFakeMerchantTrustSignals);
+        instance.getDataForUrl(mMockProfile, mMockDestinationGurl, callbackHelper::notifyCalled);
+        callbackHelper.waitForCallback(callCount);
+        Assert.assertNull(callbackHelper.getMerchantTrustSignalsResult());
+    }
+
+    @Test
     public void testIsValidMerchantTrustSignals() {
         MerchantTrustSignalsDataProvider instance = getDataProvider();
         Assert.assertTrue(instance.isValidMerchantTrustSignals(mFakeMerchantTrustSignals));
diff --git a/chrome/browser/commerce/price_tracking/android/java/src/org/chromium/chrome/browser/price_tracking/PriceTrackingFeatures.java b/chrome/browser/commerce/price_tracking/android/java/src/org/chromium/chrome/browser/price_tracking/PriceTrackingFeatures.java
index b1766a4..e50f2cc 100644
--- a/chrome/browser/commerce/price_tracking/android/java/src/org/chromium/chrome/browser/price_tracking/PriceTrackingFeatures.java
+++ b/chrome/browser/commerce/price_tracking/android/java/src/org/chromium/chrome/browser/price_tracking/PriceTrackingFeatures.java
@@ -24,6 +24,7 @@
     @VisibleForTesting
     public static final String ALLOW_DISABLE_PRICE_ANNOTATIONS_PARAM =
             "allow_disable_price_annotations";
+    private static final String PRICE_DROP_IPH_ENABLED_PARAM = "enable_price_drop_iph";
     private static final String PRICE_ANNOTATIONS_ENABLED_METRICS_WINDOW_DURATION_PARAM =
             "price_annotations_enabled_metrics_window_duration_ms";
 
@@ -119,4 +120,14 @@
         }
         return isPriceTrackingEligible();
     }
+
+    public static boolean isPriceDropIphEnabled() {
+        if (FeatureList.isInitialized()) {
+            return isPriceTrackingEligible()
+                    && ChromeFeatureList.getFieldTrialParamByFeatureAsBoolean(
+                            ChromeFeatureList.COMMERCE_PRICE_TRACKING, PRICE_DROP_IPH_ENABLED_PARAM,
+                            false);
+        }
+        return isPriceTrackingEligible();
+    }
 }
\ No newline at end of file
diff --git a/chrome/browser/component_updater/cros_component_installer_chromeos.cc b/chrome/browser/component_updater/cros_component_installer_chromeos.cc
index c53c5ee..ec2d3319 100644
--- a/chrome/browser/component_updater/cros_component_installer_chromeos.cc
+++ b/chrome/browser/component_updater/cros_component_installer_chromeos.cc
@@ -39,7 +39,7 @@
 const ComponentConfig kConfigs[] = {
     {"cros-termina", ComponentConfig::PolicyType::kEnvVersion, "980.1",
      "e9d960f84f628e1f42d05de4046bb5b3154b6f1f65c08412c6af57a29aecaffb"},
-    {"rtanalytics-full", ComponentConfig::PolicyType::kEnvVersion, "100.0",
+    {"rtanalytics-full", ComponentConfig::PolicyType::kEnvVersion, "103.0",
      "c93c3e1013c52100a20038b405ac854d69fa889f6dc4fa6f188267051e05e444"},
     {"demo-mode-resources", ComponentConfig::PolicyType::kEnvVersion, "1.0",
      "93c093ebac788581389015e9c59c5af111d2fa5174d206eb795042e6376cbd10"},
diff --git a/chrome/browser/download/download_item_model_unittest.cc b/chrome/browser/download/download_item_model_unittest.cc
index f3e279a6..e15a489 100644
--- a/chrome/browser/download/download_item_model_unittest.cc
+++ b/chrome/browser/download/download_item_model_unittest.cc
@@ -117,6 +117,11 @@
         .WillByDefault(
             Return(DownloadItem::TARGET_DISPOSITION_OVERWRITE));
     ON_CALL(item_, IsPaused()).WillByDefault(Return(false));
+    ON_CALL(item_, GetMixedContentStatus())
+        .WillByDefault(
+            Return(download::DownloadItem::MixedContentStatus::SAFE));
+    ON_CALL(item(), GetDangerType())
+        .WillByDefault(Return(download::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS));
   }
 
   void SetupInterruptedDownloadItem(download::DownloadInterruptReason reason) {
@@ -387,11 +392,6 @@
       // For GetReceivedBytes()/GetTotalBytes(), we only check whether each is
       // non-zero. In addition, if |total_bytes| is zero, then
       // |time_remaining_known| is also false.
-      //
-      //         .-- .TimeRemaining() is known.
-      //        |       .-- .GetOpenWhenComplete()
-      //        |      |      .---- .IsPaused()
-      //        |      |     |     .---- .GetRerouteInfo()
       {0,
        0,
        false,
@@ -556,6 +556,7 @@
        {},
        "1/2 B, Paused",
        "1/2 B \xE2\x80\xA2 Paused"},
+      {5, 5, false, false, false, {}, "", "5 B \xE2\x80\xA2 Done"},
       {5, 5, true, true, false, kTestRerouteInfo, reroute_status,
        base::StrCat({"5 B \xE2\x80\xA2 ", reroute_status})}};
 
@@ -638,6 +639,63 @@
             base::UTF16ToUTF8(model().GetShowInFolderText()));
 }
 
+TEST_F(DownloadItemModelTest, CompletedBubbleWarningStatusText) {
+  SetupCompletedDownloadItem(base::Hours(1));
+  SetStatusTextBuilder(/*for_bubble=*/true);
+
+  const struct MixedContentStatusTestCase {
+    download::DownloadItem::MixedContentStatus mixed_content_status;
+    std::string expected_bubble_status_msg;
+  } kMixedContentStatusTestCases[] = {
+      {download::DownloadItem::MixedContentStatus::BLOCK,
+       "Blocked \xE2\x80\xA2 Insecure download"},
+      {download::DownloadItem::MixedContentStatus::WARN,
+       "Blocked \xE2\x80\xA2 Insecure download"},
+  };
+  for (const auto& test_case : kMixedContentStatusTestCases) {
+    SetupDownloadItemDefaults();
+    ON_CALL(item(), GetMixedContentStatus())
+        .WillByDefault(Return(test_case.mixed_content_status));
+    EXPECT_EQ(base::UTF16ToUTF8(model().GetStatusText()),
+              test_case.expected_bubble_status_msg);
+  }
+
+  const struct DangerTypeTestCase {
+    download::DownloadDangerType danger_type;
+    std::string expected_bubble_status_msg;
+  } kDangerTypeTestCases[] = {
+      {download::DOWNLOAD_DANGER_TYPE_DANGEROUS_FILE,
+       "Blocked \xE2\x80\xA2 Dangerous"},
+      {download::DOWNLOAD_DANGER_TYPE_DANGEROUS_CONTENT,
+       "Blocked \xE2\x80\xA2 Dangerous"},
+      {download::DOWNLOAD_DANGER_TYPE_DANGEROUS_HOST,
+       "Blocked \xE2\x80\xA2 Dangerous"},
+      {download::DOWNLOAD_DANGER_TYPE_DANGEROUS_ACCOUNT_COMPROMISE,
+       "Blocked \xE2\x80\xA2 Dangerous"},
+      {download::DOWNLOAD_DANGER_TYPE_POTENTIALLY_UNWANTED,
+       "Blocked \xE2\x80\xA2 Dangerous"},
+      {download::DOWNLOAD_DANGER_TYPE_BLOCKED_PASSWORD_PROTECTED,
+       "Blocked \xE2\x80\xA2 Encrypted"},
+      {download::DOWNLOAD_DANGER_TYPE_DANGEROUS_URL,
+       "Blocked \xE2\x80\xA2 Malware"},
+      {download::DOWNLOAD_DANGER_TYPE_BLOCKED_TOO_LARGE,
+       "Blocked \xE2\x80\xA2 Too big"},
+      {download::DOWNLOAD_DANGER_TYPE_SENSITIVE_CONTENT_WARNING,
+       "Sensitive content"},
+      {download::DOWNLOAD_DANGER_TYPE_SENSITIVE_CONTENT_BLOCK,
+       "Blocked by your organization"},
+      {download::DOWNLOAD_DANGER_TYPE_PROMPT_FOR_SCANNING,
+       "Scan before opening"},
+  };
+  for (const auto& test_case : kDangerTypeTestCases) {
+    SetupDownloadItemDefaults();
+    ON_CALL(item(), GetDangerType())
+        .WillByDefault(Return(test_case.danger_type));
+    EXPECT_EQ(base::UTF16ToUTF8(model().GetStatusText()),
+              test_case.expected_bubble_status_msg);
+  }
+}
+
 TEST_F(DownloadItemModelTest, ShouldShowInShelf) {
   SetupDownloadItemDefaults();
 
diff --git a/chrome/browser/download/download_prefs.cc b/chrome/browser/download/download_prefs.cc
index 65a5442..06698e1 100644
--- a/chrome/browser/download/download_prefs.cc
+++ b/chrome/browser/download/download_prefs.cc
@@ -146,18 +146,21 @@
   const char* const kPathPrefs[] = {prefs::kSaveFileDefaultDirectory,
                                     prefs::kDownloadDefaultDirectory};
   for (const char* path_pref : kPathPrefs) {
-    const base::FilePath current = prefs->GetFilePath(path_pref);
-    base::FilePath migrated;
-    if (!current.empty() &&
-        file_manager::util::MigratePathFromOldFormat(
-            profile_, GetDefaultDownloadDirectory(), current, &migrated)) {
-      prefs->SetFilePath(path_pref, migrated);
-    } else if (file_manager::util::MigrateToDriveFs(profile_, current,
-                                                    &migrated)) {
-      prefs->SetFilePath(path_pref, migrated);
-    } else if (download_dir_util::ExpandDrivePolicyVariable(profile_, current,
-                                                            &migrated)) {
-      prefs->SetFilePath(path_pref, migrated);
+    // Update the download directory if the pref is from user pref store.
+    if (prefs->FindPreference(path_pref)->IsUserControlled()) {
+      const base::FilePath current = prefs->GetFilePath(path_pref);
+      base::FilePath migrated;
+      if (!current.empty() &&
+          file_manager::util::MigratePathFromOldFormat(
+              profile_, GetDefaultDownloadDirectory(), current, &migrated)) {
+        prefs->SetFilePath(path_pref, migrated);
+      } else if (file_manager::util::MigrateToDriveFs(profile_, current,
+                                                      &migrated)) {
+        prefs->SetFilePath(path_pref, migrated);
+      } else if (download_dir_util::ExpandDrivePolicyVariable(profile_, current,
+                                                              &migrated)) {
+        prefs->SetFilePath(path_pref, migrated);
+      }
     }
   }
 
@@ -172,23 +175,26 @@
   should_open_pdf_in_system_reader_ =
       prefs->GetBoolean(prefs::kOpenPdfDownloadInSystemReader);
 #endif
-
-  base::FilePath current_download_dir =
-      prefs->GetFilePath(prefs::kDownloadDefaultDirectory);
-  if (!current_download_dir.IsAbsolute()) {
-    // If we have a relative path or an empty path, we should reset to a safe,
-    // well-known path.
-    prefs->SetFilePath(prefs::kDownloadDefaultDirectory,
-                       GetDefaultDownloadDirectoryForProfile());
-  } else if (!prefs->GetBoolean(prefs::kDownloadDirUpgraded)) {
-    // If the download path is dangerous we forcefully reset it. But if we do
-    // so we set a flag to make sure we only do it once, to avoid fighting
-    // the user if they really want it on an unsafe place such as the desktop.
-    if (DownloadPathIsDangerous(current_download_dir)) {
+  // Update the download directory if the pref is from user pref store.
+  if (prefs->FindPreference(prefs::kDownloadDefaultDirectory)
+          ->IsUserControlled()) {
+    base::FilePath current_download_dir =
+        prefs->GetFilePath(prefs::kDownloadDefaultDirectory);
+    if (!current_download_dir.IsAbsolute()) {
+      // If we have a relative path or an empty path, we should reset to a safe,
+      // well-known path.
       prefs->SetFilePath(prefs::kDownloadDefaultDirectory,
                          GetDefaultDownloadDirectoryForProfile());
+    } else if (!prefs->GetBoolean(prefs::kDownloadDirUpgraded)) {
+      // If the download path is dangerous we forcefully reset it. But if we do
+      // so we set a flag to make sure we only do it once, to avoid fighting
+      // the user if they really want it on an unsafe place such as the desktop.
+      if (DownloadPathIsDangerous(current_download_dir)) {
+        prefs->SetFilePath(prefs::kDownloadDefaultDirectory,
+                           GetDefaultDownloadDirectoryForProfile());
+      }
+      prefs->SetBoolean(prefs::kDownloadDirUpgraded, true);
     }
-    prefs->SetBoolean(prefs::kDownloadDirUpgraded, true);
   }
 
   prompt_for_download_.Init(prefs::kPromptForDownload, prefs);
@@ -631,6 +637,11 @@
   base::FilePath migrated_drive_path;
   // Managed prefs may force a legacy Drive path as the download path. Ensure
   // the path is valid when DriveFS is enabled.
+  if (!path.empty() && file_manager::util::MigratePathFromOldFormat(
+                           profile_, GetDefaultDownloadDirectory(), path,
+                           &migrated_drive_path)) {
+    return SanitizeDownloadTargetPath(migrated_drive_path);
+  }
   if (file_manager::util::MigrateToDriveFs(profile_, path,
                                            &migrated_drive_path)) {
     return SanitizeDownloadTargetPath(migrated_drive_path);
diff --git a/chrome/browser/download/download_prefs_unittest.cc b/chrome/browser/download/download_prefs_unittest.cc
index a94ed196..39b00ee 100644
--- a/chrome/browser/download/download_prefs_unittest.cc
+++ b/chrome/browser/download/download_prefs_unittest.cc
@@ -5,6 +5,7 @@
 #include "chrome/browser/download/download_prefs.h"
 
 #include "base/files/file_path.h"
+#include "base/json/values_util.h"
 #include "base/test/metrics/histogram_tester.h"
 #include "base/test/scoped_feature_list.h"
 #include "build/build_config.h"
@@ -22,7 +23,9 @@
 #include "url/gurl.h"
 
 #if BUILDFLAG(IS_CHROMEOS_ASH)
+#include "base/test/scoped_running_on_chromeos.h"
 #include "chrome/browser/ash/drive/drive_integration_service.h"
+#include "chrome/browser/ash/file_manager/fake_disk_mount_manager.h"
 #include "chrome/browser/ash/file_manager/path_util.h"
 #include "chrome/browser/ash/login/users/fake_chrome_user_manager.h"
 #include "chrome/browser/ash/profiles/profile_helper.h"
@@ -441,6 +444,68 @@
   DownloadPrefs download_prefs(&profile);
   EXPECT_TRUE(download_prefs.DownloadPath().IsAbsolute())
       << "Default download directory is " << download_prefs.DownloadPath();
+  EXPECT_EQ(
+      base::ValueToFilePath(*(profile.GetTestingPrefService()->GetUserPref(
+                                prefs::kDownloadDefaultDirectory)))
+          .value(),
+      download_prefs.GetDefaultDownloadDirectory());
+}
+
+TEST(DownloadPrefsTest, ManagedRelativePathDoesNotChangeUserPref) {
+  content::BrowserTaskEnvironment task_environment;
+  TestingProfile profile;
+
+  profile.GetTestingPrefService()->SetManagedPref(
+      prefs::kDownloadDefaultDirectory,
+      base::FilePathToValue(base::FilePath::FromUTF8Unsafe("..")));
+  profile.GetTestingPrefService()->SetManagedPref(
+      prefs::kSaveFileDefaultDirectory,
+      base::FilePathToValue(base::FilePath::FromUTF8Unsafe("../../../")));
+  EXPECT_FALSE(profile.GetPrefs()
+                   ->GetFilePath(prefs::kDownloadDefaultDirectory)
+                   .IsAbsolute());
+  EXPECT_FALSE(profile.GetPrefs()
+                   ->GetFilePath(prefs::kSaveFileDefaultDirectory)
+                   .IsAbsolute());
+
+  DownloadPrefs download_prefs(&profile);
+  EXPECT_FALSE(profile.GetTestingPrefService()->GetUserPref(
+      prefs::kDownloadDefaultDirectory));
+  EXPECT_FALSE(profile.GetTestingPrefService()->GetUserPref(
+      prefs::kSaveFileDefaultDirectory));
+}
+
+TEST(DownloadPrefsTest, RecommendedRelativePathDoesNotChangeUserPref) {
+  content::BrowserTaskEnvironment task_environment;
+  TestingProfile profile;
+
+  base::FilePath save_dir = DownloadPrefs::GetDefaultDownloadDirectory().Append(
+      base::FilePath::FromUTF8Unsafe("tmp"));
+  profile.GetTestingPrefService()->SetRecommendedPref(
+      prefs::kDownloadDefaultDirectory,
+      base::FilePathToValue(base::FilePath::FromUTF8Unsafe("..")));
+  profile.GetTestingPrefService()->SetRecommendedPref(
+      prefs::kSaveFileDefaultDirectory,
+      base::FilePathToValue(base::FilePath::FromUTF8Unsafe("../../../")));
+  profile.GetTestingPrefService()->SetUserPref(prefs::kSaveFileDefaultDirectory,
+                                               base::FilePathToValue(save_dir));
+  EXPECT_FALSE(profile.GetPrefs()
+                   ->GetFilePath(prefs::kDownloadDefaultDirectory)
+                   .IsAbsolute());
+  EXPECT_EQ(profile.GetPrefs()->GetFilePath(prefs::kSaveFileDefaultDirectory),
+            save_dir);
+
+  DownloadPrefs download_prefs(&profile);
+  EXPECT_FALSE(profile.GetTestingPrefService()->GetUserPref(
+      prefs::kDownloadDefaultDirectory));
+  EXPECT_EQ(base::ValueToFilePath(profile.GetTestingPrefService()->GetUserPref(
+                                      prefs::kSaveFileDefaultDirectory))
+                .value(),
+            save_dir);
+
+  EXPECT_EQ(download_prefs.DownloadPath(),
+            DownloadPrefs::GetDefaultDownloadDirectory());
+  EXPECT_EQ(download_prefs.SaveFilePath(), save_dir);
 }
 
 TEST(DownloadPrefsTest, DefaultPathChangedToInvalidValue) {
@@ -595,6 +660,29 @@
     EXPECT_EQ(prefs2.DownloadPath(), default_dir2);
   }
 }
+
+#if BUILDFLAG(IS_CHROMEOS_ASH)
+// Tests that download path is correct when migrated from old format.
+TEST(DownloadPrefsTest, DownloadPathWithMigrationFromOldFormat) {
+  content::BrowserTaskEnvironment task_environment;
+  base::FilePath default_download_dir =
+      DownloadPrefs::GetDefaultDownloadDirectory();
+  base::FilePath path_from_pref = default_download_dir.Append("a").Append("b");
+  ash::disks::DiskMountManager::InitializeForTesting(
+      new file_manager::FakeDiskMountManager);
+
+  TestingProfile profile(base::FilePath("/home/chronos/u-0123456789abcdef"));
+  base::test::ScopedRunningOnChromeOS running_on_chromeos;
+  // Using a managed pref to set the download dir.
+  profile.GetTestingPrefService()->SetManagedPref(
+      prefs::kDownloadDefaultDirectory, base::FilePathToValue(path_from_pref));
+
+  DownloadPrefs prefs(&profile);
+  // The relative path should be preserved after migration.
+  EXPECT_EQ(prefs.DownloadPath(),
+            base::FilePath("/home/chronos/u-0123456789abcdef/MyFiles/a/b"));
+}
+#endif  // BUILDFLAG(IS_CHROMEOS_ASH)
 #endif  // BUILDFLAG(IS_CHROMEOS)
 
 #if BUILDFLAG(IS_ANDROID)
diff --git a/chrome/browser/download/download_ui_model.cc b/chrome/browser/download/download_ui_model.cc
index 3faed886..a45307cb 100644
--- a/chrome/browser/download/download_ui_model.cc
+++ b/chrome/browser/download/download_ui_model.cc
@@ -7,7 +7,6 @@
 #include "base/feature_list.h"
 #include "base/i18n/rtl.h"
 #include "base/observer_list.h"
-#include "base/strings/strcat.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/time/time.h"
 #include "build/build_config.h"
@@ -1159,17 +1158,19 @@
 
 std::u16string
 DownloadUIModel::BubbleStatusTextBuilder::GetBubbleWarningStatusText() const {
-  std::u16string prefix = base::StrCat(
-      {l10n_util::GetStringUTF16(IDS_DOWNLOAD_BUBBLE_STATUS_BLOCKED),
-       l10n_util::GetStringUTF16(IDS_DOWNLOAD_BUBBLE_DOWNLOAD_SEPERATOR)});
+  // If the detail message is "Malware", then this returns "Blocked • Malware"
+  auto get_blocked_warning = [](int detail_message_id) {
+    return l10n_util::GetStringFUTF16(
+        IDS_DOWNLOAD_BUBBLE_DOWNLOAD_STATUS_MESSAGE_WITH_SEPARATOR,
+        l10n_util::GetStringUTF16(IDS_DOWNLOAD_BUBBLE_STATUS_BLOCKED),
+        l10n_util::GetStringUTF16(detail_message_id));
+  };
 
   switch (model_->GetMixedContentStatus()) {
     case download::DownloadItem::MixedContentStatus::BLOCK:
     case download::DownloadItem::MixedContentStatus::WARN:
       // "Blocked • Insecure download"
-      return base::StrCat(
-          {prefix, l10n_util::GetStringUTF16(
-                       IDS_DOWNLOAD_BUBBLE_WARNING_STATUS_INSECURE)});
+      return get_blocked_warning(IDS_DOWNLOAD_BUBBLE_WARNING_STATUS_INSECURE);
     case download::DownloadItem::MixedContentStatus::UNKNOWN:
     case download::DownloadItem::MixedContentStatus::SAFE:
     case download::DownloadItem::MixedContentStatus::VALIDATED:
@@ -1181,30 +1182,24 @@
     case download::DOWNLOAD_DANGER_TYPE_DANGEROUS_FILE:
       // "Blocked • Unknown source"
       if (model_->IsExtensionDownload())
-        return base::StrCat(
-            {prefix, l10n_util::GetStringUTF16(
-                         IDS_DOWNLOAD_BUBBLE_STATUS_UNKNOWN_SOURCE)});
+        return get_blocked_warning(IDS_DOWNLOAD_BUBBLE_STATUS_UNKNOWN_SOURCE);
       [[fallthrough]];
     case download::DOWNLOAD_DANGER_TYPE_DANGEROUS_CONTENT:
     case download::DOWNLOAD_DANGER_TYPE_DANGEROUS_HOST:
     case download::DOWNLOAD_DANGER_TYPE_DANGEROUS_ACCOUNT_COMPROMISE:
     case download::DOWNLOAD_DANGER_TYPE_POTENTIALLY_UNWANTED:
       // "Blocked • Dangerous"
-      return base::StrCat({prefix, l10n_util::GetStringUTF16(
-                                       IDS_DOWNLOAD_BUBBLE_STATUS_DANGEROUS)});
+      return get_blocked_warning(IDS_DOWNLOAD_BUBBLE_STATUS_DANGEROUS);
 
     case download::DOWNLOAD_DANGER_TYPE_BLOCKED_PASSWORD_PROTECTED:
       // "Blocked • Encrypted"
-      return base::StrCat({prefix, l10n_util::GetStringUTF16(
-                                       IDS_DOWNLOAD_BUBBLE_STATUS_ENCRYPTED)});
+      return get_blocked_warning(IDS_DOWNLOAD_BUBBLE_STATUS_ENCRYPTED);
     case download::DOWNLOAD_DANGER_TYPE_DANGEROUS_URL:
       // "Blocked • Malware"
-      return base::StrCat({prefix, l10n_util::GetStringUTF16(
-                                       IDS_DOWNLOAD_BUBBLE_STATUS_MALWARE)});
+      return get_blocked_warning(IDS_DOWNLOAD_BUBBLE_STATUS_MALWARE);
     case download::DOWNLOAD_DANGER_TYPE_BLOCKED_TOO_LARGE:
       // "Blocked • Too big"
-      return base::StrCat({prefix, l10n_util::GetStringUTF16(
-                                       IDS_DOWNLOAD_BUBBLE_STATUS_TOO_BIG)});
+      return get_blocked_warning(IDS_DOWNLOAD_BUBBLE_STATUS_TOO_BIG);
     case download::DOWNLOAD_DANGER_TYPE_UNCOMMON_CONTENT: {
       bool request_ap_verdicts = false;
 #if BUILDFLAG(FULL_SAFE_BROWSING)
@@ -1217,9 +1212,8 @@
       return request_ap_verdicts
                  ? l10n_util::GetStringUTF16(
                        IDS_DOWNLOAD_BUBBLE_STATUS_ADVANCED_PROTECTION)
-                 : base::StrCat(
-                       {prefix, l10n_util::GetStringUTF16(
-                                    IDS_DOWNLOAD_BUBBLE_STATUS_UNCOMMON_FILE)});
+                 : get_blocked_warning(
+                       IDS_DOWNLOAD_BUBBLE_STATUS_UNCOMMON_FILE);
     }
 
     case download::DOWNLOAD_DANGER_TYPE_SENSITIVE_CONTENT_WARNING:
@@ -1231,13 +1225,17 @@
       return l10n_util::GetStringUTF16(
           IDS_DOWNLOAD_BUBBLE_INTERRUPTED_STATUS_BLOCKED_ORGANIZATION);
     case download::DOWNLOAD_DANGER_TYPE_PROMPT_FOR_SCANNING:
+      // "Scan before opening"
       return l10n_util::GetStringUTF16(
           IDS_DOWNLOAD_BUBBLE_STATUS_DEEP_SCANNING_PROMPT);
     case download::DOWNLOAD_DANGER_TYPE_ASYNC_SCANNING:
 #if BUILDFLAG(IS_ANDROID)
+      // "Scanning..."
       return l10n_util::GetStringUTF16(
           IDS_DOWNLOAD_BUBBLE_STATUS_ASYNC_SCANNING);
 #else
+      // Either "Checking with your organization's security policies..." or
+      // "Scanning..."
       return download::IsDownloadConnectorEnabled(model_->profile())
                  ? l10n_util::GetStringUTF16(
                        IDS_DOWNLOAD_BUBBLE_STATUS_ASYNC_SCANNING_ENTERPRISE)
@@ -1277,78 +1275,89 @@
   // Indication of progress. (E.g.:"100/200 MB" or "100MB")
   std::u16string size_ratio = model_->GetProgressSizesString();
 
-  std::u16string size_ratio_prefix = base::StrCat(
-      {size_ratio,
-       l10n_util::GetStringUTF16(IDS_DOWNLOAD_BUBBLE_DOWNLOAD_SEPERATOR)});
+  // If the detail message is "Paused" and the size_ratio is "100/120 MB", then
+  // this returns "100/120 MB • Paused".
+  auto get_size_ratio_string = [size_ratio](std::u16string detail_message) {
+    return l10n_util::GetStringFUTF16(
+        IDS_DOWNLOAD_BUBBLE_DOWNLOAD_STATUS_MESSAGE_WITH_SEPARATOR, size_ratio,
+        detail_message);
+  };
+  // If the detail message is "Opening in 10 seconds..." and the size_ratio is
+  // "100/120 MB", then this returns "↓ 100/120 MB • Opening in 10 seconds...".
+  auto get_active_download_size_ratio_string =
+      [size_ratio](std::u16string detail_message) {
+        return l10n_util::GetStringFUTF16(
+            IDS_DOWNLOAD_BUBBLE_DOWNLOAD_STATUS_MESSAGE_WITH_SEPARATOR,
+            l10n_util::GetStringFUTF16(
+                IDS_DOWNLOAD_BUBBLE_DOWNLOAD_STATUS_WITH_SYMBOL, size_ratio),
+            detail_message);
+      };
 
   const auto completed_bytes = model_->GetCompletedBytes();
   const auto total_bytes = model_->GetTotalBytes();
 
-  std::u16string total_prefix = base::StrCat(
-      {ui::FormatBytes(total_bytes),
-       l10n_util::GetStringUTF16(IDS_DOWNLOAD_BUBBLE_DOWNLOAD_SEPERATOR)});
+  // If the detail message is "Done" and the total_byes is "120 MB", then
+  // this returns "120 MB • Done".
+  auto get_total_string = [total_bytes](std::u16string detail_message) {
+    return l10n_util::GetStringFUTF16(
+        IDS_DOWNLOAD_BUBBLE_DOWNLOAD_STATUS_MESSAGE_WITH_SEPARATOR,
+        ui::FormatBytes(total_bytes), detail_message);
+  };
 
   // The download is a CRX (app, extension, theme, ...) and it is being unpacked
   // and validated.
   if (model_->AllDataSaved() && model_->IsExtensionDownload()) {
-    return base::StrCat(
-        {total_prefix,
-         l10n_util::GetStringUTF16(IDS_DOWNLOAD_STATUS_CRX_INSTALL_RUNNING)});
+    // "120 MB • Adding to Chrome..."
+    return get_total_string(
+        l10n_util::GetStringUTF16(IDS_DOWNLOAD_STATUS_CRX_INSTALL_RUNNING));
   }
 
   // A paused download: "100/120 MB • Paused"
   if (model_->IsPaused()) {
-    return base::StrCat({size_ratio_prefix, l10n_util::GetStringUTF16(
-                                                IDS_DOWNLOAD_PROGRESS_PAUSED)});
+    return get_size_ratio_string(
+        l10n_util::GetStringUTF16(IDS_DOWNLOAD_PROGRESS_PAUSED));
   }
 
   // A download scheduled to be opened when complete: "↓ 100/120 MB • Opening in
   // 10 seconds"
   if (web_drive.empty() && model_->GetOpenWhenComplete()) {
     if (!time_remaining_known)
-      return base::StrCat(
-          {size_ratio_prefix,
-           l10n_util::GetStringUTF16(IDS_DOWNLOAD_STATUS_OPEN_WHEN_COMPLETE)});
+      // "100/120 MB • Opening when complete"
+      return get_size_ratio_string(
+          l10n_util::GetStringUTF16(IDS_DOWNLOAD_STATUS_OPEN_WHEN_COMPLETE));
 
-    return base::StrCat(
-        {l10n_util::GetStringUTF16(IDS_DOWNLOAD_BUBBLE_DOWNLOAD_SYMBOL),
-         size_ratio_prefix,
-         l10n_util::GetStringFUTF16(
-             IDS_DOWNLOAD_STATUS_OPEN_IN,
-             ui::TimeFormat::Simple(ui::TimeFormat::FORMAT_DURATION,
-                                    ui::TimeFormat::LENGTH_LONG,
-                                    time_remaining))});
+    // "↓ 100/120 MB • Opening in 10 seconds..."
+    return get_active_download_size_ratio_string(l10n_util::GetStringFUTF16(
+        IDS_DOWNLOAD_STATUS_OPEN_IN,
+        ui::TimeFormat::Simple(ui::TimeFormat::FORMAT_DURATION,
+                               ui::TimeFormat::LENGTH_LONG, time_remaining)));
   }
 
   // In progress download with known time left: "↓ 100/120 MB • 10 seconds left"
   if (time_remaining_known) {
-    return base::StrCat(
-        {l10n_util::GetStringUTF16(IDS_DOWNLOAD_BUBBLE_DOWNLOAD_SYMBOL),
-         size_ratio_prefix,
-         ui::TimeFormat::Simple(ui::TimeFormat::FORMAT_REMAINING,
-                                ui::TimeFormat::LENGTH_LONG, time_remaining)});
+    return get_active_download_size_ratio_string(
+        ui::TimeFormat::Simple(ui::TimeFormat::FORMAT_REMAINING,
+                               ui::TimeFormat::LENGTH_LONG, time_remaining));
   }
 
   if (completed_bytes == 0) {
     // "0/120 MB • Starting..."
-    return base::StrCat({size_ratio_prefix, l10n_util::GetStringUTF16(
-                                                IDS_DOWNLOAD_STATUS_STARTING)});
+    return get_size_ratio_string(
+        l10n_util::GetStringUTF16(IDS_DOWNLOAD_STATUS_STARTING));
   } else if (completed_bytes < total_bytes || total_bytes == 0) {
     // In progress download with no known time left and non-zero completed
     // bytes: "100/120 MB • Resuming..." or "100 MB • Resuming..."
-    return base::StrCat(
-        {size_ratio_prefix,
-         l10n_util::GetStringUTF16(IDS_DOWNLOAD_BUBBLE_STATUS_RESUMING)});
+    return get_size_ratio_string(
+        l10n_util::GetStringUTF16(IDS_DOWNLOAD_BUBBLE_STATUS_RESUMING));
   } else if (web_drive.size()) {
     // If all bytes of the file has been downloaded and it is being rerouted:
     // "120 MB • Sending to <WEB_DRIVE>..."
-    return base::StrCat(
-        {total_prefix,
-         l10n_util::GetStringFUTF16(IDS_DOWNLOAD_STATUS_UPLOADING, web_drive)});
+    return get_total_string(
+        l10n_util::GetStringFUTF16(IDS_DOWNLOAD_STATUS_UPLOADING, web_drive));
   } else {
     // "120 MB • Done"
-    return base::StrCat({total_prefix, l10n_util::GetStringUTF16(
-                                           IDS_DOWNLOAD_BUBBLE_STATUS_DONE)});
+    return get_total_string(
+        l10n_util::GetStringUTF16(IDS_DOWNLOAD_BUBBLE_STATUS_DONE));
   }
 }
 
@@ -1389,6 +1398,7 @@
     std::u16string delta_str;
     if (model_->GetDangerType() ==
         download::DOWNLOAD_DANGER_TYPE_DEEP_SCANNED_SAFE) {
+      // "2 B • Done, no issues found"
       delta_str = l10n_util::GetStringUTF16(
           IDS_DOWNLOAD_BUBBLE_STATUS_DEEP_SCANNING_DONE);
     } else {
@@ -1402,10 +1412,9 @@
                                        ui::TimeFormat::LENGTH_LONG,
                                        time_elapsed);
     }
-    return base::StrCat(
-        {size_text,
-         l10n_util::GetStringUTF16(IDS_DOWNLOAD_BUBBLE_DOWNLOAD_SEPERATOR),
-         delta_str});
+    return l10n_util::GetStringFUTF16(
+        IDS_DOWNLOAD_BUBBLE_DOWNLOAD_STATUS_MESSAGE_WITH_SEPARATOR, size_text,
+        delta_str);
   }
 }
 
diff --git a/chrome/browser/enterprise/connectors/service_provider_config.cc b/chrome/browser/enterprise/connectors/service_provider_config.cc
index 5be7de8a..216f8ae7 100644
--- a/chrome/browser/enterprise/connectors/service_provider_config.cc
+++ b/chrome/browser/enterprise/connectors/service_provider_config.cc
@@ -137,22 +137,38 @@
 
 constexpr GoogleDlpSupportedFiles kGoogleDlpSupportedFiles;
 
+constexpr std::array<SupportedTag, 2> kGoogleDlpSupportedTags = {{
+    {
+        .name = "malware",
+        .display_name = "Threat protection",
+        .max_file_size = 52428800,
+        .supported_files = &kAllFilesAllowed,
+    },
+    {
+        .name = "dlp",
+        .display_name = "Sensitive data protection",
+        .max_file_size = 52428800,
+        .supported_files = &kGoogleDlpSupportedFiles,
+    },
+}};
+
 constexpr AnalysisConfig kGoogleAnalysisConfig = {
     .url = "https://safebrowsing.google.com/safebrowsing/uploads/scan",
-    .supported_tags = {{
-        {
-            .name = "malware",
-            .display_name = "Threat protection",
-            .max_file_size = 52428800,
-            .supported_files = &kAllFilesAllowed,
-        },
-        {
-            .name = "dlp",
-            .display_name = "Sensitive data protection",
-            .max_file_size = 52428800,
-            .supported_files = &kGoogleDlpSupportedFiles,
-        },
-    }},
+    .supported_tags = base::span<const SupportedTag>(kGoogleDlpSupportedTags),
+};
+
+constexpr std::array<SupportedTag, 1> kLocalTestSupportedTags = {{
+    {
+        .name = "dlp",
+        .display_name = "Sensitive data protection",
+        .max_file_size = 52428800,
+        .supported_files = &kAllFilesAllowed,
+    },
+}};
+
+constexpr AnalysisConfig kLocalTestAnalysisConfig = {
+    .local_path = "test_path",
+    .supported_tags = base::span<const SupportedTag>(kLocalTestSupportedTags),
 };
 
 constexpr ReportingConfig kGoogleReportingConfig = {
@@ -190,6 +206,15 @@
                   .file_system = &kBoxFileSystemConfig,
               },
           },
+          // TODO(b/226560946): Add the actual local content analysis service
+          // providers to this config.
+          {
+              "local_test",
+              {
+                  .display_name = "Local Test",
+                  .analysis = &kLocalTestAnalysisConfig,
+              },
+          },
       });
   return &kServiceProviderConfig;
 }
diff --git a/chrome/browser/enterprise/connectors/service_provider_config.h b/chrome/browser/enterprise/connectors/service_provider_config.h
index c2f2e2d6..a0976d87 100644
--- a/chrome/browser/enterprise/connectors/service_provider_config.h
+++ b/chrome/browser/enterprise/connectors/service_provider_config.h
@@ -10,6 +10,7 @@
 #include <vector>
 
 #include "base/containers/fixed_flat_map.h"
+#include "base/containers/span.h"
 #include "base/files/file_path.h"
 #include "base/memory/raw_ptr_exclusion.h"
 #include "base/values.h"
@@ -33,8 +34,12 @@
 };
 
 struct AnalysisConfig {
+  // Only 1 of `url` and `local_path` should be populated to differentiate
+  // between cloud analysis providers and local analysis providers.
   const char* url = nullptr;
-  std::array<SupportedTag, 2> supported_tags;
+  const char* local_path = nullptr;
+
+  const base::span<const SupportedTag> supported_tags;
 };
 
 struct ReportingConfig {
@@ -62,7 +67,7 @@
 };
 
 using ServiceProviderConfig =
-    base::fixed_flat_map<base::StringPiece, ServiceProvider, 2>;
+    base::fixed_flat_map<base::StringPiece, ServiceProvider, 3>;
 
 // Returns the global service provider configuration, containing every service
 // provider and each of their supported Connector configs.
diff --git a/chrome/browser/enterprise/connectors/service_provider_config_unittest.cc b/chrome/browser/enterprise/connectors/service_provider_config_unittest.cc
index 0c6e31be..0f0b46a6 100644
--- a/chrome/browser/enterprise/connectors/service_provider_config_unittest.cc
+++ b/chrome/browser/enterprise/connectors/service_provider_config_unittest.cc
@@ -90,14 +90,15 @@
 
 }  // namespace
 
-TEST(ServiceProviderConfigTest, CurrentConfig) {
-  // Since this class should only be initialized with 1 value for now, all
-  // that's needed is a single test on that value checking every field.
+TEST(ServiceProviderConfigTest, Google) {
   const ServiceProviderConfig* config = GetServiceProviderConfig();
-
   ASSERT_TRUE(config->count("google"));
   ServiceProvider service_provider = config->at("google");
 
+  ASSERT_TRUE(service_provider.analysis);
+  ASSERT_TRUE(service_provider.reporting);
+  ASSERT_FALSE(service_provider.file_system);
+
   ASSERT_EQ("https://safebrowsing.google.com/safebrowsing/uploads/scan",
             std::string(service_provider.analysis->url));
   ASSERT_TRUE(GURL(service_provider.analysis->url).is_valid());
@@ -105,21 +106,23 @@
             std::string(service_provider.reporting->url));
   ASSERT_TRUE(GURL(service_provider.reporting->url).is_valid());
 
-  ASSERT_EQ(2u, service_provider.analysis->supported_tags.size());
-  ASSERT_EQ(std::string(service_provider.analysis->supported_tags.at(0).name),
+  // The Google service provider has 2 tags: malware and dlp.
+  ASSERT_EQ(service_provider.analysis->supported_tags.size(), 2u);
+  ASSERT_EQ(std::string(service_provider.analysis->supported_tags[0].name),
             "malware");
-  ASSERT_EQ(service_provider.analysis->supported_tags.at(0).max_file_size,
+  ASSERT_EQ(service_provider.analysis->supported_tags[0].max_file_size,
             kMaxFileSize);
-  ASSERT_EQ(std::string(service_provider.analysis->supported_tags.at(1).name),
+  ASSERT_EQ(std::string(service_provider.analysis->supported_tags[1].name),
             "dlp");
-  ASSERT_EQ(service_provider.analysis->supported_tags.at(1).max_file_size,
+  ASSERT_EQ(service_provider.analysis->supported_tags[1].max_file_size,
             kMaxFileSize);
+
   // Only a subset of mime types and extensions are supported by Google DLP, but
   // every type is supported by malware scanning.
   const auto* malware_supported_files =
-      service_provider.analysis->supported_tags.at(0).supported_files;
+      service_provider.analysis->supported_tags[0].supported_files;
   const auto* dlp_supported_files =
-      service_provider.analysis->supported_tags.at(1).supported_files;
+      service_provider.analysis->supported_tags[1].supported_files;
   for (const base::FilePath::StringType& type : SupportedDlpFileTypes()) {
     ASSERT_TRUE(dlp_supported_files->FileExtensionSupported(FilePath(type)));
     ASSERT_TRUE(
@@ -138,9 +141,50 @@
     ASSERT_FALSE(dlp_supported_files->MimeTypeSupported(type));
     ASSERT_TRUE(malware_supported_files->MimeTypeSupported(type));
   }
+}
 
+TEST(ServiceProviderConfigTest, LocalTest) {
+  const ServiceProviderConfig* config = GetServiceProviderConfig();
+  ASSERT_TRUE(config->count("local_test"));
+  ServiceProvider service_provider = config->at("local_test");
+
+  ASSERT_TRUE(service_provider.analysis);
+  ASSERT_FALSE(service_provider.reporting);
+  ASSERT_FALSE(service_provider.file_system);
+
+  ASSERT_FALSE(service_provider.analysis->url);
+  ASSERT_TRUE(service_provider.analysis->local_path);
+  ASSERT_EQ("test_path", std::string(service_provider.analysis->local_path));
+
+  // The test local service provider has 1 tag: dlp.
+  ASSERT_EQ(service_provider.analysis->supported_tags.size(), 1u);
+  ASSERT_EQ(std::string(service_provider.analysis->supported_tags[0].name),
+            "dlp");
+  ASSERT_EQ(service_provider.analysis->supported_tags[0].max_file_size,
+            kMaxFileSize);
+
+  // Every type of file is supported for the test local content analysis service
+  // provider.
+  const auto* dlp_supported_files =
+      service_provider.analysis->supported_tags[0].supported_files;
+  for (const base::FilePath::StringType& type : SupportedDlpFileTypes())
+    ASSERT_TRUE(dlp_supported_files->FileExtensionSupported(FilePath(type)));
+  for (const base::FilePath::StringType& type : UnsupportedDlpFileTypes())
+    ASSERT_TRUE(dlp_supported_files->FileExtensionSupported(FilePath(type)));
+  for (const std::string& type : SupportedDlpMimeTypes())
+    ASSERT_TRUE(dlp_supported_files->MimeTypeSupported(type));
+  for (const std::string& type : UnsupportedDlpMimeTypes())
+    ASSERT_TRUE(dlp_supported_files->MimeTypeSupported(type));
+}
+
+TEST(ServiceProviderConfigTest, Box) {
+  const ServiceProviderConfig* config = GetServiceProviderConfig();
   ASSERT_TRUE(config->count("box"));
-  service_provider = config->at("box");
+  ServiceProvider service_provider = config->at("box");
+
+  ASSERT_FALSE(service_provider.analysis);
+  ASSERT_FALSE(service_provider.reporting);
+  ASSERT_TRUE(service_provider.file_system);
 
   ASSERT_EQ("https://box.com", std::string(service_provider.file_system->home));
   ASSERT_EQ("https://account.box.com/api/oauth2/authorize",
diff --git a/chrome/browser/extensions/api/preference/preference_api.cc b/chrome/browser/extensions/api/preference/preference_api.cc
index 4d2c202..30a4ccd 100644
--- a/chrome/browser/extensions/api/preference/preference_api.cc
+++ b/chrome/browser/extensions/api/preference/preference_api.cc
@@ -488,6 +488,10 @@
           base::BindRepeating(&PreferenceEventRouter::OnAshPrefChanged,
                               base::Unretained(this), pref_path,
                               pref.extension_pref, pref.browser_pref)));
+      registrar_.Add(
+          pref.browser_pref,
+          base::BindRepeating(&PreferenceEventRouter::OnControlledPrefChanged,
+                              base::Unretained(this), registrar_.prefs()));
       continue;
     }
 #endif
@@ -508,6 +512,43 @@
 PreferenceEventRouter::~PreferenceEventRouter() = default;
 
 #if BUILDFLAG(IS_CHROMEOS_LACROS)
+void PreferenceEventRouter::OnControlledPrefChanged(
+    PrefService* pref_service,
+    const std::string& browser_pref) {
+  // This pref has a corresponding value in ash. We should send the updated
+  // value of the pref to ash.
+  auto* lacros_service = chromeos::LacrosService::Get();
+  if (!lacros_service ||
+      !lacros_service->IsAvailable<crosapi::mojom::Prefs>()) {
+    // Without the service, we cannot update this pref in ash.
+    LOG(ERROR) << ErrorUtils::FormatErrorMessage(
+        "API unavailable to set pref * in ash.", browser_pref);
+    return;
+  }
+
+  crosapi::mojom::PrefPath pref_path =
+      PrefMapping::GetInstance()->GetPrefPathForPrefName(browser_pref);
+  // Should be a known pref path. Otherwise we would not have created this
+  // observer.
+  DCHECK(pref_path != crosapi::mojom::PrefPath::kUnknown);
+
+  const PrefService::Preference* pref =
+      pref_service->FindPreference(browser_pref);
+  CHECK(pref);
+  if (pref->IsExtensionControlled()) {
+    // The pref has been set in lacros by an extension.
+    // Transmit the value to ash to be stored in the standalone browser
+    // prefstore.
+    lacros_service->GetRemote<crosapi::mojom::Prefs>()->SetPref(
+        pref_path, pref->GetValue()->Clone(), base::OnceClosure());
+  } else {
+    // The pref hasn't been set in lacros.
+    // Remove any value from the standalone browser prefstore in ash.
+    lacros_service->GetRemote<crosapi::mojom::Prefs>()
+        ->ClearExtensionControlledPref(pref_path, base::OnceClosure());
+  }
+}
+
 void PreferenceEventRouter::OnAshPrefChanged(crosapi::mojom::PrefPath pref_path,
                                              const std::string& extension_pref,
                                              const std::string& browser_pref,
@@ -727,6 +768,13 @@
     DCHECK(rv);
     EventRouter::Get(profile_)->RegisterObserver(this, event_name);
   }
+#if BUILDFLAG(IS_CHROMEOS_LACROS)
+  // On lacros, ensure the PreferenceEventRouter is always created to watch for
+  // and notify of any pref changes, even if there's no extension listeners.
+  // TODO(crbug.com/1334829): Abstract out lacros logic from the
+  // PreferenceEventRouter so we don't needlessly dispatch extension events.
+  EnsurePreferenceEventRouterCreated();
+#endif
   content_settings_store()->AddObserver(this);
 }
 
@@ -754,10 +802,17 @@
 }
 
 void PreferenceAPI::OnListenerAdded(const EventListenerInfo& details) {
-  preference_event_router_ = std::make_unique<PreferenceEventRouter>(profile_);
+  EnsurePreferenceEventRouterCreated();
   EventRouter::Get(profile_)->UnregisterObserver(this);
 }
 
+void PreferenceAPI::EnsurePreferenceEventRouterCreated() {
+  if (!preference_event_router_) {
+    preference_event_router_ =
+        std::make_unique<PreferenceEventRouter>(profile_);
+  }
+}
+
 void PreferenceAPI::OnContentSettingChanged(const std::string& extension_id,
                                             bool incognito) {
   if (incognito) {
diff --git a/chrome/browser/extensions/api/preference/preference_api.h b/chrome/browser/extensions/api/preference/preference_api.h
index 5c0ce98..993fdc3 100644
--- a/chrome/browser/extensions/api/preference/preference_api.h
+++ b/chrome/browser/extensions/api/preference/preference_api.h
@@ -75,6 +75,11 @@
                        absl::optional<::base::Value> opt_value,
                        crosapi::mojom::PrefControlState control_state);
 
+  // Callback for lacros version of the prefs, to update ash in the event that
+  // they are changed.
+  void OnControlledPrefChanged(PrefService* pref_service,
+                               const std::string& browser_pref);
+
   std::vector<std::unique_ptr<crosapi::mojom::PrefObserver>>
       extension_pref_observers_;
 #endif
@@ -159,6 +164,9 @@
   // EventRouter::Observer implementation.
   void OnListenerAdded(const EventListenerInfo& details) override;
 
+  // Ensures that a PreferenceEventRouter is created only once.
+  void EnsurePreferenceEventRouterCreated();
+
  private:
   friend class BrowserContextKeyedAPIFactory<PreferenceAPI>;
 
diff --git a/chrome/browser/extensions/api/preference/preference_api_constants.cc b/chrome/browser/extensions/api/preference/preference_api_constants.cc
index ba641ce..7e08388 100644
--- a/chrome/browser/extensions/api/preference/preference_api_constants.cc
+++ b/chrome/browser/extensions/api/preference/preference_api_constants.cc
@@ -27,7 +27,7 @@
     "Be sure to declare in your manifest what permissions you need.";
 
 const char kPrimaryProfileOnlyErrorMessage[] =
-    "You may only access this preference in the primary profile.";
+    "You may only access the preference '*' in the primary profile.";
 
 }  // namespace preference_api_constants
 }  // namespace extensions
diff --git a/chrome/browser/extensions/api/preference/preference_api_lacros_browsertest.cc b/chrome/browser/extensions/api/preference/preference_api_lacros_browsertest.cc
index 7617830..9742fb2 100644
--- a/chrome/browser/extensions/api/preference/preference_api_lacros_browsertest.cc
+++ b/chrome/browser/extensions/api/preference/preference_api_lacros_browsertest.cc
@@ -13,6 +13,7 @@
 #include "chrome/browser/ui/browser.h"
 #include "chrome/common/pref_names.h"
 #include "chromeos/crosapi/mojom/prefs.mojom-test-utils.h"
+#include "chromeos/crosapi/mojom/prefs.mojom.h"
 #include "chromeos/lacros/lacros_service.h"
 #include "chromeos/lacros/lacros_test_helper.h"
 #include "chromeos/startup/browser_init_params.h"
@@ -122,6 +123,15 @@
   if (!IsServiceAvailable()) {
     return;
   }
+  absl::optional<::base::Value> out_value;
+  crosapi::mojom::PrefsAsyncWaiter async_waiter(
+      chromeos::LacrosService::Get()->GetRemote<crosapi::mojom::Prefs>().get());
+
+  // At start, the value in ash should not be set.
+  async_waiter.GetPref(crosapi::mojom::PrefPath::kAccessibilityAutoclickEnabled,
+                       &out_value);
+  EXPECT_FALSE(out_value.value().GetBool());
+
   base::FilePath extension_path =
       test_data_dir_.AppendASCII("preference/lacros");
   {
@@ -135,6 +145,11 @@
   }
   CheckPreferencesSet();
 
+  // In ash, the value should now be set.
+  async_waiter.GetPref(crosapi::mojom::PrefPath::kAccessibilityAutoclickEnabled,
+                       &out_value);
+  EXPECT_TRUE(out_value.value().GetBool());
+
   // The settings should not be reset when the extension is reloaded.
   {
     ExtensionTestMessageListener listener("ready", ReplyBehavior::kWillReply);
@@ -152,6 +167,15 @@
   observer.WaitForExtensionUninstalled();
   CheckPreferencesCleared();
 
+  if (DoesAshSupportObservers()) {
+    // When the extension in uninstalled, the pref in lacros should be the
+    // default value (false). This only works if Ash correctly implements
+    // extension-controlled pref observers.
+    async_waiter.GetPref(
+        crosapi::mojom::PrefPath::kAccessibilityAutoclickEnabled, &out_value);
+    EXPECT_FALSE(out_value.value().GetBool());
+  }
+
   {
     ExtensionTestMessageListener listener("ready", ReplyBehavior::kWillReply);
     EXPECT_TRUE(LoadExtension(extension_path));
diff --git a/chrome/browser/extensions/updater/extension_updater_unittest.cc b/chrome/browser/extensions/updater/extension_updater_unittest.cc
index 15ba00df..a48d9b71 100644
--- a/chrome/browser/extensions/updater/extension_updater_unittest.cc
+++ b/chrome/browser/extensions/updater/extension_updater_unittest.cc
@@ -1507,7 +1507,11 @@
     std::set<int> requests({0});
     std::unique_ptr<ExtensionDownloader::ExtensionFetch> fetch =
         std::make_unique<ExtensionDownloader::ExtensionFetch>(
-            id, test_url, hash, version.GetString(), requests, fetch_priority);
+            ExtensionDownloaderTask(id, GURL() /*update_url*/,
+                                    ManifestLocation::kInternal,
+                                    false /*is_corrupt_reinstall*/,
+                                    0 /*request_id*/, fetch_priority),
+            test_url, hash, version.GetString(), requests, fetch_priority);
 
     updater.downloader_->FetchUpdatedExtension(std::move(fetch), absl::nullopt);
 
@@ -1550,7 +1554,11 @@
     requests.insert(0);
     std::unique_ptr<ExtensionDownloader::ExtensionFetch> fetch =
         std::make_unique<ExtensionDownloader::ExtensionFetch>(
-            id, test_url, hash, version.GetString(), requests,
+            ExtensionDownloaderTask(
+                id, GURL() /*update_url*/, ManifestLocation::kInternal,
+                false /*is_corrupt_reinstall*/, 0 /*request_id*/,
+                DownloadFetchPriority::kBackground),
+            test_url, hash, version.GetString(), requests,
             DownloadFetchPriority::kBackground);
     updater.downloader_->FetchUpdatedExtension(std::move(fetch), absl::nullopt);
 
@@ -1824,7 +1832,11 @@
     requests.insert(0);
     std::unique_ptr<ExtensionDownloader::ExtensionFetch> extension_fetch =
         std::make_unique<ExtensionDownloader::ExtensionFetch>(
-            id, test_url, hash, version.GetString(), requests,
+            ExtensionDownloaderTask(
+                id, GURL() /*update_url*/, ManifestLocation::kInternal,
+                false /*is_corrupt_reinstall*/, 0 /*request_id*/,
+                DownloadFetchPriority::kBackground),
+            test_url, hash, version.GetString(), requests,
             DownloadFetchPriority::kBackground);
     updater.downloader_->FetchUpdatedExtension(std::move(extension_fetch),
                                                absl::nullopt);
@@ -2046,11 +2058,19 @@
     // Start two fetches
     std::unique_ptr<ExtensionDownloader::ExtensionFetch> fetch1 =
         std::make_unique<ExtensionDownloader::ExtensionFetch>(
-            id1, url1, hash1, version1, requests,
+            ExtensionDownloaderTask(
+                id1, GURL() /*update_url*/, ManifestLocation::kInternal,
+                false /*is_corrupt_reinstall*/, 0 /*request_id*/,
+                DownloadFetchPriority::kBackground),
+            url1, hash1, version1, requests,
             DownloadFetchPriority::kBackground);
     std::unique_ptr<ExtensionDownloader::ExtensionFetch> fetch2 =
         std::make_unique<ExtensionDownloader::ExtensionFetch>(
-            id2, url2, hash2, version2, requests,
+            ExtensionDownloaderTask(
+                id2, GURL() /*update_url*/, ManifestLocation::kInternal,
+                false /*is_corrupt_reinstall*/, 0 /*request_id*/,
+                DownloadFetchPriority::kBackground),
+            url2, hash2, version2, requests,
             DownloadFetchPriority::kBackground);
     updater.downloader_->FetchUpdatedExtension(std::move(fetch1),
                                                absl::optional<std::string>());
diff --git a/chrome/browser/flag-metadata.json b/chrome/browser/flag-metadata.json
index 6b960d55..ff01e52 100644
--- a/chrome/browser/flag-metadata.json
+++ b/chrome/browser/flag-metadata.json
@@ -2991,6 +2991,13 @@
     "expiry_milestone": -1
   },
   {
+    "name": "enable-webgpu-developer-features",
+    "owners": [ "//third_party/blink/renderer/modules/webgpu/OWNERS" ],
+    // This flag is the only way for developers to use WebGPU features
+    // that are development-centric.
+    "expiry_milestone": -1
+  },
+  {
     "name": "enable-webrtc-analog-agc-clipping-control",
     "owners": [ "alessiob", "silen", "minyue" ],
     "expiry_milestone": 103
diff --git a/chrome/browser/flag-never-expire-list.json b/chrome/browser/flag-never-expire-list.json
index d7a4d86c..1494c37 100644
--- a/chrome/browser/flag-never-expire-list.json
+++ b/chrome/browser/flag-never-expire-list.json
@@ -66,6 +66,7 @@
   "enable-webassembly-tiering",
   "enable-webgl-developer-extensions",
   "enable-webgl-draft-extensions",
+  "enable-webgpu-developer-features",
   "enable-zero-copy",
   "extensions-on-chrome-urls",
   "force-color-profile",
diff --git a/chrome/browser/flag_descriptions.cc b/chrome/browser/flag_descriptions.cc
index e12e5ec..e51bdccc 100644
--- a/chrome/browser/flag_descriptions.cc
+++ b/chrome/browser/flag_descriptions.cc
@@ -2856,6 +2856,11 @@
     "Enabling this option allows web applications to access the WebGL "
     "extensions that are still in draft status.";
 
+const char kWebGpuDeveloperFeaturesName[] = "WebGPU Developer Features";
+const char kWebGpuDeveloperFeaturesDescription[] =
+    "Enables web applications to access WebGPU features intended only for use "
+    "during development.";
+
 const char kWebPaymentsExperimentalFeaturesName[] =
     "Experimental Web Payments API features";
 const char kWebPaymentsExperimentalFeaturesDescription[] =
diff --git a/chrome/browser/flag_descriptions.h b/chrome/browser/flag_descriptions.h
index 701ff70..ef09398 100644
--- a/chrome/browser/flag_descriptions.h
+++ b/chrome/browser/flag_descriptions.h
@@ -1607,6 +1607,9 @@
 extern const char kWebglDraftExtensionsName[];
 extern const char kWebglDraftExtensionsDescription[];
 
+extern const char kWebGpuDeveloperFeaturesName[];
+extern const char kWebGpuDeveloperFeaturesDescription[];
+
 extern const char kWebPaymentsExperimentalFeaturesName[];
 extern const char kWebPaymentsExperimentalFeaturesDescription[];
 
diff --git a/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/CachedFeatureFlags.java b/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/CachedFeatureFlags.java
index 02a1ad65..25f9e39 100644
--- a/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/CachedFeatureFlags.java
+++ b/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/CachedFeatureFlags.java
@@ -29,7 +29,7 @@
  *
  * To cache a flag from ChromeFeatureList:
  * - Set its default value by adding an entry to {@link #sDefaults}.
- * - Add it to the list passed to {@link #cacheNativeFlags(List)}.
+ * - Add it to the list passed to {@link ChromeCachedFlags#cacheNativeFlags(List)}.
  * - Call {@link #isEnabled(String)} to query whether the cached flag is enabled.
  *   Consider this the source of truth for whether the flag is turned on in the current session.
  * - When querying whether a cached feature is enabled from native, a @CalledByNative method can be
@@ -49,60 +49,60 @@
     private static Map<String, Boolean> sDefaults =
             ImmutableMap.<String, Boolean>builder()
                     .put(ChromeFeatureList.ANONYMOUS_UPDATE_CHECKS, true)
-                    .put(ChromeFeatureList.CONDITIONAL_TAB_STRIP_ANDROID, false)
-                    .put(ChromeFeatureList.LENS_CAMERA_ASSISTED_SEARCH, false)
-                    .put(ChromeFeatureList.COMMAND_LINE_ON_NON_ROOTED, false)
-                    .put(ChromeFeatureList.DOWNLOADS_AUTO_RESUMPTION_NATIVE, true)
-                    .put(ChromeFeatureList.EARLY_LIBRARY_LOAD, true)
-                    .put(ChromeFeatureList.ELASTIC_OVERSCROLL, true)
-                    .put(ChromeFeatureList.ELIDE_PRIORITIZATION_OF_PRE_NATIVE_BOOTSTRAP_TASKS, true)
-                    .put(ChromeFeatureList.IMMERSIVE_UI_MODE, false)
-                    .put(ChromeFeatureList.OMNIBOX_ANDROID_AUXILIARY_SEARCH, false)
-                    .put(ChromeFeatureList.SWAP_PIXEL_FORMAT_TO_FIX_CONVERT_FROM_TRANSLUCENT, true)
-                    .put(ChromeFeatureList.START_SURFACE_ANDROID, false)
-                    .put(ChromeFeatureList.PAINT_PREVIEW_DEMO, false)
-                    .put(ChromeFeatureList.PAINT_PREVIEW_SHOW_ON_STARTUP, false)
-                    .put(ChromeFeatureList.PREFETCH_NOTIFICATION_SCHEDULING_INTEGRATION, false)
-                    .put(ChromeFeatureList.STORE_HOURS, false)
-                    .put(ChromeFeatureList.TAB_GRID_LAYOUT_ANDROID, true)
-                    .put(ChromeFeatureList.TAB_GROUPS_ANDROID, true)
-                    .put(ChromeFeatureList.TAB_GROUPS_CONTINUATION_ANDROID, false)
-                    .put(ChromeFeatureList.TOOLBAR_USE_HARDWARE_BITMAP_DRAW, false)
-                    .put(ChromeFeatureList.CLOSE_TAB_SUGGESTIONS, false)
-                    .put(ChromeFeatureList.CRITICAL_PERSISTED_TAB_DATA, false)
-                    .put(ChromeFeatureList.DYNAMIC_COLOR_ANDROID, true)
-                    .put(ChromeFeatureList.DYNAMIC_COLOR_BUTTONS_ANDROID, false)
-                    .put(ChromeFeatureList.INSTANT_START, false)
-                    .put(ChromeFeatureList.TAB_TO_GTS_ANIMATION, true)
-                    .put(ChromeFeatureList.TEST_DEFAULT_DISABLED, false)
-                    .put(ChromeFeatureList.TEST_DEFAULT_ENABLED, true)
-                    .put(ChromeFeatureList.INTEREST_FEED_V2, true)
-                    .put(ChromeFeatureList.USE_CHIME_ANDROID_SDK, false)
-                    .put(ChromeFeatureList.CCT_INCOGNITO_AVAILABLE_TO_THIRD_PARTY, false)
-                    .put(ChromeFeatureList.READ_LATER, false)
-                    .put(ChromeFeatureList.CCT_REMOVE_REMOTE_VIEW_IDS, true)
-                    .put(ChromeFeatureList.CCT_INCOGNITO, true)
-                    .put(ChromeFeatureList.EXPERIMENTS_FOR_AGSA, true)
                     .put(ChromeFeatureList.APP_MENU_MOBILE_SITE_OPTION, false)
-                    .put(ChromeFeatureList.OPTIMIZATION_GUIDE_PUSH_NOTIFICATIONS, false)
-                    .put(ChromeFeatureList.NEW_WINDOW_APP_MENU, true)
+                    .put(ChromeFeatureList.BACK_GESTURE_REFACTOR, false)
+                    .put(ChromeFeatureList.CCT_INCOGNITO, true)
+                    .put(ChromeFeatureList.CCT_INCOGNITO_AVAILABLE_TO_THIRD_PARTY, false)
+                    .put(ChromeFeatureList.CCT_REMOVE_REMOTE_VIEW_IDS, true)
                     .put(ChromeFeatureList.CCT_RESIZABLE_90_MAXIMUM_HEIGHT, false)
                     .put(ChromeFeatureList.CCT_RESIZABLE_ALLOW_RESIZE_BY_USER_GESTURE, false)
                     .put(ChromeFeatureList.CCT_RESIZABLE_FOR_FIRST_PARTIES, true)
                     .put(ChromeFeatureList.CCT_RESIZABLE_FOR_THIRD_PARTIES, false)
                     .put(ChromeFeatureList.CCT_TOOLBAR_CUSTOMIZATIONS, true)
-                    .put(ChromeFeatureList.INSTANCE_SWITCHER, true)
-                    .put(ChromeFeatureList.WEB_APK_TRAMPOLINE_ON_INITIAL_INTENT, true)
+                    .put(ChromeFeatureList.CLOSE_TAB_SUGGESTIONS, false)
+                    .put(ChromeFeatureList.COMMAND_LINE_ON_NON_ROOTED, false)
+                    .put(ChromeFeatureList.CONDITIONAL_TAB_STRIP_ANDROID, false)
+                    .put(ChromeFeatureList.CREATE_SAFEBROWSING_ON_STARTUP, false)
+                    .put(ChromeFeatureList.CRITICAL_PERSISTED_TAB_DATA, false)
+                    .put(ChromeFeatureList.DOWNLOADS_AUTO_RESUMPTION_NATIVE, true)
+                    .put(ChromeFeatureList.DYNAMIC_COLOR_ANDROID, true)
+                    .put(ChromeFeatureList.DYNAMIC_COLOR_BUTTONS_ANDROID, false)
+                    .put(ChromeFeatureList.EARLY_LIBRARY_LOAD, true)
+                    .put(ChromeFeatureList.ELASTIC_OVERSCROLL, true)
+                    .put(ChromeFeatureList.ELIDE_PRIORITIZATION_OF_PRE_NATIVE_BOOTSTRAP_TASKS, true)
+                    .put(ChromeFeatureList.EXPERIMENTS_FOR_AGSA, true)
                     .put(ChromeFeatureList.FEED_LOADING_PLACEHOLDER, false)
                     .put(ChromeFeatureList.GRID_TAB_SWITCHER_FOR_TABLETS, false)
+                    .put(ChromeFeatureList.IMMERSIVE_UI_MODE, false)
+                    .put(ChromeFeatureList.INCOGNITO_REAUTHENTICATION_FOR_ANDROID, false)
+                    .put(ChromeFeatureList.INSTANCE_SWITCHER, true)
+                    .put(ChromeFeatureList.INSTANT_START, false)
+                    .put(ChromeFeatureList.INTEREST_FEED_V2, true)
+                    .put(ChromeFeatureList.LENS_CAMERA_ASSISTED_SEARCH, false)
+                    .put(ChromeFeatureList.NEW_WINDOW_APP_MENU, true)
+                    .put(ChromeFeatureList.OMNIBOX_ANDROID_AUXILIARY_SEARCH, false)
+                    .put(ChromeFeatureList.OPTIMIZATION_GUIDE_PUSH_NOTIFICATIONS, false)
+                    .put(ChromeFeatureList.PAINT_PREVIEW_DEMO, false)
+                    .put(ChromeFeatureList.PAINT_PREVIEW_SHOW_ON_STARTUP, false)
+                    .put(ChromeFeatureList.PREFETCH_NOTIFICATION_SCHEDULING_INTEGRATION, false)
+                    .put(ChromeFeatureList.READ_LATER, false)
+                    .put(ChromeFeatureList.START_SURFACE_ANDROID, false)
+                    .put(ChromeFeatureList.STORE_HOURS, false)
+                    .put(ChromeFeatureList.SWAP_PIXEL_FORMAT_TO_FIX_CONVERT_FROM_TRANSLUCENT, true)
+                    .put(ChromeFeatureList.TAB_GRID_LAYOUT_ANDROID, true)
+                    .put(ChromeFeatureList.TAB_GROUPS_ANDROID, true)
+                    .put(ChromeFeatureList.TAB_GROUPS_CONTINUATION_ANDROID, false)
                     .put(ChromeFeatureList.TAB_GROUPS_FOR_TABLETS, false)
                     .put(ChromeFeatureList.TAB_STRIP_IMPROVEMENTS, false)
-                    .put(ChromeFeatureList.BACK_GESTURE_REFACTOR, false)
+                    .put(ChromeFeatureList.TAB_TO_GTS_ANIMATION, true)
+                    .put(ChromeFeatureList.TEST_DEFAULT_DISABLED, false)
+                    .put(ChromeFeatureList.TEST_DEFAULT_ENABLED, true)
+                    .put(ChromeFeatureList.TOOLBAR_USE_HARDWARE_BITMAP_DRAW, false)
                     .put(ChromeFeatureList.TRUSTED_WEB_ACTIVITY_NOTIFICATION_PERMISSION_DELEGATION,
                             true)
-                    .put(ChromeFeatureList.CREATE_SAFEBROWSING_ON_STARTUP, false)
+                    .put(ChromeFeatureList.USE_CHIME_ANDROID_SDK, false)
+                    .put(ChromeFeatureList.WEB_APK_TRAMPOLINE_ON_INITIAL_INTENT, true)
                     .build();
-
     /**
      * Non-dynamic preference keys used historically for specific features.
      *
diff --git a/chrome/browser/incognito/android/java/src/org/chromium/chrome/browser/incognito/reauth/IncognitoReauthManager.java b/chrome/browser/incognito/android/java/src/org/chromium/chrome/browser/incognito/reauth/IncognitoReauthManager.java
index dae25b3..a8216b8 100644
--- a/chrome/browser/incognito/android/java/src/org/chromium/chrome/browser/incognito/reauth/IncognitoReauthManager.java
+++ b/chrome/browser/incognito/android/java/src/org/chromium/chrome/browser/incognito/reauth/IncognitoReauthManager.java
@@ -11,6 +11,7 @@
 
 import org.chromium.chrome.browser.device_reauth.BiometricAuthRequester;
 import org.chromium.chrome.browser.device_reauth.ReauthenticatorBridge;
+import org.chromium.chrome.browser.flags.CachedFeatureFlags;
 import org.chromium.chrome.browser.flags.ChromeFeatureList;
 import org.chromium.chrome.browser.preferences.Pref;
 import org.chromium.chrome.browser.profiles.Profile;
@@ -82,7 +83,7 @@
         // The implementation relies on {@link BiometricManager} which was introduced in API
         // level 29. Android Q is not supported due to a potential bug in BiometricPrompt.
         return (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R)
-                && ChromeFeatureList.isEnabled(
+                && CachedFeatureFlags.isEnabled(
                         ChromeFeatureList.INCOGNITO_REAUTHENTICATION_FOR_ANDROID);
     }
 
diff --git a/chrome/browser/notifications/BUILD.gn b/chrome/browser/notifications/BUILD.gn
index 1af211e..d5928adc 100644
--- a/chrome/browser/notifications/BUILD.gn
+++ b/chrome/browser/notifications/BUILD.gn
@@ -110,6 +110,7 @@
       "android/java/src/org/chromium/chrome/browser/notifications/PendingIntentProviderTest.java",
       "android/java/src/org/chromium/chrome/browser/notifications/StandardNotificationBuilderTest.java",
       "android/java/src/org/chromium/chrome/browser/notifications/ThrottlingNotificationSchedulerTest.java",
+      "android/java/src/org/chromium/chrome/browser/notifications/channels/ChannelsUpdaterTest.java",
       "android/java/src/org/chromium/chrome/browser/notifications/channels/ChromeChannelDefinitionsTest.java",
       "android/java/src/org/chromium/chrome/browser/notifications/permissions/NotificationPermissionChangeReceiverTest.java",
       "android/java/src/org/chromium/chrome/browser/notifications/permissions/NotificationPermissionControllerTest.java",
@@ -127,6 +128,7 @@
       "//chrome/test/android:chrome_java_unit_test_support",
       "//components/browser_ui/notifications/android:java",
       "//components/embedder_support/android:junit_test_support",
+      "//components/url_formatter/android:url_formatter_java",
       "//third_party/android_deps:espresso_java",
       "//third_party/android_deps:robolectric_all_java",
       "//third_party/androidx:androidx_annotation_annotation_java",
@@ -150,7 +152,6 @@
       "android/java/src/org/chromium/chrome/browser/notifications/NotificationTestUtil.java",
       "android/java/src/org/chromium/chrome/browser/notifications/NotificationWrapperBuilderFactoryTest.java",
       "android/java/src/org/chromium/chrome/browser/notifications/channels/ChannelsInitializerTest.java",
-      "android/java/src/org/chromium/chrome/browser/notifications/channels/ChannelsUpdaterTest.java",
     ]
 
     deps = [
diff --git a/chrome/browser/notifications/android/java/src/org/chromium/chrome/browser/notifications/channels/ChannelsUpdaterTest.java b/chrome/browser/notifications/android/java/src/org/chromium/chrome/browser/notifications/channels/ChannelsUpdaterTest.java
index 5d668a8..efe99283 100644
--- a/chrome/browser/notifications/android/java/src/org/chromium/chrome/browser/notifications/channels/ChannelsUpdaterTest.java
+++ b/chrome/browser/notifications/android/java/src/org/chromium/chrome/browser/notifications/channels/ChannelsUpdaterTest.java
@@ -15,28 +15,28 @@
 import android.content.Context;
 import android.content.res.Resources;
 import android.os.Build;
-import android.support.test.InstrumentationRegistry;
 
 import androidx.annotation.RequiresApi;
-import androidx.test.filters.SmallTest;
 
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
-import org.junit.rules.TestRule;
 import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RuntimeEnvironment;
 
-import org.chromium.base.test.BaseJUnit4ClassRunner;
-import org.chromium.base.test.util.Batch;
+import org.chromium.base.test.BaseRobolectricTestRunner;
+import org.chromium.base.test.util.JniMocker;
 import org.chromium.base.test.util.MinAndroidSdkLevel;
 import org.chromium.chrome.browser.preferences.ChromePreferenceKeys;
 import org.chromium.chrome.browser.preferences.SharedPreferencesManager;
-import org.chromium.chrome.test.util.browser.Features;
 import org.chromium.components.browser_ui.notifications.NotificationManagerProxy;
 import org.chromium.components.browser_ui.notifications.NotificationManagerProxyImpl;
 import org.chromium.components.browser_ui.notifications.channels.ChannelsInitializer;
-import org.chromium.content_public.browser.test.NativeLibraryTestUtils;
+import org.chromium.components.url_formatter.UrlFormatter;
+import org.chromium.components.url_formatter.UrlFormatterJni;
 
 import java.util.ArrayList;
 import java.util.Iterator;
@@ -45,8 +45,7 @@
 /**
  * Tests that ChannelsUpdater correctly initializes channels on the notification manager.
  */
-@RunWith(BaseJUnit4ClassRunner.class)
-@Batch(Batch.UNIT_TESTS)
+@RunWith(BaseRobolectricTestRunner.class)
 @RequiresApi(Build.VERSION_CODES.O)
 public class ChannelsUpdaterTest {
     private NotificationManagerProxy mNotificationManagerProxy;
@@ -55,15 +54,16 @@
     private Resources mMockResources;
 
     @Rule
-    public TestRule processor = new Features.JUnitProcessor();
+    public JniMocker mJniMocker = new JniMocker();
+    @Mock
+    private UrlFormatter.Natives mUrlFormatterJniMock;
 
     @Before
     public void setUp() {
-        // Not initializing the browser process is safe because
-        // UrlFormatter.formatUrlForSecurityDisplay() is stand-alone.
-        NativeLibraryTestUtils.loadNativeLibraryNoBrowserProcess();
+        MockitoAnnotations.initMocks(this);
+        mJniMocker.mock(UrlFormatterJni.TEST_HOOKS, mUrlFormatterJniMock);
 
-        Context context = InstrumentationRegistry.getTargetContext();
+        Context context = RuntimeEnvironment.getApplication();
         mNotificationManagerProxy = new NotificationManagerProxyImpl(context);
 
         mMockResources = context.getResources();
@@ -89,7 +89,6 @@
     }
 
     @Test
-    @SmallTest
     @MinAndroidSdkLevel(Build.VERSION_CODES.O)
     @RequiresApi(Build.VERSION_CODES.O)
     public void testShouldUpdateChannels_returnsFalsePreO() {
@@ -99,7 +98,6 @@
     }
 
     @Test
-    @SmallTest
     @MinAndroidSdkLevel(Build.VERSION_CODES.O)
     @RequiresApi(Build.VERSION_CODES.O)
     public void testShouldUpdateChannels_returnsTrueIfOAndNoSavedVersionInPrefs() {
@@ -109,7 +107,6 @@
     }
 
     @Test
-    @SmallTest
     @MinAndroidSdkLevel(Build.VERSION_CODES.O)
     @RequiresApi(Build.VERSION_CODES.O)
     public void testShouldUpdateChannels_returnsTrueIfOAndDifferentVersionInPrefs() {
@@ -120,7 +117,6 @@
     }
 
     @Test
-    @SmallTest
     @MinAndroidSdkLevel(Build.VERSION_CODES.O)
     @RequiresApi(Build.VERSION_CODES.O)
     public void testShouldUpdateChannels_returnsFalseIfOAndSameVersionInPrefs() {
@@ -131,7 +127,6 @@
     }
 
     @Test
-    @SmallTest
     @MinAndroidSdkLevel(Build.VERSION_CODES.O)
     @RequiresApi(Build.VERSION_CODES.O)
     public void testUpdateChannels_noopPreO() {
@@ -146,7 +141,6 @@
     }
 
     @Test
-    @SmallTest
     @MinAndroidSdkLevel(Build.VERSION_CODES.O)
     @RequiresApi(Build.VERSION_CODES.O)
     public void testUpdateChannels_createsExpectedChannelsAndUpdatesPref() {
@@ -166,7 +160,6 @@
     }
 
     @Test
-    @SmallTest
     @MinAndroidSdkLevel(Build.VERSION_CODES.O)
     @RequiresApi(Build.VERSION_CODES.O)
     public void testUpdateChannels_deletesLegacyChannelsAndCreatesExpectedOnes() {
diff --git a/chrome/browser/partnercustomizations/BUILD.gn b/chrome/browser/partnercustomizations/BUILD.gn
index 0c9132e6..82482ef 100644
--- a/chrome/browser/partnercustomizations/BUILD.gn
+++ b/chrome/browser/partnercustomizations/BUILD.gn
@@ -62,7 +62,7 @@
   ]
 }
 
-android_library("javatests") {
+android_library("unit_device_javatests") {
   testonly = true
   sources = [
     "junit/src/org/chromium/chrome/browser/partnercustomizations/PartnerBrowserCustomizationsUnitTest.java",
diff --git a/chrome/browser/pdf/pdf_extension_test.cc b/chrome/browser/pdf/pdf_extension_test.cc
index d233f67..5008f1b 100644
--- a/chrome/browser/pdf/pdf_extension_test.cc
+++ b/chrome/browser/pdf/pdf_extension_test.cc
@@ -3423,14 +3423,9 @@
   SendCopyCommandAndCheckCopyPasteClipboard("HEL");
 }
 
-// Flaky on Linux (https://crbug.com/1121446)
-#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
-#define MAYBE_CombinedShiftArrowPresses DISABLED_CombinedShiftArrowPresses
-#else
-#define MAYBE_CombinedShiftArrowPresses CombinedShiftArrowPresses
-#endif
+// Flaky on multiple platforms (https://crbug.com/1121446)
 IN_PROC_BROWSER_TEST_F(PDFExtensionClipboardTest,
-                       MAYBE_CombinedShiftArrowPresses) {
+                       DISABLED_CombinedShiftArrowPresses) {
   LoadTestComboBoxPdfGetGuestContents();
 
   // Give the editable combo box focus.
diff --git a/chrome/browser/renderer_context_menu/render_view_context_menu.cc b/chrome/browser/renderer_context_menu/render_view_context_menu.cc
index 3fa6907..3e19cfac 100644
--- a/chrome/browser/renderer_context_menu/render_view_context_menu.cc
+++ b/chrome/browser/renderer_context_menu/render_view_context_menu.cc
@@ -73,8 +73,6 @@
 #include "chrome/browser/sharing/click_to_call/click_to_call_metrics.h"
 #include "chrome/browser/sharing/click_to_call/click_to_call_utils.h"
 #include "chrome/browser/sharing/features.h"
-#include "chrome/browser/sharing/shared_clipboard/shared_clipboard_context_menu_observer.h"
-#include "chrome/browser/sharing/shared_clipboard/shared_clipboard_utils.h"
 #include "chrome/browser/spellchecker/spellcheck_service.h"
 #include "chrome/browser/sync/send_tab_to_self_sync_service_factory.h"
 #include "chrome/browser/translate/chrome_translate_client.h"
@@ -2087,7 +2085,6 @@
 #if !BUILDFLAG(IS_FUCHSIA)
   AppendClickToCallItem();
 #endif
-  AppendSharedClipboardItem();
 
   // Add an ending separator if there are sharing items, otherwise remove the
   // starting separator iff we added one above.
@@ -2127,18 +2124,6 @@
 }
 #endif  // !BUILDFLAG(IS_FUCHSIA)
 
-void RenderViewContextMenu::AppendSharedClipboardItem() {
-  if (!ShouldOfferSharedClipboard(browser_context_, params_.selection_text))
-    return;
-
-  if (!shared_clipboard_context_menu_observer_) {
-    shared_clipboard_context_menu_observer_ =
-        std::make_unique<SharedClipboardContextMenuObserver>(this);
-    observers_.AddObserver(shared_clipboard_context_menu_observer_.get());
-  }
-  shared_clipboard_context_menu_observer_->InitMenu(params_);
-}
-
 void RenderViewContextMenu::AppendRegionSearchItem() {
   // IDS_CONTENT_CONTEXT_LENS_REGION_SEARCH_ALT4 is the currently launched
   // string for the regions search menu item.
diff --git a/chrome/browser/renderer_context_menu/render_view_context_menu.h b/chrome/browser/renderer_context_menu/render_view_context_menu.h
index e95f4669..f7f5df2a 100644
--- a/chrome/browser/renderer_context_menu/render_view_context_menu.h
+++ b/chrome/browser/renderer_context_menu/render_view_context_menu.h
@@ -48,7 +48,6 @@
 class PrintPreviewContextMenuObserver;
 class Profile;
 class QuickAnswersMenuObserver;
-class SharedClipboardContextMenuObserver;
 class SpellingMenuObserver;
 class SpellingOptionsSubMenuObserver;
 
@@ -236,7 +235,6 @@
 #if !BUILDFLAG(IS_FUCHSIA)
   void AppendClickToCallItem();
 #endif
-  void AppendSharedClipboardItem();
   void AppendRegionSearchItem();
   bool AppendFollowUnfollowItem();
   void AppendSendTabToSelfItem(bool add_separator);
@@ -382,10 +380,6 @@
   std::unique_ptr<ClickToCallContextMenuObserver>
       click_to_call_context_menu_observer_;
 
-  // Shared clipboard menu observer.
-  std::unique_ptr<SharedClipboardContextMenuObserver>
-      shared_clipboard_context_menu_observer_;
-
   // The system app (if any) associated with the WebContents we're in.
   raw_ptr<const ash::SystemWebAppDelegate> system_app_ = nullptr;
 
diff --git a/chrome/browser/resources/chromeos/accessibility/accessibility_common/BUILD.gn b/chrome/browser/resources/chromeos/accessibility/accessibility_common/BUILD.gn
index e9b0039..01a256ee 100644
--- a/chrome/browser/resources/chromeos/accessibility/accessibility_common/BUILD.gn
+++ b/chrome/browser/resources/chromeos/accessibility/accessibility_common/BUILD.gn
@@ -51,6 +51,7 @@
     "dictation/macros/list_commands_macro.js",
     "dictation/macros/macro.js",
     "dictation/macros/macro_names.js",
+    "dictation/macros/nav_sent_macro.js",
     "dictation/macros/repeatable_key_press_macro.js",
     "dictation/macros/smart_delete_phrase_macro.js",
     "dictation/macros/smart_insert_before_macro.js",
@@ -247,6 +248,7 @@
     "dictation/macros/list_commands_macro.js",
     "dictation/macros/macro.js",
     "dictation/macros/macro_names.js",
+    "dictation/macros/nav_sent_macro.js",
     "dictation/macros/repeatable_key_press_macro.js",
     "dictation/macros/smart_delete_phrase_macro.js",
     "dictation/macros/smart_insert_before_macro.js",
diff --git a/chrome/browser/resources/chromeos/accessibility/accessibility_common/accessibility_common_test.js b/chrome/browser/resources/chromeos/accessibility/accessibility_common/accessibility_common_test.js
index 20d8b9f..64348e3 100644
--- a/chrome/browser/resources/chromeos/accessibility/accessibility_common/accessibility_common_test.js
+++ b/chrome/browser/resources/chromeos/accessibility/accessibility_common/accessibility_common_test.js
@@ -41,7 +41,7 @@
 
   async getPref(name) {
     return new Promise(resolve => {
-      chrome.settingsPrivate.getPref(name, (ret) => {
+      chrome.settingsPrivate.getPref(name, ret => {
         resolve(ret);
       });
     });
diff --git a/chrome/browser/resources/chromeos/accessibility/accessibility_common/autoclick/autoclick.js b/chrome/browser/resources/chromeos/accessibility/accessibility_common/autoclick/autoclick.js
index 4c7f27c..163ecf2f 100644
--- a/chrome/browser/resources/chromeos/accessibility/accessibility_common/autoclick/autoclick.js
+++ b/chrome/browser/resources/chromeos/accessibility/accessibility_common/autoclick/autoclick.js
@@ -73,7 +73,7 @@
     this.scrollableBoundsListener_ = (x, y) =>
         this.findScrollingContainerForPoint_(x, y);
 
-    chrome.automation.getDesktop((desktop) => {
+    chrome.automation.getDesktop(desktop => {
       this.desktop_ = desktop;
 
       // We use a hit test at a point to determine what automation node is
diff --git a/chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/dictation_test_base.js b/chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/dictation_test_base.js
index 3c445b2a..768aa78 100644
--- a/chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/dictation_test_base.js
+++ b/chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/dictation_test_base.js
@@ -228,7 +228,7 @@
    */
   async getPref(name) {
     return new Promise(resolve => {
-      chrome.settingsPrivate.getPref(name, (ret) => {
+      chrome.settingsPrivate.getPref(name, ret => {
         resolve(ret);
       });
     });
diff --git a/chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/editing_util.js b/chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/editing_util.js
index 2c51176..8a75968 100644
--- a/chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/editing_util.js
+++ b/chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/editing_util.js
@@ -103,6 +103,62 @@
   }
 
   /**
+   * Returns the start index of the sentence to the right of the caret.
+   * Indices are relative to `value`. Assumes that sentences are separated by
+   * punctuation specified in `EditingUtil.END_OF_SENTENCE_REGEX_`. If no next
+   * sentence can be found, returns `value.length`.
+   * @param {string} value
+   * @param {number} caretIndex
+   * @return {number}
+   */
+  static navNextSent(value, caretIndex) {
+    const rightOfCaret = value.substring(caretIndex);
+    const index = rightOfCaret.search(EditingUtil.END_OF_SENTENCE_REGEX_);
+    if (index === -1) {
+      return value.length;
+    }
+
+    // `index` should be relative to `value`;
+    return index + caretIndex + 1;
+  }
+
+  /**
+   * Returns the start index of the sentence to the left of the caret. Indices
+   * are relative to `value`. Assumes that sentences are separated by
+   * punctuation specified in `EditingUtil.END_OF_SENTENCE_REGEX_`. If no
+   * previous sentence can be found, returns 0.
+   * @param {string} value
+   * @param {number} caretIndex
+   * @return {number|null}
+   */
+  static navPrevSent(value, caretIndex) {
+    let encounteredText = false;
+    if (caretIndex === value.length) {
+      --caretIndex;
+    }
+
+    while (caretIndex >= 0) {
+      const valueAtCaret = value[caretIndex];
+      if (encounteredText &&
+          EditingUtil.END_OF_SENTENCE_REGEX_.test(valueAtCaret)) {
+        // Adjust if there is whitespace immediately to the right of the caret.
+        return EditingUtil.BEGINS_WITH_WHITESPACE_REGEX_.test(
+                   value[caretIndex + 1]) ?
+            caretIndex + 1 :
+            caretIndex;
+      }
+
+      if (!EditingUtil.BEGINS_WITH_WHITESPACE_REGEX_.test(valueAtCaret) &&
+          !EditingUtil.PUNCTUATION_REGEX_.test(valueAtCaret)) {
+        encounteredText = true;
+      }
+      --caretIndex;
+    }
+
+    return 0;
+  }
+
+  /**
    * Returns a RegExp that matches on the right-most occurrence of a phrase.
    * The returned RegExp is case insensitive and requires that `phrase` is
    * separated by word boundaries.
@@ -134,3 +190,22 @@
     return new RegExp(`(\\b${phrase}\\b )(?!.*\\b\\1\\b)`, 'i');
   }
 }
+
+/**
+ * @private {!RegExp}
+ * @const
+ */
+EditingUtil.END_OF_SENTENCE_REGEX_ = /[;!.?]/;
+
+/**
+ * @private {!RegExp}
+ * @const
+ */
+EditingUtil.BEGINS_WITH_WHITESPACE_REGEX_ = /^\s/;
+
+/**
+ * @private {!RegExp}
+ * @const
+ */
+EditingUtil.PUNCTUATION_REGEX_ =
+    /[-$#"()*;:<>\n\\\/\{\}\[\]+='~`!@_.,?%\u2022\u25e6\u25a0]/g;
diff --git a/chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/editing_util_test.js b/chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/editing_util_test.js
index 26907466..c976830b 100644
--- a/chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/editing_util_test.js
+++ b/chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/editing_util_test.js
@@ -241,3 +241,71 @@
   assertEquals(5, selection.start);
   assertEquals(9, selection.end);
 });
+
+SYNC_TEST_F('DictationEditingUtilTest', 'NavNextSent', function() {
+  let value;
+  let caretIndex;
+  const f = () => EditingUtil.navNextSent(value, caretIndex);
+
+  // Simple.
+  value = 'Hello world. Goodnight world.';
+  caretIndex = 0;
+  assertEquals(12, f());
+
+  // If the end of the sentence can't be found, then moving to the next sentence
+  // should take us to the end of the value.
+  value = 'Hello world goodnight world';
+  caretIndex = 0;
+  assertEquals(value.length, f());
+
+  // Various punctuation.
+  value = 'This?\nIs! A. Test;';
+  caretIndex = 0;
+  assertEquals(5, f());
+  caretIndex = 5;
+  assertEquals(9, f());
+  caretIndex = 9;
+  assertEquals(12, f());
+  caretIndex = 12;
+  assertEquals(value.length, f());
+
+
+  // Edge case: empty value.
+  value = '';
+  caretIndex = 0;
+  assertEquals(0, f());
+});
+
+SYNC_TEST_F('DictationEditingUtilTest', 'NavPrevSent', function() {
+  let value;
+  let caretIndex;
+  const f = () => EditingUtil.navPrevSent(value, caretIndex);
+
+  // Simple.
+  value = 'Hello world. Goodnight world.';
+  caretIndex = value.length;
+  assertEquals(12, f());
+
+  // If the end of the sentence can't be found, then moving to the previous
+  // sentence should take us to the beginning of the value.
+  value = 'Hello world goodnight world';
+  caretIndex = value.length;
+  assertEquals(0, f());
+
+  // Various punctuation and whitespace.
+  value = 'This?\nIs! A. Test;';
+  caretIndex = value.length;
+  assertEquals(12, f());
+  caretIndex = 12;
+  assertEquals(9, f());
+
+  caretIndex = 9;
+  assertEquals(5, f());
+  caretIndex = 5;
+  assertEquals(0, f());
+
+  // Edge case: empty value.
+  value = '';
+  caretIndex = 0;
+  assertEquals(0, f());
+});
diff --git a/chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/input_controller.js b/chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/input_controller.js
index 76dbf39..6c130e29 100644
--- a/chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/input_controller.js
+++ b/chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/input_controller.js
@@ -178,44 +178,12 @@
 
     const value = editableNode.value;
     const caretIndex = editableNode.textSelStart;
-    const prevSentenceStart =
-        this.findPrevSentenceStartIndex_(value, caretIndex);
+    const prevSentenceStart = EditingUtil.navPrevSent(value, caretIndex);
     const length = caretIndex - prevSentenceStart;
     this.deleteSurroundingText_(length, -length);
   }
 
   /**
-   * Returns the start index of the sentence to the left of the caret. Indices
-   * are relative to `text`. Assumes that sentences are separated by punctuation
-   * specified in `InputController.END_OF_SENTENCE_REGEX_`.
-   * @param {string} text
-   * @param {number} caretIndex The index of the text caret.
-   */
-  findPrevSentenceStartIndex_(text, caretIndex) {
-    let encounteredText = false;
-    if (caretIndex === text.length) {
-      --caretIndex;
-    }
-
-    while (caretIndex >= 0) {
-      const valueAtCaret = text[caretIndex];
-      if (encounteredText &&
-          InputController.END_OF_SENTENCE_REGEX_.test(valueAtCaret)) {
-        // Adjust if there is another sentence after this one.
-        return text[caretIndex + 1] === ' ' ? caretIndex + 2 : caretIndex;
-      }
-
-      if (!InputController.BEGINS_WITH_WHITESPACE_REGEX_.test(valueAtCaret) &&
-          !InputController.PUNCTUATION_REGEX_.test(valueAtCaret)) {
-        encounteredText = true;
-      }
-      --caretIndex;
-    }
-
-    return 0;
-  }
-
-  /**
    * @param {number} length The number of characters to be deleted.
    * @param {number} offset The offset from the caret position where deletion
    * will start. This value can be negative.
@@ -307,6 +275,34 @@
 
     editableNode.setSelection(selection.start, selection.end);
   }
+
+  /** Moves the text caret to the next sentence. */
+  navNextSent() {
+    const editableNode = this.focusHandler_.getEditableNode();
+    if (!editableNode || !editableNode.value ||
+        editableNode.textSelStart !== editableNode.textSelEnd) {
+      return;
+    }
+
+    const value = editableNode.value;
+    const caretIndex = editableNode.textSelStart;
+    const newCaretIndex = EditingUtil.navNextSent(value, caretIndex);
+    editableNode.setSelection(newCaretIndex, newCaretIndex);
+  }
+
+  /** Moves the text caret to the previous sentence. */
+  navPrevSent() {
+    const editableNode = this.focusHandler_.getEditableNode();
+    if (!editableNode || !editableNode.value ||
+        editableNode.textSelStart !== editableNode.textSelEnd) {
+      return;
+    }
+
+    const value = editableNode.value;
+    const caretIndex = editableNode.textSelStart;
+    const newCaretIndex = EditingUtil.navPrevSent(value, caretIndex);
+    editableNode.setSelection(newCaretIndex, newCaretIndex);
+  }
 }
 
 /**
@@ -327,16 +323,3 @@
  * @const
  */
 InputController.BEGINS_WITH_WHITESPACE_REGEX_ = /^\s/;
-
-/**
- * @private {!RegExp}
- * @const
- */
-InputController.PUNCTUATION_REGEX_ =
-    /[-$#"()*;:<>\n\\\/\{\}\[\]+='~`!@_.,?%\u2022\u25e6\u25a0]/g;
-
-/**
- * @private {!RegExp}
- * @const
- */
-InputController.END_OF_SENTENCE_REGEX_ = /[;!.?]/g;
diff --git a/chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/macros/hidden_macro_manager.js b/chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/macros/hidden_macro_manager.js
index 29c7a64..c3229b5 100644
--- a/chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/macros/hidden_macro_manager.js
+++ b/chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/macros/hidden_macro_manager.js
@@ -4,6 +4,7 @@
 
 import {DeletePrevSentMacro} from '/accessibility_common/dictation/macros/delete_prev_sent_macro.js';
 import {MacroName} from '/accessibility_common/dictation/macros/macro_names.js';
+import {NavNextSentMacro, NavPrevSentMacro} from '/accessibility_common/dictation/macros/nav_sent_macro.js';
 import {DeletePrevWordMacro, NavNextWordMacro, NavPrevWordMacro} from '/accessibility_common/dictation/macros/repeatable_key_press_macro.js';
 import {SmartDeletePhraseMacro} from '/accessibility_common/dictation/macros/smart_delete_phrase_macro.js';
 import {SmartInsertBeforeMacro} from '/accessibility_common/dictation/macros/smart_insert_before_macro.js';
@@ -38,10 +39,18 @@
         new DeletePrevSentMacro(this.inputController_).runMacro();
         break;
       case MacroName.NAV_NEXT_WORD:
-        new NavNextWordMacro(/*isRtlLocale=*/ false).runMacro();
+        new NavNextWordMacro(/*isRTLLocale=*/ false).runMacro();
         break;
       case MacroName.NAV_PREV_WORD:
-        new NavPrevWordMacro(/*isRtlLocale=*/ false).runMacro();
+        new NavPrevWordMacro(/*isRTLLocale=*/ false).runMacro();
+        break;
+      case MacroName.NAV_NEXT_SENT:
+        new NavNextSentMacro(this.inputController_, /*isRTLLocale=*/ false)
+            .runMacro();
+        break;
+      case MacroName.NAV_PREV_SENT:
+        new NavPrevSentMacro(this.inputController_, /*isRTLLocale=*/ false)
+            .runMacro();
         break;
       default:
         throw new Error(`Cannot run macro: ${name} for testing`);
@@ -120,4 +129,6 @@
   MacroName.SMART_REPLACE_PHRASE,
   MacroName.SMART_INSERT_BEFORE,
   MacroName.SMART_SELECT_BTWN_INCL,
+  MacroName.NAV_NEXT_SENT,
+  MacroName.NAV_PREV_SENT,
 ];
diff --git a/chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/macros/macro_names.js b/chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/macros/macro_names.js
index 64de379..328477c0 100644
--- a/chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/macros/macro_names.js
+++ b/chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/macros/macro_names.js
@@ -94,5 +94,11 @@
   // Sets selection between two provided words or phrases.
   SMART_SELECT_BTWN_INCL: 24,
 
+  // Move the cursor to the next sentence.
+  NAV_NEXT_SENT: 25,
+
+  // Move the cursor to the previous sentence.
+  NAV_PREV_SENT: 26,
+
   // Any new actions should match with Voice Access's semantic tags.
 };
diff --git a/chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/macros/nav_sent_macro.js b/chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/macros/nav_sent_macro.js
new file mode 100644
index 0000000..197bb2fd
--- /dev/null
+++ b/chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/macros/nav_sent_macro.js
@@ -0,0 +1,68 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import {Macro} from '/accessibility_common/dictation/macros/macro.js';
+import {MacroName} from '/accessibility_common/dictation/macros/macro_names.js';
+
+/** Implements a macro that moves the text caret to the next sentence. */
+export class NavNextSentMacro extends Macro {
+  /**
+   * @param {!InputController} inputController
+   * @param {boolean} isRTLLocale
+   */
+  constructor(inputController, isRTLLocale) {
+    super(MacroName.NAV_NEXT_SENT);
+    /** @private {!InputController} */
+    this.inputController_ = inputController;
+    /** @private {boolean} */
+    this.isRTLLocale_ = isRTLLocale;
+  }
+
+  /** @override */
+  checkContext() {
+    return this.createSuccessCheckContextResult_(
+        /*willImmediatelyDisambiguate=*/ false);
+  }
+
+  /** @override */
+  runMacro() {
+    if (!this.inputController_.isActive()) {
+      return this.createRunMacroResult_(
+          /*isSuccess=*/ false, MacroError.FAILED_ACTUATION);
+    }
+    this.inputController_.navNextSent();
+    return this.createRunMacroResult_(/*isSuccess=*/ true);
+  }
+}
+
+/** Implements a macro that moves the text caret to the previous sentence. */
+export class NavPrevSentMacro extends Macro {
+  /**
+   * @param {!InputController} inputController
+   * @param {boolean} isRTLLocale
+   */
+  constructor(inputController, isRTLLocale) {
+    super(MacroName.NAV_PREV_SENT);
+    /** @private {!InputController} */
+    this.inputController_ = inputController;
+    /** @private {boolean} */
+    this.isRTLLocale_ = isRTLLocale;
+  }
+
+  /** @override */
+  checkContext() {
+    return this.createSuccessCheckContextResult_(
+        /*willImmediatelyDisambiguate=*/ false);
+  }
+
+  /** @override */
+  runMacro() {
+    if (!this.inputController_.isActive()) {
+      return this.createRunMacroResult_(
+          /*isSuccess=*/ false, MacroError.FAILED_ACTUATION);
+    }
+    this.inputController_.navPrevSent();
+    return this.createRunMacroResult_(/*isSuccess=*/ true);
+  }
+}
diff --git a/chrome/browser/resources/chromeos/accessibility/accessibility_common/magnifier/magnifier.js b/chrome/browser/resources/chromeos/accessibility/accessibility_common/magnifier/magnifier.js
index d1eaac66..c31eed2 100644
--- a/chrome/browser/resources/chromeos/accessibility/accessibility_common/magnifier/magnifier.js
+++ b/chrome/browser/resources/chromeos/accessibility/accessibility_common/magnifier/magnifier.js
@@ -137,7 +137,7 @@
     }, Magnifier.IGNORE_FOCUS_UPDATES_INITIALIZATION_MS);
 
     chrome.commandLinePrivate.hasSwitch(
-        'enable-magnifier-debug-draw-rect', (enabled) => {
+        'enable-magnifier-debug-draw-rect', enabled => {
           if (enabled) {
             this.magnifierDebugDrawRect_ = true;
           }
@@ -170,7 +170,7 @@
    * @private
    */
   updateFromPrefs_(prefs) {
-    prefs.forEach((pref) => {
+    prefs.forEach(pref => {
       switch (pref.key) {
         case Magnifier.Prefs.SCREEN_MAGNIFIER_FOCUS_FOLLOWING:
           this.screenMagnifierFocusFollowing_ = Boolean(pref.value);
diff --git a/chrome/browser/resources/chromeos/accessibility/accessibility_common/magnifier/magnifier_test.js b/chrome/browser/resources/chromeos/accessibility/accessibility_common/magnifier/magnifier_test.js
index 159f2cfc..2100c32 100644
--- a/chrome/browser/resources/chromeos/accessibility/accessibility_common/magnifier/magnifier_test.js
+++ b/chrome/browser/resources/chromeos/accessibility/accessibility_common/magnifier/magnifier_test.js
@@ -16,7 +16,7 @@
 
   async getNextMagnifierBounds() {
     return new Promise(resolve => {
-      const listener = (magnifierBounds) => {
+      const listener = magnifierBounds => {
         chrome.accessibilityPrivate.onMagnifierBoundsChanged.removeListener(
             listener);
         resolve(magnifierBounds);
@@ -28,7 +28,7 @@
 
   async getPref(name) {
     return new Promise(resolve => {
-      chrome.settingsPrivate.getPref(name, (ret) => {
+      chrome.settingsPrivate.getPref(name, ret => {
         resolve(ret);
       });
     });
@@ -183,7 +183,7 @@
         const magnifier = accessibilityCommon.getMagnifierForTest();
         magnifier.setIsInitializingForTest(false);
 
-        const moveMenuSelectionAssertBounds = async (targetBounds) => {
+        const moveMenuSelectionAssertBounds = async targetBounds => {
           // Send arrow up key.
           chrome.accessibilityPrivate.sendSyntheticKeyEvent({
             type:
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/background/panel/panel_node_menu_background_test.js b/chrome/browser/resources/chromeos/accessibility/chromevox/background/panel/panel_node_menu_background_test.js
index 47e1cc3..6c7b291 100644
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/background/panel/panel_node_menu_background_test.js
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/background/panel/panel_node_menu_background_test.js
@@ -14,7 +14,7 @@
 
 // Fake PanelBridge.
 const PanelBridge = {
-  addMenuItem: (item, id) => PanelBridge.calls.append({item, id}),
+  addMenuItem: item => PanelBridge.calls.push(item),
   calls: [],
 };
 
@@ -33,18 +33,18 @@
   assertMenuItemIndicatesNoNodesFound(item) {
     assertNotNullNorUndefined(item);
     assertEquals('None', item.title);
-    assertEquals(-1, item.callbackId);
+    assertEquals(-1, item.callbackNodeIndex);
     assertFalse(item.isActive);
   }
 
   assertItemMatches(expectedName, item, opt_isActive) {
     assertEquals(expectedName, item.title);
     assertTrue(
-        item.callbackId >= 0 &&
-        item.callbackId < PanelNodeMenuBackground.callbackNodes_.length);
+        item.callbackNodeIndex >= 0 &&
+        item.callbackNodeIndex < PanelNodeMenuBackground.callbackNodes_.length);
     assertEquals(
         expectedName,
-        PanelNodeMenuBackground.callbackNodes_[item.callbackId].name);
+        PanelNodeMenuBackground.callbackNodes_[item.callbackNodeIndex].name);
     if (opt_isActive) {
       assertTrue(item.isActive);
     } else {
@@ -53,13 +53,13 @@
   }
 
   assertMenusHaveNoNodesFoundExcept(id) {
-    for (const menu of PanelNodeMenuData.ALL_NODE_MENUS) {
+    for (const menu of ALL_NODE_MENU_DATA) {
       if (menu.menuId === id) {
         continue;
       }
-      const call = PanelBridge.calls.find(args => args.id === menu.menuId);
+      const call = PanelBridge.calls.find(item => item.menuId === menu.menuId);
       assertNotNullNorUndefined(call);
-      this.assertMenuItemIndicatesNoNodesFound(call.item);
+      this.assertMenuItemIndicatesNoNodesFound(call);
     }
   }
 
@@ -68,24 +68,24 @@
     PanelBackground.instance.createAllNodeMenuBackgrounds_();
   }
 
-  isFormControl(args) {
-    return args.id === PanelNodeMenuId.FORM_CONTROL;
+  isFormControl(item) {
+    return item.menuId === PanelNodeMenuId.FORM_CONTROL;
   }
 
-  isHeading(args) {
-    return args.id === PanelNodeMenuId.HEADING;
+  isHeading(item) {
+    return item.menuId === PanelNodeMenuId.HEADING;
   }
 
-  isLandmark(args) {
-    return args.id === PanelNodeMenuId.LANDMARK;
+  isLandmark(item) {
+    return item.menuId === PanelNodeMenuId.LANDMARK;
   }
 
-  isLink(args) {
-    return args.id === PanelNodeMenuId.LINK;
+  isLink(item) {
+    return item.menuId === PanelNodeMenuId.LINK;
   }
 
-  isTable(args) {
-    return args.id === PanelNodeMenuId.TABLE;
+  isTable(item) {
+    return item.menuId === PanelNodeMenuId.TABLE;
   }
 
   get formControlsDoc() {
@@ -158,8 +158,7 @@
 
       // Expect that one element is added per menu, specifying that no nodes
       // of that type are found.
-      assertEquals(
-          PanelNodeMenuData.ALL_NODE_MENUS.length, PanelBridge.calls.length);
+      assertEquals(ALL_NODE_MENU_DATA.length, PanelBridge.calls.length);
       // Assert all menus have a no nodes found element.
       this.assertMenusHaveNoNodesFoundExcept(null);
     });
@@ -168,14 +167,12 @@
   await this.runWithLoadedTree(this.headingsDoc);
   this.createAllNodeMenuBackgrounds();
 
-  // Check that there are the correct number of calls (one for each menu, plus
-  // two extra for the additional headings found).
-  assertEquals(
-      PanelNodeMenuData.ALL_NODE_MENUS_.length + 2, PanelBridge.calls.length);
+  // Check that there are the correct number of calls (one for each menu,
+  // plus two extra for the additional headings found).
+  assertEquals(ALL_NODE_MENU_DATA.length + 2, PanelBridge.calls.length);
 
   // Expect that the three items are added to the headings menu
-  const headingItems =
-      PanelBridge.calls.findAll(this.isHeading).map(args => args.item);
+  const headingItems = PanelBridge.calls.findAll(this.isHeading);
   assertEquals(3, headingItems.length);
 
   this.assertItemMatches('Heading 1', headingItems.unshift());
@@ -189,14 +186,12 @@
   await this.runWithLoadedTree(this.landmarksDoc);
   this.createAllNodeMenuBackgrounds();
 
-  // Check that there are the correct number of calls (one for each menu, plus
-  // seven extra for the additional landmarks found).
-  assertEquals(
-      PanelNodeMenuData.ALL_NODE_MENUS_.length + 7, PanelBridge.calls.length);
+  // Check that there are the correct number of calls (one for each menu,
+  // plus seven extra for the additional landmarks found).
+  assertEquals(ALL_NODE_MENU_DATA.length + 7, PanelBridge.calls.length);
 
   // Verify that eight items were added to the landmarks menu.
-  const landmarkItems =
-      PanelBridge.calls.findAll(this.isLandmark).map(args => args.item);
+  const landmarkItems = PanelBridge.calls.findAll(this.isLandmark);
   assertEquals(8, landmarkItems.length);
 
   this.assertItemMatches('application', landmarkItems.unshift());
@@ -217,12 +212,10 @@
 
   // Check that there are the correct number of calls (one for each menu, plus
   // three extra for the additional links found).
-  assertEquals(
-      PanelNodeMenuData.ALL_NODE_MENUS_.length + 3, PanelBridge.calls.length);
+  assertEquals(ALL_NODE_MENU_DATA.length + 3, PanelBridge.calls.length);
 
   // Verify that four items were added to the links menu.
-  const linkItems =
-      PanelBridge.calls.findAll(this.isLink).map(args => args.item);
+  const linkItems = PanelBridge.calls.findAll(this.isLink);
   assertEquals(4, linkItems.length);
 
   this.assertItemMatches('Link 1', linkItems.unshift());
@@ -240,13 +233,10 @@
 
       // Check that there are the correct number of calls (one for each menu,
       // plus eight extra for the additional form controls found).
-      assertEquals(
-          PanelNodeMenuData.ALL_NODE_MENUS_.length + 8,
-          PanelBridge.calls.length);
+      assertEquals(ALL_NODE_MENU_DATA.length + 8, PanelBridge.calls.length);
 
       // Verify that nine items were added to the form controls menu.
-      const formItems =
-          PanelBridge.calls.findAll(this.isFormControl).map(args => args.item);
+      const formItems = PanelBridge.calls.findAll(this.isFormControl);
       assertEquals(9, formItems.length);
 
       this.assertItemMatches('button', formItems.unshift());
@@ -266,14 +256,12 @@
   await this.runWithLoadedTree(this.tablesDoc);
   this.createAllNodeMenuBackgrounds();
 
-  // Check that there are the correct number of calls (one for each menu, plus
-  // one extra for the additional links found).
-  assertEquals(
-      PanelNodeMenuData.ALL_NODE_MENUS_.length + 1, PanelBridge.calls.length);
+  // Check that there are the correct number of calls (one for each menu,
+  // plus one extra for the additional links found).
+  assertEquals(ALL_NODE_MENU_DATA.length + 1, PanelBridge.calls.length);
 
   // Verify that two items were added to the tables menu.
-  const tableItems =
-      PanelBridge.calls.findAll(this.isTable).map(args => args.item);
+  const tableItems = PanelBridge.calls.findAll(this.isTable);
   assertEquals(2, tableItems.length);
 
   this.assertItemMatches('grid', tableItems.unshift());
@@ -287,22 +275,21 @@
   this.createAllNodeMenuBackgrounds();
 
   // Check that there are the correct number of calls (one for each menu).
-  assertEquals(
-      PanelNodeMenuData.ALL_NODE_MENUS_.length, PanelBridge.calls.length);
+  assertEquals(ALL_NODE_MENU_DATA.length, PanelBridge.calls.length);
 
   // Check that each item was added to the correct menu.
-  const formItem = PanelBridge.calls.find(this.isFormControl).item;
+  const formItem = PanelBridge.calls.find(this.isFormControl);
   this.assertItemMatches('button', formItem);
 
-  const headingItem = PanelBridge.calls.find(this.isHeading).item;
+  const headingItem = PanelBridge.calls.find(this.isHeading);
   this.assertItemMatches('header', headingItem);
 
-  const landmarkItem = PanelBridge.calls.find(this.isLandmark).item;
+  const landmarkItem = PanelBridge.calls.find(this.isLandmark);
   this.assertItemMatches('region', landmarkItem);
 
-  const linkItem = PanelBridge.calls.find(this.isLink).item;
+  const linkItem = PanelBridge.calls.find(this.isLink);
   this.assertItemMatches('link', linkItem);
 
-  const tableItem = PanelBridge.calls.find(this.isTable).item;
+  const tableItem = PanelBridge.calls.find(this.isTable);
   this.assertItemMatches('table', tableItem);
 });
diff --git a/chrome/browser/resources/chromeos/accessibility/common/automation_predicate.js b/chrome/browser/resources/chromeos/accessibility/common/automation_predicate.js
index 3d5a7893..b6a10a68 100644
--- a/chrome/browser/resources/chromeos/accessibility/common/automation_predicate.js
+++ b/chrome/browser/resources/chromeos/accessibility/common/automation_predicate.js
@@ -815,7 +815,7 @@
 /** @type {AutomationPredicate.Unary} */
 AutomationPredicate.simpleListItem = AutomationPredicate.match({
   anyPredicate:
-      [(node) => node.role === Role.LIST_ITEM && node.children.length === 2 &&
+      [node => node.role === Role.LIST_ITEM && node.children.length === 2 &&
            node.firstChild.role === Role.LIST_MARKER &&
            node.lastChild.role === Role.STATIC_TEXT]
 });
@@ -876,7 +876,7 @@
 AutomationPredicate.clickable = AutomationPredicate.match({
   anyPredicate: [
     AutomationPredicate.button, AutomationPredicate.link,
-    (node) => {
+    node => {
       return node.defaultActionVerb ===
           chrome.automation.DefaultActionVerb.CLICK;
     }
diff --git a/chrome/browser/resources/chromeos/accessibility/common/automation_util_test.js b/chrome/browser/resources/chromeos/accessibility/common/automation_util_test.js
index f5db965..a693382 100644
--- a/chrome/browser/resources/chromeos/accessibility/common/automation_util_test.js
+++ b/chrome/browser/resources/chromeos/accessibility/common/automation_util_test.js
@@ -208,7 +208,7 @@
       assertEquals(
           'x',
           AutomationUtil
-              .findLastNode(r, (n) => n.role === RoleType.GENERIC_CONTAINER)
+              .findLastNode(r, n => n.role === RoleType.GENERIC_CONTAINER)
               .name);
     });
 
@@ -223,8 +223,7 @@
     `);
       assertEquals(
           'outer',
-          AutomationUtil.findLastNode(r, (n) => n.role === RoleType.BUTTON)
-              .name);
+          AutomationUtil.findLastNode(r, n => n.role === RoleType.BUTTON).name);
     });
 
 TEST_F(
@@ -238,6 +237,6 @@
       assertEquals(
           'inner',
           AutomationUtil
-              .findLastNode(r, (n) => n.role === RoleType.GENERIC_CONTAINER)
+              .findLastNode(r, n => n.role === RoleType.GENERIC_CONTAINER)
               .name);
     });
diff --git a/chrome/browser/resources/chromeos/accessibility/common/event_generator_test.js b/chrome/browser/resources/chromeos/accessibility/common/event_generator_test.js
index 23265ba..d67c801 100644
--- a/chrome/browser/resources/chromeos/accessibility/common/event_generator_test.js
+++ b/chrome/browser/resources/chromeos/accessibility/common/event_generator_test.js
@@ -21,7 +21,7 @@
     'EventGeneratorTest', 'DISABLED_MouseEventsProcessedSequentially',
     function() {
       const mouseEventLog = [];
-      chrome.accessibilityPrivate.sendSyntheticMouseEvent = (event) =>
+      chrome.accessibilityPrivate.sendSyntheticMouseEvent = event =>
           mouseEventLog.push(event);
 
       // Set a 1ms delay so that a timeout is set between the press and release.
diff --git a/chrome/browser/resources/chromeos/accessibility/common/event_handler.js b/chrome/browser/resources/chromeos/accessibility/common/event_handler.js
index f3d78ee..68bf83c 100644
--- a/chrome/browser/resources/chromeos/accessibility/common/event_handler.js
+++ b/chrome/browser/resources/chromeos/accessibility/common/event_handler.js
@@ -48,7 +48,7 @@
      * Default is a function that always returns true.
      * @private {!function(chrome.automation.AutomationEvent): boolean}
      */
-    this.predicate_ = options.predicate || ((e) => true);
+    this.predicate_ = options.predicate || (e => true);
 
     /** @private {boolean} */
     this.listening_ = false;
@@ -120,7 +120,7 @@
    * @param {!chrome.automation.AutomationNode} node
    */
   removeNode(node) {
-    this.nodes_ = this.nodes_.filter((n) => n !== node);
+    this.nodes_ = this.nodes_.filter(n => n !== node);
 
     if (this.listening_) {
       for (const type of this.types_) {
diff --git a/chrome/browser/resources/chromeos/accessibility/common/rect_util.js b/chrome/browser/resources/chromeos/accessibility/common/rect_util.js
index 8d335761..099c682 100644
--- a/chrome/browser/resources/chromeos/accessibility/common/rect_util.js
+++ b/chrome/browser/resources/chromeos/accessibility/common/rect_util.js
@@ -57,14 +57,14 @@
    * @param {ScreenRect|undefined} rect
    * @return {number}
    */
-  area: (rect) => (rect ? rect.width * rect.height : 0),
+  area: rect => (rect ? rect.width * rect.height : 0),
 
   /**
    * Finds the bottom of a rect.
    * @param {!ScreenRect} rect
    * @return {number}
    */
-  bottom: (rect) => rect.top + rect.height,
+  bottom: rect => rect.top + rect.height,
 
   /**
    * Returns the point at the center of the rectangle.
@@ -72,7 +72,7 @@
    * @return {!{x: number, y: number}} an object containing the x and y
    *     coordinates of the center.
    */
-  center: (rect) => {
+  center: rect => {
     const x = rect.left + Math.round(rect.width / 2);
     const y = rect.top + Math.round(rect.height / 2);
     return {x, y};
@@ -118,7 +118,7 @@
    * @param {!ScreenRect} rect
    * @return {!ScreenRect}
    */
-  deepCopy: (rect) => /** @type {!ScreenRect} */ (Object.assign({}, rect)),
+  deepCopy: rect => /** @type {!ScreenRect} */ (Object.assign({}, rect)),
 
   /**
    * Returns the largest rectangle contained within the outer rect that does not
@@ -295,7 +295,7 @@
    * @param {!ScreenRect} rect
    * @return {number}
    */
-  right: (rect) => rect.left + rect.width,
+  right: rect => rect.left + rect.width,
 
   /*
    * @param {ScreenRect=} rect1
@@ -317,7 +317,7 @@
    * @param {ScreenRect|undefined} rect
    * @return {string}
    */
-  toString: (rect) => {
+  toString: rect => {
     let str = '';
     if (rect) {
       str = rect.left + ',' + rect.top + ' ';
@@ -355,7 +355,7 @@
    * @param {!Array<!ScreenRect>} rects
    * @return {!ScreenRect}
    */
-  unionAll: (rects) => {
+  unionAll: rects => {
     if (rects.length < 1) {
       return RectUtil.ZERO_RECT;
     }
diff --git a/chrome/browser/resources/chromeos/accessibility/common/repeated_tree_change_handler.js b/chrome/browser/resources/chromeos/accessibility/common/repeated_tree_change_handler.js
index 865ffc5..f9ca297 100644
--- a/chrome/browser/resources/chromeos/accessibility/common/repeated_tree_change_handler.js
+++ b/chrome/browser/resources/chromeos/accessibility/common/repeated_tree_change_handler.js
@@ -26,7 +26,7 @@
      * default to always return true.
      * @private {!function(!chrome.automation.TreeChange)}
      */
-    this.predicate_ = options.predicate || ((c) => true);
+    this.predicate_ = options.predicate || (c => true);
 
     /** @private {!function(!chrome.automation.TreeChange)} */
     this.handler_ = change => this.onChange_(change);
diff --git a/chrome/browser/resources/chromeos/accessibility/common/repeated_tree_change_handler_test.js b/chrome/browser/resources/chromeos/accessibility/common/repeated_tree_change_handler_test.js
index 09401c7..1b44148 100644
--- a/chrome/browser/resources/chromeos/accessibility/common/repeated_tree_change_handler_test.js
+++ b/chrome/browser/resources/chromeos/accessibility/common/repeated_tree_change_handler_test.js
@@ -39,8 +39,7 @@
     const handler = () => this.handlerCallCount++;
 
     const repeatedHandler = new RepeatedTreeChangeHandler(
-        'allTreeChanges', handler,
-        {predicate: (c) => c.type === 'nodeRemoved'});
+        'allTreeChanges', handler, {predicate: c => c.type === 'nodeRemoved'});
 
     // Simulate events being fired.
     repeatedHandler.onChange_({type: 'nodeAdded'});
diff --git a/chrome/browser/resources/chromeos/accessibility/common/testing/e2e_test_base.js b/chrome/browser/resources/chromeos/accessibility/common/testing/e2e_test_base.js
index 6bf9b05..590d6dd 100644
--- a/chrome/browser/resources/chromeos/accessibility/common/testing/e2e_test_base.js
+++ b/chrome/browser/resources/chromeos/accessibility/common/testing/e2e_test_base.js
@@ -240,7 +240,7 @@
 
       // Listener for both load complete and focus events that eventually
       // triggers the test.
-      const listener = async (event) => {
+      const listener = async event => {
         if (hasLacrosChromePath && !didNavigateForLacros) {
           // We have yet to request navigation in the Lacros tab. Do so now by
           // getting the default focus (the address bar), setting the value to
@@ -302,8 +302,8 @@
    */
   runWithLoadedOptionsPage(callback, matchUrlRegExp = /options.html/) {
     callback = this.newCallback(callback);
-    chrome.automation.getDesktop((desktop) => {
-      const listener = (event) => {
+    chrome.automation.getDesktop(desktop => {
+      const listener = event => {
         if (!matchUrlRegExp.test(event.target.docUrl) ||
             !event.target.docLoaded) {
           return;
diff --git a/chrome/browser/resources/chromeos/accessibility/common/testing/mock_input_ime.js b/chrome/browser/resources/chromeos/accessibility/common/testing/mock_input_ime.js
index d8a6f81..4d65b617 100644
--- a/chrome/browser/resources/chromeos/accessibility/common/testing/mock_input_ime.js
+++ b/chrome/browser/resources/chromeos/accessibility/common/testing/mock_input_ime.js
@@ -52,7 +52,7 @@
      * Adds a listener to onFocus.
      * @param {function<InputContext>} listener
      */
-    addListener: (listener) => {
+    addListener: listener => {
       MockInputIme.onFocusListener_ = listener;
     },
 
@@ -60,7 +60,7 @@
      * Removes the listener.
      * @param {function<InputContext>} listener
      */
-    removeListener: (listener) => {
+    removeListener: listener => {
       if (MockInputIme.onFocusListener_ === listener) {
         MockInputIme.onFocusListener_ = null;
       }
@@ -72,7 +72,7 @@
      * Adds a listener to onBlur.
      * @param {function<number>} listener
      */
-    addListener: (listener) => {
+    addListener: listener => {
       MockInputIme.onBlurListener_ = listener;
     },
 
@@ -80,7 +80,7 @@
      * Removes the listener.
      * @param {function<number>} listener
      */
-    removeListener: (listener) => {
+    removeListener: listener => {
       if (MockInputIme.onBlurListener_ === listener) {
         MockInputIme.onBlurListener_ = null;
       }
diff --git a/chrome/browser/resources/chromeos/accessibility/common/testing/mock_speech_recognition_private.js b/chrome/browser/resources/chromeos/accessibility/common/testing/mock_speech_recognition_private.js
index 89ee2ca..b450508 100644
--- a/chrome/browser/resources/chromeos/accessibility/common/testing/mock_speech_recognition_private.js
+++ b/chrome/browser/resources/chromeos/accessibility/common/testing/mock_speech_recognition_private.js
@@ -53,10 +53,10 @@
      *  removeListener: function(Function):void}}
      */
     this.onStop = {
-      addListener: (listener) => {
+      addListener: listener => {
         this.onStopListener_ = listener;
       },
-      removeListener: (listener) => {
+      removeListener: listener => {
         if (this.onStopListener_ === listener) {
           this.onStopListener_ = null;
         }
@@ -69,10 +69,10 @@
      *  removeListener: function(Function):void}}
      */
     this.onResult = {
-      addListener: (listener) => {
+      addListener: listener => {
         this.onResultListener_ = listener;
       },
-      removeListener: (listener) => {
+      removeListener: listener => {
         if (this.onResultListener_ === listener) {
           this.onResultListener_ = null;
         }
@@ -85,10 +85,10 @@
      *  removeListener: function(Function):void}}
      */
     this.onError = {
-      addListener: (listener) => {
+      addListener: listener => {
         this.onErrorListener_ = listener;
       },
-      removeListener: (listener) => {
+      removeListener: listener => {
         if (this.onErrorListener_ === listener) {
           this.onErrorListener_ = null;
         }
diff --git a/chrome/browser/resources/chromeos/accessibility/common/testing/mock_storage.js b/chrome/browser/resources/chromeos/accessibility/common/testing/mock_storage.js
index cc25519..b8d9b0ae 100644
--- a/chrome/browser/resources/chromeos/accessibility/common/testing/mock_storage.js
+++ b/chrome/browser/resources/chromeos/accessibility/common/testing/mock_storage.js
@@ -19,8 +19,8 @@
     /**
      * @param {Object<string, string>} updates Map from keys to values to store.
      */
-    set: (updates) => {
-      Object.keys(updates).forEach((key) => {
+    set: updates => {
+      Object.keys(updates).forEach(key => {
         MockStorage.local_[key] = updates[key];
       });
       MockStorage.callOnChangedListeners(this.local_);
@@ -40,7 +40,7 @@
      * Removes the value with the given key.
      * @param {string} key The key to remove.
      */
-    remove: (key) => {
+    remove: key => {
       delete MockStorage.local_[key];
       MockStorage.callOnChangedListeners(this.local_);
     }
@@ -50,8 +50,8 @@
     /**
      * @param {Object<string, *>} updates Map from keys to values to store.
      */
-    set: (updates) => {
-      Object.keys(updates).forEach((key) => {
+    set: updates => {
+      Object.keys(updates).forEach(key => {
         MockStorage.sync_[key] = updates[key];
       });
       MockStorage.callOnChangedListeners(this.sync_);
@@ -71,7 +71,7 @@
      * Removes the value with the given key.
      * @param {string} key The key to remove.
      */
-    remove: (key) => {
+    remove: key => {
       delete MockStorage.sync_[key];
       MockStorage.callOnChangedListeners(this.sync_);
     }
@@ -82,7 +82,7 @@
      * Set the onChanged callback.
      * @param {function(Object<string, *>)}
      */
-    addListener: (callback) => {
+    addListener: callback => {
       MockStorage.callbacks_.push(callback);
     },
   },
@@ -92,8 +92,8 @@
    * This is functionality for testing and not part of the API.
    * @param {!Object<string, *>} opt_values
    */
-  callOnChangedListeners: (opt_values) => {
-    MockStorage.callbacks_.forEach((callback) => {
+  callOnChangedListeners: opt_values => {
+    MockStorage.callbacks_.forEach(callback => {
       const baseObject = opt_values || MockStorage.sync_;
       const result = {};
       for (const key in baseObject) {
diff --git a/chrome/browser/resources/chromeos/accessibility/common/tutorial/chromevox_tutorial.js b/chrome/browser/resources/chromeos/accessibility/common/tutorial/chromevox_tutorial.js
index 8e8ca913..f8395d3c 100644
--- a/chrome/browser/resources/chromeos/accessibility/common/tutorial/chromevox_tutorial.js
+++ b/chrome/browser/resources/chromeos/accessibility/common/tutorial/chromevox_tutorial.js
@@ -407,11 +407,11 @@
     // finished loading.
     this.hideAllScreens();
     document.addEventListener('keydown', this.onKeyDown.bind(this));
-    this.addEventListener('startpractice', (evt) => {
+    this.addEventListener('startpractice', evt => {
       this.isPracticeAreaActive = true;
       this.startNudges(NudgeType.PRACTICE_AREA);
     });
-    this.addEventListener('endpractice', (evt) => {
+    this.addEventListener('endpractice', evt => {
       this.isPracticeAreaActive = false;
       this.startNudges(NudgeType.GENERAL);
     });
@@ -536,7 +536,7 @@
    * which is defined on the Panel window.
    */
   initializeNudges(type) {
-    const maybeGiveNudge = (msg) => {
+    const maybeGiveNudge = msg => {
       if (this.interactiveMode_) {
         // Do not announce message since ChromeVox blocks actions in interactive
         // mode.
@@ -720,7 +720,7 @@
       const link = document.createElement('a');
       link.innerText = this.getMsg(resource.msgId);
       link.href = resource.link;
-      link.addEventListener('click', (evt) => {
+      link.addEventListener('click', evt => {
         this.stopNudges();
         this.dispatchEvent(new CustomEvent(
             'openUrl', {composed: true, detail: {url: link.href}}));
diff --git a/chrome/browser/resources/chromeos/accessibility/common/tutorial/lesson_container.js b/chrome/browser/resources/chromeos/accessibility/common/tutorial/lesson_container.js
index efc2797..1a8ea3d 100644
--- a/chrome/browser/resources/chromeos/accessibility/common/tutorial/lesson_container.js
+++ b/chrome/browser/resources/chromeos/accessibility/common/tutorial/lesson_container.js
@@ -30,7 +30,7 @@
 
   /** @override */
   ready() {
-    this.$.lessonTemplate.addEventListener('dom-change', (evt) => {
+    this.$.lessonTemplate.addEventListener('dom-change', evt => {
       // Executes once all lessons have been added to the dom.
       this.onLessonsLoaded_();
     });
diff --git a/chrome/browser/resources/chromeos/accessibility/common/tutorial/tutorial_lesson.js b/chrome/browser/resources/chromeos/accessibility/common/tutorial/tutorial_lesson.js
index ded9699..31b09a9 100644
--- a/chrome/browser/resources/chromeos/accessibility/common/tutorial/tutorial_lesson.js
+++ b/chrome/browser/resources/chromeos/accessibility/common/tutorial/tutorial_lesson.js
@@ -59,7 +59,7 @@
 
   /** @override */
   ready() {
-    this.$.contentTemplate.addEventListener('dom-change', (evt) => {
+    this.$.contentTemplate.addEventListener('dom-change', evt => {
       this.dispatchEvent(new CustomEvent('lessonready', {composed: true}));
     });
 
@@ -70,12 +70,12 @@
         this.$.practiceContent.addEventListener(
             evt, event => this.onPracticeEvent(event), true);
       }
-      this.$.practiceContent.addEventListener('focus', (evt) => {
+      this.$.practiceContent.addEventListener('focus', evt => {
         // The practice area has the potential to overflow, so ensure elements
         // are scrolled into view when focused.
         evt.target.scrollIntoView();
       }, true);
-      this.$.practiceContent.addEventListener('click', (evt) => {
+      this.$.practiceContent.addEventListener('click', evt => {
         // Intercept click events. For example, clicking a link will exit the
         // tutorial without this listener.
         evt.preventDefault();
@@ -139,7 +139,7 @@
     const path = '../tutorial/practice_areas/' + this.practiceFile + '.html';
     const xhr = new XMLHttpRequest();
     xhr.open('GET', path, true);
-    xhr.onload = (evt) => {
+    xhr.onload = evt => {
       if (xhr.readyState === 4 && xhr.status === 200) {
         this.$.practiceContent.innerHTML = xhr.responseText;
         this.localizePracticeAreaContent();
diff --git a/chrome/browser/resources/chromeos/accessibility/enhanced_network_tts/enhanced_network_tts.js b/chrome/browser/resources/chromeos/accessibility/enhanced_network_tts/enhanced_network_tts.js
index 6e5a644..780f6a0 100644
--- a/chrome/browser/resources/chromeos/accessibility/enhanced_network_tts/enhanced_network_tts.js
+++ b/chrome/browser/resources/chromeos/accessibility/enhanced_network_tts/enhanced_network_tts.js
@@ -80,7 +80,7 @@
     const request = EnhancedNetworkTts.generateRequest(utterance, options);
     await (this.api_.getAudioDataWithCallback(
         request,
-        (response) => this.queueResponse_(
+        response => this.queueResponse_(
             response, audioStreamOptions, sendTtsAudio, sendError)));
   }
 
diff --git a/chrome/browser/resources/chromeos/accessibility/enhanced_network_tts/enhanced_network_tts_e2e_test_base.js b/chrome/browser/resources/chromeos/accessibility/enhanced_network_tts/enhanced_network_tts_e2e_test_base.js
index 5822346..37fb27e 100644
--- a/chrome/browser/resources/chromeos/accessibility/enhanced_network_tts/enhanced_network_tts_e2e_test_base.js
+++ b/chrome/browser/resources/chromeos/accessibility/enhanced_network_tts/enhanced_network_tts_e2e_test_base.js
@@ -18,12 +18,12 @@
     this.onStopListeners = [];
     chrome.ttsEngine = {
       onSpeakWithAudioStream: {
-        addListener: (callback) => {
+        addListener: callback => {
           this.onSpeakWithAudioStreamListeners.push(callback);
         },
       },
       onStop: {
-        addListener: (callback) => {
+        addListener: callback => {
           this.onStopListeners.push(callback);
         },
       }
diff --git a/chrome/browser/resources/chromeos/accessibility/enhanced_network_tts/enhanced_network_tts_unittest.js b/chrome/browser/resources/chromeos/accessibility/enhanced_network_tts/enhanced_network_tts_unittest.js
index ac2c3f8..1b663bb 100644
--- a/chrome/browser/resources/chromeos/accessibility/enhanced_network_tts/enhanced_network_tts_unittest.js
+++ b/chrome/browser/resources/chromeos/accessibility/enhanced_network_tts/enhanced_network_tts_unittest.js
@@ -242,7 +242,7 @@
           expectedBuffers.map(buffer => Object.assign({}, buffer));
       expectedBuffersWithoutLastBuffer[5].isLastBuffer = false;
 
-      let mockSendTtsAudio = (receivedBuffer) => {
+      let mockSendTtsAudio = receivedBuffer => {
         const expectedBuffer = expectedBuffers.shift();
         assertEqualsJSON(expectedBuffer, receivedBuffer);
       };
@@ -250,7 +250,7 @@
           decodedAudioData, sampleRate, bufferSize, timeInfo, mockSendTtsAudio,
           /* lastData= */ true);
 
-      mockSendTtsAudio = (receivedBuffer) => {
+      mockSendTtsAudio = receivedBuffer => {
         const expectedBuffer = expectedBuffersWithoutLastBuffer.shift();
         assertEqualsJSON(expectedBuffer, receivedBuffer);
       };
diff --git a/chrome/browser/resources/chromeos/accessibility/enhanced_network_tts/error_handling_unittest.js b/chrome/browser/resources/chromeos/accessibility/enhanced_network_tts/error_handling_unittest.js
index 7acbf8a..a1c15d6 100644
--- a/chrome/browser/resources/chromeos/accessibility/enhanced_network_tts/error_handling_unittest.js
+++ b/chrome/browser/resources/chromeos/accessibility/enhanced_network_tts/error_handling_unittest.js
@@ -27,10 +27,10 @@
       const utterance = 'test';
       const options = {'voiceName': 'Enhanced TTS English (Australian Accent)'};
       const audioStreamOptions = {'bufferSize': 10000, 'sampleRate': 22000};
-      const sendTtsAudio = (receivedBuffer) => {
+      const sendTtsAudio = receivedBuffer => {
         throw new Error('Assertion failed: does not expect incoming buffer.');
       };
-      const sendError = (error) => {
+      const sendError = error => {
         assertEquals(error, 'Error: unable to get mojoPrivate bindings');
       };
 
@@ -51,10 +51,10 @@
       const utterance = 'test';
       const options = {'voiceName': 'Enhanced TTS English (Australian Accent)'};
       const audioStreamOptions = {'bufferSize': 10000, 'sampleRate': 22000};
-      const sendTtsAudio = (receivedBuffer) => {
+      const sendTtsAudio = receivedBuffer => {
         throw new Error('Assertion failed: does not expect incoming buffer.');
       };
-      const sendError = (error) => {
+      const sendError = error => {
         assertEquals(error, 'Error: utterance too long');
       };
 
diff --git a/chrome/browser/resources/chromeos/accessibility/select_to_speak/metrics_utils.js b/chrome/browser/resources/chromeos/accessibility/select_to_speak/metrics_utils.js
index adf72dc..d0c0382 100644
--- a/chrome/browser/resources/chromeos/accessibility/select_to_speak/metrics_utils.js
+++ b/chrome/browser/resources/chromeos/accessibility/select_to_speak/metrics_utils.js
@@ -15,7 +15,7 @@
   static recordCancelIfSpeaking() {
     // TODO(b/1157214): Use select-to-speak's internal state instead of TTS
     // state.
-    chrome.tts.isSpeaking((speaking) => {
+    chrome.tts.isSpeaking(speaking => {
       if (speaking) {
         MetricsUtils.recordCancelEvent_();
       }
diff --git a/chrome/browser/resources/chromeos/accessibility/select_to_speak/node_navigation_utils.js b/chrome/browser/resources/chromeos/accessibility/select_to_speak/node_navigation_utils.js
index e5a3c99..1d8efd7 100644
--- a/chrome/browser/resources/chromeos/accessibility/select_to_speak/node_navigation_utils.js
+++ b/chrome/browser/resources/chromeos/accessibility/select_to_speak/node_navigation_utils.js
@@ -85,7 +85,7 @@
     if (containingRoot.role === RoleType.ROOT_WEB_AREA) {
       const ancestors = AutomationUtil.getAncestors(containingRoot);
       const topRootWebArea =
-          ancestors.find((a) => a.role === RoleType.ROOT_WEB_AREA);
+          ancestors.find(a => a.role === RoleType.ROOT_WEB_AREA);
       if (topRootWebArea) {
         containingRoot = topRootWebArea;
       }
@@ -96,7 +96,7 @@
     }
     const nextNode = AutomationUtil.findNextNode(
         startNode, direction, NodeUtils.isValidLeafNode, {
-          root: (n) => containingRoot === n,
+          root: n => containingRoot === n,
           skipInitialSubtree: true,
         });
     if (nextNode === null) {
@@ -239,8 +239,7 @@
     const nodes = AutomationUtil.findAllNodes(
         node, direction,
         /* pred= */ NodeUtils.isValidLeafNode, /* opt_restrictions= */ {
-          root: (node) =>
-              node === blockParent,  // Only traverse within the block
+          root: node => node === blockParent,  // Only traverse within the block
         });
 
     // Reverse the nodes if we were traversing backward, so the returned result
diff --git a/chrome/browser/resources/chromeos/accessibility/select_to_speak/node_navigation_utils_unittest.js b/chrome/browser/resources/chromeos/accessibility/select_to_speak/node_navigation_utils_unittest.js
index 21743e06..03ff7db 100644
--- a/chrome/browser/resources/chromeos/accessibility/select_to_speak/node_navigation_utils_unittest.js
+++ b/chrome/browser/resources/chromeos/accessibility/select_to_speak/node_navigation_utils_unittest.js
@@ -71,7 +71,7 @@
       // paragraph 2.
       result = NodeNavigationUtils.getNodesForNextParagraph(
           nodeGroupForParagraph1, constants.Dir.FORWARD,
-          (nodes) => !(nodes.find(
+          nodes => !(nodes.find(
               n => n.parent ===
                   paragraph2) /* filter out nodes belong to paragraph 2 */));
       assertEquals(result.length, 0);
@@ -688,7 +688,7 @@
       ({nodes, offset} = NodeNavigationUtils.getNodesForNextSentence(
            nodeGroupForParagraph1, 8 /* currentCharIndex */,
            constants.Dir.FORWARD,
-           (nodes) => !(nodes.find(
+           nodes => !(nodes.find(
                n => n.parent ===
                    paragraph2) /* filter out nodes belong to paragraph 2 */)));
       assertEquals(nodes.length, 0);
@@ -737,7 +737,7 @@
       ({nodes, offset} = NodeNavigationUtils.getNodesForNextSentence(
            nodeGroupForParagraph2, 0 /* currentCharIndex */,
            constants.Dir.BACKWARD,
-           (nodes) => !(nodes.find(
+           nodes => !(nodes.find(
                n => n.parent ===
                    paragraph1) /* filter out nodes belong to paragraph 1 */)));
       assertEquals(nodes.length, 0);
diff --git a/chrome/browser/resources/chromeos/accessibility/select_to_speak/node_utils.js b/chrome/browser/resources/chromeos/accessibility/select_to_speak/node_utils.js
index 8a86fc8..9cb07c43 100644
--- a/chrome/browser/resources/chromeos/accessibility/select_to_speak/node_utils.js
+++ b/chrome/browser/resources/chromeos/accessibility/select_to_speak/node_utils.js
@@ -478,8 +478,7 @@
     return AutomationUtil.findAllNodes(
         blockParent, constants.Dir.FORWARD,
         /* pred= */ NodeUtils.isValidLeafNode, /* opt_restrictions= */ {
-          root: (node) =>
-              node === blockParent,  // Only traverse within the block
+          root: node => node === blockParent,  // Only traverse within the block
         });
   }
 
diff --git a/chrome/browser/resources/chromeos/accessibility/select_to_speak/paragraph_utils.js b/chrome/browser/resources/chromeos/accessibility/select_to_speak/paragraph_utils.js
index eb251e2..cbb1a76 100644
--- a/chrome/browser/resources/chromeos/accessibility/select_to_speak/paragraph_utils.js
+++ b/chrome/browser/resources/chromeos/accessibility/select_to_speak/paragraph_utils.js
@@ -229,7 +229,7 @@
       }
 
       node.unclippedBoundsForRange(
-          boundQueryStartIndex, boundQueryEndIndex, (b) => {
+          boundQueryStartIndex, boundQueryEndIndex, b => {
             // If the word is entirely out of the blockparent bounds,
             // replace the word with space characters.
             if (b.left + b.width <= leftBound || b.left >= rightBound ||
diff --git a/chrome/browser/resources/chromeos/accessibility/select_to_speak/prefs_manager.js b/chrome/browser/resources/chromeos/accessibility/select_to_speak/prefs_manager.js
index 4f95d571..6529d3f 100644
--- a/chrome/browser/resources/chromeos/accessibility/select_to_speak/prefs_manager.js
+++ b/chrome/browser/resources/chromeos/accessibility/select_to_speak/prefs_manager.js
@@ -78,14 +78,14 @@
     var uiLocale = chrome.i18n.getMessage('@@ui_locale');
     uiLocale = uiLocale.replace('_', '-').toLowerCase();
 
-    chrome.tts.getVoices((voices) => {
+    chrome.tts.getVoices(voices => {
       this.validVoiceNames_ = new Set();
 
       if (voices.length === 0) {
         return;
       }
 
-      voices.forEach((voice) => {
+      voices.forEach(voice => {
         if (!voice.eventTypes.includes(chrome.tts.EventType.START) ||
             !voice.eventTypes.includes(chrome.tts.EventType.END) ||
             !voice.eventTypes.includes(chrome.tts.EventType.WORD) ||
@@ -126,7 +126,7 @@
         this.voiceNameFromLocale_ = firstVoiceName;
       }
 
-      chrome.storage.sync.get(['voice'], (prefs) => {
+      chrome.storage.sync.get(['voice'], prefs => {
         if (!prefs['voice']) {
           chrome.storage.sync.set({'voice': PrefsManager.SYSTEM_VOICE});
         }
@@ -162,7 +162,7 @@
     // rate before doing migration logic.
     const getPrefsPromises = [];
     getPrefsPromises.push(new Promise((resolve, reject) => {
-      chrome.settingsPrivate.getPref('settings.tts.speech_rate', (pref) => {
+      chrome.settingsPrivate.getPref('settings.tts.speech_rate', pref => {
         if (pref === undefined) {
           reject();
         }
@@ -171,7 +171,7 @@
       });
     }));
     getPrefsPromises.push(new Promise((resolve, reject) => {
-      chrome.settingsPrivate.getPref('settings.tts.speech_pitch', (pref) => {
+      chrome.settingsPrivate.getPref('settings.tts.speech_pitch', pref => {
         if (pref === undefined) {
           reject();
         }
@@ -204,7 +204,7 @@
                 setPrefsPromises.push(new Promise((resolve, reject) => {
                   chrome.settingsPrivate.setPref(
                       'settings.tts.speech_rate', stsRate,
-                      '' /* unused, see crbug.com/866161 */, (success) => {
+                      '' /* unused, see crbug.com/866161 */, success => {
                         if (success) {
                           resolve();
                         } else {
@@ -215,7 +215,7 @@
                 setPrefsPromises.push(new Promise((resolve, reject) => {
                   chrome.settingsPrivate.setPref(
                       'settings.tts.speech_pitch', stsPitch,
-                      '' /* unused, see crbug.com/866161 */, (success) => {
+                      '' /* unused, see crbug.com/866161 */, success => {
                         if (success) {
                           resolve();
                         } else {
@@ -225,8 +225,7 @@
                 }));
                 Promise.all(setPrefsPromises)
                     .then(
-                        () => this.onTtsSettingsMigrationSuccess_(),
-                        (error) => {
+                        () => this.onTtsSettingsMigrationSuccess_(), error => {
                           console.log(error);
                           this.migrationInProgress_ = false;
                         });
@@ -236,7 +235,7 @@
                 this.onTtsSettingsMigrationSuccess_();
               }
             },
-            (error) => {
+            error => {
               console.log(error);
               this.migrationInProgress_ = false;
             });
@@ -261,7 +260,7 @@
   initPreferences() {
     const updatePolicy = () => {
       chrome.settingsPrivate.getPref(
-          PrefsManager.ENHANCED_VOICES_POLICY_KEY, (pref) => {
+          PrefsManager.ENHANCED_VOICES_POLICY_KEY, pref => {
             if (pref === undefined) {
               return;
             }
@@ -275,7 +274,7 @@
             'backgroundShading', 'navigationControls', 'enhancedNetworkVoices',
             'enhancedVoicesDialogShown', 'enhancedVoiceName'
           ],
-          (prefs) => {
+          prefs => {
             if (prefs['voice']) {
               this.voiceNameFromPrefs_ = prefs['voice'];
             }
diff --git a/chrome/browser/resources/chromeos/accessibility/select_to_speak/select_to_speak.js b/chrome/browser/resources/chromeos/accessibility/select_to_speak/select_to_speak.js
index 46669486..974a402 100644
--- a/chrome/browser/resources/chromeos/accessibility/select_to_speak/select_to_speak.js
+++ b/chrome/browser/resources/chromeos/accessibility/select_to_speak/select_to_speak.js
@@ -70,7 +70,7 @@
     /** @private {number|undefined} */
     this.intervalRef_;
 
-    chrome.automation.getDesktop((desktop) => {
+    chrome.automation.getDesktop(desktop => {
       this.desktop_ = desktop;
 
       // After the user selects a region of the screen, we do a hit test at
@@ -179,7 +179,7 @@
 
     // TODO(chrishall): do we want to (also?) expose this in preferences?
     chrome.commandLinePrivate.hasSwitch(
-        'enable-experimental-accessibility-language-detection', (result) => {
+        'enable-experimental-accessibility-language-detection', result => {
           this.enableLanguageDetectionIntegration_ = result;
         });
 
@@ -189,13 +189,13 @@
      */
     this.enhancedVoicesFlag_ = false;
     chrome.accessibilityPrivate.isFeatureEnabled(
-        AccessibilityFeature.ENHANCED_NETWORK_VOICES, (result) => {
+        AccessibilityFeature.ENHANCED_NETWORK_VOICES, result => {
           this.enhancedVoicesFlag_ = result;
         });
 
     /** @private {number} Default speech rate set in system settings. */
     this.systemSpeechRate_ = 1.0;
-    chrome.settingsPrivate.getPref(SPEECH_RATE_KEY, (pref) => {
+    chrome.settingsPrivate.getPref(SPEECH_RATE_KEY, pref => {
       if (!pref) {
         return;
       }
@@ -263,7 +263,7 @@
 
     var rect = this.inputHandler_.getMouseRect();
     var nodes = [];
-    chrome.automation.getFocus((focusedNode) => {
+    chrome.automation.getFocus(focusedNode => {
       // In some cases, e.g. ARC++, the window received in the hit test request,
       // which is computed based on which window is the event handler for the
       // hit point, isn't the part of the tree that contains the actual
@@ -453,7 +453,7 @@
       if (!gsuiteAppRootNode) {
         return;
       }
-      chrome.tabs.query({active: true}, (tabs) => {
+      chrome.tabs.query({active: true}, tabs => {
         // Closure doesn't realize that we did a !gsuiteAppRootNode earlier
         // so we check again here.
         if (tabs.length === 0 || !gsuiteAppRootNode) {
@@ -488,7 +488,7 @@
       return;
     }
     this.scrollToSpokenNode_ = true;
-    const listener = (event) => {
+    const listener = event => {
       if (event.eventFrom !== 'action') {
         // User initiated event. Cancel all future scrolling to spoken nodes.
         // If the user wants a certain scroll position we will respect that.
@@ -693,8 +693,8 @@
             'https://docs.sandbox.google.com/*'
           ]
         },
-        (tabs) => {
-          tabs.forEach((tab) => {
+        tabs => {
+          tabs.forEach(tab => {
             chrome.tabs.executeScript(tab.id, {file: script});
           });
         });
@@ -850,7 +850,7 @@
    * @private
    */
   onPrefsChanged_(prefs) {
-    const ratePref = prefs.find((pref) => pref.key === SPEECH_RATE_KEY);
+    const ratePref = prefs.find(pref => pref.key === SPEECH_RATE_KEY);
     if (ratePref) {
       this.systemSpeechRate_ = ratePref.value;
     }
@@ -870,7 +870,7 @@
     }
     const {nodes, offset} = NodeNavigationUtils.getNodesForNextSentence(
         this.getCurrentNodeGroup_(), this.currentCharIndex_, direction,
-        (nodes) => this.skipPanel_(nodes));
+        nodes => this.skipPanel_(nodes));
     if (nodes.length === 0) {
       return;
     }
@@ -893,7 +893,7 @@
 
     const nodes = NodeNavigationUtils.getNodesForNextParagraph(
         this.getCurrentNodeGroup_(), direction,
-        (nodes) => this.skipPanel_(nodes));
+        nodes => this.skipPanel_(nodes));
     // Return early if the nodes are empty.
     if (nodes.length === 0) {
       return;
@@ -914,7 +914,7 @@
    */
   skipPanel_(nodes) {
     return !AutomationUtil.getAncestors(nodes[0]).find(
-        (n) => UiManager.isPanel(n));
+        n => UiManager.isPanel(n));
   }
 
   /**
@@ -933,7 +933,7 @@
 
       // Without nodes to anchor on, navigate is not supported.
       this.supportsNavigationPanel_ = false;
-      options.onEvent = (event) => {
+      options.onEvent = event => {
         if (event.type === 'start') {
           this.onStateChanged_(SelectToSpeakState.SPEAKING);
           this.updateUi_();
@@ -1127,7 +1127,7 @@
 
     const nodeGroupText = nodeGroup.text || '';
 
-    options.onEvent = (event) => {
+    options.onEvent = event => {
       switch (event.type) {
         case chrome.tts.EventType.START:
           if (nodeGroup.nodes.length <= 0) {
@@ -1443,10 +1443,10 @@
    * @private
    */
   isNodeInForeground_(node) {
-    return new Promise((resolve) => {
+    return new Promise(resolve => {
       this.desktop_.hitTestWithReply(
-          node.location.left, node.location.top, (nodeAtLocation) => {
-            chrome.automation.getFocus((focusedNode) => {
+          node.location.left, node.location.top, nodeAtLocation => {
+            chrome.automation.getFocus(focusedNode => {
               const window =
                   NodeUtils.getNearestContainingWindow(nodeAtLocation);
               const currentWindow = NodeUtils.getNearestContainingWindow(node);
@@ -1580,7 +1580,7 @@
       const description = chrome.i18n.getMessage(
           'select_to_speak_natural_voice_dialog_description');
       chrome.accessibilityPrivate.showConfirmationDialog(
-          title, description, (confirm) => {
+          title, description, confirm => {
             this.prefsManager_.setEnhancedNetworkVoicesFromDialog(confirm);
             if (callback !== undefined) {
               callback();
@@ -1672,7 +1672,7 @@
     // auto-dismissing behavior (see http://crbug.com/1157148), but also
     // navigation controls do not work well for control-rich interfaces that are
     // light on text (and therefore no sentence and paragraph structures).
-    return !nodes.some((n) => n.root && n.root.role === RoleType.DESKTOP);
+    return !nodes.some(n => n.root && n.root.role === RoleType.DESKTOP);
   }
 
   /**
diff --git a/chrome/browser/resources/chromeos/accessibility/select_to_speak/select_to_speak_mouse_selection_test.js b/chrome/browser/resources/chromeos/accessibility/select_to_speak/select_to_speak_mouse_selection_test.js
index eeb5a9e..b7b4d6c47 100644
--- a/chrome/browser/resources/chromeos/accessibility/select_to_speak/select_to_speak_mouse_selection_test.js
+++ b/chrome/browser/resources/chromeos/accessibility/select_to_speak/select_to_speak_mouse_selection_test.js
@@ -229,7 +229,7 @@
       // stylus may act as a laser pointer unless it taps on the stylus options
       // button, which always opens on a tap regardless of the stylus behavior
       // selected.
-      this.runWithLoadedDesktop((desktop) => {
+      this.runWithLoadedDesktop(desktop => {
         this.tapTrayButton(desktop, () => {
           assertEquals(selectToSpeak.state_, SelectToSpeakState.SELECTING);
           const button = desktop.find({
@@ -240,8 +240,8 @@
           // sure we actually don't start speech after the hittest and focus
           // callbacks are used to check which nodes should be spoken.
           desktop.addEventListener(
-              EventType.MOUSE_RELEASED, this.newCallback((evt) => {
-                chrome.automation.getFocus(this.newCallback((node) => {
+              EventType.MOUSE_RELEASED, this.newCallback(evt => {
+                chrome.automation.getFocus(this.newCallback(node => {
                   assertEquals(
                       selectToSpeak.state_, SelectToSpeakState.INACTIVE);
                   assertFalse(this.mockTts.currentlySpeaking());
diff --git a/chrome/browser/resources/chromeos/accessibility/select_to_speak/select_to_speak_navigation_control_test.js b/chrome/browser/resources/chromeos/accessibility/select_to_speak/select_to_speak_navigation_control_test.js
index d5053d31..d15e7c4 100644
--- a/chrome/browser/resources/chromeos/accessibility/select_to_speak/select_to_speak_navigation_control_test.js
+++ b/chrome/browser/resources/chromeos/accessibility/select_to_speak/select_to_speak_navigation_control_test.js
@@ -63,7 +63,7 @@
   waitForPanelFocus(root, callback) {
     callback = this.newCallback(callback);
     const focusCallback = () => {
-      chrome.automation.getFocus((node) => {
+      chrome.automation.getFocus(node => {
         if (!this.isNodeWithinPanel(node)) {
           return;
         }
@@ -146,7 +146,7 @@
     `;
       const root = await this.runWithLoadedTree(bodyHtml);
       this.mockTts.setOnSpeechCallbacks([
-        this.newCallback((utterance) => {
+        this.newCallback(utterance => {
           // Speech for first click.
           assertTrue(this.mockTts.currentlySpeaking());
           assertEquals(this.mockTts.pendingUtterances().length, 1);
@@ -154,7 +154,7 @@
               this.mockTts.pendingUtterances()[0],
               'Sentence one . Sentence two.');
 
-          this.mockTts.setOnSpeechCallbacks([this.newCallback((utterance) => {
+          this.mockTts.setOnSpeechCallbacks([this.newCallback(utterance => {
             // Speech for second click.
             assertTrue(this.mockTts.currentlySpeaking());
             assertEquals(this.mockTts.pendingUtterances().length, 1);
@@ -436,7 +436,7 @@
     async function() {
       const bodyHtml =
           '<p>This is some <b>bold</b> text</p><p>Second paragraph</p>';
-      const setFocusCallback = this.newCallback((root) => {
+      const setFocusCallback = this.newCallback(root => {
         const firstNode = this.findTextNode(root, 'This is some ');
         const lastNode = this.findTextNode(root, 'Second paragraph');
         // Sets the selection from "is some" to "Second".
@@ -816,7 +816,7 @@
     'SelectToSpeakNavigationControlTest', 'ResumeFromSelectionEndingInSpace',
     async function() {
       const bodyHtml = '<p>This is some text with space.</p>';
-      const setFocusCallback = this.newCallback((root) => {
+      const setFocusCallback = this.newCallback(root => {
         const node = this.findTextNode(root, 'This is some text with space.');
         // Sets the selection to "This ".
         chrome.automation.setDocumentSelection({
@@ -828,7 +828,7 @@
       });
       const root = await this.runWithLoadedTree(bodyHtml);
       root.addEventListener(
-          'documentSelectionChanged', this.newCallback((event) => {
+          'documentSelectionChanged', this.newCallback(event => {
             this.triggerReadSelectedText();
 
             assertTrue(this.mockTts.currentlySpeaking());
@@ -1007,7 +1007,7 @@
             {keyCode: SelectToSpeakConstants.SEARCH_KEY_CODE});
 
         // Verify focus is still on button within panel.
-        chrome.automation.getFocus(this.newCallback((focusedNode) => {
+        chrome.automation.getFocus(this.newCallback(focusedNode => {
           assertEquals(focusedNode.role, RoleType.TOGGLE_BUTTON);
           assertTrue(this.isNodeWithinPanel(focusedNode));
         }));
@@ -1029,7 +1029,7 @@
       const root = await this.runWithLoadedTree(bodyHtml);
       // Expect call to updateSelectToSpeakPanel to set panel to be hidden.
       chrome.accessibilityPrivate.updateSelectToSpeakPanel =
-          this.newCallback((visible) => {
+          this.newCallback(visible => {
             assertFalse(visible);
           });
 
diff --git a/chrome/browser/resources/chromeos/accessibility/select_to_speak/select_to_speak_options.js b/chrome/browser/resources/chromeos/accessibility/select_to_speak/select_to_speak_options.js
index e2fdf62..0bb0e48 100644
--- a/chrome/browser/resources/chromeos/accessibility/select_to_speak/select_to_speak_options.js
+++ b/chrome/browser/resources/chromeos/accessibility/select_to_speak/select_to_speak_options.js
@@ -20,7 +20,7 @@
     const AccessibilityFeature =
         chrome.accessibilityPrivate.AccessibilityFeature;
     chrome.accessibilityPrivate.isFeatureEnabled(
-        AccessibilityFeature.ENHANCED_NETWORK_VOICES, (result) => {
+        AccessibilityFeature.ENHANCED_NETWORK_VOICES, result => {
           const newElem = document.getElementById('naturalVoicesOptions');
           const legacyElem = document.getElementById('noNaturalVoicesOptions');
           if (!result) {
@@ -43,7 +43,7 @@
             });
 
             const select = document.getElementById('language');
-            select.onchange = (_) => {
+            select.onchange = _ => {
               this.populateVoicesAndLanguages_();
             };
 
@@ -52,7 +52,7 @@
                 'naturalVoice', 'enhancedVoiceName', 'voiceName');
             chrome.settingsPrivate.getPref(
                 PrefsManager.ENHANCED_VOICES_POLICY_KEY,
-                (network_voices_allowed) => {
+                network_voices_allowed => {
                   if (network_voices_allowed !== undefined &&
                       !network_voices_allowed.value) {
                     // If the feature is disallowed, sets the checkbox to false.
@@ -64,7 +64,7 @@
                   } else {
                     // If the feature is allowed, syncs the checkbox with pref.
                     this.syncCheckboxControlToPref_(
-                        'naturalVoices', 'enhancedNetworkVoices', (checked) => {
+                        'naturalVoices', 'enhancedNetworkVoices', checked => {
                           this.setVoiceSelectionAndPreviewVisibility_(
                               /* isVisible = */ checked);
                         });
@@ -74,14 +74,14 @@
         });
 
     this.syncCheckboxControlToPref_(
-        'wordHighlight', 'wordHighlight', (checked) => {
+        'wordHighlight', 'wordHighlight', checked => {
           const elem = document.getElementById('highlightSubOption');
           const select = document.getElementById('highlightColor');
           this.setElementVisible(elem, checked);
           select.disabled = !checked;
         });
     this.syncCheckboxControlToPref_(
-        'backgroundShading', 'backgroundShading', (checked) => {
+        'backgroundShading', 'backgroundShading', checked => {
           const elem = document.getElementById('backgroundPreviewContainer');
           this.setElementVisible(elem, checked);
         });
@@ -171,7 +171,7 @@
    * @private
    */
   populateVoiceList_(selectId) {
-    chrome.tts.getVoices((voices) => {
+    chrome.tts.getVoices(voices => {
       const select = document.getElementById(selectId);
       // Add the system voice.
       this.initializeSelectWithDefault_(
@@ -185,7 +185,7 @@
       voices.sort(function(a, b) {
         return a.voiceName.localeCompare(b.voiceName || '');
       });
-      voices.forEach((voice) => {
+      voices.forEach(voice => {
         if (!this.isVoiceUsable_(voice) ||
             (voice.extensionId === PrefsManager.ENHANCED_TTS_EXTENSION_ID)) {
           // Don't show network voices for legacy interface.
@@ -209,7 +209,7 @@
    * @private
    */
   populateVoicesAndLanguages_() {
-    chrome.tts.getVoices((voices) => {
+    chrome.tts.getVoices(voices => {
       // Initialize language select.
       const languageSelect = document.getElementById('language');
       const originalLanguageValue =
@@ -280,7 +280,7 @@
     const networkVoices = new Map();
     const currentLocale = chrome.i18n.getUILanguage().toLowerCase() || '';
 
-    voices.forEach((voice) => {
+    voices.forEach(voice => {
       if (!this.isVoiceUsable_(voice)) {
         return;
       }
@@ -314,7 +314,7 @@
     });
 
     // Populate local and network selects.
-    voiceLanguagesList.forEach((voiceLang) => {
+    voiceLanguagesList.forEach(voiceLang => {
       this.appendVoicesToSelect_(
           localSelect, localVoices.get(voiceLang), /*numberVoices=*/ false);
       this.appendVoicesToSelect_(
diff --git a/chrome/browser/resources/chromeos/accessibility/select_to_speak/select_to_speak_prefs_test.js b/chrome/browser/resources/chromeos/accessibility/select_to_speak/select_to_speak_prefs_test.js
index f444779..9b45378 100644
--- a/chrome/browser/resources/chromeos/accessibility/select_to_speak/select_to_speak_prefs_test.js
+++ b/chrome/browser/resources/chromeos/accessibility/select_to_speak/select_to_speak_prefs_test.js
@@ -71,15 +71,15 @@
       // Once prefs are removed from storage, make sure the global prefs are
       // updated to the appropriate values.
       this.mockSettingsPrivate_.getPref(
-          'settings.tts.speech_rate', this.newCallback((pref) => {
+          'settings.tts.speech_rate', this.newCallback(pref => {
             assertEquals(rate, pref.value);
           }));
       this.mockSettingsPrivate_.getPref(
-          'settings.tts.speech_pitch', this.newCallback((pref) => {
+          'settings.tts.speech_pitch', this.newCallback(pref => {
             assertEquals(pitch, pref.value);
           }));
     });
-    this.mockStorage_.onChanged.addListener((prefs) => {
+    this.mockStorage_.onChanged.addListener(prefs => {
       // checks that rate and pitch are removed.
       if (prefs !== undefined && !('rate' in prefs) && !('pitch' in prefs)) {
         onPrefsRemovedFromStorage();
diff --git a/chrome/browser/resources/chromeos/accessibility/select_to_speak/tts_manager.js b/chrome/browser/resources/chromeos/accessibility/select_to_speak/tts_manager.js
index fd4bc73..fd94a403 100644
--- a/chrome/browser/resources/chromeos/accessibility/select_to_speak/tts_manager.js
+++ b/chrome/browser/resources/chromeos/accessibility/select_to_speak/tts_manager.js
@@ -93,7 +93,7 @@
         /** @type {!chrome.tts.TtsOptions} */ (Object.assign({}, ttsOptions));
     // Saves a copy of the ttsOptions for resume.
     Object.assign(this.clientTtsOptions_, ttsOptions);
-    modifiedOptions.onEvent = (event) => {
+    modifiedOptions.onEvent = event => {
       switch (event.type) {
         case chrome.tts.EventType.ERROR:
           if (this.isNetworkVoice_) {
@@ -178,7 +178,7 @@
    * @return {!Promise}
    */
   pause() {
-    return new Promise((resolve) => {
+    return new Promise(resolve => {
       this.pauseCompleteCallback_ = () => {
         this.pauseCompleteCallback_ = null;
         resolve();
diff --git a/chrome/browser/resources/chromeos/accessibility/select_to_speak/tts_manager_unittest.js b/chrome/browser/resources/chromeos/accessibility/select_to_speak/tts_manager_unittest.js
index f3f020a..c5ea9817d 100644
--- a/chrome/browser/resources/chromeos/accessibility/select_to_speak/tts_manager_unittest.js
+++ b/chrome/browser/resources/chromeos/accessibility/select_to_speak/tts_manager_unittest.js
@@ -13,7 +13,7 @@
 
   getTtsOptions() {
     const options = /** @type {!chrome.tts.TtsOptions} */ ({});
-    options.onEvent = (event) => this.receivedEvent = event;
+    options.onEvent = event => this.receivedEvent = event;
     return options;
   }
 }
diff --git a/chrome/browser/resources/chromeos/accessibility/select_to_speak/ui_manager.js b/chrome/browser/resources/chromeos/accessibility/select_to_speak/ui_manager.js
index 28b30d60..da63e33 100644
--- a/chrome/browser/resources/chromeos/accessibility/select_to_speak/ui_manager.js
+++ b/chrome/browser/resources/chromeos/accessibility/select_to_speak/ui_manager.js
@@ -106,13 +106,13 @@
     this.panelButton_ = null;
 
     // Cache desktop and listen to focus changes.
-    chrome.automation.getDesktop((desktop) => {
+    chrome.automation.getDesktop(desktop => {
       this.desktop_ = desktop;
 
       // Listen to focus changes so we can grab the floating panel when it
       // goes into focus, so it can be used later without having to search
       // through the entire tree.
-      desktop.addEventListener(EventType.FOCUS, (evt) => {
+      desktop.addEventListener(EventType.FOCUS, evt => {
         this.onFocusChange_(evt);
       }, true);
     });
@@ -303,7 +303,7 @@
         0;
     node.boundsForRange(
         currentWord.start - charIndexInParent,
-        currentWord.end - charIndexInParent, (bounds) => {
+        currentWord.end - charIndexInParent, bounds => {
           const highlights = bounds ? [bounds] : [];
           chrome.accessibilityPrivate.setHighlights(
               highlights, this.prefsManager_.highlightColor());
@@ -393,7 +393,7 @@
     if (!node) {
       return false;
     }
-    return AutomationUtil.getAncestors(node).find((n) => {
+    return AutomationUtil.getAncestors(node).find(n => {
       return n.className === SELECT_TO_SPEAK_TRAY_CLASS_NAME;
     }) !== undefined;
   }
diff --git a/chrome/browser/resources/chromeos/accessibility/switch_access/action_manager.js b/chrome/browser/resources/chromeos/accessibility/switch_access/action_manager.js
index c003d13..2fe18e1 100644
--- a/chrome/browser/resources/chromeos/accessibility/switch_access/action_manager.js
+++ b/chrome/browser/resources/chromeos/accessibility/switch_access/action_manager.js
@@ -235,7 +235,7 @@
     }
     let actions = this.actionNode_.actions;
     const possibleActions = this.actionsForType_(this.currentMenuType_);
-    actions = actions.filter((a) => possibleActions.includes(a));
+    actions = actions.filter(a => possibleActions.includes(a));
     if (this.currentMenuType_ === SAConstants.MenuType.MAIN_MENU) {
       actions = this.addGlobalActions_(actions);
     }
diff --git a/chrome/browser/resources/chromeos/accessibility/switch_access/focus_ring_manager.js b/chrome/browser/resources/chromeos/accessibility/switch_access/focus_ring_manager.js
index 80acdab..4beaf1d4 100644
--- a/chrome/browser/resources/chromeos/accessibility/switch_access/focus_ring_manager.js
+++ b/chrome/browser/resources/chromeos/accessibility/switch_access/focus_ring_manager.js
@@ -55,7 +55,7 @@
               'a valid CSS color string.'));
       return;
     }
-    manager.rings_.forEach((ring) => ring.color = color);
+    manager.rings_.forEach(ring => ring.color = color);
   }
 
   /**
@@ -124,7 +124,7 @@
   /** Clears all focus rings. */
   static clearAll() {
     const manager = FocusRingManager.instance;
-    manager.rings_.forEach((ring) => {
+    manager.rings_.forEach(ring => {
       ring.rects = [];
     });
     manager.updateFocusRings_(null, null);
@@ -185,7 +185,7 @@
     }
 
     const focusRings = [];
-    this.rings_.forEach((ring) => focusRings.push(ring));
+    this.rings_.forEach(ring => focusRings.push(ring));
     chrome.accessibilityPrivate.setFocusRings(focusRings);
 
     // Keep track of the nodes associated with each focus ring for testing
diff --git a/chrome/browser/resources/chromeos/accessibility/switch_access/focus_ring_manager_test.js b/chrome/browser/resources/chromeos/accessibility/switch_access/focus_ring_manager_test.js
index e205c93..bc88931 100644
--- a/chrome/browser/resources/chromeos/accessibility/switch_access/focus_ring_manager_test.js
+++ b/chrome/browser/resources/chromeos/accessibility/switch_access/focus_ring_manager_test.js
@@ -23,7 +23,7 @@
 };
 
 TEST_F('SwitchAccessFocusRingManagerTest', 'BackButtonFocus', function() {
-  this.runWithLoadedDesktop((desktop) => {
+  this.runWithLoadedDesktop(desktop => {
     // Focus the back button.
     Navigator.byItem.moveTo_(
         desktop.find({role: chrome.automation.RoleType.TAB}));
diff --git a/chrome/browser/resources/chromeos/accessibility/switch_access/item_scan_manager.js b/chrome/browser/resources/chromeos/accessibility/switch_access/item_scan_manager.js
index 696e165..c57fb1dd 100644
--- a/chrome/browser/resources/chromeos/accessibility/switch_access/item_scan_manager.js
+++ b/chrome/browser/resources/chromeos/accessibility/switch_access/item_scan_manager.js
@@ -103,7 +103,7 @@
   /** @override */
   exitKeyboard() {
     this.ignoreFocusInKeyboard_ = false;
-    const isKeyboard = (data) => data.group instanceof KeyboardRootNode;
+    const isKeyboard = data => data.group instanceof KeyboardRootNode;
     // If we are not in the keyboard, do nothing.
     if (!(this.group_ instanceof KeyboardRootNode) &&
         !this.history_.containsDataMatchingPredicate(isKeyboard)) {
@@ -164,7 +164,7 @@
   /** @override */
   moveBackward() {
     if (this.node_.isValidAndVisible()) {
-      this.tryMoving(this.node_.previous, (node) => node.previous, this.node_);
+      this.tryMoving(this.node_.previous, node => node.previous, this.node_);
     } else {
       this.moveToValidNode();
     }
@@ -173,7 +173,7 @@
   /** @override */
   moveForward() {
     if (this.node_.isValidAndVisible()) {
-      this.tryMoving(this.node_.next, (node) => node.next, this.node_);
+      this.tryMoving(this.node_.next, node => node.next, this.node_);
     } else {
       this.moveToValidNode();
     }
@@ -208,7 +208,7 @@
     // Check if the top center is visible as a proxy for occlusion. It's
     // possible that other parts of the window are occluded, but in Chrome we
     // can't drag windows off the top of the screen.
-    this.desktop_.hitTestWithReply(center.x, location.top, (hitNode) => {
+    this.desktop_.hitTestWithReply(center.x, location.top, hitNode => {
       if (AutomationUtil.isDescendantOf(hitNode, node.automationNode)) {
         this.setNode_(node);
       } else if (node.isValidAndVisible()) {
@@ -253,7 +253,7 @@
   restart() {
     const point = Navigator.byPoint.currentPoint;
     SwitchAccess.mode = SAConstants.Mode.ITEM_SCAN;
-    this.desktop_.hitTestWithReply(point.x, point.y, (node) => {
+    this.desktop_.hitTestWithReply(point.x, point.y, node => {
       this.moveTo_(node);
     });
   }
@@ -416,7 +416,7 @@
     new RepeatedTreeChangeHandler(
         chrome.automation.TreeChangeObserverFilter.ALL_TREE_CHANGES,
         treeChange => this.onTreeChange_(treeChange), {
-          predicate: (treeChange) =>
+          predicate: treeChange =>
               this.group_.findChild(treeChange.target) != null ||
               this.group_.isEquivalentTo(treeChange.target)
         });
diff --git a/chrome/browser/resources/chromeos/accessibility/switch_access/item_scan_manager_test.js b/chrome/browser/resources/chromeos/accessibility/switch_access/item_scan_manager_test.js
index 4115c30..226951e 100644
--- a/chrome/browser/resources/chromeos/accessibility/switch_access/item_scan_manager_test.js
+++ b/chrome/browser/resources/chromeos/accessibility/switch_access/item_scan_manager_test.js
@@ -34,9 +34,9 @@
     const cache = new SACache();
     if (!SwitchAccessPredicate.isGroup(pageContents, null, cache)) {
       pageContents =
-          new AutomationTreeWalker(pageContents, constants.Dir.FORWARD, {
-            visit: (node) => SwitchAccessPredicate.isGroup(node, null, cache)
-          })
+          new AutomationTreeWalker(
+              pageContents, constants.Dir.FORWARD,
+              {visit: node => SwitchAccessPredicate.isGroup(node, null, cache)})
               .next()
               .node;
     }
@@ -174,7 +174,7 @@
 
   node.addEventListener(
       chrome.automation.EventType.CHECKED_STATE_CHANGED,
-      this.newCallback((event) => {
+      this.newCallback(event => {
         assertEquals(
             node.name, event.target.name,
             'Checked state changed on unexpected node');
@@ -383,7 +383,7 @@
         // Wait for the potential value change.
         await new Promise(resolve => {
           input.automationNode.addEventListener(
-              chrome.automation.EventType.VALUE_CHANGED, (event) => {
+              chrome.automation.EventType.VALUE_CHANGED, event => {
                 if (event.target.value === 'q') {
                   resolve();
                 }
@@ -436,7 +436,7 @@
       });
       await new Promise(resolve => {
         keyboard.automationNode.addEventListener(
-            chrome.automation.EventType.STATE_CHANGED, (event) => {
+            chrome.automation.EventType.STATE_CHANGED, event => {
               if (event.target.role === chrome.automation.RoleType.KEYBOARD &&
                   event.target.state.invisible) {
                 resolve();
@@ -611,7 +611,7 @@
       });
 
       // The button is no longer in the tree because the screen is locked.
-      const predicate = (node) => node.name === 'kitties!' &&
+      const predicate = node => node.name === 'kitties!' &&
           node.role === chrome.automation.RoleType.BUTTON;
       assertNotNullNorUndefined(
           this.desktop_, 'this.desktop_ is null or undefined.');
diff --git a/chrome/browser/resources/chromeos/accessibility/switch_access/metrics.js b/chrome/browser/resources/chromeos/accessibility/switch_access/metrics.js
index 4546f93..af01687 100644
--- a/chrome/browser/resources/chromeos/accessibility/switch_access/metrics.js
+++ b/chrome/browser/resources/chromeos/accessibility/switch_access/metrics.js
@@ -9,7 +9,7 @@
   /**
    * @param {string} menuAction
    */
-  recordMenuAction: (menuAction) => {
+  recordMenuAction: menuAction => {
     const metricName = 'Accessibility.CrosSwitchAccess.MenuAction.' +
         SwitchAccessMetrics.toUpperCamelCase(menuAction);
     chrome.metricsPrivate.recordUserAction(metricName);
@@ -19,10 +19,10 @@
    * @param {string} str
    * @return {string}
    */
-  toUpperCamelCase: (str) => {
+  toUpperCamelCase: str => {
     const wordRegex = /(?:^\w|[A-Z]|(?:\b|_)\w)/g;
     const underscoreAndWhitespaceRegex = /(\s|_)+/g;
-    return str.replace(wordRegex, (word) => word.toUpperCase())
+    return str.replace(wordRegex, word => word.toUpperCase())
         .replace(underscoreAndWhitespaceRegex, '');
   }
 };
diff --git a/chrome/browser/resources/chromeos/accessibility/switch_access/nodes/basic_node.js b/chrome/browser/resources/chromeos/accessibility/switch_access/nodes/basic_node.js
index 61c73a1..55db5c6 100644
--- a/chrome/browser/resources/chromeos/accessibility/switch_access/nodes/basic_node.js
+++ b/chrome/browser/resources/chromeos/accessibility/switch_access/nodes/basic_node.js
@@ -298,7 +298,7 @@
     super.onFocus();
     this.childrenChangedHandler_ = new RepeatedEventHandler(
         this.automationNode, chrome.automation.EventType.CHILDREN_CHANGED,
-        (event) => {
+        event => {
           const cache = new SACache();
           if (SwitchAccessPredicate.isInterestingSubtree(event.target, cache)) {
             this.refresh();
@@ -316,7 +316,7 @@
 
   /** @override */
   refreshChildren() {
-    const childConstructor = (node) => BasicNode.create(node, this);
+    const childConstructor = node => BasicNode.create(node, this);
     try {
       BasicRootNode.findAndSetChildren(this, childConstructor);
     } catch (e) {
@@ -371,7 +371,7 @@
     }
 
     const root = new BasicRootNode(rootNode);
-    const childConstructor = (node) => BasicNode.create(node, root);
+    const childConstructor = node => BasicNode.create(node, root);
 
     BasicRootNode.findAndSetChildren(root, childConstructor);
     return root;
@@ -387,7 +387,7 @@
   static findAndSetChildren(root, childConstructor) {
     const interestingChildren = BasicRootNode.getInterestingChildren(root);
     const children = interestingChildren.map(childConstructor)
-                         .filter((child) => child.isValidAndVisible());
+                         .filter(child => child.isValidAndVisible());
 
     if (children.length < 1) {
       throw SwitchAccess.error(
diff --git a/chrome/browser/resources/chromeos/accessibility/switch_access/nodes/basic_node_test.js b/chrome/browser/resources/chromeos/accessibility/switch_access/nodes/basic_node_test.js
index e29f651..5694b978 100644
--- a/chrome/browser/resources/chromeos/accessibility/switch_access/nodes/basic_node_test.js
+++ b/chrome/browser/resources/chromeos/accessibility/switch_access/nodes/basic_node_test.js
@@ -55,7 +55,7 @@
 });
 
 TEST_F('SwitchAccessBasicNodeTest', 'Equals', function() {
-  this.runWithLoadedDesktop((desktop) => {
+  this.runWithLoadedDesktop(desktop => {
     const desktopNode = DesktopNode.build(desktop);
 
     let childGroup = desktopNode.firstChild;
diff --git a/chrome/browser/resources/chromeos/accessibility/switch_access/nodes/desktop_node.js b/chrome/browser/resources/chromeos/accessibility/switch_access/nodes/desktop_node.js
index f8d903c..8407fcd 100644
--- a/chrome/browser/resources/chromeos/accessibility/switch_access/nodes/desktop_node.js
+++ b/chrome/browser/resources/chromeos/accessibility/switch_access/nodes/desktop_node.js
@@ -48,7 +48,7 @@
     }
 
     // Update this DesktopNode's children.
-    const childConstructor = (node) => BasicNode.create(node, this);
+    const childConstructor = node => BasicNode.create(node, this);
     DesktopNode.findAndSetChildren(this, childConstructor);
 
     // Set the new instance of that child to be the focused node.
@@ -72,7 +72,7 @@
    */
   static build(desktop) {
     const root = new DesktopNode(desktop);
-    const childConstructor = (autoNode) => BasicNode.create(autoNode, root);
+    const childConstructor = autoNode => BasicNode.create(autoNode, root);
 
     DesktopNode.findAndSetChildren(root, childConstructor);
     return root;
diff --git a/chrome/browser/resources/chromeos/accessibility/switch_access/nodes/desktop_node_test.js b/chrome/browser/resources/chromeos/accessibility/switch_access/nodes/desktop_node_test.js
index 2b8109b..9820538e 100644
--- a/chrome/browser/resources/chromeos/accessibility/switch_access/nodes/desktop_node_test.js
+++ b/chrome/browser/resources/chromeos/accessibility/switch_access/nodes/desktop_node_test.js
@@ -17,7 +17,7 @@
 };
 
 TEST_F('SwitchAccessDesktopNodeTest', 'Build', function() {
-  this.runWithLoadedDesktop((desktop) => {
+  this.runWithLoadedDesktop(desktop => {
     const desktopNode = DesktopNode.build(desktop);
 
     const children = desktopNode.children;
diff --git a/chrome/browser/resources/chromeos/accessibility/switch_access/nodes/group_node_test.js b/chrome/browser/resources/chromeos/accessibility/switch_access/nodes/group_node_test.js
index 712100e..85360f8 100644
--- a/chrome/browser/resources/chromeos/accessibility/switch_access/nodes/group_node_test.js
+++ b/chrome/browser/resources/chromeos/accessibility/switch_access/nodes/group_node_test.js
@@ -18,7 +18,7 @@
 
 TEST_F('SwitchAccessGroupNodeTest', 'NodesRemoved', function() {
   const website = `<button></button>`;
-  this.runWithLoadedTree(website, (rootWebArea) => {
+  this.runWithLoadedTree(website, rootWebArea => {
     const button = rootWebArea.find({role: chrome.automation.RoleType.BUTTON});
     assertNotEquals(undefined, button);
 
diff --git a/chrome/browser/resources/chromeos/accessibility/switch_access/nodes/keyboard_node.js b/chrome/browser/resources/chromeos/accessibility/switch_access/nodes/keyboard_node.js
index 11dd273..abea39a 100644
--- a/chrome/browser/resources/chromeos/accessibility/switch_access/nodes/keyboard_node.js
+++ b/chrome/browser/resources/chromeos/accessibility/switch_access/nodes/keyboard_node.js
@@ -223,7 +223,7 @@
    * @private
    */
   static findAndSetChildren_(root) {
-    const childConstructor = (node) => new KeyboardNode(node, root);
+    const childConstructor = node => new KeyboardNode(node, root);
     const interestingChildren =
         root.automationNode.findAll({role: chrome.automation.RoleType.BUTTON});
     /** @type {!Array<!SAChildNode>} */
diff --git a/chrome/browser/resources/chromeos/accessibility/switch_access/nodes/modal_dialog_node.js b/chrome/browser/resources/chromeos/accessibility/switch_access/nodes/modal_dialog_node.js
index 774e0a3c..475e2ad 100644
--- a/chrome/browser/resources/chromeos/accessibility/switch_access/nodes/modal_dialog_node.js
+++ b/chrome/browser/resources/chromeos/accessibility/switch_access/nodes/modal_dialog_node.js
@@ -22,7 +22,7 @@
    */
   static buildTree(dialogNode) {
     const root = new ModalDialogRootNode(dialogNode);
-    const childConstructor = (node) => BasicNode.create(node, root);
+    const childConstructor = node => BasicNode.create(node, root);
 
     BasicRootNode.findAndSetChildren(root, childConstructor);
     return root;
diff --git a/chrome/browser/resources/chromeos/accessibility/switch_access/nodes/switch_access_node.js b/chrome/browser/resources/chromeos/accessibility/switch_access/nodes/switch_access_node.js
index d3558a1..7869114 100644
--- a/chrome/browser/resources/chromeos/accessibility/switch_access/nodes/switch_access_node.js
+++ b/chrome/browser/resources/chromeos/accessibility/switch_access/nodes/switch_access_node.js
@@ -329,8 +329,8 @@
   /** @return {!chrome.accessibilityPrivate.ScreenRect} */
   get location() {
     const children = this.children_.filter(
-        (c) => !c.ignoreWhenComputingUnionOfBoundingBoxes());
-    const childLocations = children.map((c) => c.location);
+        c => !c.ignoreWhenComputingUnionOfBoundingBoxes());
+    const childLocations = children.map(c => c.location);
     return RectUtil.unionAll(childLocations);
   }
 
@@ -395,7 +395,7 @@
     // Must have one interesting child whose location is important.
     return this.children_
                .filter(
-                   (child) =>
+                   child =>
                        !(child.ignoreWhenComputingUnionOfBoundingBoxes()) &&
                        child.isValidAndVisible())
                .length >= 1;
@@ -403,8 +403,7 @@
 
   /** @return {SAChildNode} */
   firstValidChild() {
-    const children =
-        this.children_.filter((child) => child.isValidAndVisible());
+    const children = this.children_.filter(child => child.isValidAndVisible());
     return children.length > 0 ? children[0] : null;
   }
 
@@ -419,7 +418,7 @@
 
   /** Called when a group should recalculate its children. */
   refreshChildren() {
-    this.children = this.children.filter((child) => child.isValidAndVisible());
+    this.children = this.children.filter(child => child.isValidAndVisible());
   }
 
   /** Called when the group's children may have changed. */
diff --git a/chrome/browser/resources/chromeos/accessibility/switch_access/nodes/tab_node_test.js b/chrome/browser/resources/chromeos/accessibility/switch_access/nodes/tab_node_test.js
index eeb9bbce..4df1e4b6 100644
--- a/chrome/browser/resources/chromeos/accessibility/switch_access/nodes/tab_node_test.js
+++ b/chrome/browser/resources/chromeos/accessibility/switch_access/nodes/tab_node_test.js
@@ -17,7 +17,7 @@
 };
 
 TEST_F('SwitchAccessTabNodeTest', 'FindCloseButton', function() {
-  this.runWithLoadedDesktop((desktop) => {
+  this.runWithLoadedDesktop(desktop => {
     const tab = desktop.find({role: chrome.automation.RoleType.TAB});
 
     // To find the close button, Switch Access relies on it being the only
@@ -33,7 +33,7 @@
 });
 
 TEST_F('SwitchAccessTabNodeTest', 'Construction', function() {
-  this.runWithLoadedDesktop((desktop) => {
+  this.runWithLoadedDesktop(desktop => {
     Navigator.byItem.moveTo_(
         desktop.find({role: chrome.automation.RoleType.TAB}));
 
diff --git a/chrome/browser/resources/chromeos/accessibility/switch_access/nodes/window_node.js b/chrome/browser/resources/chromeos/accessibility/switch_access/nodes/window_node.js
index 9938657..1bf8e05 100644
--- a/chrome/browser/resources/chromeos/accessibility/switch_access/nodes/window_node.js
+++ b/chrome/browser/resources/chromeos/accessibility/switch_access/nodes/window_node.js
@@ -28,7 +28,7 @@
    */
   static buildTree(windowNode) {
     const root = new WindowRootNode(windowNode);
-    const childConstructor = (node) => BasicNode.create(node, root);
+    const childConstructor = node => BasicNode.create(node, root);
 
     BasicRootNode.findAndSetChildren(root, childConstructor);
     return root;
diff --git a/chrome/browser/resources/chromeos/accessibility/switch_access/point_scan_manager_test.js b/chrome/browser/resources/chromeos/accessibility/switch_access/point_scan_manager_test.js
index aac7d8e..80ab094 100644
--- a/chrome/browser/resources/chromeos/accessibility/switch_access/point_scan_manager_test.js
+++ b/chrome/browser/resources/chromeos/accessibility/switch_access/point_scan_manager_test.js
@@ -92,7 +92,7 @@
       const rootWebArea = await this.runWithLoadedTree(site);
       let setFocusRingsCallCount = 0;
       // Mock this API to track how many times it's called.
-      chrome.accessibilityPrivate.setFocusRings = (focusRings) => {
+      chrome.accessibilityPrivate.setFocusRings = focusRings => {
         setFocusRingsCallCount += 1;
       };
       assertEquals(0, setFocusRingsCallCount);
diff --git a/chrome/browser/resources/chromeos/accessibility/switch_access/preference_manager.js b/chrome/browser/resources/chromeos/accessibility/switch_access/preference_manager.js
index 9714677b..9ebdce5c 100644
--- a/chrome/browser/resources/chromeos/accessibility/switch_access/preference_manager.js
+++ b/chrome/browser/resources/chromeos/accessibility/switch_access/preference_manager.js
@@ -81,9 +81,9 @@
   /** @private */
   init_() {
     chrome.settingsPrivate.onPrefsChanged.addListener(
-        (prefs) => this.updateFromSettings_(prefs));
+        prefs => this.updateFromSettings_(prefs));
     chrome.settingsPrivate.getAllPrefs(
-        (prefs) => this.updateFromSettings_(prefs, true /* isFirstLoad */));
+        prefs => this.updateFromSettings_(prefs, true /* isFirstLoad */));
   }
 
   /**
diff --git a/chrome/browser/resources/chromeos/accessibility/switch_access/saatlite/compiler/compiler.js b/chrome/browser/resources/chromeos/accessibility/switch_access/saatlite/compiler/compiler.js
index 38f1e4ea..87a1ff6c 100644
--- a/chrome/browser/resources/chromeos/accessibility/switch_access/saatlite/compiler/compiler.js
+++ b/chrome/browser/resources/chromeos/accessibility/switch_access/saatlite/compiler/compiler.js
@@ -48,7 +48,7 @@
 
   // Read all the files in the tests/ directory.
   const filenames = fs.readdirSync(testDir);
-  filenames.forEach((filename) => {
+  filenames.forEach(filename => {
     console.log('Compiling file: ', filename);
     const contents = fs.readFileSync(testDir + filename, {encoding: 'utf8'});
     stream.write(parse(contents).output + '\n');
diff --git a/chrome/browser/resources/chromeos/accessibility/switch_access/saatlite/compiler/parser.js b/chrome/browser/resources/chromeos/accessibility/switch_access/saatlite/compiler/parser.js
index 5fbbeda..b4fb73f 100644
--- a/chrome/browser/resources/chromeos/accessibility/switch_access/saatlite/compiler/parser.js
+++ b/chrome/browser/resources/chromeos/accessibility/switch_access/saatlite/compiler/parser.js
@@ -3523,7 +3523,7 @@
   const decreaseIndent = () => indent = indent.substring(2);
 
   let buffer = '';
-  const addToBuffer = (text) => buffer += indent + text + '\n';
+  const addToBuffer = text => buffer += indent + text + '\n';
   const flushBuffer = () => {
     const result = buffer;
     buffer = '';
@@ -3549,7 +3549,7 @@
     return flushBuffer();
   };
 
-  const finishTest = (opt_url) => {
+  const finishTest = opt_url => {
     decreaseIndent();
     if (opt_url) {
       addToBuffer(`}, {url: ${opt_url.output}});`);
diff --git a/chrome/browser/resources/chromeos/accessibility/switch_access/saatlite/gen/saatlite_tests.js b/chrome/browser/resources/chromeos/accessibility/switch_access/saatlite/gen/saatlite_tests.js
index 40ad965..52ac2f1 100644
--- a/chrome/browser/resources/chromeos/accessibility/switch_access/saatlite/gen/saatlite_tests.js
+++ b/chrome/browser/resources/chromeos/accessibility/switch_access/saatlite/gen/saatlite_tests.js
@@ -13,7 +13,7 @@
 };
 
 TEST_F('SwitchAccessSAATLiteTest', 'Demo', function() {
-  this.runWithLoadedTree('<button>Hi</button>', async (rootWebArea) => {
+  this.runWithLoadedTree('<button>Hi</button>', async rootWebArea => {
     TestUtility.startFocusInside(rootWebArea);
     TestUtility.pressNextSwitch();
     TestUtility.pressPreviousSwitch();
diff --git a/chrome/browser/resources/chromeos/accessibility/switch_access/switch_access.js b/chrome/browser/resources/chromeos/accessibility/switch_access/switch_access.js
index e57365e..157921e 100644
--- a/chrome/browser/resources/chromeos/accessibility/switch_access/switch_access.js
+++ b/chrome/browser/resources/chromeos/accessibility/switch_access/switch_access.js
@@ -19,7 +19,7 @@
   static initialize() {
     SwitchAccess.instance = new SwitchAccess();
 
-    chrome.automation.getDesktop((desktop) => {
+    chrome.automation.getDesktop(desktop => {
       chrome.automation.getFocus(focus => {
         // Focus is available. Finish init without waiting for further events.
         // Disallow web view nodes, which indicate a root web area is still
@@ -62,7 +62,7 @@
     this.enableImprovedTextInput_ = false;
 
     chrome.commandLinePrivate.hasSwitch(
-        'enable-experimental-accessibility-switch-access-text', (result) => {
+        'enable-experimental-accessibility-switch-access-text', result => {
           this.enableImprovedTextInput_ = result;
         });
 
@@ -110,7 +110,7 @@
         desktop, chrome.automation.EventType.CHILDREN_CHANGED,
         null /** callback */);
 
-    const onEvent = (event) => {
+    const onEvent = event => {
       if (event.target.matches(findParams)) {
         // If the event target is the node we're looking for, we've found it.
         eventHandler.stop();
diff --git a/chrome/browser/resources/chromeos/accessibility/switch_access/switch_access_e2e_test_base.js b/chrome/browser/resources/chromeos/accessibility/switch_access/switch_access_e2e_test_base.js
index c3591fa..115ca3b 100644
--- a/chrome/browser/resources/chromeos/accessibility/switch_access/switch_access_e2e_test_base.js
+++ b/chrome/browser/resources/chromeos/accessibility/switch_access/switch_access_e2e_test_base.js
@@ -38,7 +38,7 @@
    * @return {!AutomationNode}
    */
   findNodeById(id) {
-    const predicate = (node) => node.htmlAttributes.id === id;
+    const predicate = node => node.htmlAttributes.id === id;
     const nodeString = 'node with id "' + id + '"';
     return this.findNodeMatchingPredicate(predicate, nodeString);
   }
@@ -49,7 +49,7 @@
    * @return {!AutomationNode}
    */
   findNodeByNameAndRole(name, role) {
-    const predicate = (node) => node.name === name && node.role === role;
+    const predicate = node => node.name === name && node.role === role;
     const nodeString = 'node with name "' + name + '" and role ' + role;
     return this.findNodeMatchingPredicate(predicate, nodeString);
   }
@@ -69,7 +69,7 @@
    * @return {!Promise}
    */
   untilFocusIs(expected) {
-    const doesMatch = (expected) => {
+    const doesMatch = expected => {
       const newNode = Navigator.byItem.node_;
       const automationNode = newNode.automationNode || {};
       return (!expected.instance || newNode instanceof expected.instance) &&
@@ -102,7 +102,7 @@
         return;
       }
       const original = Navigator.byItem.setNode_.bind(Navigator.byItem);
-      Navigator.byItem.setNode_ = (node) => {
+      Navigator.byItem.setNode_ = node => {
         original(node);
         lastFocusChangeTime = new Date();
         if (doesMatch(expected)) {
diff --git a/chrome/browser/resources/chromeos/accessibility/switch_access/switch_access_predicate.js b/chrome/browser/resources/chromeos/accessibility/switch_access/switch_access_predicate.js
index 869087e..a284a17 100644
--- a/chrome/browser/resources/chromeos/accessibility/switch_access/switch_access_predicate.js
+++ b/chrome/browser/resources/chromeos/accessibility/switch_access/switch_access_predicate.js
@@ -99,7 +99,7 @@
     // child is an interesting subtree.
     if (state[StateType.FOCUSABLE] || role === RoleType.MENU_ITEM) {
       const result = !node.children.some(
-          (child) => SwitchAccessPredicate.isInterestingSubtree(child, cache));
+          child => SwitchAccessPredicate.isInterestingSubtree(child, cache));
       cache.isActionable.set(node, result);
       return result;
     }
@@ -181,7 +181,7 @@
    * @param {AutomationNode} node
    * @return {boolean}
    */
-  isVisible: (node) => Boolean(
+  isVisible: node => Boolean(
       !node.state[StateType.OFFSCREEN] && node.location &&
       node.location.top >= 0 && node.location.left >= 0 &&
       !node.state[StateType.INVISIBLE]),
@@ -205,8 +205,7 @@
     }
     const result = SwitchAccessPredicate.isActionable(node, cache) ||
         node.children.some(
-            (child) =>
-                SwitchAccessPredicate.isInterestingSubtree(child, cache));
+            child => SwitchAccessPredicate.isInterestingSubtree(child, cache));
     cache.isInterestingSubtree.set(node, result);
     return result;
   },
@@ -216,14 +215,14 @@
    * @param {AutomationNode} node
    * @return {boolean}
    */
-  isTextInput: (node) => Boolean(node && node.state[StateType.EDITABLE]),
+  isTextInput: node => Boolean(node && node.state[StateType.EDITABLE]),
 
   /**
    * Returns true if |node| should be considered a window.
    * @param {AutomationNode} node
    * @return {boolean}
    */
-  isWindow: (node) => Boolean(
+  isWindow: node => Boolean(
       node &&
       (node.role === chrome.automation.RoleType.WINDOW ||
        (node.role === chrome.automation.RoleType.CLIENT && node.parent &&
@@ -235,7 +234,7 @@
    * @param {!AutomationNode} scope
    * @return {!AutomationTreeWalkerRestriction}
    */
-  restrictions: (scope) => {
+  restrictions: scope => {
     const cache = new SACache();
     return {
       leaf: SwitchAccessPredicate.leaf(scope, cache),
@@ -253,7 +252,7 @@
    * @return {function(!AutomationNode): boolean}
    */
   leaf(scope, cache) {
-    return (node) => !SwitchAccessPredicate.isInterestingSubtree(node, cache) ||
+    return node => !SwitchAccessPredicate.isInterestingSubtree(node, cache) ||
         (scope !== node &&
          SwitchAccessPredicate.isInteresting(node, scope, cache));
   },
@@ -266,7 +265,7 @@
    * @return {function(!AutomationNode): boolean}
    */
   root(scope) {
-    return (node) => scope === node;
+    return node => scope === node;
   },
 
   /**
@@ -278,7 +277,7 @@
    * @return {function(!AutomationNode): boolean}
    */
   visit(scope, cache) {
-    return (node) => node.role !== RoleType.DESKTOP &&
+    return node => node.role !== RoleType.DESKTOP &&
         SwitchAccessPredicate.isInteresting(node, scope, cache);
   }
 };
diff --git a/chrome/browser/resources/chromeos/accessibility/switch_access/text_navigation_manager.js b/chrome/browser/resources/chromeos/accessibility/switch_access/text_navigation_manager.js
index c07a7ec..322752c 100644
--- a/chrome/browser/resources/chromeos/accessibility/switch_access/text_navigation_manager.js
+++ b/chrome/browser/resources/chromeos/accessibility/switch_access/text_navigation_manager.js
@@ -250,7 +250,7 @@
    */
   static saveSelectStart() {
     const manager = TextNavigationManager.instance;
-    chrome.automation.getFocus((focusedNode) => {
+    chrome.automation.getFocus(focusedNode => {
       manager.selectionStartObject_ = focusedNode;
       manager.selectionStartIndex_ = manager.getSelectionIndexFromNode_(
           manager.selectionStartObject_,
@@ -319,7 +319,7 @@
    */
   static saveSelectEnd() {
     const manager = TextNavigationManager.instance;
-    chrome.automation.getFocus((focusedNode) => {
+    chrome.automation.getFocus(focusedNode => {
       manager.selectionEndObject_ = focusedNode;
       manager.selectionEndIndex_ = manager.getSelectionIndexFromNode_(
           manager.selectionEndObject_,
diff --git a/chrome/browser/resources/chromeos/accessibility/switch_access/text_navigation_manager_test.js b/chrome/browser/resources/chromeos/accessibility/switch_access/text_navigation_manager_test.js
index 81b0af4c..b316806 100644
--- a/chrome/browser/resources/chromeos/accessibility/switch_access/text_navigation_manager_test.js
+++ b/chrome/browser/resources/chromeos/accessibility/switch_access/text_navigation_manager_test.js
@@ -155,7 +155,7 @@
  * @param {!AutomationNode} inputNode
  */
 function checkNodeIsFocused(inputNode) {
-  chrome.automation.getFocus((focusedNode) => {
+  chrome.automation.getFocus(focusedNode => {
     assertEquals(focusedNode.role, inputNode.role);
   });
 }
diff --git a/chrome/browser/resources/settings/chromeos/os_printing_page/cups_add_print_server_dialog.js b/chrome/browser/resources/settings/chromeos/os_printing_page/cups_add_print_server_dialog.js
index df0254b..39761eb 100644
--- a/chrome/browser/resources/settings/chromeos/os_printing_page/cups_add_print_server_dialog.js
+++ b/chrome/browser/resources/settings/chromeos/os_printing_page/cups_add_print_server_dialog.js
@@ -7,94 +7,116 @@
  *   add a print server.
  */
 
-import '//resources/cr_elements/cr_button/cr_button.m.js';
-import '//resources/cr_elements/cr_input/cr_input.m.js';
+import 'chrome://resources/cr_elements/cr_button/cr_button.m.js';
+import 'chrome://resources/cr_elements/cr_input/cr_input.m.js';
 import './cups_add_printer_dialog.js';
 import './cups_printer_dialog_error.js';
 import './cups_printer_shared_css.js';
 
-import {afterNextRender, flush, html, Polymer, TemplateInstanceBase, Templatizer} from '//resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+import {html, PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 
-import {loadTimeData} from '../../i18n_setup.js';
+import {getPrintServerErrorText} from './cups_printer_dialog_util.js';
+import {CupsPrintersBrowserProxy, CupsPrintersBrowserProxyImpl, CupsPrintersList, PrintServerResult} from './cups_printers_browser_proxy.js';
 
-import {getBaseName, getErrorText, getPrintServerErrorText, isNameAndAddressValid, isNetworkProtocol, isPPDInfoValid, matchesSearchTerm, sortPrinters} from './cups_printer_dialog_util.js';
-import {CupsPrinterInfo, CupsPrintersBrowserProxy, CupsPrintersBrowserProxyImpl, CupsPrintersList, ManufacturersInfo, ModelsInfo, PrinterMakeModel, PrinterPpdMakeModel, PrinterSetupResult, PrintServerResult} from './cups_printers_browser_proxy.js';
+/** @polymer */
+class AddPrintServerDialogElement extends PolymerElement {
+  static get is() {
+    return 'add-print-server-dialog';
+  }
 
-Polymer({
-  _template: html`{__html_template__}`,
-  is: 'add-print-server-dialog',
+  static get template() {
+    return html`{__html_template__}`;
+  }
 
-  properties: {
-    /** @private {string} */
-    printServerAddress_: {
-      type: String,
-      value: '',
-    },
+  static get properties() {
+    return {
+      /** @private {string} */
+      printServerAddress_: {
+        type: String,
+        value: '',
+      },
 
-    /** @private {string} */
-    errorText_: {
-      type: String,
-      value: '',
-    },
+      /** @private {string} */
+      errorText_: {
+        type: String,
+        value: '',
+      },
 
-    /** @private {boolean} */
-    inProgress_: {
-      type: Boolean,
-      value: false,
-    },
-  },
+      /** @private {boolean} */
+      inProgress_: {
+        type: Boolean,
+        value: false,
+      },
+
+    };
+  }
+
+  constructor() {
+    super();
+
+    /** @private {!CupsPrintersBrowserProxy} */
+    this.browserProxy_ = CupsPrintersBrowserProxyImpl.getInstance();
+  }
 
   /** @private */
-  onCancelTap_: function() {
-    this.$$('add-printer-dialog').close();
-  },
+  onCancelTap_() {
+    this.shadowRoot.querySelector('add-printer-dialog').close();
+  }
 
   /** @private */
-  onAddPrintServerTap_: function() {
+  onAddPrintServerTap_() {
     this.inProgress_ = true;
-    this.$$('#printServerAddressInput').invalid = false;
-    CupsPrintersBrowserProxyImpl.getInstance()
-        .queryPrintServer(this.printServerAddress_)
+    this.shadowRoot.querySelector('#printServerAddressInput').invalid = false;
+    this.browserProxy_.queryPrintServer(this.printServerAddress_)
         .then(
             this.onPrintServerAddedSucceeded_.bind(this),
             this.onPrintServerAddedFailed_.bind(this));
-  },
+  }
 
   /**
    * @param {!CupsPrintersList} printers
    * @private
    */
-  onPrintServerAddedSucceeded_: function(printers) {
+  onPrintServerAddedSucceeded_(printers) {
     this.inProgress_ = false;
-    this.fire('add-print-server-and-show-toast', {printers: printers});
-    this.$$('add-printer-dialog').close();
-  },
+    const addPrintServerEvent =
+        new CustomEvent('add-print-server-and-show-toast', {
+          bubbles: true,
+          composed: true,
+          detail: {printers},
+        });
+    this.dispatchEvent(addPrintServerEvent);
+    this.shadowRoot.querySelector('add-printer-dialog').close();
+  }
 
   /**
    * @param {*} addPrintServerError
    * @private
    */
-  onPrintServerAddedFailed_: function(addPrintServerError) {
+  onPrintServerAddedFailed_(addPrintServerError) {
     this.inProgress_ = false;
     if (addPrintServerError === PrintServerResult.INCORRECT_URL) {
-      this.$$('#printServerAddressInput').invalid = true;
+      this.shadowRoot.querySelector('#printServerAddressInput').invalid = true;
       return;
     }
     this.errorText_ = getPrintServerErrorText(
         /** @type {PrintServerResult} */ (addPrintServerError));
-  },
+  }
 
   /**
    * Keypress event handler. If enter is pressed, trigger the add event.
    * @param {!Event} event
    * @private
    */
-  onKeypress_: function(event) {
+  onKeypress_(event) {
     if (event.key !== 'Enter') {
       return;
     }
     event.stopPropagation();
 
     this.onAddPrintServerTap_();
-  },
-});
+  }
+}
+
+customElements.define(
+    AddPrintServerDialogElement.is, AddPrintServerDialogElement);
diff --git a/chrome/browser/resources/settings/chromeos/os_printing_page/cups_add_printer_dialog.js b/chrome/browser/resources/settings/chromeos/os_printing_page/cups_add_printer_dialog.js
index d5dfbab..e6c7d81d 100644
--- a/chrome/browser/resources/settings/chromeos/os_printing_page/cups_add_printer_dialog.js
+++ b/chrome/browser/resources/settings/chromeos/os_printing_page/cups_add_printer_dialog.js
@@ -2,27 +2,37 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-/** 'add-printer-dialog' is the template of the Add Printer dialog. */
-import '//resources/cr_elements/cr_dialog/cr_dialog.m.js';
-import '//resources/cr_elements/icons.m.js';
-import '//resources/polymer/v3_0/iron-icon/iron-icon.js';
+/**
+ * @fileoverview 'add-printer-dialog' is the template of the Add Printer
+ * dialog.
+ */
+import 'chrome://resources/cr_elements/cr_dialog/cr_dialog.m.js';
+import 'chrome://resources/cr_elements/icons.m.js';
+import 'chrome://resources/polymer/v3_0/iron-icon/iron-icon.js';
 import './cups_printer_shared_css.js';
 
-import {afterNextRender, flush, html, Polymer, TemplateInstanceBase, Templatizer} from '//resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+import {html, PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 
-import {getBaseName, getErrorText, getPrintServerErrorText, isNameAndAddressValid, isNetworkProtocol, isPPDInfoValid, matchesSearchTerm, sortPrinters} from './cups_printer_dialog_util.js';
-import {CupsPrinterInfo, CupsPrintersBrowserProxy, CupsPrintersBrowserProxyImpl, CupsPrintersList, ManufacturersInfo, ModelsInfo, PrinterMakeModel, PrinterPpdMakeModel, PrinterSetupResult, PrintServerResult} from './cups_printers_browser_proxy.js';
+/** @polymer */
+class AddPrinterDialogElement extends PolymerElement {
+  static get is() {
+    return 'add-printer-dialog';
+  }
 
-Polymer({
-  _template: html`{__html_template__}`,
-  is: 'add-printer-dialog',
+  static get template() {
+    return html`{__html_template__}`;
+  }
 
   /** @private */
-  attached() {
+  connectedCallback() {
+    super.connectedCallback();
+
     this.$.dialog.showModal();
-  },
+  }
 
   close() {
     this.$.dialog.close();
-  },
-});
+  }
+}
+
+customElements.define(AddPrinterDialogElement.is, AddPrinterDialogElement);
diff --git a/chrome/browser/resources/settings/chromeos/os_printing_page/cups_add_printer_manually_dialog.js b/chrome/browser/resources/settings/chromeos/os_printing_page/cups_add_printer_manually_dialog.js
index 21deecd..bf6959f 100644
--- a/chrome/browser/resources/settings/chromeos/os_printing_page/cups_add_printer_manually_dialog.js
+++ b/chrome/browser/resources/settings/chromeos/os_printing_page/cups_add_printer_manually_dialog.js
@@ -7,6 +7,19 @@
  * manually enter the information to set up a new printer.
  */
 
+import 'chrome://resources/cr_elements/cr_button/cr_button.m.js';
+import 'chrome://resources/cr_elements/cr_input/cr_input.m.js';
+import 'chrome://resources/cr_components/localized_link/localized_link.js';
+import './cups_add_printer_dialog.js';
+import './cups_printer_dialog_error.js';
+import './cups_add_print_server_dialog.js';
+import './cups_printer_shared_css.js';
+
+import {html, PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+
+import {getErrorText, isNameAndAddressValid} from './cups_printer_dialog_util.js';
+import {CupsPrinterInfo, CupsPrintersBrowserProxy, CupsPrintersBrowserProxyImpl, PrinterMakeModel, PrinterSetupResult} from './cups_printers_browser_proxy.js';
+
 function getEmptyPrinter_() {
   return {
     ppdManufacturer: '',
@@ -29,57 +42,61 @@
   };
 }
 
-import {afterNextRender, Polymer, html, flush, Templatizer, TemplateInstanceBase} from '//resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+/** @polymer */
+class AddPrinterManuallyDialogElement extends PolymerElement {
+  static get is() {
+    return 'add-printer-manually-dialog';
+  }
 
-import '//resources/cr_elements/cr_button/cr_button.m.js';
-import '//resources/cr_elements/cr_input/cr_input.m.js';
-import '//resources/cr_components/localized_link/localized_link.js';
-import {loadTimeData} from '../../i18n_setup.js';
-import './cups_add_printer_dialog.js';
-import './cups_printer_dialog_error.js';
-import './cups_add_print_server_dialog.js';
-import {sortPrinters, matchesSearchTerm, getBaseName, getErrorText, isNetworkProtocol, isNameAndAddressValid, isPPDInfoValid, getPrintServerErrorText} from './cups_printer_dialog_util.js';
-import './cups_printer_shared_css.js';
-import {CupsPrintersBrowserProxy, CupsPrintersBrowserProxyImpl, CupsPrinterInfo, PrinterSetupResult, CupsPrintersList, PrinterPpdMakeModel, ManufacturersInfo, ModelsInfo, PrintServerResult, PrinterMakeModel} from './cups_printers_browser_proxy.js';
+  static get template() {
+    return html`{__html_template__}`;
+  }
 
-Polymer({
-  _template: html`{__html_template__}`,
-  is: 'add-printer-manually-dialog',
+  static get properties() {
+    return {
+      /** @type {!CupsPrinterInfo} */
+      newPrinter: {type: Object, notify: true, value: getEmptyPrinter_},
 
-  properties: {
-    /** @type {!CupsPrinterInfo} */
-    newPrinter: {type: Object, notify: true, value: getEmptyPrinter_},
+      /** @private */
+      addPrinterInProgress_: {
+        type: Boolean,
+        value: false,
+      },
 
-    /** @private */
-    addPrinterInProgress_: {
-      type: Boolean,
-      value: false,
-    },
+      /**
+       * The error text to be displayed on the dialog.
+       * @private
+       */
+      errorText_: {
+        type: String,
+        value: '',
+      },
 
-    /**
-     * The error text to be displayed on the dialog.
-     * @private
-     */
-    errorText_: {
-      type: String,
-      value: '',
-    },
+      /** @private */
+      showPrinterQueue_: {
+        type: Boolean,
+        value: true,
+      },
+    };
+  }
 
-    /** @private */
-    showPrinterQueue_: {
-      type: Boolean,
-      value: true,
-    },
-  },
+  static get observers() {
+    return [
+      'printerInfoChanged_(newPrinter.*)',
+    ];
+  }
 
-  observers: [
-    'printerInfoChanged_(newPrinter.*)',
-  ],
+  constructor() {
+    super();
+
+    /** @private {!CupsPrintersBrowserProxy} */
+    this.browserProxy_ = CupsPrintersBrowserProxyImpl.getInstance();
+  }
 
   /** @private */
   onCancelTap_() {
-    this.$$('add-printer-dialog').close();
-  },
+    this.shadowRoot.querySelector('add-printer-dialog').close();
+  }
 
   /**
    * Handler for addCupsPrinter success.
@@ -87,11 +104,15 @@
    * @private
    */
   onAddPrinterSucceeded_(result) {
-    this.fire(
-        'show-cups-printer-toast',
-        {resultCode: result, printerName: this.newPrinter.printerName});
-    this.$$('add-printer-dialog').close();
-  },
+    const showCupsPrinterToastEvent =
+        new CustomEvent('show-cups-printer-toast', {
+          bubbles: true,
+          composed: true,
+          detail: {resultCode: result, printerName: this.newPrinter.printerName}
+        });
+    this.dispatchEvent(showCupsPrinterToastEvent);
+    this.shadowRoot.querySelector('add-printer-dialog').close();
+  }
 
   /**
    * Handler for addCupsPrinter failure.
@@ -101,7 +122,16 @@
   onAddPrinterFailed_(result) {
     this.errorText_ = getErrorText(
         /** @type {PrinterSetupResult} */ (result));
-  },
+  }
+
+  /** @private */
+  openManufacturerModelDialog_() {
+    const event = new CustomEvent('open-manufacturer-model-dialog', {
+      bubbles: true,
+      composed: true,
+    });
+    this.dispatchEvent(event);
+  }
 
   /**
    * Handler for getPrinterInfo success.
@@ -125,16 +155,15 @@
     // Add the printer if it's configurable. Otherwise, forward to the
     // manufacturer dialog.
     if (info.ppdReferenceResolved) {
-      CupsPrintersBrowserProxyImpl.getInstance()
-          .addCupsPrinter(this.newPrinter)
+      this.browserProxy_.addCupsPrinter(this.newPrinter)
           .then(
               this.onAddPrinterSucceeded_.bind(this),
               this.onAddPrinterFailed_.bind(this));
     } else {
-      this.$$('add-printer-dialog').close();
-      this.fire('open-manufacturer-model-dialog');
+      this.shadowRoot.querySelector('add-printer-dialog').close();
+      this.openManufacturerModelDialog_();
     }
-  },
+  }
 
   /**
    * Handler for getPrinterInfo failure.
@@ -150,7 +179,7 @@
     }
     this.errorText_ = getErrorText(
         /** @type {PrinterSetupResult} */ (result));
-  },
+  }
 
   /** @private */
   addPressed_() {
@@ -158,20 +187,25 @@
 
     if (this.newPrinter.printerProtocol === 'ipp' ||
         this.newPrinter.printerProtocol === 'ipps') {
-      CupsPrintersBrowserProxyImpl.getInstance()
-          .getPrinterInfo(this.newPrinter)
+      this.browserProxy_.getPrinterInfo(this.newPrinter)
           .then(this.onPrinterFound_.bind(this), this.infoFailed_.bind(this));
     } else {
-      this.$$('add-printer-dialog').close();
-      this.fire('open-manufacturer-model-dialog');
+      this.shadowRoot.querySelector('add-printer-dialog').close();
+      this.openManufacturerModelDialog_();
     }
-  },
+  }
 
   /** @private */
-  onPrintServerTap_: function() {
-    this.$$('add-printer-dialog').close();
-    this.fire('open-add-print-server-dialog');
-  },
+  onPrintServerTap_() {
+    this.shadowRoot.querySelector('add-printer-dialog').close();
+
+    const openAddPrintServerDialogEvent =
+        new CustomEvent('open-add-print-server-dialog', {
+          bubbles: true,
+          composed: true,
+        });
+    this.dispatchEvent(openAddPrintServerDialogEvent);
+  }
 
   /**
    * @param {!Event} event
@@ -181,7 +215,7 @@
     // Queue input should be hidden when protocol is set to "App Socket".
     this.showPrinterQueue_ = event.target.value !== 'socket';
     this.set('newPrinter.printerProtocol', event.target.value);
-  },
+  }
 
   /**
    * @return {boolean} Whether the add printer button is enabled.
@@ -190,13 +224,13 @@
   canAddPrinter_() {
     return !this.addPrinterInProgress_ &&
         isNameAndAddressValid(this.newPrinter);
-  },
+  }
 
   /** @private */
   printerInfoChanged_() {
     this.$.printerAddressInput.invalid = false;
     this.errorText_ = '';
-  },
+  }
 
   /**
    * Keypress event handler. If enter is pressed, printer is added if
@@ -213,5 +247,8 @@
     if (this.canAddPrinter_()) {
       this.addPressed_();
     }
-  },
-});
+  }
+}
+
+customElements.define(
+    AddPrinterManuallyDialogElement.is, AddPrinterManuallyDialogElement);
diff --git a/chrome/browser/resources/settings/chromeos/os_printing_page/cups_add_printer_manufacturer_model_dialog.js b/chrome/browser/resources/settings/chromeos/os_printing_page/cups_add_printer_manufacturer_model_dialog.js
index 96ce71a2..122ed05 100644
--- a/chrome/browser/resources/settings/chromeos/os_printing_page/cups_add_printer_manufacturer_model_dialog.js
+++ b/chrome/browser/resources/settings/chromeos/os_printing_page/cups_add_printer_manufacturer_model_dialog.js
@@ -7,106 +7,125 @@
  * 'add-printer-manufacturer-model-dialog' is a dialog in which the user can
  *   manually select the manufacture and model of the new printer.
  */
-import '//resources/cr_elements/cr_button/cr_button.m.js';
-import '//resources/cr_elements/cr_input/cr_input.m.js';
-import '//resources/cr_components/localized_link/localized_link.js';
+import 'chrome://resources/cr_elements/cr_button/cr_button.m.js';
+import 'chrome://resources/cr_elements/cr_input/cr_input.m.js';
+import 'chrome://resources/cr_components/localized_link/localized_link.js';
 import './cups_add_printer_dialog.js';
 import './cups_printer_dialog_error.js';
 import './cups_printer_shared_css.js';
 
-import {afterNextRender, flush, html, Polymer, TemplateInstanceBase, Templatizer} from '//resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+import {html, PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 
 import {loadTimeData} from '../../i18n_setup.js';
 
-import {getBaseName, getErrorText, getPrintServerErrorText, isNameAndAddressValid, isNetworkProtocol, isPPDInfoValid, matchesSearchTerm, sortPrinters} from './cups_printer_dialog_util.js';
-import {CupsPrinterInfo, CupsPrintersBrowserProxy, CupsPrintersBrowserProxyImpl, CupsPrintersList, ManufacturersInfo, ModelsInfo, PrinterMakeModel, PrinterPpdMakeModel, PrinterSetupResult, PrintServerResult} from './cups_printers_browser_proxy.js';
+import {getBaseName, getErrorText, isPPDInfoValid} from './cups_printer_dialog_util.js';
+import {CupsPrinterInfo, CupsPrintersBrowserProxy, CupsPrintersBrowserProxyImpl, ManufacturersInfo, ModelsInfo, PrinterSetupResult} from './cups_printers_browser_proxy.js';
 
-Polymer({
-  _template: html`{__html_template__}`,
-  is: 'add-printer-manufacturer-model-dialog',
+/** @polymer */
+class AddPrinterManufacturerModelDialogElement extends PolymerElement {
+  static get is() {
+    return 'add-printer-manufacturer-model-dialog';
+  }
 
-  properties: {
-    /** @type {!CupsPrinterInfo} */
-    activePrinter: {
-      type: Object,
-      notify: true,
-    },
+  static get template() {
+    return html`{__html_template__}`;
+  }
 
-    /** @type {?Array<string>} */
-    manufacturerList: Array,
+  static get properties() {
+    return {
+      /** @type {!CupsPrinterInfo} */
+      activePrinter: {
+        type: Object,
+        notify: true,
+      },
 
-    /** @type {?Array<string>} */
-    modelList: Array,
+      /** @type {?Array<string>} */
+      manufacturerList: Array,
 
-    /**
-     * Whether the user selected PPD file is valid.
-     * @private
-     */
-    invalidPPD_: {
-      type: Boolean,
-      value: false,
-    },
+      /** @type {?Array<string>} */
+      modelList: Array,
 
-    /**
-     * The base name of a newly selected PPD file.
-     * @private
-     */
-    newUserPPD_: String,
+      /**
+       * Whether the user selected PPD file is valid.
+       * @private
+       */
+      invalidPPD_: {
+        type: Boolean,
+        value: false,
+      },
 
-    /**
-     * The URL to a printer's EULA.
-     * @private
-     */
-    eulaUrl_: {
-      type: String,
-      value: '',
-    },
+      /**
+       * The base name of a newly selected PPD file.
+       * @private
+       */
+      newUserPPD_: String,
 
-    /** @private */
-    addPrinterInProgress_: {
-      type: Boolean,
-      value: false,
-    },
+      /**
+       * The URL to a printer's EULA.
+       * @private
+       */
+      eulaUrl_: {
+        type: String,
+        value: '',
+      },
 
-    /**
-     * The error text to be displayed on the dialog.
-     * @private
-     */
-    errorText_: {
-      type: String,
-      value: '',
-    },
+      /** @private */
+      addPrinterInProgress_: {
+        type: Boolean,
+        value: false,
+      },
 
-    /**
-     * Indicates whether the value in the Manufacturer dropdown is a valid
-     * printer manufacturer.
-     * @private
-     */
-    isManufacturerInvalid_: Boolean,
+      /**
+       * The error text to be displayed on the dialog.
+       * @private
+       */
+      errorText_: {
+        type: String,
+        value: '',
+      },
 
-    /**
-     * Indicates whether the value in the Model dropdown is a valid printer
-     * model.
-     * @private
-     */
-    isModelInvalid_: Boolean,
-  },
+      /**
+       * Indicates whether the value in the Manufacturer dropdown is a valid
+       * printer manufacturer.
+       * @private
+       */
+      isManufacturerInvalid_: Boolean,
 
-  observers: [
-    'selectedManufacturerChanged_(activePrinter.ppdManufacturer)',
-    'selectedModelChanged_(activePrinter.ppdModel)',
-  ],
+      /**
+       * Indicates whether the value in the Model dropdown is a valid printer
+       * model.
+       * @private
+       */
+      isModelInvalid_: Boolean,
+    };
+  }
+
+  static get observers() {
+    return [
+      'selectedManufacturerChanged_(activePrinter.ppdManufacturer)',
+      'selectedModelChanged_(activePrinter.ppdModel)',
+
+    ];
+  }
+
+  constructor() {
+    super();
+
+    /** @private {!CupsPrintersBrowserProxy} */
+    this.browserProxy_ = CupsPrintersBrowserProxyImpl.getInstance();
+  }
 
   /** @override */
-  attached() {
-    CupsPrintersBrowserProxyImpl.getInstance()
-        .getCupsPrinterManufacturersList()
-        .then(this.manufacturerListChanged_.bind(this));
-  },
+  connectedCallback() {
+    super.connectedCallback();
+
+    this.browserProxy_.getCupsPrinterManufacturersList().then(
+        this.manufacturerListChanged_.bind(this));
+  }
 
   close() {
-    this.$$('add-printer-dialog').close();
-  },
+    this.shadowRoot.querySelector('add-printer-dialog').close();
+  }
 
   /**
    * Handler for addCupsPrinter success.
@@ -114,11 +133,16 @@
    * @private
    */
   onPrinterAddedSucceeded_(result) {
-    this.fire(
-        'show-cups-printer-toast',
-        {resultCode: result, printerName: this.activePrinter.printerName});
+    const showCupsPrinterToastEvent =
+        new CustomEvent('show-cups-printer-toast', {
+          bubbles: true,
+          composed: true,
+          detail:
+              {resultCode: result, printerName: this.activePrinter.printerName},
+        });
+    this.dispatchEvent(showCupsPrinterToastEvent);
     this.close();
-  },
+  }
 
   /**
    * Handler for addCupsPrinter failure.
@@ -129,7 +153,7 @@
     this.addPrinterInProgress_ = false;
     this.errorText_ = getErrorText(
         /** @type {PrinterSetupResult} */ (result));
-  },
+  }
 
   /**
    * If the printer is a nearby printer, return make + model with the subtext.
@@ -147,7 +171,7 @@
     return loadTimeData.getStringF(
         'manufacturerAndModelAdditionalInformation',
         this.activePrinter.printerName);
-  },
+  }
 
   /**
    * @param {string} manufacturer The manufacturer for which we are retrieving
@@ -159,11 +183,10 @@
     this.set('activePrinter.ppdModel', '');
     this.modelList = [];
     if (manufacturer && manufacturer.length !== 0) {
-      CupsPrintersBrowserProxyImpl.getInstance()
-          .getCupsPrinterModelsList(manufacturer)
+      this.browserProxy_.getCupsPrinterModelsList(manufacturer)
           .then(this.modelListChanged_.bind(this));
     }
-  },
+  }
 
   /**
    * Attempts to get the EULA Url if the selected printer has one.
@@ -178,11 +201,11 @@
       return;
     }
 
-    CupsPrintersBrowserProxyImpl.getInstance()
+    this.browserProxy_
         .getEulaUrl(
             this.activePrinter.ppdManufacturer, this.activePrinter.ppdModel)
         .then(this.onGetEulaUrlCompleted_.bind(this));
-  },
+  }
 
   /**
    * @param {string} eulaUrl The URL for the printer's EULA.
@@ -190,7 +213,7 @@
    */
   onGetEulaUrlCompleted_(eulaUrl) {
     this.eulaUrl_ = eulaUrl;
-  },
+  }
 
   /**
    * @param {!ManufacturersInfo} manufacturersInfo
@@ -202,11 +225,11 @@
     }
     this.manufacturerList = manufacturersInfo.manufacturers;
     if (this.activePrinter.ppdManufacturer.length !== 0) {
-      CupsPrintersBrowserProxyImpl.getInstance()
+      this.browserProxy_
           .getCupsPrinterModelsList(this.activePrinter.ppdManufacturer)
           .then(this.modelListChanged_.bind(this));
     }
-  },
+  }
 
   /**
    * @param {!ModelsInfo} modelsInfo
@@ -216,13 +239,13 @@
     if (modelsInfo.success) {
       this.modelList = modelsInfo.models;
     }
-  },
+  }
 
   /** @private */
   onBrowseFile_() {
-    CupsPrintersBrowserProxyImpl.getInstance().getCupsPrinterPPDPath().then(
+    this.browserProxy_.getCupsPrinterPPDPath().then(
         this.printerPPDPathChanged_.bind(this));
-  },
+  }
 
   /**
    * @param {string} path The full path to the selected PPD file
@@ -232,24 +255,22 @@
     this.set('activePrinter.printerPPDPath', path);
     this.invalidPPD_ = !path;
     this.newUserPPD_ = getBaseName(path);
-  },
+  }
 
   /** @private */
   onCancelTap_() {
     this.close();
-    CupsPrintersBrowserProxyImpl.getInstance().cancelPrinterSetUp(
-        this.activePrinter);
-  },
+    this.browserProxy_.cancelPrinterSetUp(this.activePrinter);
+  }
 
   /** @private */
   addPrinter_() {
     this.addPrinterInProgress_ = true;
-    CupsPrintersBrowserProxyImpl.getInstance()
-        .addCupsPrinter(this.activePrinter)
+    this.browserProxy_.addCupsPrinter(this.activePrinter)
         .then(
             this.onPrinterAddedSucceeded_.bind(this),
             this.onPrinterAddedFailed_.bind(this));
-  },
+  }
 
   /**
    * @param {string} ppdManufacturer
@@ -264,5 +285,9 @@
     return !addPrinterInProgress &&
         isPPDInfoValid(ppdManufacturer, ppdModel, printerPPDPath) &&
         !isManufacturerInvalid && !isModelInvalid;
-  },
-});
+  }
+}
+
+customElements.define(
+    AddPrinterManufacturerModelDialogElement.is,
+    AddPrinterManufacturerModelDialogElement);
diff --git a/chrome/browser/resources/settings/chromeos/os_printing_page/cups_edit_printer_dialog.js b/chrome/browser/resources/settings/chromeos/os_printing_page/cups_edit_printer_dialog.js
index 0bfec1d4..b38ec37 100644
--- a/chrome/browser/resources/settings/chromeos/os_printing_page/cups_edit_printer_dialog.js
+++ b/chrome/browser/resources/settings/chromeos/os_printing_page/cups_edit_printer_dialog.js
@@ -7,196 +7,212 @@
  * existing printer's information and re-configure it.
  */
 
-import '//resources/cr_elements/cr_button/cr_button.m.js';
-import '//resources/cr_elements/cr_input/cr_input.m.js';
-import '//resources/cr_elements/cr_searchable_drop_down/cr_searchable_drop_down.js';
-import '//resources/cr_elements/shared_style_css.m.js';
-import '//resources/polymer/v3_0/iron-icon/iron-icon.js';
-import '//resources/cr_components/localized_link/localized_link.js';
+import 'chrome://resources/cr_elements/cr_button/cr_button.m.js';
+import 'chrome://resources/cr_elements/cr_input/cr_input.m.js';
+import 'chrome://resources/cr_elements/cr_searchable_drop_down/cr_searchable_drop_down.js';
+import 'chrome://resources/cr_elements/shared_style_css.m.js';
+import 'chrome://resources/polymer/v3_0/iron-icon/iron-icon.js';
+import 'chrome://resources/cr_components/localized_link/localized_link.js';
 import './cups_add_printer_dialog.js';
 import './cups_printer_dialog_error.js';
 import './cups_printer_shared_css.js';
 
-import {MojoInterfaceProvider, MojoInterfaceProviderImpl} from '//resources/cr_components/chromeos/network/mojo_interface_provider.m.js';
-import {NetworkListenerBehavior} from '//resources/cr_components/chromeos/network/network_listener_behavior.m.js';
-import {OncMojo} from '//resources/cr_components/chromeos/network/onc_mojo.m.js';
-import {CrScrollableBehavior} from '//resources/cr_elements/cr_scrollable_behavior.m.js';
-import {afterNextRender, flush, html, Polymer, TemplateInstanceBase, Templatizer} from '//resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+import {MojoInterfaceProvider, MojoInterfaceProviderImpl} from 'chrome://resources/cr_components/chromeos/network/mojo_interface_provider.m.js';
+import {NetworkListenerBehavior, NetworkListenerBehaviorInterface} from 'chrome://resources/cr_components/chromeos/network/network_listener_behavior.m.js';
+import {OncMojo} from 'chrome://resources/cr_components/chromeos/network/onc_mojo.m.js';
+import {I18nBehavior, I18nBehaviorInterface} from 'chrome://resources/js/i18n_behavior.m.js';
+import {html, mixinBehaviors, PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 
-import {I18nBehavior} from 'chrome://resources/js/i18n_behavior.m.js';
-
-import {loadTimeData} from '../../i18n_setup.js';
 import {recordSettingChange} from '../metrics_recorder.js';
 
-import {getBaseName, getErrorText, getPrintServerErrorText, isNameAndAddressValid, isNetworkProtocol, isPPDInfoValid, matchesSearchTerm, sortPrinters} from './cups_printer_dialog_util.js';
-import {CupsPrinterInfo, CupsPrintersBrowserProxy, CupsPrintersBrowserProxyImpl, CupsPrintersList, ManufacturersInfo, ModelsInfo, PrinterMakeModel, PrinterPpdMakeModel, PrinterSetupResult, PrintServerResult} from './cups_printers_browser_proxy.js';
-
-Polymer({
-  _template: html`{__html_template__}`,
-  is: 'settings-cups-edit-printer-dialog',
-
-  behaviors: [
-    I18nBehavior,
-    NetworkListenerBehavior,
-  ],
-
-  properties: {
-    /**
-     * The currently saved printer.
-     * @type {CupsPrinterInfo}
-     */
-    activePrinter: Object,
-
-    /**
-     * Printer that holds the modified changes to activePrinter and only
-     * applies these changes when the save button is clicked.
-     * @type {CupsPrinterInfo}
-     */
-    pendingPrinter_: Object,
-
-    /**
-     * If the printer needs to be re-configured.
-     * @private {boolean}
-     */
-    needsReconfigured_: {
-      type: Boolean,
-      value: false,
-    },
-
-    /**
-     * The current PPD in use by the printer.
-     * @private
-     */
-    userPPD_: String,
+import {getBaseName, getErrorText, isNameAndAddressValid, isNetworkProtocol, isPPDInfoValid} from './cups_printer_dialog_util.js';
+import {CupsPrinterInfo, CupsPrintersBrowserProxy, CupsPrintersBrowserProxyImpl, ManufacturersInfo, ModelsInfo, PrinterPpdMakeModel, PrinterSetupResult} from './cups_printers_browser_proxy.js';
 
 
-    /**
-     * Tracks whether the dialog is fully initialized. This is required because
-     * the dialog isn't fully initialized until Model and Manufacturer are set.
-     * Allows us to ignore changes made to these fields until initialization is
-     * complete.
-     * @private
-     */
-    arePrinterFieldsInitialized_: {
-      type: Boolean,
-      value: false,
-    },
+/**
+ * @constructor
+ * @extends {PolymerElement}
+ * @implements {I18nBehaviorInterface}
+ * @implements {NetworkListenerBehaviorInterface}
+ */
+const SettingsCupsEditPrinterDialogElementBase =
+    mixinBehaviors([I18nBehavior, NetworkListenerBehavior], PolymerElement);
 
-    /**
-     * If the printer info has changed since loading this dialog. This will
-     * only track the freeform input fields, since the other fields contain
-     * input selected from dropdown menus.
-     * @private
-     */
-    printerInfoChanged_: {
-      type: Boolean,
-      value: false,
-    },
+/** @polymer */
+class SettingsCupsEditPrinterDialogElement extends
+    SettingsCupsEditPrinterDialogElementBase {
+  static get is() {
+    return 'settings-cups-edit-printer-dialog';
+  }
 
-    networkProtocolActive_: {
-      type: Boolean,
-      computed: 'isNetworkProtocol_(pendingPrinter_.printerProtocol)',
-    },
+  static get template() {
+    return html`{__html_template__}`;
+  }
 
-    /** @type {?Array<string>} */
-    manufacturerList: Array,
+  static get properties() {
+    return {
+      /**
+       * The currently saved printer.
+       * @type {CupsPrinterInfo}
+       */
+      activePrinter: Object,
 
-    /** @type {?Array<string>} */
-    modelList: Array,
+      /**
+       * Printer that holds the modified changes to activePrinter and only
+       * applies these changes when the save button is clicked.
+       * @type {CupsPrinterInfo}
+       */
+      pendingPrinter_: Object,
 
-    /**
-     * Whether the user selected PPD file is valid.
-     * @private
-     */
-    invalidPPD_: {
-      type: Boolean,
-      value: false,
-    },
+      /**
+       * If the printer needs to be re-configured.
+       * @private {boolean}
+       */
+      needsReconfigured_: {
+        type: Boolean,
+        value: false,
+      },
 
-    /**
-     * The base name of a newly selected PPD file.
-     * @private
-     */
-    newUserPPD_: String,
+      /**
+       * The current PPD in use by the printer.
+       * @private
+       */
+      userPPD_: String,
 
-    /**
-     * The URL to a printer's EULA.
-     * @private
-     */
-    eulaUrl_: {
-      type: String,
-      value: '',
-    },
+      /**
+       * Tracks whether the dialog is fully initialized. This is required
+       * because the dialog isn't fully initialized until Model and Manufacturer
+       * are set. Allows us to ignore changes made to these fields until
+       * initialization is complete.
+       * @private
+       */
+      arePrinterFieldsInitialized_: {
+        type: Boolean,
+        value: false,
+      },
 
-    /** @private */
-    isOnline_: {
-      type: Boolean,
-      value: true,
-    },
+      /**
+       * If the printer info has changed since loading this dialog. This will
+       * only track the freeform input fields, since the other fields contain
+       * input selected from dropdown menus.
+       * @private
+       */
+      printerInfoChanged_: {
+        type: Boolean,
+        value: false,
+      },
 
-    /**
-     * The error text to be displayed on the dialog.
-     * @private
-     */
-    errorText_: {
-      type: String,
-      value: '',
-    },
+      networkProtocolActive_: {
+        type: Boolean,
+        computed: 'isNetworkProtocol_(pendingPrinter_.printerProtocol)',
+      },
 
-    /**
-     * Indicates whether the value in the Manufacturer dropdown is a valid
-     * printer manufacturer.
-     * @private
-     */
-    isManufacturerInvalid_: {
-      type: Boolean,
-      value: false,
-    },
+      /** @type {?Array<string>} */
+      manufacturerList: Array,
 
-    /**
-     * Indicates whether the value in the Model dropdown is a valid printer
-     * model.
-     * @private
-     */
-    isModelInvalid_: {
-      type: Boolean,
-      value: false,
-    },
-  },
+      /** @type {?Array<string>} */
+      modelList: Array,
 
-  observers: [
-    'printerPathChanged_(pendingPrinter_.*)',
-    'selectedEditManufacturerChanged_(pendingPrinter_.ppdManufacturer)',
-    'onModelChanged_(pendingPrinter_.ppdModel)',
-  ],
+      /**
+       * Whether the user selected PPD file is valid.
+       * @private
+       */
+      invalidPPD_: {
+        type: Boolean,
+        value: false,
+      },
 
-  /** @private {?chromeos.networkConfig.mojom.CrosNetworkConfigRemote} */
-  networkConfig_: null,
+      /**
+       * The base name of a newly selected PPD file.
+       * @private
+       */
+      newUserPPD_: String,
+
+      /**
+       * The URL to a printer's EULA.
+       * @private
+       */
+      eulaUrl_: {
+        type: String,
+        value: '',
+      },
+
+      /** @private */
+      isOnline_: {
+        type: Boolean,
+        value: true,
+      },
+
+      /**
+       * The error text to be displayed on the dialog.
+       * @private
+       */
+      errorText_: {
+        type: String,
+        value: '',
+      },
+
+      /**
+       * Indicates whether the value in the Manufacturer dropdown is a valid
+       * printer manufacturer.
+       * @private
+       */
+      isManufacturerInvalid_: {
+        type: Boolean,
+        value: false,
+      },
+
+      /**
+       * Indicates whether the value in the Model dropdown is a valid printer
+       * model.
+       * @private
+       */
+      isModelInvalid_: {
+        type: Boolean,
+        value: false,
+      },
+    };
+  }
+
+  static get observers() {
+    return [
+      'printerPathChanged_(pendingPrinter_.*)',
+      'selectedEditManufacturerChanged_(pendingPrinter_.ppdManufacturer)',
+      'onModelChanged_(pendingPrinter_.ppdModel)',
+    ];
+  }
 
   /** @override */
-  created() {
+  constructor() {
+    super();
+
+    /** @private {!CupsPrintersBrowserProxy} */
+    this.browserProxy_ = CupsPrintersBrowserProxyImpl.getInstance();
+
+    /** @private {!chromeos.networkConfig.mojom.CrosNetworkConfigRemote} */
     this.networkConfig_ =
         MojoInterfaceProviderImpl.getInstance().getMojoServiceRemote();
-  },
+  }
 
   /** @override */
-  attached() {
+  connectedCallback() {
+    super.connectedCallback();
+
     // Create a copy of activePrinter so that we can modify its fields.
     this.pendingPrinter_ = /** @type{CupsPrinterInfo} */
         (Object.assign({}, this.activePrinter));
 
     this.refreshNetworks_();
 
-    CupsPrintersBrowserProxyImpl.getInstance()
+    this.browserProxy_
         .getPrinterPpdManufacturerAndModel(this.pendingPrinter_.printerId)
         .then(
             this.onGetPrinterPpdManufacturerAndModel_.bind(this),
             this.onGetPrinterPpdManufacturerAndModelFailed_.bind(this));
-    CupsPrintersBrowserProxyImpl.getInstance()
-        .getCupsPrinterManufacturersList()
-        .then(this.manufacturerListChanged_.bind(this));
+    this.browserProxy_.getCupsPrinterManufacturersList().then(
+        this.manufacturerListChanged_.bind(this));
     this.userPPD_ = getBaseName(this.pendingPrinter_.printerPPDPath);
-  },
+  }
 
   /**
    * CrosNetworkConfigObserver impl
@@ -208,7 +224,7 @@
     this.isOnline_ = networks.some(function(network) {
       return OncMojo.connectionStateIsConnected(network.connectionState);
     });
-  },
+  }
 
   /**
    * @param {!{path: string, value: string}} change
@@ -218,7 +234,7 @@
     if (change.path !== 'pendingPrinter_.printerName') {
       this.needsReconfigured_ = true;
     }
-  },
+  }
 
   /**
    * @param {!Event} event
@@ -227,17 +243,17 @@
   onProtocolChange_(event) {
     this.set('pendingPrinter_.printerProtocol', event.target.value);
     this.onPrinterInfoChange_();
-  },
+  }
 
   /** @private */
   onPrinterInfoChange_() {
     this.printerInfoChanged_ = true;
-  },
+  }
 
   /** @private */
   onCancelTap_() {
-    this.$$('add-printer-dialog').close();
-  },
+    this.shadowRoot.querySelector('add-printer-dialog').close();
+  }
 
   /**
    * Handler for update|reconfigureCupsPrinter success.
@@ -245,11 +261,17 @@
    * @private
    */
   onPrinterEditSucceeded_(result) {
-    this.fire(
-        'show-cups-printer-toast',
-        {resultCode: result, printerName: this.activePrinter.printerName});
-    this.$$('add-printer-dialog').close();
-  },
+    const showCupsPrinterToastEvent =
+        new CustomEvent('show-cups-printer-toast', {
+          bubbles: true,
+          composed: true,
+          detail:
+              {resultCode: result, printerName: this.activePrinter.printerName},
+        });
+    this.dispatchEvent(showCupsPrinterToastEvent);
+
+    this.shadowRoot.querySelector('add-printer-dialog').close();
+  }
 
   /**
    * Handler for update|reconfigureCupsPrinter failure.
@@ -259,7 +281,7 @@
   onPrinterEditFailed_(result) {
     this.errorText_ = getErrorText(
         /** @type {PrinterSetupResult} */ (result));
-  },
+  }
 
   /** @private */
   onSaveTap_() {
@@ -267,21 +289,20 @@
     if (!this.needsReconfigured_ || !this.isOnline_) {
       // If we don't need to reconfigure or we are offline, just update the
       // printer name.
-      CupsPrintersBrowserProxyImpl.getInstance()
+      this.browserProxy_
           .updateCupsPrinter(
               this.activePrinter.printerId, this.activePrinter.printerName)
           .then(
               this.onPrinterEditSucceeded_.bind(this),
               this.onPrinterEditFailed_.bind(this));
     } else {
-      CupsPrintersBrowserProxyImpl.getInstance()
-          .reconfigureCupsPrinter(this.activePrinter)
+      this.browserProxy_.reconfigureCupsPrinter(this.activePrinter)
           .then(
               this.onPrinterEditSucceeded_.bind(this),
               this.onPrinterEditFailed_.bind(this));
     }
     recordSettingChange();
-  },
+  }
 
   /**
    * @return {string} The i18n string for the dialog title.
@@ -291,7 +312,7 @@
     return this.pendingPrinter_.isManaged ?
         this.i18n('viewPrinterDialogTitle') :
         this.i18n('editPrinterDialogTitle');
-  },
+  }
 
   /**
    * @param {!CupsPrinterInfo} printer
@@ -311,7 +332,7 @@
     } else {
       return '';
     }
-  },
+  }
 
   /**
    * Handler for getPrinterPpdManufacturerAndModel() success case.
@@ -325,7 +346,7 @@
     // |needsReconfigured_| needs to reset to false after |ppdManufacturer| and
     // |ppdModel| are initialized to their correct values.
     this.needsReconfigured_ = false;
-  },
+  }
 
   /**
    * Handler for getPrinterPpdManufacturerAndModel() failure case.
@@ -333,7 +354,7 @@
    */
   onGetPrinterPpdManufacturerAndModelFailed_() {
     this.needsReconfigured_ = false;
-  },
+  }
 
   /**
    * @param {string} protocol
@@ -342,7 +363,7 @@
    */
   isNetworkProtocol_(protocol) {
     return isNetworkProtocol(protocol);
-  },
+  }
 
   /**
    * @return {boolean} Whether the current printer was auto configured.
@@ -350,7 +371,7 @@
    */
   isAutoconfPrinter_() {
     return this.pendingPrinter_.printerPpdReference.autoconf;
-  },
+  }
 
   /**
    * @return {boolean} Whether the Save button is enabled.
@@ -360,7 +381,7 @@
     return this.printerInfoChanged_ &&
         (this.isPrinterConfigured_() || !this.isOnline_) &&
         !this.isManufacturerInvalid_ && !this.isModelInvalid_;
-  },
+  }
 
   /**
    * @param {string} manufacturer The manufacturer for which we are retrieving
@@ -372,11 +393,10 @@
     this.set('pendingPrinter_.ppdModel', '');
     this.modelList = [];
     if (!!manufacturer && manufacturer.length !== 0) {
-      CupsPrintersBrowserProxyImpl.getInstance()
-          .getCupsPrinterModelsList(manufacturer)
+      this.browserProxy_.getCupsPrinterModelsList(manufacturer)
           .then(this.modelListChanged_.bind(this));
     }
-  },
+  }
 
   /**
    * Sets printerInfoChanged_ to true to show that the model has changed. Also
@@ -397,7 +417,7 @@
     }
 
     this.attemptPpdEulaFetch_();
-  },
+  }
 
   /**
    * @param {string} eulaUrl The URL for the printer's EULA.
@@ -405,13 +425,13 @@
    */
   onGetEulaUrlCompleted_(eulaUrl) {
     this.eulaUrl_ = eulaUrl;
-  },
+  }
 
   /** @private */
   onBrowseFile_() {
-    CupsPrintersBrowserProxyImpl.getInstance().getCupsPrinterPPDPath().then(
+    this.browserProxy_.getCupsPrinterPPDPath().then(
         this.printerPPDPathChanged_.bind(this));
-  },
+  }
 
   /**
    * @param {!ManufacturersInfo} manufacturersInfo
@@ -423,11 +443,11 @@
     }
     this.manufacturerList = manufacturersInfo.manufacturers;
     if (this.pendingPrinter_.ppdManufacturer.length !== 0) {
-      CupsPrintersBrowserProxyImpl.getInstance()
+      this.browserProxy_
           .getCupsPrinterModelsList(this.pendingPrinter_.ppdManufacturer)
           .then(this.modelListChanged_.bind(this));
     }
-  },
+  }
 
   /**
    * @param {!ModelsInfo} modelsInfo
@@ -443,7 +463,7 @@
       // |modelList|.
       this.attemptPpdEulaFetch_();
     }
-  },
+  }
 
   /**
    * @param {string} path The full path to the selected PPD file
@@ -457,7 +477,7 @@
       this.onPrinterInfoChange_();
     }
     this.userPPD_ = getBaseName(path);
-  },
+  }
 
   /**
    * Returns true if the printer has valid name, address, and valid PPD or was
@@ -472,7 +492,7 @@
              this.pendingPrinter_.ppdManufacturer,
              this.pendingPrinter_.ppdModel,
              this.pendingPrinter_.printerPPDPath));
-  },
+  }
 
   /**
    * Helper function to copy over modified fields to activePrinter.
@@ -490,7 +510,7 @@
     // Set ppdModel since there is an observer that clears ppdmodel's value when
     // ppdManufacturer changes.
     this.activePrinter.ppdModel = this.pendingPrinter_.ppdModel;
-  },
+  }
 
   /**
    * Callback function when networks change.
@@ -506,7 +526,7 @@
         .then((responseParams) => {
           this.onActiveNetworksChanged(responseParams.result);
         });
-  },
+  }
 
   /**
    * Returns true if the printer protocol select field should be enabled.
@@ -528,7 +548,7 @@
     }
 
     return this.isOnline_ && this.networkProtocolActive_;
-  },
+  }
 
   /**
    * Attempts fetching for the EULA Url based off of the current printer's
@@ -541,11 +561,11 @@
       return;
     }
 
-    CupsPrintersBrowserProxyImpl.getInstance()
+    this.browserProxy_
         .getEulaUrl(
             this.pendingPrinter_.ppdManufacturer, this.pendingPrinter_.ppdModel)
         .then(this.onGetEulaUrlCompleted_.bind(this));
-  },
+  }
 
   /**
    * @return {boolean} True if we're on an active network and the printer
@@ -560,7 +580,7 @@
     }
 
     return this.networkProtocolActive_;
-  },
+  }
 
   /**
    * @return {boolean} True if the printer is managed or not online.
@@ -569,6 +589,9 @@
   isInputFieldReadonly_() {
     return !this.isOnline_ ||
         (this.pendingPrinter_ && this.pendingPrinter_.isManaged);
-  },
+  }
+}
 
-});
+customElements.define(
+    SettingsCupsEditPrinterDialogElement.is,
+    SettingsCupsEditPrinterDialogElement);
diff --git a/chrome/browser/resources/settings/chromeos/os_printing_page/cups_enterprise_printers.js b/chrome/browser/resources/settings/chromeos/os_printing_page/cups_enterprise_printers.js
index c2578333..2f6262c 100644
--- a/chrome/browser/resources/settings/chromeos/os_printing_page/cups_enterprise_printers.js
+++ b/chrome/browser/resources/settings/chromeos/os_printing_page/cups_enterprise_printers.js
@@ -2,21 +2,26 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import '//resources/cr_elements/cr_action_menu/cr_action_menu.js';
-import '//resources/cr_elements/icons.m.js';
-import '//resources/polymer/v3_0/iron-flex-layout/iron-flex-layout-classes.js';
-import '//resources/polymer/v3_0/iron-list/iron-list.js';
+/**
+ * @fileoverview 'settings-cups-enterprise-printers' is a list container for
+ * Enterprise Printers.
+ */
+
+import 'chrome://resources/cr_elements/cr_action_menu/cr_action_menu.js';
+import 'chrome://resources/cr_elements/icons.m.js';
+import 'chrome://resources/polymer/v3_0/iron-flex-layout/iron-flex-layout-classes.js';
+import 'chrome://resources/polymer/v3_0/iron-list/iron-list.js';
 import './cups_printers_entry.js';
 import '../../settings_shared_css.js';
 
-import {ListPropertyUpdateBehavior} from '//resources/js/list_property_update_behavior.m.js';
-import {WebUIListenerBehavior} from '//resources/js/web_ui_listener_behavior.m.js';
-import {html, Polymer} from '//resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+import {ListPropertyUpdateBehavior, ListPropertyUpdateBehaviorInterface} from 'chrome://resources/js/list_property_update_behavior.m.js';
+import {WebUIListenerBehavior, WebUIListenerBehaviorInterface} from 'chrome://resources/js/web_ui_listener_behavior.m.js';
+import {html, mixinBehaviors, PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 
 import {matchesSearchTerm, sortPrinters} from './cups_printer_dialog_util.js';
 import {PrinterListEntry} from './cups_printer_types.js';
 import {CupsPrinterInfo, CupsPrintersBrowserProxy, CupsPrintersBrowserProxyImpl} from './cups_printers_browser_proxy.js';
-import {CupsPrintersEntryListBehavior} from './cups_printers_entry_list_behavior.js';
+import {CupsPrintersEntryListBehavior, CupsPrintersEntryListBehaviorInterface} from './cups_printers_entry_list_behavior.js';
 
 // If the Show more button is visible, the minimum number of printers we show
 // is 3.
@@ -35,109 +40,131 @@
 }
 
 /**
- * @fileoverview 'settings-cups-enterprise-printers' is a list container for
- * Enterprise Printers.
+ * @constructor
+ * @extends {PolymerElement}
+ * @implements {CupsPrintersEntryListBehaviorInterface}
+ * @implements {ListPropertyUpdateBehaviorInterface}
+ * @implements {WebUIListenerBehaviorInterface}
  */
-Polymer({
-  _template: html`{__html_template__}`,
-  is: 'settings-cups-enterprise-printers',
+const SettingsCupsEnterprisePrintersElementBase = mixinBehaviors(
+    [
+      CupsPrintersEntryListBehavior, ListPropertyUpdateBehavior,
+      WebUIListenerBehavior
+    ],
+    PolymerElement);
 
-  // ListPropertyUpdateBehavior is used in CupsPrintersEntryListBehavior.
-  behaviors: [
-    CupsPrintersEntryListBehavior,
-    ListPropertyUpdateBehavior,
-    WebUIListenerBehavior,
-  ],
+/** @polymer */
+class SettingsCupsEnterprisePrintersElement extends
+    SettingsCupsEnterprisePrintersElementBase {
+  static get is() {
+    return 'settings-cups-enterprise-printers';
+  }
 
-  properties: {
-    /**
-     * Search term for filtering |enterprisePrinters|.
-     * @type {string}
-     */
-    searchTerm: {
-      type: String,
-      value: '',
-    },
+  static get template() {
+    return html`{__html_template__}`;
+  }
 
-    /** @type {?CupsPrinterInfo} */
-    activePrinter: {
-      type: Object,
-      notify: true,
-    },
+  static get properties() {
+    return {
+      /**
+       * Search term for filtering |enterprisePrinters|.
+       * @type {string}
+       */
+      searchTerm: {
+        type: String,
+        value: '',
+      },
 
-    /**
-     * @type {number}
-     * @private
-     */
-    activePrinterListEntryIndex_: {
-      type: Number,
-      value: -1,
-    },
+      /** @type {?CupsPrinterInfo} */
+      activePrinter: {
+        type: Object,
+        notify: true,
+      },
 
-    printersCount: {
-      type: Number,
-      computed: 'getFilteredPrintersLength_(filteredPrinters_.*)',
-      notify: true,
-    },
+      /**
+       * @type {number}
+       * @private
+       */
+      activePrinterListEntryIndex_: {
+        type: Number,
+        value: -1,
+      },
 
-    /**
-     * List of printers filtered through a search term.
-     * @type {!Array<!PrinterListEntry>}
-     * @private
-     */
-    filteredPrinters_: {
-      type: Array,
-      value: () => [],
-    },
+      printersCount: {
+        type: Number,
+        computed: 'getFilteredPrintersLength_(filteredPrinters_.*)',
+        notify: true,
+      },
 
-    /**
-     * Keeps track of whether the user has tapped the Show more button. A search
-     * term will expand the collapsed list, so we need to keep track of whether
-     * the list expanded because of a search term or because the user tapped on
-     * the Show more button.
-     * @private
-     */
-    hasShowMoreBeenTapped_: {
-      type: Boolean,
-      value: false,
-    },
+      /**
+       * List of printers filtered through a search term.
+       * @type {!Array<!PrinterListEntry>}
+       * @private
+       */
+      filteredPrinters_: {
+        type: Array,
+        value: () => [],
+      },
 
-    /**
-     * Used by FocusRowBehavior to track the last focused element on a row.
-     * @private
-     */
-    lastFocused_: Object,
+      /**
+       * Keeps track of whether the user has tapped the Show more button. A
+       * search term will expand the collapsed list, so we need to keep track of
+       * whether the list expanded because of a search term or because the user
+       * tapped on the Show more button.
+       * @private
+       */
+      hasShowMoreBeenTapped_: {
+        type: Boolean,
+        value: false,
+      },
 
-    /**
-     * Used by FocusRowBehavior to track if the list has been blurred.
-     * @private
-     */
-    listBlurred_: Boolean,
-  },
+      /**
+       * Used by FocusRowBehavior to track the last focused element on a row.
+       * @private
+       */
+      lastFocused_: Object,
 
-  listeners: {
-    'open-action-menu': 'onOpenActionMenu_',
-  },
+      /**
+       * Used by FocusRowBehavior to track if the list has been blurred.
+       * @private
+       */
+      listBlurred_: Boolean,
+    };
+  }
 
-  observers: [
-    'onSearchOrPrintersChanged_(enterprisePrinters.*, searchTerm, ' +
-        'hasShowMoreBeenTapped_)',
-  ],
-
-  /** @private {CupsPrintersBrowserProxy} */
-  browserProxy_: null,
-
-  /**
-   * The number of printers we display if hidden printers are allowed.
-   * kMinVisiblePrinters is the default value and we never show fewer printers
-   * if the Show more button is visible.
-   */
-  visiblePrinterCounter_: kMinVisiblePrinters,
+  static get observers() {
+    return [
+      'onSearchOrPrintersChanged_(enterprisePrinters.*, searchTerm, ' +
+          'hasShowMoreBeenTapped_)',
+    ];
+  }
 
   /** @override */
-  created() {
+  constructor() {
+    super();
+
+    /** @private {!CupsPrintersBrowserProxy} */
     this.browserProxy_ = CupsPrintersBrowserProxyImpl.getInstance();
-  },
+
+    /**
+     * The number of printers we display if hidden printers are allowed.
+     * kMinVisiblePrinters is the default value and we never show fewer printers
+     * if the Show more button is visible.
+     */
+    this.visiblePrinterCounter_ = kMinVisiblePrinters;
+  }
+
+  ready() {
+    super.ready();
+    this.addEventListener('open-action-menu', (event) => {
+      this.onOpenActionMenu_(
+          /**
+             @type {!CustomEvent<{target: !HTMLElement, item:
+                 !PrinterListEntry}>}
+           */
+          (event));
+    });
+  }
 
   /**
    * Redoes the search whenever |searchTerm| or |enterprisePrinters| changes.
@@ -165,12 +192,12 @@
     this.updateList(
         'filteredPrinters_', printer => printer.printerInfo.printerId,
         updatedPrinters);
-  },
+  }
 
   /** @private */
   onShowMoreTap_() {
     this.hasShowMoreBeenTapped_ = true;
-  },
+  }
 
   /**
    * Keeps track of whether the Show more button should be visible which means
@@ -198,7 +225,7 @@
     }
 
     return true;
-  },
+  }
 
   /**
    * @return {boolean} Returns true if the no search message should be visible.
@@ -206,7 +233,7 @@
    */
   showNoSearchResultsMessage_() {
     return !!this.searchTerm && !this.filteredPrinters_.length;
-  },
+  }
 
   /**
    * @return {number} Length of |filteredPrinters_|.
@@ -214,7 +241,7 @@
    */
   getFilteredPrintersLength_() {
     return this.filteredPrinters_.length;
-  },
+  }
 
   /**
    * @param {!CustomEvent<{target: !HTMLElement, item: !PrinterListEntry}>} e
@@ -230,18 +257,24 @@
             .printerInfo;
 
     const target = /** @type {!HTMLElement} */ (e.detail.target);
-    this.$$('cr-action-menu').showAt(target);
-  },
+    this.shadowRoot.querySelector('cr-action-menu').showAt(target);
+  }
 
   /** @private */
   onViewTap_() {
     // Event is caught by 'settings-cups-printers'.
-    this.fire('edit-cups-printer-details');
+    const editCupsPrinterDetailsEvent = new CustomEvent(
+        'edit-cups-printer-details', {bubbles: true, composed: true});
+    this.dispatchEvent(editCupsPrinterDetailsEvent);
     this.closeActionMenu_();
-  },
+  }
 
   /** @private */
   closeActionMenu_() {
-    this.$$('cr-action-menu').close();
-  },
-});
+    this.shadowRoot.querySelector('cr-action-menu').close();
+  }
+}
+
+customElements.define(
+    SettingsCupsEnterprisePrintersElement.is,
+    SettingsCupsEnterprisePrintersElement);
diff --git a/chrome/browser/resources/settings/chromeos/os_printing_page/cups_nearby_printers.js b/chrome/browser/resources/settings/chromeos/os_printing_page/cups_nearby_printers.js
index 21db7fb..b38f991 100644
--- a/chrome/browser/resources/settings/chromeos/os_printing_page/cups_nearby_printers.js
+++ b/chrome/browser/resources/settings/chromeos/os_printing_page/cups_nearby_printers.js
@@ -6,112 +6,142 @@
  * @fileoverview 'settings-cups-nearby-printers' is a list container for
  * Nearby Printers.
  */
-import '//resources/polymer/v3_0/iron-list/iron-list.js';
+import 'chrome://resources/polymer/v3_0/iron-list/iron-list.js';
 import './cups_printers_entry.js';
 import '../../settings_shared_css.js';
 
-import {ListPropertyUpdateBehavior} from '//resources/js/list_property_update_behavior.m.js';
-import {WebUIListenerBehavior} from '//resources/js/web_ui_listener_behavior.m.js';
-import {afterNextRender, flush, html, Polymer, TemplateInstanceBase, Templatizer} from '//resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+import {ListPropertyUpdateBehavior, ListPropertyUpdateBehaviorInterface} from 'chrome://resources/js/list_property_update_behavior.m.js';
+import {WebUIListenerBehavior, WebUIListenerBehaviorInterface} from 'chrome://resources/js/web_ui_listener_behavior.m.js';
+import {html, mixinBehaviors, PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 
 import {recordSettingChange} from '../metrics_recorder.js';
 
-import {getBaseName, getErrorText, getPrintServerErrorText, isNameAndAddressValid, isNetworkProtocol, isPPDInfoValid, matchesSearchTerm, sortPrinters} from './cups_printer_dialog_util.js';
-import {PrinterListEntry, PrinterType} from './cups_printer_types.js';
-import {CupsPrinterInfo, CupsPrintersBrowserProxy, CupsPrintersBrowserProxyImpl, CupsPrintersList, ManufacturersInfo, ModelsInfo, PrinterMakeModel, PrinterPpdMakeModel, PrinterSetupResult, PrintServerResult} from './cups_printers_browser_proxy.js';
-import {CupsPrintersEntryListBehavior} from './cups_printers_entry_list_behavior.js';
-import {CupsPrintersEntryManager} from './cups_printers_entry_manager.js';
+import {matchesSearchTerm, sortPrinters} from './cups_printer_dialog_util.js';
+import {PrinterListEntry} from './cups_printer_types.js';
+import {CupsPrinterInfo, CupsPrintersBrowserProxy, CupsPrintersBrowserProxyImpl, PrinterSetupResult} from './cups_printers_browser_proxy.js';
+import {CupsPrintersEntryListBehavior, CupsPrintersEntryListBehaviorInterface} from './cups_printers_entry_list_behavior.js';
 
-Polymer({
-  _template: html`{__html_template__}`,
-  is: 'settings-cups-nearby-printers',
+/**
+ * @constructor
+ * @extends {PolymerElement}
+ * @implements {CupsPrintersEntryListBehaviorInterface}
+ * @implements {ListPropertyUpdateBehaviorInterface}
+ * @implements {WebUIListenerBehaviorInterface}
+ */
+const SettingsCupsNearbyPrintersElementBase = mixinBehaviors(
+    [
+      CupsPrintersEntryListBehavior, ListPropertyUpdateBehavior,
+      WebUIListenerBehavior
+    ],
+    PolymerElement);
 
-  // ListPropertyUpdateBehavior is used in CupsPrintersEntryListBehavior.
-  behaviors: [
-    CupsPrintersEntryListBehavior,
-    ListPropertyUpdateBehavior,
-    WebUIListenerBehavior,
-  ],
+/** @polymer */
+class SettingsCupsNearbyPrintersElement extends
+    SettingsCupsNearbyPrintersElementBase {
+  static get is() {
+    return 'settings-cups-nearby-printers';
+  }
 
-  properties: {
-    /**
-     * Search term for filtering |nearbyPrinters|.
-     * @type {string}
-     */
-    searchTerm: {
-      type: String,
-      value: '',
-    },
+  static get template() {
+    return html`{__html_template__}`;
+  }
 
-    /**
-     * This value is set to true if UserPrintersAllowed policy is enabled.
-     */
-    userPrintersAllowed: {
-      type: Boolean,
-      value: false,
-    },
+  static get properties() {
+    return {
+      /**
+       * Search term for filtering |nearbyPrinters|.
+       * @type {string}
+       */
+      searchTerm: {
+        type: String,
+        value: '',
+      },
 
-    /** @type {?CupsPrinterInfo} */
-    activePrinter: {
-      type: Object,
-      notify: true,
-    },
+      /**
+       * This value is set to true if UserPrintersAllowed policy is enabled.
+       */
+      userPrintersAllowed: {
+        type: Boolean,
+        value: false,
+      },
 
-    printersCount: {
-      type: Number,
-      computed: 'getFilteredPrintersLength_(filteredPrinters_.*)',
-      notify: true,
-    },
+      /** @type {?CupsPrinterInfo} */
+      activePrinter: {
+        type: Object,
+        notify: true,
+      },
 
-    /**
-     * @type {number}
-     * @private
-     */
-    activePrinterListEntryIndex_: {
-      type: Number,
-      value: -1,
-    },
+      printersCount: {
+        type: Number,
+        computed: 'getFilteredPrintersLength_(filteredPrinters_.*)',
+        notify: true,
+      },
 
-    /**
-     * List of printers filtered through a search term.
-     * @type {!Array<!PrinterListEntry>}
-     * @private
-     */
-    filteredPrinters_: {
-      type: Array,
-      value: () => [],
-    },
+      /**
+       * @type {number}
+       * @private
+       */
+      activePrinterListEntryIndex_: {
+        type: Number,
+        value: -1,
+      },
 
-    /**
-     * Used by FocusRowBehavior to track the last focused element on a row.
-     * @private
-     */
-    lastFocused_: Object,
+      /**
+       * List of printers filtered through a search term.
+       * @type {!Array<!PrinterListEntry>}
+       * @private
+       */
+      filteredPrinters_: {
+        type: Array,
+        value: () => [],
+      },
 
-    /**
-     * Used by FocusRowBehavior to track if the list has been blurred.
-     * @private
-     */
-    listBlurred_: Boolean,
+      /**
+       * Used by FocusRowBehavior to track the last focused element on a row.
+       * @private
+       */
+      lastFocused_: Object,
 
-    /**
-     * This is set to true while waiting for a response during a printer setup.
-     * @type {boolean}
-     * @private
-     */
-    savingPrinter_: {
-      type: Boolean,
-      value: false,
-    },
-  },
+      /**
+       * Used by FocusRowBehavior to track if the list has been blurred.
+       * @private
+       */
+      listBlurred_: Boolean,
 
-  listeners: {
-    'add-automatic-printer': 'onAddAutomaticPrinter_',
-    'add-print-server-printer': 'onAddPrintServerPrinter_',
-    'query-discovered-printer': 'onQueryDiscoveredPrinter_',
-  },
+      /**
+       * This is set to true while waiting for a response during a printer
+       * setup.
+       * @type {boolean}
+       * @private
+       */
+      savingPrinter_: {
+        type: Boolean,
+        value: false,
+      },
+    };
+  }
 
-  observers: ['onSearchOrPrintersChanged_(nearbyPrinters.*, searchTerm)'],
+  static get observers() {
+    return ['onSearchOrPrintersChanged_(nearbyPrinters.*, searchTerm)'];
+  }
+
+  ready() {
+    super.ready();
+    this.addEventListener('add-automatic-printer', (event) => {
+      this.onAddAutomaticPrinter_(
+          /** @type {!CustomEvent<{item: !PrinterListEntry}>} */ (event));
+    });
+
+    this.addEventListener('add-print-server-printer', (event) => {
+      this.onAddPrintServerPrinter_(
+          /** @type {!CustomEvent<{item: !PrinterListEntry}>} */ (event));
+    });
+
+    this.addEventListener('query-discovered-printer', (event) => {
+      this.onQueryDiscoveredPrinter_(
+          /** @type {!CustomEvent<{item: !PrinterListEntry}>} */ (event));
+    });
+  }
 
   /**
    * Redoes the search whenever |searchTerm| or |nearbyPrinters| changes.
@@ -133,7 +163,7 @@
     this.updateList(
         'filteredPrinters_', printer => printer.printerInfo.printerId,
         updatedPrinters);
-  },
+  }
 
   /**
    * @param {!CustomEvent<{item: !PrinterListEntry}>} e
@@ -151,7 +181,7 @@
                 this, item.printerInfo.printerName),
             this.onAddNearbyPrinterFailed_.bind(this));
     recordSettingChange();
-  },
+  }
 
   /**
    * @param {!CustomEvent<{item: !PrinterListEntry}>} e
@@ -168,7 +198,7 @@
             this.onAddNearbyPrintersSucceeded_.bind(
                 this, item.printerInfo.printerName),
             this.onAddNearbyPrinterFailed_.bind(this));
-  },
+  }
 
   /**
    * @param {!CustomEvent<{item: !PrinterListEntry}>} e
@@ -191,7 +221,7 @@
                 this, item.printerInfo.printerName),
             queryDiscoveredPrinterFailed);
     recordSettingChange();
-  },
+  }
 
   /**
    * Retrieves the index of |item| in |nearbyPrinters_| and sets that printer as
@@ -207,7 +237,24 @@
     this.activePrinter =
         this.get(['nearbyPrinters', this.activePrinterListEntryIndex_])
             .printerInfo;
-  },
+  }
+
+  /**
+   * @param {!PrinterSetupResult} resultCode
+   * @param {string} printerName
+   * @private
+   */
+  showCupsPrinterToast_(resultCode, printerName) {
+    const event = new CustomEvent('show-cups-printer-toast', {
+      bubbles: true,
+      composed: true,
+      detail: {
+        resultCode,
+        printerName,
+      }
+    });
+    this.dispatchEvent(event);
+  }
 
   /**
    * Handler for addDiscoveredPrinter success.
@@ -217,10 +264,8 @@
    */
   onAddNearbyPrintersSucceeded_(printerName, result) {
     this.savingPrinter_ = false;
-    this.fire(
-        'show-cups-printer-toast',
-        {resultCode: result, printerName: printerName});
-  },
+    this.showCupsPrinterToast_(result, printerName);
+  }
 
   /**
    * Handler for addDiscoveredPrinter failure.
@@ -229,11 +274,9 @@
    */
   onAddNearbyPrinterFailed_(printer) {
     this.savingPrinter_ = false;
-    this.fire('show-cups-printer-toast', {
-      resultCode: PrinterSetupResult.PRINTER_UNREACHABLE,
-      printerName: printer.printerName
-    });
-  },
+    this.showCupsPrinterToast_(
+        PrinterSetupResult.PRINTER_UNREACHABLE, printer.printerName);
+  }
 
   /**
    * Handler for queryDiscoveredPrinter success.
@@ -243,10 +286,8 @@
    */
   onQueryDiscoveredPrinterSucceeded_(printerName, result) {
     this.savingPrinter_ = false;
-    this.fire(
-        'show-cups-printer-toast',
-        {resultCode: result, printerName: printerName});
-  },
+    this.showCupsPrinterToast_(result, printerName);
+  }
 
   /**
    * Handler for queryDiscoveredPrinter failure.
@@ -255,10 +296,14 @@
    */
   onQueryDiscoveredPrinterFailed_(printer) {
     this.savingPrinter_ = false;
-    this.fire(
-        'open-manufacturer-model-dialog-for-specified-printer',
-        {item: /** @type {CupsPrinterInfo} */ (printer)});
-  },
+    const openManufacturerDialogEvent = new CustomEvent(
+        'open-manufacturer-model-dialog-for-specified-printer', {
+          bubbles: true,
+          composed: true,
+          detail: {item: /** @type {CupsPrinterInfo} */ (printer)}
+        });
+    this.dispatchEvent(openManufacturerDialogEvent);
+  }
 
   /**
    * @return {boolean} Returns true if the no search message should be visible.
@@ -266,7 +311,7 @@
    */
   showNoSearchResultsMessage_() {
     return !!this.searchTerm && !this.filteredPrinters_.length;
-  },
+  }
 
   /**
    * @private
@@ -274,5 +319,8 @@
    */
   getFilteredPrintersLength_() {
     return this.filteredPrinters_.length;
-  },
-});
+  }
+}
+
+customElements.define(
+    SettingsCupsNearbyPrintersElement.is, SettingsCupsNearbyPrintersElement);
diff --git a/chrome/browser/resources/settings/chromeos/os_printing_page/cups_printer_dialog_error.js b/chrome/browser/resources/settings/chromeos/os_printing_page/cups_printer_dialog_error.js
index 9f51e80c..ef3f1fe 100644
--- a/chrome/browser/resources/settings/chromeos/os_printing_page/cups_printer_dialog_error.js
+++ b/chrome/browser/resources/settings/chromeos/os_printing_page/cups_printer_dialog_error.js
@@ -5,23 +5,30 @@
 /**
  * 'printer-dialog-error' is the error container for dialogs.
  */
-import '//resources/polymer/v3_0/iron-icon/iron-icon.js';
+import 'chrome://resources/polymer/v3_0/iron-icon/iron-icon.js';
 import './cups_printer_shared_css.js';
 
-import {afterNextRender, flush, html, Polymer, TemplateInstanceBase, Templatizer} from '//resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+import {html, PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 
-import {getBaseName, getErrorText, getPrintServerErrorText, isNameAndAddressValid, isNetworkProtocol, isPPDInfoValid, matchesSearchTerm, sortPrinters} from './cups_printer_dialog_util.js';
-import {CupsPrinterInfo, CupsPrintersBrowserProxy, CupsPrintersBrowserProxyImpl, CupsPrintersList, ManufacturersInfo, ModelsInfo, PrinterMakeModel, PrinterPpdMakeModel, PrinterSetupResult, PrintServerResult} from './cups_printers_browser_proxy.js';
+/** @polymer */
+class PrinterDialogErrorElement extends PolymerElement {
+  static get is() {
+    return 'printer-dialog-error';
+  }
 
-Polymer({
-  _template: html`{__html_template__}`,
-  is: 'printer-dialog-error',
+  static get template() {
+    return html`{__html_template__}`;
+  }
 
-  properties: {
-    /** The error text to be displayed on the dialog. */
-    errorText: {
-      type: String,
-      value: '',
-    },
-  },
-});
+  static get properties() {
+    return {
+      /** The error text to be displayed on the dialog. */
+      errorText: {
+        type: String,
+        value: '',
+      },
+    };
+  }
+}
+
+customElements.define(PrinterDialogErrorElement.is, PrinterDialogErrorElement);
diff --git a/chrome/browser/resources/settings/chromeos/os_printing_page/cups_printer_dialog_util.js b/chrome/browser/resources/settings/chromeos/os_printing_page/cups_printer_dialog_util.js
index a77b7bd2..55365a3 100644
--- a/chrome/browser/resources/settings/chromeos/os_printing_page/cups_printer_dialog_util.js
+++ b/chrome/browser/resources/settings/chromeos/os_printing_page/cups_printer_dialog_util.js
@@ -12,24 +12,24 @@
  * @fileoverview  Utility functions that are used in Cups printer setup dialogs.
  */
 
-  /**
-   * @param {string} protocol
-   * @return {boolean} Whether |protocol| is a network protocol
-   */
+/**
+ * @param {string} protocol
+ * @return {boolean} Whether |protocol| is a network protocol
+ */
 export function isNetworkProtocol(protocol) {
   return ['ipp', 'ipps', 'http', 'https', 'socket', 'lpd'].includes(protocol);
 }
 
-  /**
-   * Returns true if the printer's name and address is valid. This function
-   * uses regular expressions to determine whether the provided printer name
-   * and address are valid. Address can be either an ipv4/6 address or a
-   * hostname followed by an optional port.
-   * NOTE: The regular expression for hostnames will allow hostnames that are
-   * over 255 characters.
-   * @param {CupsPrinterInfo} printer
-   * @return {boolean}
-   */
+/**
+ * Returns true if the printer's name and address is valid. This function
+ * uses regular expressions to determine whether the provided printer name
+ * and address are valid. Address can be either an ipv4/6 address or a
+ * hostname followed by an optional port.
+ * NOTE: The regular expression for hostnames will allow hostnames that are
+ * over 255 characters.
+ * @param {CupsPrinterInfo} printer
+ * @return {boolean}
+ */
 export function isNameAndAddressValid(printer) {
   if (!printer) {
     return false;
@@ -78,22 +78,22 @@
       (ipv6AddressRegex.test(address) && !invalidIpv6Regex.test(address));
 }
 
-  /**
-   * Returns true if the printer's manufacturer and model or ppd path is valid.
-   * @param {string} manufacturer
-   * @param {string} model
-   * @param {string} ppdPath
-   * @return {boolean}
-   */
+/**
+ * Returns true if the printer's manufacturer and model or ppd path is valid.
+ * @param {string} manufacturer
+ * @param {string} model
+ * @param {string} ppdPath
+ * @return {boolean}
+ */
 export function isPPDInfoValid(manufacturer, model, ppdPath) {
   return !!((manufacturer && model) || ppdPath);
 }
 
-  /**
-   * Returns the base name of a filepath.
-   * @param {string} path The full path of the file
-   * @return {string} The base name of the file
-   */
+/**
+ * Returns the base name of a filepath.
+ * @param {string} path The full path of the file
+ * @return {string} The base name of the file
+ */
 export function getBaseName(path) {
   if (path && path.length > 0) {
     return path.substring(path.lastIndexOf('/') + 1);
@@ -101,110 +101,110 @@
   return '';
 }
 
-  /**
-   * A function used for sorting printer names based on the current locale's
-   * collation order.
-   * @param {!CupsPrinterInfo} first
-   * @param {!CupsPrinterInfo} second
-   * @return {number} The result of the comparison.
-   */
-  function alphabeticalSort(first, second) {
-    return first.printerName.toLocaleLowerCase().localeCompare(
-        second.printerName.toLocaleLowerCase());
+/**
+ * A function used for sorting printer names based on the current locale's
+ * collation order.
+ * @param {!CupsPrinterInfo} first
+ * @param {!CupsPrinterInfo} second
+ * @return {number} The result of the comparison.
+ */
+function alphabeticalSort(first, second) {
+  return first.printerName.toLocaleLowerCase().localeCompare(
+      second.printerName.toLocaleLowerCase());
+}
+
+/**
+ * Return the error string corresponding to the result code.
+ * @param {!PrinterSetupResult} result
+ * @return {string}
+ */
+export function getErrorText(result) {
+  switch (result) {
+    case PrinterSetupResult.FATAL_ERROR:
+      return loadTimeData.getString('printerAddedFatalErrorMessage');
+    case PrinterSetupResult.PRINTER_UNREACHABLE:
+      return loadTimeData.getString('printerAddedUnreachableMessage');
+    case PrinterSetupResult.DBUS_ERROR:
+      // Simply return a generic error message as this error should only
+      // occur when a call to Dbus fails which isn't meaningful to the user.
+      return loadTimeData.getString('printerAddedFailedMessage');
+    case PrinterSetupResult.NATIVE_PRINTERS_NOT_ALLOWED:
+      return loadTimeData.getString(
+          'printerAddedNativePrintersNotAllowedMessage');
+    case PrinterSetupResult.INVALID_PRINTER_UPDATE:
+      return loadTimeData.getString('editPrinterInvalidPrinterUpdate');
+    case PrinterSetupResult.PPD_TOO_LARGE:
+      return loadTimeData.getString('printerAddedPpdTooLargeMessage');
+    case PrinterSetupResult.INVALID_PPD:
+      return loadTimeData.getString('printerAddedInvalidPpdMessage');
+    case PrinterSetupResult.PPD_NOT_FOUND:
+      return loadTimeData.getString('printerAddedPpdNotFoundMessage');
+    case PrinterSetupResult.PPD_UNRETRIEVABLE:
+      return loadTimeData.getString('printerAddedPpdUnretrievableMessage');
+    default:
+      assertNotReached();
+  }
+}
+
+/**
+ * Return the error string corresponding to the result code for print servers.
+ * @param {!PrintServerResult} result
+ * @return {string}
+ */
+export function getPrintServerErrorText(result) {
+  switch (result) {
+    case PrintServerResult.CONNECTION_ERROR:
+      return loadTimeData.getString('printServerConnectionError');
+    case PrintServerResult.CANNOT_PARSE_IPP_RESPONSE:
+    case PrintServerResult.HTTP_ERROR:
+      return loadTimeData.getString('printServerConfigurationErrorMessage');
+    default:
+      assertNotReached();
+  }
+}
+
+/**
+ * We sort by printer type, which is based off of a maintained list in
+ * cups_printers_types.js. If the types are the same, we sort alphabetically.
+ * @param {!PrinterListEntry} first
+ * @param {!PrinterListEntry} second
+ * @return {number}
+ */
+export function sortPrinters(first, second) {
+  if (first.printerType === second.printerType) {
+    return alphabeticalSort(first.printerInfo, second.printerInfo);
   }
 
-  /**
-   * Return the error string corresponding to the result code.
-   * @param {!PrinterSetupResult} result
-   * @return {string}
-   */
-  export function getErrorText(result) {
-    switch (result) {
-      case PrinterSetupResult.FATAL_ERROR:
-        return loadTimeData.getString('printerAddedFatalErrorMessage');
-      case PrinterSetupResult.PRINTER_UNREACHABLE:
-        return loadTimeData.getString('printerAddedUnreachableMessage');
-      case PrinterSetupResult.DBUS_ERROR:
-        // Simply return a generic error message as this error should only
-        // occur when a call to Dbus fails which isn't meaningful to the user.
-        return loadTimeData.getString('printerAddedFailedMessage');
-      case PrinterSetupResult.NATIVE_PRINTERS_NOT_ALLOWED:
-        return loadTimeData.getString(
-            'printerAddedNativePrintersNotAllowedMessage');
-      case PrinterSetupResult.INVALID_PRINTER_UPDATE:
-        return loadTimeData.getString('editPrinterInvalidPrinterUpdate');
-      case PrinterSetupResult.PPD_TOO_LARGE:
-        return loadTimeData.getString('printerAddedPpdTooLargeMessage');
-      case PrinterSetupResult.INVALID_PPD:
-        return loadTimeData.getString('printerAddedInvalidPpdMessage');
-      case PrinterSetupResult.PPD_NOT_FOUND:
-        return loadTimeData.getString('printerAddedPpdNotFoundMessage');
-      case PrinterSetupResult.PPD_UNRETRIEVABLE:
-        return loadTimeData.getString('printerAddedPpdUnretrievableMessage');
-      default:
-        assertNotReached();
-    }
-  }
+  return first.printerType - second.printerType;
+}
 
-  /**
-   * Return the error string corresponding to the result code for print servers.
-   * @param {!PrintServerResult} result
-   * @return {string}
-   */
-  export function getPrintServerErrorText(result) {
-    switch (result) {
-      case PrintServerResult.CONNECTION_ERROR:
-        return loadTimeData.getString('printServerConnectionError');
-      case PrintServerResult.CANNOT_PARSE_IPP_RESPONSE:
-      case PrintServerResult.HTTP_ERROR:
-        return loadTimeData.getString('printServerConfigurationErrorMessage');
-      default:
-        assertNotReached();
-    }
-  }
+/**
+ * @param {!CupsPrinterInfo} printer
+ * @param {string} searchTerm
+ * @return {boolean} True if the printer has |searchTerm| in its name.
+ */
+export function matchesSearchTerm(printer, searchTerm) {
+  return printer.printerName.toLowerCase().includes(searchTerm.toLowerCase());
+}
 
-  /**
-   * We sort by printer type, which is based off of a maintained list in
-   * cups_printers_types.js. If the types are the same, we sort alphabetically.
-   * @param {!PrinterListEntry} first
-   * @param {!PrinterListEntry} second
-   * @return {number}
-   */
-  export function sortPrinters(first, second) {
-    if (first.printerType === second.printerType) {
-      return alphabeticalSort(first.printerInfo, second.printerInfo);
-    }
+/**
+ * @param {!PrinterListEntry} first
+ * @param {!PrinterListEntry} second
+ * @return {boolean}
+ */
+function arePrinterIdsEqual(first, second) {
+  return first.printerInfo.printerId === second.printerInfo.printerId;
+}
 
-    return first.printerType - second.printerType;
-  }
-
-  /**
-   * @param {!CupsPrinterInfo} printer
-   * @param {string} searchTerm
-   * @return {boolean} True if the printer has |searchTerm| in its name.
-   */
-  export function matchesSearchTerm(printer, searchTerm) {
-    return printer.printerName.toLowerCase().includes(searchTerm.toLowerCase());
-  }
-
-  /**
-   * @param {!PrinterListEntry} first
-   * @param {!PrinterListEntry} second
-   * @return {boolean}
-   */
-  function arePrinterIdsEqual(first, second) {
-    return first.printerInfo.printerId === second.printerInfo.printerId;
-  }
-
-  /**
-   * Finds the printers that are in |firstArr| but not in |secondArr|.
-   * @param {!Array<!PrinterListEntry>} firstArr
-   * @param {!Array<!PrinterListEntry>} secondArr
-   * @return {!Array<!PrinterListEntry>}
-   */
-  export function findDifference(firstArr, secondArr) {
-    return firstArr.filter(p1 => {
-      return !secondArr.some(
-          p2 => p2.printerInfo.printerId === p1.printerInfo.printerId);
-    });
-  }
+/**
+ * Finds the printers that are in |firstArr| but not in |secondArr|.
+ * @param {!Array<!PrinterListEntry>} firstArr
+ * @param {!Array<!PrinterListEntry>} secondArr
+ * @return {!Array<!PrinterListEntry>}
+ */
+export function findDifference(firstArr, secondArr) {
+  return firstArr.filter(p1 => {
+    return !secondArr.some(
+        p2 => p2.printerInfo.printerId === p1.printerInfo.printerId);
+  });
+}
diff --git a/chrome/browser/resources/settings/chromeos/os_printing_page/cups_printer_shared_css.js b/chrome/browser/resources/settings/chromeos/os_printing_page/cups_printer_shared_css.js
index 6a37f68f..53e890a 100644
--- a/chrome/browser/resources/settings/chromeos/os_printing_page/cups_printer_shared_css.js
+++ b/chrome/browser/resources/settings/chromeos/os_printing_page/cups_printer_shared_css.js
@@ -2,13 +2,13 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import '//resources/cr_elements/md_select_css.m.js';
-import '//resources/cr_elements/shared_vars_css.m.js';
-import '//resources/cr_elements/shared_style_css.m.js';
+import 'chrome://resources/cr_elements/md_select_css.m.js';
+import 'chrome://resources/cr_elements/shared_vars_css.m.js';
+import 'chrome://resources/cr_elements/shared_style_css.m.js';
 import '../../settings_shared_css.js';
 
 const template = document.createElement('template');
 template.innerHTML = `
 <dom-module id="cups-printer-shared">{__html_template__}</dom-module>
 `;
-document.body.appendChild(template.content.cloneNode(true));
\ No newline at end of file
+document.body.appendChild(template.content.cloneNode(true));
diff --git a/chrome/browser/resources/settings/chromeos/os_printing_page/cups_printers.js b/chrome/browser/resources/settings/chromeos/os_printing_page/cups_printers.js
index 47d7e75..43318004 100644
--- a/chrome/browser/resources/settings/chromeos/os_printing_page/cups_printers.js
+++ b/chrome/browser/resources/settings/chromeos/os_printing_page/cups_printers.js
@@ -8,193 +8,206 @@
  * set up legacy & non-CloudPrint printers on ChromeOS by leveraging CUPS (the
  * unix printing system) and the many open source drivers built for CUPS.
  */
+
 // TODO(xdai): Rename it to 'settings-cups-printers-page'.
-import '//resources/cr_elements/cr_button/cr_button.m.js';
-import '//resources/cr_elements/cr_icon_button/cr_icon_button.m.js';
-import '//resources/cr_elements/cr_toast/cr_toast.js';
-import '//resources/cr_elements/policy/cr_policy_pref_indicator.m.js';
-import '//resources/js/action_link.js';
-import '//resources/cr_elements/action_link_css.m.js';
-import '//resources/polymer/v3_0/iron-flex-layout/iron-flex-layout-classes.js';
-import '//resources/polymer/v3_0/iron-icon/iron-icon.js';
+import 'chrome://resources/cr_elements/cr_button/cr_button.m.js';
+import 'chrome://resources/cr_elements/cr_icon_button/cr_icon_button.m.js';
+import 'chrome://resources/cr_elements/cr_toast/cr_toast.js';
+import 'chrome://resources/cr_elements/policy/cr_policy_pref_indicator.m.js';
+import 'chrome://resources/js/action_link.js';
+import 'chrome://resources/cr_elements/action_link_css.m.js';
+import 'chrome://resources/polymer/v3_0/iron-flex-layout/iron-flex-layout-classes.js';
+import 'chrome://resources/polymer/v3_0/iron-icon/iron-icon.js';
 import './cups_settings_add_printer_dialog.js';
 import './cups_edit_printer_dialog.js';
 import './cups_enterprise_printers.js';
 import './cups_printer_shared_css.js';
 import './cups_saved_printers.js';
 import './cups_nearby_printers.js';
-import '//resources/cr_components/localized_link/localized_link.js';
+import 'chrome://resources/cr_components/localized_link/localized_link.js';
 import '../../icons.html.js';
 
-import {MojoInterfaceProvider, MojoInterfaceProviderImpl} from '//resources/cr_components/chromeos/network/mojo_interface_provider.m.js';
-import {NetworkListenerBehavior} from '//resources/cr_components/chromeos/network/network_listener_behavior.m.js';
-import {OncMojo} from '//resources/cr_components/chromeos/network/onc_mojo.m.js';
-import {assert, assertNotReached} from '//resources/js/assert.m.js';
-import {addWebUIListener, removeWebUIListener, sendWithPromise, WebUIListener} from '//resources/js/cr.m.js';
-import {focusWithoutInk} from '//resources/js/cr/ui/focus_without_ink.m.js';
-import {loadTimeData} from '//resources/js/load_time_data.m.js';
-import {WebUIListenerBehavior} from '//resources/js/web_ui_listener_behavior.m.js';
-import {afterNextRender, flush, html, Polymer, TemplateInstanceBase, Templatizer} from '//resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+import {MojoInterfaceProvider, MojoInterfaceProviderImpl} from 'chrome://resources/cr_components/chromeos/network/mojo_interface_provider.m.js';
+import {NetworkListenerBehavior, NetworkListenerBehaviorInterface} from 'chrome://resources/cr_components/chromeos/network/network_listener_behavior.m.js';
+import {assert, assertNotReached} from 'chrome://resources/js/assert.m.js';
+import {addWebUIListener, removeWebUIListener, sendWithPromise, WebUIListener} from 'chrome://resources/js/cr.m.js';
+import {focusWithoutInk} from 'chrome://resources/js/cr/ui/focus_without_ink.m.js';
+import {loadTimeData} from 'chrome://resources/js/load_time_data.m.js';
+import {WebUIListenerBehavior, WebUIListenerBehaviorInterface} from 'chrome://resources/js/web_ui_listener_behavior.m.js';
+import {afterNextRender, html, mixinBehaviors, PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 
-import {Route, Router} from '../../router.js';
-import {DeepLinkingBehavior} from '../deep_linking_behavior.js';
+import {Route} from '../../router.js';
+import {DeepLinkingBehavior, DeepLinkingBehaviorInterface} from '../deep_linking_behavior.js';
 import {routes} from '../os_route.js';
-import {RouteObserverBehavior} from '../route_observer_behavior.js';
+import {RouteObserverBehavior, RouteObserverBehaviorInterface} from '../route_observer_behavior.js';
 
 import {PrinterListEntry, PrinterType} from './cups_printer_types.js';
-import {CupsPrinterInfo, CupsPrintersBrowserProxy, CupsPrintersBrowserProxyImpl, CupsPrintersList, ManufacturersInfo, ModelsInfo, PrinterMakeModel, PrinterPpdMakeModel, PrinterSetupResult, PrintServerResult} from './cups_printers_browser_proxy.js';
+import {CupsPrinterInfo, CupsPrintersBrowserProxy, CupsPrintersBrowserProxyImpl, CupsPrintersList, PrinterSetupResult} from './cups_printers_browser_proxy.js';
 import {CupsPrintersEntryManager} from './cups_printers_entry_manager.js';
 
-Polymer({
-  _template: html`{__html_template__}`,
-  is: 'settings-cups-printers',
+/**
+ * @constructor
+ * @extends {PolymerElement}
+ * @implements {DeepLinkingBehaviorInterface}
+ * @implements {NetworkListenerBehaviorInterface}
+ * @implements {RouteObserverBehaviorInterface}
+ * @implements {WebUIListenerBehaviorInterface}
+ */
+const SettingsCupsPrintersElementBase = mixinBehaviors(
+    [
+      DeepLinkingBehavior, NetworkListenerBehavior, RouteObserverBehavior,
+      WebUIListenerBehavior
+    ],
+    PolymerElement);
 
-  behaviors: [
-    DeepLinkingBehavior,
-    NetworkListenerBehavior,
-    RouteObserverBehavior,
-    WebUIListenerBehavior,
-  ],
+/** @polymer */
+class SettingsCupsPrintersElement extends SettingsCupsPrintersElementBase {
+  static get is() {
+    return 'settings-cups-printers';
+  }
 
-  properties: {
-    /** @type {!Array<!CupsPrinterInfo>} */
-    printers: {
-      type: Array,
-      notify: true,
-    },
+  static get template() {
+    return html`{__html_template__}`;
+  }
 
-    prefs: Object,
+  static get properties() {
+    return {
+      /** @type {!Array<!CupsPrinterInfo>} */
+      printers: {
+        type: Array,
+        notify: true,
+      },
 
-    /** @type {?CupsPrinterInfo} */
-    activePrinter: {
-      type: Object,
-      notify: true,
-    },
+      prefs: Object,
 
-    /** @private {?WebUIListener} */
-    onPrintersChangedListener_: {
-      type: Object,
-      value: null,
-    },
+      /** @type {?CupsPrinterInfo} */
+      activePrinter: {
+        type: Object,
+        notify: true,
+      },
 
-    /** @private {?WebUIListener} */
-    onEnterprisePrintersChangedListener_: {
-      type: Object,
-      value: null,
-    },
+      /** @private {?WebUIListener} */
+      onPrintersChangedListener_: {
+        type: Object,
+        value: null,
+      },
 
-    searchTerm: {
-      type: String,
-    },
+      /** @private {?WebUIListener} */
+      onEnterprisePrintersChangedListener_: {
+        type: Object,
+        value: null,
+      },
 
-    /** This is also used as an attribute for css styling. */
-    canAddPrinter: {
-      type: Boolean,
-      reflectToAttribute: true,
-    },
+      searchTerm: {
+        type: String,
+      },
 
-    /**
-     * @type {!Array<!PrinterListEntry>}
-     * @private
-     */
-    savedPrinters_: {
-      type: Array,
-      value: () => [],
-    },
+      /** This is also used as an attribute for css styling. */
+      canAddPrinter: {
+        type: Boolean,
+        reflectToAttribute: true,
+      },
 
-    /**
-     * @type {!Array<!PrinterListEntry>}
-     * @private
-     */
-    enterprisePrinters_: {
-      type: Array,
-      value: () => [],
-    },
+      /**
+       * @type {!Array<!PrinterListEntry>}
+       * @private
+       */
+      savedPrinters_: {
+        type: Array,
+        value: () => [],
+      },
 
-    /** @private */
-    attemptedLoadingPrinters_: {
-      type: Boolean,
-      value: false,
-    },
+      /**
+       * @type {!Array<!PrinterListEntry>}
+       * @private
+       */
+      enterprisePrinters_: {
+        type: Array,
+        value: () => [],
+      },
 
-    /** @private */
-    showCupsEditPrinterDialog_: Boolean,
+      /** @private */
+      attemptedLoadingPrinters_: {
+        type: Boolean,
+        value: false,
+      },
 
-    /**@private */
-    addPrinterResultText_: String,
+      /** @private */
+      showCupsEditPrinterDialog_: Boolean,
 
-    /**@private */
-    nearbyPrintersAriaLabel_: {
-      type: String,
-      computed: 'getNearbyPrintersAriaLabel_(nearbyPrinterCount_)',
-    },
+      /**@private */
+      addPrinterResultText_: String,
 
-    /**@private */
-    savedPrintersAriaLabel_: {
-      type: String,
-      computed: 'getSavedPrintersAriaLabel_(savedPrinterCount_)',
-    },
+      /**@private */
+      nearbyPrintersAriaLabel_: {
+        type: String,
+        computed: 'getNearbyPrintersAriaLabel_(nearbyPrinterCount_)',
+      },
 
-    /**@private */
-    enterprisePrintersAriaLabel_: {
-      type: String,
-      computed: 'getEnterprisePrintersAriaLabel_(enterprisePrinterCount_)',
-    },
+      /**@private */
+      savedPrintersAriaLabel_: {
+        type: String,
+        computed: 'getSavedPrintersAriaLabel_(savedPrinterCount_)',
+      },
 
-    /**@private */
-    nearbyPrinterCount_: {
-      type: Number,
-      value: 0,
-    },
+      /**@private */
+      enterprisePrintersAriaLabel_: {
+        type: String,
+        computed: 'getEnterprisePrintersAriaLabel_(enterprisePrinterCount_)',
+      },
 
-    /**@private */
-    savedPrinterCount_: {
-      type: Number,
-      value: 0,
-    },
+      /**@private */
+      nearbyPrinterCount_: {
+        type: Number,
+        value: 0,
+      },
 
-    /** @private */
-    enterprisePrinterCount_: {
-      type: Number,
-      value: 0,
-    },
+      /**@private */
+      savedPrinterCount_: {
+        type: Number,
+        value: 0,
+      },
 
-    /**
-     * Used by DeepLinkingBehavior to focus this page's deep links.
-     * @type {!Set<!chromeos.settings.mojom.Setting>}
-     */
-    supportedSettingIds: {
-      type: Object,
-      value: () => new Set([
-        chromeos.settings.mojom.Setting.kAddPrinter,
-        chromeos.settings.mojom.Setting.kSavedPrinters,
-      ]),
-    },
-  },
+      /** @private */
+      enterprisePrinterCount_: {
+        type: Number,
+        value: 0,
+      },
 
-  listeners: {
-    'edit-cups-printer-details': 'onShowCupsEditPrinterDialog_',
-    'show-cups-printer-toast': 'openResultToast_',
-    'add-print-server-and-show-toast': 'addPrintServerAndShowResultToast_',
-    'open-manufacturer-model-dialog-for-specified-printer':
-        'openManufacturerModelDialogForSpecifiedPrinter_',
-  },
-
-  /** @private {?chromeos.networkConfig.mojom.CrosNetworkConfigRemote} */
-  networkConfig_: null,
-
-  /** @private {CupsPrintersEntryManager} */
-  entryManager_: null,
+      /**
+       * Used by DeepLinkingBehavior to focus this page's deep links.
+       * @type {!Set<!chromeos.settings.mojom.Setting>}
+       */
+      supportedSettingIds: {
+        type: Object,
+        value: () => new Set([
+          chromeos.settings.mojom.Setting.kAddPrinter,
+          chromeos.settings.mojom.Setting.kSavedPrinters,
+        ]),
+      },
+    };
+  }
 
   /** @override */
-  created() {
+  constructor() {
+    super();
+
+    /** @private {!chromeos.networkConfig.mojom.CrosNetworkConfigRemote} */
     this.networkConfig_ =
         MojoInterfaceProviderImpl.getInstance().getMojoServiceRemote();
+
+
+    /** @private {!CupsPrintersEntryManager} */
     this.entryManager_ = CupsPrintersEntryManager.getInstance();
-  },
+
+    /** @private */
+    this.addPrintServerResultText_ = '';
+  }
 
   /** @override */
-  attached() {
+  connectedCallback() {
+    super.connectedCallback();
+
     this.networkConfig_
         .getNetworkStateList({
           filter: chromeos.networkConfig.mojom.FilterType.kActive,
@@ -204,12 +217,36 @@
         .then((responseParams) => {
           this.onActiveNetworksChanged(responseParams.result);
         });
-  },
+  }
 
   /** @override */
   ready() {
+    super.ready();
+
     this.updateCupsPrintersList_();
-  },
+
+    this.addEventListener(
+        'edit-cups-printer-details', this.onShowCupsEditPrinterDialog_);
+    this.addEventListener('show-cups-printer-toast', (event) => {
+      this.openResultToast_(
+          /**
+           * @type {!CustomEvent<!{
+           *      resultCode: PrinterSetupResult,
+           *      printerName: string
+           * }>}
+           */
+          (event));
+    });
+    this.addEventListener('add-print-server-and-show-toast', (event) => {
+      this.addPrintServerAndShowResultToast_(
+          /** @type {!CustomEvent<!{printers: !CupsPrintersList}>} */ (event));
+    });
+    this.addEventListener(
+        'open-manufacturer-model-dialog-for-specified-printer', (event) => {
+          this.openManufacturerModelDialogForSpecifiedPrinter_(
+              /** @type {!CustomEvent<{item: !CupsPrinterInfo}>} */ (event));
+        });
+  }
 
   /**
    * Overridden from DeepLinkingBehavior.
@@ -224,7 +261,7 @@
     }
 
     afterNextRender(this, () => {
-      const savedPrinters = this.$$('#savedPrinters');
+      const savedPrinters = this.shadowRoot.querySelector('#savedPrinters');
       const printerEntry =
           savedPrinters && savedPrinters.$$('settings-cups-printers-entry');
       const deepLinkElement = printerEntry && printerEntry.$$('#moreActions');
@@ -236,7 +273,7 @@
     });
     // Stop deep link attempt since we completed it manually.
     return false;
-  },
+  }
 
   /**
    * RouteObserverBehavior
@@ -262,7 +299,7 @@
         this.onEnterprisePrintersChanged_.bind(this));
     this.updateCupsPrintersList_();
     this.attemptDeepLink();
-  },
+  }
 
   /**
    * CrosNetworkConfigObserver impl
@@ -271,14 +308,14 @@
    * @private
    */
   onActiveNetworksChanged(networks) {
-    this.canAddPrinter = networks.some(function(network) {
+    this.canAddPrinter = networks.some((network) => {
       // Note: Check for kOnline rather than using
       // OncMojo.connectionStateIsConnected() since the latter could return true
       // for networks without connectivity (e.g., captive portals).
       return network.connectionState ===
           chromeos.networkConfig.mojom.ConnectionStateType.kOnline;
     });
-  },
+  }
 
   /**
    * @param {!CustomEvent<!{
@@ -307,7 +344,7 @@
     }
 
     this.$.errorToast.show();
-  },
+  }
 
   /**
    * @param {!CustomEvent<!{
@@ -315,7 +352,7 @@
    * }>} event
    * @private
    */
-  addPrintServerAndShowResultToast_: function(event) {
+  addPrintServerAndShowResultToast_(event) {
     this.entryManager_.addPrintServerPrinters(event.detail.printers);
     const length = event.detail.printers.printerList.length;
     if (length === 0) {
@@ -329,7 +366,7 @@
           loadTimeData.getStringF('printServerFoundManyPrinters', length);
     }
     this.$.printServerErrorToast.show();
-  },
+  }
 
   /**
    * @param {!CustomEvent<{item: !CupsPrinterInfo}>} e
@@ -339,7 +376,7 @@
     const item = e.detail.item;
     this.$.addPrinterDialog.openManufacturerModelDialogForSpecifiedPrinter(
         item);
-  },
+  }
 
   /** @private */
   updateCupsPrintersList_() {
@@ -349,7 +386,7 @@
     CupsPrintersBrowserProxyImpl.getInstance()
         .getCupsEnterprisePrintersList()
         .then(this.onEnterprisePrintersChanged_.bind(this));
-  },
+  }
 
   /**
    * @param {!CupsPrintersList} cupsPrintersList
@@ -363,7 +400,7 @@
     // Used to delay rendering nearby and add printer sections to prevent
     // "Add Printer" flicker when clicking "Printers" in settings page.
     this.attemptedLoadingPrinters_ = true;
-  },
+  }
 
   /**
    * @param {!CupsPrintersList} cupsPrintersList
@@ -374,27 +411,28 @@
         printer => /** @type {!PrinterListEntry} */ (
             {printerInfo: printer, printerType: PrinterType.ENTERPRISE}));
     this.entryManager_.setEnterprisePrintersList(this.enterprisePrinters_);
-  },
+  }
 
   /** @private */
   onAddPrinterTap_() {
     this.$.addPrinterDialog.open();
-  },
+  }
 
   /** @private */
   onAddPrinterDialogClose_() {
-    focusWithoutInk(assert(this.$$('#addManualPrinterIcon')));
-  },
+    focusWithoutInk(
+        assert(this.shadowRoot.querySelector('#addManualPrinterIcon')));
+  }
 
   /** @private */
   onShowCupsEditPrinterDialog_() {
     this.showCupsEditPrinterDialog_ = true;
-  },
+  }
 
   /** @private */
   onEditPrinterDialogClose_() {
     this.showCupsEditPrinterDialog_ = false;
-  },
+  }
 
   /**
    * @param {string} searchTerm
@@ -409,7 +447,7 @@
     return !this.printers.some(printer => {
       return printer.printerName.toLowerCase().includes(searchTerm);
     });
-  },
+  }
 
   /**
    * @param {boolean} connectedToNetwork Whether the device is connected to
@@ -421,7 +459,7 @@
    */
   addPrinterButtonActive_(connectedToNetwork, userPrintersAllowed) {
     return connectedToNetwork && userPrintersAllowed;
-  },
+  }
 
   /**
    * @return {boolean} Whether |savedPrinters_| is empty.
@@ -429,7 +467,7 @@
    */
   doesAccountHaveSavedPrinters_() {
     return !!this.savedPrinters_.length;
-  },
+  }
 
   /**
    * @return {boolean} Whether |enterprisePrinters_| is empty.
@@ -437,7 +475,7 @@
    */
   doesAccountHaveEnterprisePrinters_() {
     return !!this.enterprisePrinters_.length;
-  },
+  }
 
   /** @private */
   getSavedPrintersAriaLabel_() {
@@ -450,7 +488,7 @@
       printerLabel = 'savedPrintersCountMany';
     }
     return loadTimeData.getStringF(printerLabel, this.savedPrinterCount_);
-  },
+  }
 
   /** @private */
   getNearbyPrintersAriaLabel_() {
@@ -463,7 +501,7 @@
       printerLabel = 'nearbyPrintersCountMany';
     }
     return loadTimeData.getStringF(printerLabel, this.nearbyPrinterCount_);
-  },
+  }
 
   /** @private */
   getEnterprisePrintersAriaLabel_() {
@@ -476,5 +514,8 @@
       printerLabel = 'enterprisePrintersCountMany';
     }
     return loadTimeData.getStringF(printerLabel, this.enterprisePrinterCount_);
-  },
-});
+  }
+}
+
+customElements.define(
+    SettingsCupsPrintersElement.is, SettingsCupsPrintersElement);
diff --git a/chrome/browser/resources/settings/chromeos/os_printing_page/cups_printers_browser_proxy.js b/chrome/browser/resources/settings/chromeos/os_printing_page/cups_printers_browser_proxy.js
index c559794..b45552d 100644
--- a/chrome/browser/resources/settings/chromeos/os_printing_page/cups_printers_browser_proxy.js
+++ b/chrome/browser/resources/settings/chromeos/os_printing_page/cups_printers_browser_proxy.js
@@ -2,13 +2,13 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import {addSingletonGetter, sendWithPromise} from 'chrome://resources/js/cr.m.js';
-
 /**
  * @fileoverview A helper object used from the "CUPS printing" section to
  * interact with the browser. Used only on Chrome OS.
  */
 
+import {sendWithPromise} from 'chrome://resources/js/cr.m.js';
+
 /**
  * @typedef {{
  *   isManaged: boolean,
@@ -227,10 +227,23 @@
   openScanningApp() {}
 }
 
+/** @type {?CupsPrintersBrowserProxy} */
+let instance = null;
+
 /**
  * @implements {CupsPrintersBrowserProxy}
  */
 export class CupsPrintersBrowserProxyImpl {
+  /** @return {!CupsPrintersBrowserProxy} */
+  static getInstance() {
+    return instance || (instance = new CupsPrintersBrowserProxyImpl());
+  }
+
+  /** @param {!CupsPrintersBrowserProxy} obj */
+  static setInstanceForTesting(obj) {
+    instance = obj;
+  }
+
   /** @override */
   getCupsSavedPrintersList() {
     return sendWithPromise('getCupsSavedPrintersList');
@@ -326,5 +339,3 @@
     chrome.send('openScanningApp');
   }
 }
-
-addSingletonGetter(CupsPrintersBrowserProxyImpl);
diff --git a/chrome/browser/resources/settings/chromeos/os_printing_page/cups_printers_entry.js b/chrome/browser/resources/settings/chromeos/os_printing_page/cups_printers_entry.js
index 9974f83..78bc3f9 100644
--- a/chrome/browser/resources/settings/chromeos/os_printing_page/cups_printers_entry.js
+++ b/chrome/browser/resources/settings/chromeos/os_printing_page/cups_printers_entry.js
@@ -6,76 +6,110 @@
  * @fileoverview 'settings-cups-printers-entry' is a component that holds a
  * printer.
  */
-import '//resources/cr_elements/cr_button/cr_button.m.js';
-import '//resources/polymer/v3_0/iron-icon/iron-icon.js';
+import 'chrome://resources/cr_elements/cr_button/cr_button.m.js';
+import 'chrome://resources/polymer/v3_0/iron-icon/iron-icon.js';
 import '../../settings_shared_css.js';
 
-import {FocusRowBehavior} from '//resources/js/cr/ui/focus_row_behavior.m.js';
-import {afterNextRender, flush, html, Polymer, TemplateInstanceBase, Templatizer} from '//resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+import {FocusRowBehavior, FocusRowBehaviorInterface} from 'chrome://resources/js/cr/ui/focus_row_behavior.m.js';
+import {html, mixinBehaviors, PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 
 import {loadTimeData} from '../../i18n_setup.js';
 
 import {PrinterListEntry, PrinterType} from './cups_printer_types.js';
-import {CupsPrinterInfo, CupsPrintersBrowserProxy, CupsPrintersBrowserProxyImpl, CupsPrintersList, ManufacturersInfo, ModelsInfo, PrinterMakeModel, PrinterPpdMakeModel, PrinterSetupResult, PrintServerResult} from './cups_printers_browser_proxy.js';
 
-Polymer({
-  _template: html`{__html_template__}`,
-  is: 'settings-cups-printers-entry',
+/**
+ * @constructor
+ * @extends {PolymerElement}
+ * @implements {FocusRowBehaviorInterface}
+ */
+const SettingsCupsPrintersEntryElementBase =
+    mixinBehaviors([FocusRowBehavior], PolymerElement);
 
-  behaviors: [
-    FocusRowBehavior,
-  ],
-  properties: {
-    /** @type {!PrinterListEntry} */
-    printerEntry: Object,
+/** @polymer */
+class SettingsCupsPrintersEntryElement extends
+    SettingsCupsPrintersEntryElementBase {
+  static get is() {
+    return 'settings-cups-printers-entry';
+  }
 
-    /**
-     * TODO(jimmyxgong): Determine how subtext should be set and what
-     * information it should have, including necessary ARIA labeling
-     * The additional information subtext for a printer.
-     * @type {string}
-     */
-    subtext: {type: String, value: ''},
+  static get template() {
+    return html`{__html_template__}`;
+  }
 
-    /**
-     * This value is set to true if the printer is in saving mode.
-     */
-    savingPrinter: Boolean,
+  static get properties() {
+    return {
+      /** @type {!PrinterListEntry} */
+      printerEntry: Object,
 
-    /**
-     * This value is set to true if UserPrintersAllowed policy is enabled.
-     */
-    userPrintersAllowed: {
-      type: Boolean,
-      value: false,
-    }
-  },
+      /**
+       * TODO(jimmyxgong): Determine how subtext should be set and what
+       * information it should have, including necessary ARIA labeling
+       * The additional information subtext for a printer.
+       * @type {string}
+       */
+      subtext: {type: String, value: ''},
+
+      /**
+       * This value is set to true if the printer is in saving mode.
+       */
+      savingPrinter: Boolean,
+
+      /**
+       * This value is set to true if UserPrintersAllowed policy is enabled.
+       */
+      userPrintersAllowed: {
+        type: Boolean,
+        value: false,
+      }
+    };
+  }
 
   /**
    * Fires a custom event when the menu button is clicked. Sends the details of
    * the printer and where the menu should appear.
    */
   onOpenActionMenuTap_(e) {
-    this.fire('open-action-menu', {
-      target: e.target,
-      item: this.printerEntry,
+    const openActionMenuEvent = new CustomEvent('open-action-menu', {
+      bubbles: true,
+      composed: true,
+      detail: {
+        target: e.target,
+        item: this.printerEntry,
+      },
     });
-  },
+    this.dispatchEvent(openActionMenuEvent);
+  }
 
   /** @private */
   onAddDiscoveredPrinterTap_(e) {
-    this.fire('query-discovered-printer', {item: this.printerEntry});
-  },
+    const queryDiscoveredPrinterEvent =
+        new CustomEvent('query-discovered-printer', {
+          bubbles: true,
+          composed: true,
+          detail: {item: this.printerEntry},
+        });
+    this.dispatchEvent(queryDiscoveredPrinterEvent);
+  }
 
   /** @private */
   onAddAutomaticPrinterTap_() {
-    this.fire('add-automatic-printer', {item: this.printerEntry});
-  },
+    const addAutomaticPrinterEvent = new CustomEvent('add-automatic-printer', {
+      bubbles: true,
+      composed: true,
+      detail: {item: this.printerEntry},
+    });
+    this.dispatchEvent(addAutomaticPrinterEvent);
+  }
 
   /** @private */
-  onAddServerPrinterTap_: function() {
-    this.fire('add-print-server-printer', {item: this.printerEntry});
-  },
+  onAddServerPrinterTap_() {
+    const addPrintServer = new CustomEvent('add-print-server-printer', {
+      bubbles: true,
+      composed: true,
+      detail: {item: this.printerEntry},
+    });
+    this.dispatchEvent(addPrintServer);
+  }
 
   /**
    * @return {boolean}
@@ -84,7 +118,7 @@
   showActionsMenu_() {
     return this.printerEntry.printerType === PrinterType.SAVED ||
         this.printerEntry.printerType === PrinterType.ENTERPRISE;
-  },
+  }
 
   /**
    * @return {boolean}
@@ -92,7 +126,7 @@
    */
   isDiscoveredPrinter_() {
     return this.printerEntry.printerType === PrinterType.DISCOVERED;
-  },
+  }
 
   /**
    * @return {boolean}
@@ -100,7 +134,7 @@
    */
   isAutomaticPrinter_() {
     return this.printerEntry.printerType === PrinterType.AUTOMATIC;
-  },
+  }
 
   /**
    * @return {boolean}
@@ -108,7 +142,7 @@
    */
   isPrintServerPrinter_() {
     return this.printerEntry.printerType === PrinterType.PRINTSERVER;
-  },
+  }
 
   /**
    * @return {boolean}
@@ -116,15 +150,18 @@
    */
   isConfigureDisabled_() {
     return !this.userPrintersAllowed || this.savingPrinter;
-  },
+  }
 
   getSaveButtonAria_() {
     return loadTimeData.getStringF(
         'savePrinterAria', this.printerEntry.printerInfo.printerName);
-  },
+  }
 
   getSetupButtonAria_() {
     return loadTimeData.getStringF(
         'setupPrinterAria', this.printerEntry.printerInfo.printerName);
-  },
-});
+  }
+}
+
+customElements.define(
+    SettingsCupsPrintersEntryElement.is, SettingsCupsPrintersEntryElement);
diff --git a/chrome/browser/resources/settings/chromeos/os_printing_page/cups_printers_entry_list_behavior.js b/chrome/browser/resources/settings/chromeos/os_printing_page/cups_printers_entry_list_behavior.js
index 6b7d484..081bff9 100644
--- a/chrome/browser/resources/settings/chromeos/os_printing_page/cups_printers_entry_list_behavior.js
+++ b/chrome/browser/resources/settings/chromeos/os_printing_page/cups_printers_entry_list_behavior.js
@@ -2,18 +2,18 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import {assert} from 'chrome://resources/js/assert.m.js';
-
-import {findDifference} from './cups_printer_dialog_util.js';
-import {PrinterListEntry} from './cups_printer_types.js';
-import {CupsPrintersEntryManager} from './cups_printers_entry_manager.js';
-
 /**
  * @fileoverview Polymer behavior for observing CupsPrintersEntryManager events.
  * Use this behavior if you want to receive a dynamically updated list of both
  * saved and nearby printers.
  */
 
+import {assert} from 'chrome://resources/js/assert.m.js';
+
+import {findDifference} from './cups_printer_dialog_util.js';
+import {PrinterListEntry} from './cups_printer_types.js';
+import {CupsPrintersEntryManager} from './cups_printers_entry_manager.js';
+
 /** @polymerBehavior */
 export const CupsPrintersEntryListBehavior = {
   properties: {
@@ -127,4 +127,24 @@
 
   /** @param{!Array<!PrinterListEntry>} removedPrinters */
   onSavedPrintersRemoved(removedPrinters) {},
-};
\ No newline at end of file
+};
+
+/** @interface */
+export class CupsPrintersEntryListBehaviorInterface {
+  constructor() {
+    /** @type {!Array<!PrinterListEntry>} */
+    this.savedPrinters;
+
+    /** @type {!Array<!PrinterListEntry>} */
+    this.nearbyPrinters;
+
+    /** @type {!Array<!PrinterListEntry>} */
+    this.enterprisePrinters;
+  }
+
+  /** @param {!Array<!PrinterListEntry>} addedPrinters */
+  onSavedPrintersAdded(addedPrinters) {}
+
+  /** @param {!Array<!PrinterListEntry>} removedPrinters */
+  onSavedPrintersRemoved(removedPrinters) {}
+}
diff --git a/chrome/browser/resources/settings/chromeos/os_printing_page/cups_printers_entry_manager.js b/chrome/browser/resources/settings/chromeos/os_printing_page/cups_printers_entry_manager.js
index bef4ace..860114e 100644
--- a/chrome/browser/resources/settings/chromeos/os_printing_page/cups_printers_entry_manager.js
+++ b/chrome/browser/resources/settings/chromeos/os_printing_page/cups_printers_entry_manager.js
@@ -2,8 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import {addSingletonGetter, addWebUIListener, removeWebUIListener} from 'chrome://resources/js/cr.m.js';
-import {WebUIListener} from 'chrome://resources/js/cr.m.js';
+import {addWebUIListener, removeWebUIListener, WebUIListener} from 'chrome://resources/js/cr.m.js';
 
 import {findDifference} from './cups_printer_dialog_util.js';
 import {PrinterListEntry, PrinterType} from './cups_printer_types.js';
@@ -28,12 +27,25 @@
  */
 let PrintersListCallback;
 
+/** @type {?CupsPrintersEntryManager} */
+let instance = null;
+
 /**
  * Class for managing printer entries. Holds Saved, Nearby, Enterprise, Print
  * Server printers and notifies observers of any applicable changes to either
  * printer lists.
  */
 export class CupsPrintersEntryManager {
+  /** @return {!CupsPrintersEntryManager} */
+  static getInstance() {
+    return instance || (instance = new CupsPrintersEntryManager());
+  }
+
+  /** @param {!CupsPrintersEntryManager} obj */
+  static setInstanceForTesting(obj) {
+    instance = obj;
+  }
+
   constructor() {
     /** @private {!Array<!PrinterListEntry>} */
     this.savedPrinters_ = [];
@@ -249,5 +261,3 @@
         listener => listener(this.enterprisePrinters_));
   }
 }
-
-addSingletonGetter(CupsPrintersEntryManager);
diff --git a/chrome/browser/resources/settings/chromeos/os_printing_page/cups_saved_printers.js b/chrome/browser/resources/settings/chromeos/os_printing_page/cups_saved_printers.js
index 30d5fc7..ed78bf2 100644
--- a/chrome/browser/resources/settings/chromeos/os_printing_page/cups_saved_printers.js
+++ b/chrome/browser/resources/settings/chromeos/os_printing_page/cups_saved_printers.js
@@ -2,24 +2,28 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import '//resources/cr_elements/cr_action_menu/cr_action_menu.js';
-import '//resources/cr_elements/icons.m.js';
-import '//resources/polymer/v3_0/iron-flex-layout/iron-flex-layout-classes.js';
-import '//resources/polymer/v3_0/iron-list/iron-list.js';
+/**
+ * @fileoverview 'settings-cups-saved-printers' is a list container for Saved
+ * Printers.
+ */
+
+import 'chrome://resources/cr_elements/cr_action_menu/cr_action_menu.js';
+import 'chrome://resources/cr_elements/icons.m.js';
+import 'chrome://resources/polymer/v3_0/iron-flex-layout/iron-flex-layout-classes.js';
+import 'chrome://resources/polymer/v3_0/iron-list/iron-list.js';
 import './cups_printers_entry.js';
 import '../../settings_shared_css.js';
 
-import {ListPropertyUpdateBehavior} from '//resources/js/list_property_update_behavior.m.js';
-import {WebUIListenerBehavior} from '//resources/js/web_ui_listener_behavior.m.js';
-import {afterNextRender, flush, html, Polymer, TemplateInstanceBase, Templatizer} from '//resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+import {ListPropertyUpdateBehavior, ListPropertyUpdateBehaviorInterface} from 'chrome://resources/js/list_property_update_behavior.m.js';
+import {WebUIListenerBehavior, WebUIListenerBehaviorInterface} from 'chrome://resources/js/web_ui_listener_behavior.m.js';
+import {html, mixinBehaviors, PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 
 import {recordSettingChange} from '../metrics_recorder.js';
 
-import {getBaseName, getErrorText, getPrintServerErrorText, isNameAndAddressValid, isNetworkProtocol, isPPDInfoValid, matchesSearchTerm, sortPrinters} from './cups_printer_dialog_util.js';
-import {PrinterListEntry, PrinterType} from './cups_printer_types.js';
-import {CupsPrinterInfo, CupsPrintersBrowserProxy, CupsPrintersBrowserProxyImpl, CupsPrintersList, ManufacturersInfo, ModelsInfo, PrinterMakeModel, PrinterPpdMakeModel, PrinterSetupResult, PrintServerResult} from './cups_printers_browser_proxy.js';
-import {CupsPrintersEntryListBehavior} from './cups_printers_entry_list_behavior.js';
-
+import {matchesSearchTerm, sortPrinters} from './cups_printer_dialog_util.js';
+import {PrinterListEntry} from './cups_printer_types.js';
+import {CupsPrinterInfo, CupsPrintersBrowserProxy, CupsPrintersBrowserProxyImpl} from './cups_printers_browser_proxy.js';
+import {CupsPrintersEntryListBehavior, CupsPrintersEntryListBehaviorInterface} from './cups_printers_entry_list_behavior.js';
 
 // If the Show more button is visible, the minimum number of printers we show
 // is 3.
@@ -38,118 +42,144 @@
 }
 
 /**
- * @fileoverview 'settings-cups-saved-printers' is a list container for Saved
- * Printers.
+ * @constructor
+ * @extends {PolymerElement}
+ * @implements {CupsPrintersEntryListBehaviorInterface}
+ * @implements {ListPropertyUpdateBehaviorInterface}
+ * @implements {WebUIListenerBehaviorInterface}
  */
-Polymer({
-  _template: html`{__html_template__}`,
-  is: 'settings-cups-saved-printers',
+const SettingsCupsSavedPrintersElementBase = mixinBehaviors(
+    [
+      CupsPrintersEntryListBehavior, ListPropertyUpdateBehavior,
+      WebUIListenerBehavior
+    ],
+    PolymerElement);
 
-  // ListPropertyUpdateBehavior is used in CupsPrintersEntryListBehavior.
-  behaviors: [
-    CupsPrintersEntryListBehavior,
-    ListPropertyUpdateBehavior,
-    WebUIListenerBehavior,
-  ],
+/** @polymer */
+class SettingsCupsSavedPrintersElement extends
+    SettingsCupsSavedPrintersElementBase {
+  static get is() {
+    return 'settings-cups-saved-printers';
+  }
 
-  properties: {
-    /**
-     * Search term for filtering |savedPrinters|.
-     * @type {string}
-     */
-    searchTerm: {
-      type: String,
-      value: '',
-    },
+  static get template() {
+    return html`{__html_template__}`;
+  }
 
-    /** @type {?CupsPrinterInfo} */
-    activePrinter: {
-      type: Object,
-      notify: true,
-    },
+  static get properties() {
+    return {
+      /**
+       * Search term for filtering |savedPrinters|.
+       * @type {string}
+       */
+      searchTerm: {
+        type: String,
+        value: '',
+      },
 
-    printersCount: {
-      type: Number,
-      computed: 'getFilteredPrintersLength_(filteredPrinters_.*)',
-      notify: true,
-    },
+      /** @type {?CupsPrinterInfo} */
+      activePrinter: {
+        type: Object,
+        notify: true,
+      },
 
-    /**
-     * @type {number}
-     * @private
-     */
-    activePrinterListEntryIndex_: {
-      type: Number,
-      value: -1,
-    },
+      printersCount: {
+        type: Number,
+        computed: 'getFilteredPrintersLength_(filteredPrinters_.*)',
+        notify: true,
+      },
 
-    /**
-     * List of printers filtered through a search term.
-     * @type {!Array<!PrinterListEntry>}
-     * @private
-     */
-    filteredPrinters_: {
-      type: Array,
-      value: () => [],
-    },
+      /**
+       * @type {number}
+       * @private
+       */
+      activePrinterListEntryIndex_: {
+        type: Number,
+        value: -1,
+      },
 
-    /**
-     * Array of new PrinterListEntry's that were added during this session.
-     * @type {!Array<!PrinterListEntry>}
-     * @private
-     */
-    newPrinters_: {
-      type: Array,
-      value: () => [],
-    },
+      /**
+       * List of printers filtered through a search term.
+       * @type {!Array<!PrinterListEntry>}
+       * @private
+       */
+      filteredPrinters_: {
+        type: Array,
+        value: () => [],
+      },
 
-    /**
-     * Keeps track of whether the user has tapped the Show more button. A search
-     * term will expand the collapsed list, so we need to keep track of whether
-     * the list expanded because of a search term or because the user tapped on
-     * the Show more button.
-     * @private
-     */
-    hasShowMoreBeenTapped_: {
-      type: Boolean,
-      value: false,
-    },
+      /**
+       * Array of new PrinterListEntry's that were added during this session.
+       * @type {!Array<!PrinterListEntry>}
+       * @private
+       */
+      newPrinters_: {
+        type: Array,
+        value: () => [],
+      },
 
-    /**
-     * Used by FocusRowBehavior to track the last focused element on a row.
-     * @private
-     */
-    lastFocused_: Object,
+      /**
+       * Keeps track of whether the user has tapped the Show more button. A
+       * search term will expand the collapsed list, so we need to keep track of
+       * whether the list expanded because of a search term or because the user
+       * tapped on the Show more button.
+       * @private
+       */
+      hasShowMoreBeenTapped_: {
+        type: Boolean,
+        value: false,
+      },
 
-    /**
-     * Used by FocusRowBehavior to track if the list has been blurred.
-     * @private
-     */
-    listBlurred_: Boolean,
-  },
+      /**
+       * Used by FocusRowBehavior to track the last focused element on a row.
+       * @private
+       */
+      lastFocused_: Object,
 
-  listeners: {
-    'open-action-menu': 'onOpenActionMenu_',
-  },
+      /**
+       * Used by FocusRowBehavior to track if the list has been blurred.
+       * @private
+       */
+      listBlurred_: Boolean,
+    };
+  }
 
-  observers:
-      ['onSearchOrPrintersChanged_(savedPrinters.*, searchTerm,' +
-       'hasShowMoreBeenTapped_, newPrinters_.*)'],
-
-  /** @private {CupsPrintersBrowserProxy} */
-  browserProxy_: null,
-
-  /**
-   * The number of printers we display if hidden printers are allowed.
-   * kMinVisiblePrinters is the default value and we never show fewer printers
-   * if the Show more button is visible.
-   */
-  visiblePrinterCounter_: kMinVisiblePrinters,
+  static get observers() {
+    return [
+      'onSearchOrPrintersChanged_(savedPrinters.*, searchTerm,' +
+      'hasShowMoreBeenTapped_, newPrinters_.*)'
+    ];
+  }
 
   /** @override */
-  created() {
+  constructor() {
+    super();
+
+    /** @private {CupsPrintersBrowserProxy} */
     this.browserProxy_ = CupsPrintersBrowserProxyImpl.getInstance();
-  },
+
+    /**
+     * The number of printers we display if hidden printers are allowed.
+     * kMinVisiblePrinters is the default value and we never show fewer printers
+     * if the Show more button is visible.
+     * @private
+     */
+    this.visiblePrinterCounter_ = kMinVisiblePrinters;
+  }
+
+  /** @override */
+  ready() {
+    super.ready();
+
+    this.addEventListener('open-action-menu', (event) => {
+      this.onOpenActionMenu_(
+          /**
+           * @type {!CustomEvent<{target: !HTMLElement, item:
+           *    !PrinterListEntry}>}
+           */
+          (event));
+    });
+  }
 
   /**
    * Redoes the search whenever |searchTerm| or |savedPrinters| changes.
@@ -165,7 +195,7 @@
     this.updateList(
         'filteredPrinters_', printer => printer.printerInfo.printerId,
         updatedPrinters);
-  },
+  }
 
   /**
    * @param {!CustomEvent<{target: !HTMLElement, item: !PrinterListEntry}>} e
@@ -181,15 +211,20 @@
             .printerInfo;
 
     const target = /** @type {!HTMLElement} */ (e.detail.target);
-    this.$$('cr-action-menu').showAt(target);
-  },
+    this.shadowRoot.querySelector('cr-action-menu').showAt(target);
+  }
 
   /** @private */
   onEditTap_() {
     // Event is caught by 'settings-cups-printers'.
-    this.fire('edit-cups-printer-details');
+    const editCupsPrinterDetailsEvent =
+        new CustomEvent('edit-cups-printer-details', {
+          bubbles: true,
+          composed: true,
+        });
+    this.dispatchEvent(editCupsPrinterDetailsEvent);
     this.closeActionMenu_();
-  },
+  }
 
   /** @private */
   onRemoveTap_() {
@@ -197,14 +232,14 @@
         this.activePrinter.printerId, this.activePrinter.printerName);
     recordSettingChange();
     this.activePrinter = null;
-    this.activeListEntryIndex_ = -1;
+    this.activePrinterListEntryIndex_ = -1;
     this.closeActionMenu_();
-  },
+  }
 
   /** @private */
   onShowMoreTap_() {
     this.hasShowMoreBeenTapped_ = true;
-  },
+  }
 
   /**
    * Gets the printers to be shown in the UI. These printers are filtered
@@ -232,12 +267,12 @@
           (printer, idx) => idx < this.visiblePrinterCounter_);
     }
     return updatedPrinters;
-  },
+  }
 
   /** @private */
   closeActionMenu_() {
-    this.$$('cr-action-menu').close();
-  },
+    this.shadowRoot.querySelector('cr-action-menu').close();
+  }
 
   /**
    * @return {boolean} Returns true if the no search message should be visible.
@@ -245,7 +280,7 @@
    */
   showNoSearchResultsMessage_() {
     return !!this.searchTerm && !this.filteredPrinters_.length;
-  },
+  }
 
   /** @param{!Array<!PrinterListEntry>} addedPrinters */
   onSavedPrintersAdded(addedPrinters) {
@@ -256,7 +291,7 @@
     }
 
     this.set('newPrinters_', currArr);
-  },
+  }
 
   /** @param{!Array<!PrinterListEntry>} removedPrinters */
   onSavedPrintersRemoved(removedPrinters) {
@@ -275,7 +310,7 @@
     }
 
     this.set('newPrinters_', currArr);
-  },
+  }
 
   /**
    * Keeps track of whether the Show more button should be visible which means
@@ -303,7 +338,7 @@
     }
 
     return true;
-  },
+  }
 
   /**
    * Moves printers that are in |newPrinters_| to position |toIndex| of
@@ -326,7 +361,7 @@
         moveEntryInPrinters(printerArr, idx, toIndex);
       }
     }
-  },
+  }
 
   /**
    * @private
@@ -334,5 +369,8 @@
    */
   getFilteredPrintersLength_() {
     return this.filteredPrinters_.length;
-  },
-});
+  }
+}
+
+customElements.define(
+    SettingsCupsSavedPrintersElement.is, SettingsCupsSavedPrintersElement);
diff --git a/chrome/browser/resources/settings/chromeos/os_printing_page/cups_settings_add_printer_dialog.js b/chrome/browser/resources/settings/chromeos/os_printing_page/cups_settings_add_printer_dialog.js
index 94ec809..5836c5588 100644
--- a/chrome/browser/resources/settings/chromeos/os_printing_page/cups_settings_add_printer_dialog.js
+++ b/chrome/browser/resources/settings/chromeos/os_printing_page/cups_settings_add_printer_dialog.js
@@ -14,6 +14,17 @@
  *   add a print server.
  */
 
+import 'chrome://resources/cr_elements/cr_input/cr_input.m.js';
+import 'chrome://resources/cr_components/localized_link/localized_link.js';
+import './cups_add_print_server_dialog.js';
+import './cups_add_printer_manually_dialog.js';
+import './cups_add_printer_manufacturer_model_dialog.js';
+import './cups_printer_shared_css.js';
+
+import {html, microTask, PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+
+import {CupsPrinterInfo} from './cups_printers_browser_proxy.js';
+
 /**
  * Different dialogs in add printer flow.
  * @enum {string}
@@ -51,68 +62,67 @@
   };
 }
 
+/** @polymer */
+class SettingsCupsAddPrinterDialogElement extends PolymerElement {
+  static get is() {
+    return 'settings-cups-add-printer-dialog';
+  }
 
-import {afterNextRender, Polymer, html, flush, Templatizer, TemplateInstanceBase} from '//resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+  static get template() {
+    return html`{__html_template__}`;
+  }
 
-import '//resources/cr_elements/cr_input/cr_input.m.js';
-import '//resources/cr_components/localized_link/localized_link.js';
-import {loadTimeData} from '../../i18n_setup.js';
-import './cups_add_print_server_dialog.js';
-import './cups_add_printer_manually_dialog.js';
-import './cups_add_printer_manufacturer_model_dialog.js';
-import {sortPrinters, matchesSearchTerm, getBaseName, getErrorText, isNetworkProtocol, isNameAndAddressValid, isPPDInfoValid, getPrintServerErrorText} from './cups_printer_dialog_util.js';
-import './cups_printer_shared_css.js';
-import {CupsPrintersBrowserProxy, CupsPrintersBrowserProxyImpl, CupsPrinterInfo, PrinterSetupResult, CupsPrintersList, PrinterPpdMakeModel, ManufacturersInfo, ModelsInfo, PrintServerResult, PrinterMakeModel} from './cups_printers_browser_proxy.js';
+  static get properties() {
+    return {
+      /** @type {!CupsPrinterInfo} */
+      newPrinter: {
+        type: Object,
+      },
 
-Polymer({
-  _template: html`{__html_template__}`,
-  is: 'settings-cups-add-printer-dialog',
+      /** @private {string} */
+      previousDialog_: String,
 
-  properties: {
-    /** @type {!CupsPrinterInfo} */
-    newPrinter: {
-      type: Object,
-    },
+      /** @private {string} */
+      currentDialog_: String,
 
-    /** @private {string} */
-    previousDialog_: String,
+      /** @private {boolean} */
+      showManuallyAddDialog_: {
+        type: Boolean,
+        value: false,
+      },
 
-    /** @private {string} */
-    currentDialog_: String,
+      /** @private {boolean} */
+      showManufacturerDialog_: {
+        type: Boolean,
+        value: false,
+      },
 
-    /** @private {boolean} */
-    showManuallyAddDialog_: {
-      type: Boolean,
-      value: false,
-    },
+      /** @private {boolean} */
+      showAddPrintServerDialog_: {
+        type: Boolean,
+        value: false,
+      },
+    };
+  }
 
-    /** @private {boolean} */
-    showManufacturerDialog_: {
-      type: Boolean,
-      value: false,
-    },
+  ready() {
+    super.ready();
 
-    /** @private {boolean} */
-    showAddPrintServerDialog_: {
-      type: Boolean,
-      value: false,
-    },
-  },
-
-  listeners: {
-    'open-manually-add-printer-dialog': 'openManuallyAddPrinterDialog_',
-    'open-manufacturer-model-dialog':
-        'openManufacturerModelDialogForCurrentPrinter_',
-    'open-add-print-server-dialog': 'openPrintServerDialog_',
-    'no-detected-printer': 'onNoDetectedPrinter_',
-  },
+    this.addEventListener(
+        'open-manually-add-printer-dialog', this.openManuallyAddPrinterDialog_);
+    this.addEventListener(
+        'open-manufacturer-model-dialog',
+        this.openManufacturerModelDialogForCurrentPrinter_);
+    this.addEventListener(
+        'open-add-print-server-dialog', this.openPrintServerDialog_);
+  }
 
   /** Opens the Add manual printer dialog. */
   open() {
     this.resetData_();
     this.switchDialog_(
         '', AddPrinterDialogs.MANUALLY, 'showManuallyAddDialog_');
-  },
+  }
 
   /**
    * Reset all the printer data in the Add printer flow.
@@ -122,35 +132,35 @@
     if (this.newPrinter) {
       this.newPrinter = getEmptyPrinter_();
     }
-  },
+  }
 
   /** @private */
   openManuallyAddPrinterDialog_() {
     this.switchDialog_(
         this.currentDialog_, AddPrinterDialogs.MANUALLY,
         'showManuallyAddDialog_');
-  },
+  }
 
   /** @private */
   openManufacturerModelDialogForCurrentPrinter_() {
     this.switchDialog_(
         this.currentDialog_, AddPrinterDialogs.MANUFACTURER,
         'showManufacturerDialog_');
-  },
+  }
 
   /** @param {!CupsPrinterInfo} printer */
   openManufacturerModelDialogForSpecifiedPrinter(printer) {
     this.newPrinter = printer;
     this.switchDialog_(
         '', AddPrinterDialogs.MANUFACTURER, 'showManufacturerDialog_');
-  },
+  }
 
   /** @private */
-  openPrintServerDialog_: function() {
+  openPrintServerDialog_() {
     this.switchDialog_(
         this.currentDialog_, AddPrinterDialogs.PRINTSERVER,
         'showAddPrintServerDialog_');
-  },
+  }
 
   /**
    * Switch dialog from |fromDialog| to |toDialog|.
@@ -165,11 +175,15 @@
     this.currentDialog_ = toDialog;
 
     this.set(domIfBooleanName, true);
-    this.async(function() {
-      const dialog = this.$$(toDialog);
+    microTask.run(() => {
+      const dialog = this.shadowRoot.querySelector(toDialog);
       dialog.addEventListener('close', () => {
         this.set(domIfBooleanName, false);
       });
     });
-  },
-});
+  }
+}
+
+customElements.define(
+    SettingsCupsAddPrinterDialogElement.is,
+    SettingsCupsAddPrinterDialogElement);
diff --git a/chrome/browser/resources/settings/chromeos/os_printing_page/os_printing_page.js b/chrome/browser/resources/settings/chromeos/os_printing_page/os_printing_page.js
index 3ed8b84..143e0f00 100644
--- a/chrome/browser/resources/settings/chromeos/os_printing_page/os_printing_page.js
+++ b/chrome/browser/resources/settings/chromeos/os_printing_page/os_printing_page.js
@@ -7,67 +7,88 @@
 import '../../settings_shared_css.js';
 import './cups_printers.js';
 
-import {assert, assertNotReached} from '//resources/js/assert.m.js';
-import {loadTimeData} from '//resources/js/load_time_data.m.js';
-import {afterNextRender, flush, html, Polymer, TemplateInstanceBase, Templatizer} from '//resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+import {html, mixinBehaviors, PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 
 import {Route, Router} from '../../router.js';
-import {DeepLinkingBehavior} from '../deep_linking_behavior.js';
+import {DeepLinkingBehavior, DeepLinkingBehaviorInterface} from '../deep_linking_behavior.js';
 import {recordSettingChange} from '../metrics_recorder.js';
 import {routes} from '../os_route.js';
-import {RouteObserverBehavior} from '../route_observer_behavior.js';
+import {RouteObserverBehavior, RouteObserverBehaviorInterface} from '../route_observer_behavior.js';
 
-import {CupsPrinterInfo, CupsPrintersBrowserProxy, CupsPrintersBrowserProxyImpl, CupsPrintersList, ManufacturersInfo, ModelsInfo, PrinterMakeModel, PrinterPpdMakeModel, PrinterSetupResult, PrintServerResult} from './cups_printers_browser_proxy.js';
+import {CupsPrintersBrowserProxy, CupsPrintersBrowserProxyImpl} from './cups_printers_browser_proxy.js';
 
-Polymer({
-  _template: html`{__html_template__}`,
-  is: 'os-settings-printing-page',
+/**
+ * @constructor
+ * @extends {PolymerElement}
+ * @implements {DeepLinkingBehaviorInterface}
+ * @implements {RouteObserverBehaviorInterface}
+ */
+const OsSettingsPrintingPageElementBase = mixinBehaviors(
+    [
+      DeepLinkingBehavior,
+      RouteObserverBehavior,
+    ],
+    PolymerElement);
 
-  behaviors: [
-    DeepLinkingBehavior,
-    RouteObserverBehavior,
-  ],
+/** @polymer */
+class OsSettingsPrintingPageElement extends OsSettingsPrintingPageElementBase {
+  static get is() {
+    return 'os-settings-printing-page';
+  }
 
-  properties: {
-    /** Preferences state. */
-    prefs: {
-      type: Object,
-      notify: true,
-    },
+  static get template() {
+    return html`{__html_template__}`;
+  }
 
-    /** Printer search string. */
-    searchTerm: {
-      type: String,
-    },
-
-    /** @private {!Map<string, string>} */
-    focusConfig_: {
-      type: Object,
-      value() {
-        const map = new Map();
-        if (routes.CUPS_PRINTERS) {
-          map.set(routes.CUPS_PRINTERS.path, '#cupsPrinters');
-        }
-        return map;
+  static get properties() {
+    return {
+      /** Preferences state. */
+      prefs: {
+        type: Object,
+        notify: true,
       },
-    },
 
-    /**
-     * Used by DeepLinkingBehavior to focus this page's deep links.
-     * @type {!Set<!chromeos.settings.mojom.Setting>}
-     */
-    supportedSettingIds: {
-      type: Object,
-      value: () => new Set([
-        chromeos.settings.mojom.Setting.kPrintJobs,
-        chromeos.settings.mojom.Setting.kScanningApp
-      ]),
-    },
-  },
+      /** Printer search string. */
+      searchTerm: {
+        type: String,
+      },
+
+      /** @private {!Map<string, string>} */
+      focusConfig_: {
+        type: Object,
+        value() {
+          const map = new Map();
+          if (routes.CUPS_PRINTERS) {
+            map.set(routes.CUPS_PRINTERS.path, '#cupsPrinters');
+          }
+          return map;
+        },
+      },
+
+      /**
+       * Used by DeepLinkingBehavior to focus this page's deep links.
+       * @type {!Set<!chromeos.settings.mojom.Setting>}
+       */
+      supportedSettingIds: {
+        type: Object,
+        value: () => new Set([
+          chromeos.settings.mojom.Setting.kPrintJobs,
+          chromeos.settings.mojom.Setting.kScanningApp
+        ]),
+      },
+    };
+  }
+
+  constructor() {
+    super();
+
+    /** @private {!CupsPrintersBrowserProxy} */
+    this.browserProxy_ = CupsPrintersBrowserProxyImpl.getInstance();
+  }
 
   /**
    * @param {!Route} route
-   * @param {!Route} oldRoute
+   * @param {!Route=} oldRoute
    */
   currentRouteChanged(route, oldRoute) {
     // Does not apply to this page.
@@ -76,21 +97,24 @@
     }
 
     this.attemptDeepLink();
-  },
+  }
 
   /** @private */
   onTapCupsPrinters_() {
     Router.getInstance().navigateTo(routes.CUPS_PRINTERS);
-  },
+  }
 
   /** @private */
   onOpenPrintManagement_() {
-    CupsPrintersBrowserProxyImpl.getInstance().openPrintManagementApp();
-  },
+    this.browserProxy_.openPrintManagementApp();
+  }
 
   /** @private */
   onOpenScanningApp_() {
-    CupsPrintersBrowserProxyImpl.getInstance().openScanningApp();
+    this.browserProxy_.openScanningApp();
     recordSettingChange(chromeos.settings.mojom.Setting.kScanningApp);
   }
-});
+}
+
+customElements.define(
+    OsSettingsPrintingPageElement.is, OsSettingsPrintingPageElement);
diff --git a/chrome/browser/resources/settings/controls/settings_textarea.html b/chrome/browser/resources/settings/controls/settings_textarea.html
index 8e3e477..879eb5f 100644
--- a/chrome/browser/resources/settings/controls/settings_textarea.html
+++ b/chrome/browser/resources/settings/controls/settings_textarea.html
@@ -38,6 +38,7 @@
     display: block;
     visibility: hidden;
     white-space: pre-wrap;
+    word-wrap: break-word;
   }
 
   :host([autogrow]) #mirror,
diff --git a/chrome/browser/resources/settings/languages_page/languages_settings_metrics_proxy.ts b/chrome/browser/resources/settings/languages_page/languages_settings_metrics_proxy.ts
index 41149e56..f7b0281 100644
--- a/chrome/browser/resources/settings/languages_page/languages_settings_metrics_proxy.ts
+++ b/chrome/browser/resources/settings/languages_page/languages_settings_metrics_proxy.ts
@@ -22,6 +22,7 @@
   DISABLE_TRANSLATE_FOR_SINGLE_LANGUAGE = 6,
   ENABLE_TRANSLATE_FOR_SINGLE_LANGUAGE = 7,
   LANGUAGE_LIST_REORDERED = 8,
+  CHANGE_CHROME_LANGUAGE = 9,
 }
 
 /**
diff --git a/chrome/browser/resources/settings/languages_page/languages_subpage.ts b/chrome/browser/resources/settings/languages_page/languages_subpage.ts
index 8500880..0534e9e 100644
--- a/chrome/browser/resources/settings/languages_page/languages_subpage.ts
+++ b/chrome/browser/resources/settings/languages_page/languages_subpage.ts
@@ -415,6 +415,8 @@
         this.detailLanguage_!.language.code);
     this.languageHelper.moveLanguageToFront(
         this.detailLanguage_!.language.code);
+    LanguageSettingsMetricsProxyImpl.getInstance().recordSettingsMetric(
+        LanguageSettingsActionType.CHANGE_CHROME_LANGUAGE);
 
     this.closeMenuSoon_();
   }
diff --git a/chrome/browser/sharing/shared_clipboard/shared_clipboard_context_menu_observer.cc b/chrome/browser/sharing/shared_clipboard/shared_clipboard_context_menu_observer.cc
deleted file mode 100644
index bdffb4c..0000000
--- a/chrome/browser/sharing/shared_clipboard/shared_clipboard_context_menu_observer.cc
+++ /dev/null
@@ -1,149 +0,0 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chrome/browser/sharing/shared_clipboard/shared_clipboard_context_menu_observer.h"
-
-#include "base/bind.h"
-#include "build/build_config.h"
-#include "chrome/app/chrome_command_ids.h"
-#include "chrome/browser/renderer_context_menu/render_view_context_menu.h"
-#include "chrome/browser/sharing/shared_clipboard/shared_clipboard_ui_controller.h"
-#include "chrome/browser/sharing/sharing_constants.h"
-#include "chrome/browser/sharing/sharing_metrics.h"
-#include "chrome/grit/generated_resources.h"
-#include "components/sync_device_info/device_info.h"
-#include "ui/base/l10n/l10n_util.h"
-#include "ui/base/models/image_model.h"
-#include "ui/color/color_id.h"
-#include "ui/gfx/color_palette.h"
-#include "ui/gfx/paint_vector_icon.h"
-
-SharedClipboardContextMenuObserver::SubMenuDelegate::SubMenuDelegate(
-    SharedClipboardContextMenuObserver* parent)
-    : parent_(parent) {}
-
-SharedClipboardContextMenuObserver::SubMenuDelegate::~SubMenuDelegate() =
-    default;
-
-bool SharedClipboardContextMenuObserver::SubMenuDelegate::IsCommandIdEnabled(
-    int command_id) const {
-  // All supported commands are enabled in sub menu.
-  return true;
-}
-
-void SharedClipboardContextMenuObserver::SubMenuDelegate::ExecuteCommand(
-    int command_id,
-    int event_flags) {
-  if (command_id < kSubMenuFirstDeviceCommandId ||
-      command_id > kSubMenuLastDeviceCommandId)
-    return;
-  int device_index = command_id - kSubMenuFirstDeviceCommandId;
-  parent_->SendSharedClipboardMessage(device_index);
-}
-
-SharedClipboardContextMenuObserver::SharedClipboardContextMenuObserver(
-    RenderViewContextMenuProxy* proxy)
-    : proxy_(proxy),
-      controller_(SharedClipboardUiController::GetOrCreateFromWebContents(
-          proxy_->GetWebContents())) {}
-
-SharedClipboardContextMenuObserver::~SharedClipboardContextMenuObserver() =
-    default;
-
-void SharedClipboardContextMenuObserver::InitMenu(
-    const content::ContextMenuParams& params) {
-  text_ = params.selection_text;
-  devices_ = controller_->GetDevices();
-  LogSharingDevicesToShow(controller_->GetFeatureMetricsPrefix(),
-                          nullptr /* No suffix */, devices_.size());
-
-  if (devices_.empty())
-    return;
-
-  if (devices_.size() == 1) {
-#if BUILDFLAG(IS_MAC)
-    proxy_->AddMenuItem(
-        IDC_CONTENT_CONTEXT_SHARING_SHARED_CLIPBOARD_SINGLE_DEVICE,
-        l10n_util::GetStringFUTF16(
-            IDS_CONTENT_CONTEXT_SHARING_SHARED_CLIPBOARD_SINGLE_DEVICE,
-            base::UTF8ToUTF16(devices_[0]->client_name())));
-#else
-    proxy_->AddMenuItemWithIcon(
-        IDC_CONTENT_CONTEXT_SHARING_SHARED_CLIPBOARD_SINGLE_DEVICE,
-        l10n_util::GetStringFUTF16(
-            IDS_CONTENT_CONTEXT_SHARING_SHARED_CLIPBOARD_SINGLE_DEVICE,
-            base::UTF8ToUTF16(devices_[0]->client_name())),
-        ui::ImageModel::FromVectorIcon(controller_->GetVectorIcon(),
-                                       ui::kColorMenuIcon,
-                                       ui::SimpleMenuModel::kDefaultIconSize));
-#endif
-  } else {
-    BuildSubMenu();
-#if BUILDFLAG(IS_MAC)
-    proxy_->AddSubMenu(
-        IDC_CONTENT_CONTEXT_SHARING_SHARED_CLIPBOARD_MULTIPLE_DEVICES,
-        l10n_util::GetStringUTF16(
-            IDS_CONTENT_CONTEXT_SHARING_SHARED_CLIPBOARD_MULTIPLE_DEVICES),
-        sub_menu_model_.get());
-#else
-    proxy_->AddSubMenuWithStringIdAndIcon(
-        IDC_CONTENT_CONTEXT_SHARING_SHARED_CLIPBOARD_MULTIPLE_DEVICES,
-        IDS_CONTENT_CONTEXT_SHARING_SHARED_CLIPBOARD_MULTIPLE_DEVICES,
-        sub_menu_model_.get(),
-        ui::ImageModel::FromVectorIcon(controller_->GetVectorIcon(),
-                                       ui::kColorMenuIcon,
-                                       ui::SimpleMenuModel::kDefaultIconSize));
-#endif
-  }
-}
-
-void SharedClipboardContextMenuObserver::BuildSubMenu() {
-  sub_menu_model_ = std::make_unique<ui::SimpleMenuModel>(&sub_menu_delegate_);
-
-  int command_id = kSubMenuFirstDeviceCommandId;
-  for (const auto& device : devices_) {
-    if (command_id > kSubMenuLastDeviceCommandId)
-      break;
-    sub_menu_model_->AddItem(command_id++,
-                             base::UTF8ToUTF16(device->client_name()));
-  }
-}
-
-bool SharedClipboardContextMenuObserver::IsCommandIdSupported(int command_id) {
-  size_t device_count = devices_.size();
-  if (device_count == 0)
-    return false;
-
-  if (device_count == 1) {
-    return command_id ==
-           IDC_CONTENT_CONTEXT_SHARING_SHARED_CLIPBOARD_SINGLE_DEVICE;
-  } else {
-    return command_id ==
-           IDC_CONTENT_CONTEXT_SHARING_SHARED_CLIPBOARD_MULTIPLE_DEVICES;
-  }
-}
-
-bool SharedClipboardContextMenuObserver::IsCommandIdEnabled(int command_id) {
-  // All supported commands are enabled.
-  return true;
-}
-
-void SharedClipboardContextMenuObserver::ExecuteCommand(int command_id) {
-  if (command_id ==
-      IDC_CONTENT_CONTEXT_SHARING_SHARED_CLIPBOARD_SINGLE_DEVICE) {
-    DCHECK_EQ(1u, devices_.size());
-    SendSharedClipboardMessage(0);
-  }
-}
-
-void SharedClipboardContextMenuObserver::SendSharedClipboardMessage(
-    int chosen_device_index) {
-  if (static_cast<size_t>(chosen_device_index) >= devices_.size())
-    return;
-  LogSharingSelectedIndex(controller_->GetFeatureMetricsPrefix(),
-                          nullptr /* No suffix */, chosen_device_index);
-
-  controller_->OnDeviceSelected(text_, *devices_[chosen_device_index]);
-  LogSharedClipboardSelectedTextSize(text_.size());
-}
diff --git a/chrome/browser/sharing/shared_clipboard/shared_clipboard_context_menu_observer.h b/chrome/browser/sharing/shared_clipboard/shared_clipboard_context_menu_observer.h
deleted file mode 100644
index ce9ceee..0000000
--- a/chrome/browser/sharing/shared_clipboard/shared_clipboard_context_menu_observer.h
+++ /dev/null
@@ -1,84 +0,0 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CHROME_BROWSER_SHARING_SHARED_CLIPBOARD_SHARED_CLIPBOARD_CONTEXT_MENU_OBSERVER_H_
-#define CHROME_BROWSER_SHARING_SHARED_CLIPBOARD_SHARED_CLIPBOARD_CONTEXT_MENU_OBSERVER_H_
-
-#include <memory>
-#include <string>
-#include <vector>
-
-#include "base/gtest_prod_util.h"
-#include "base/memory/raw_ptr.h"
-#include "components/renderer_context_menu/render_view_context_menu_observer.h"
-#include "ui/base/models/simple_menu_model.h"
-
-namespace syncer {
-class DeviceInfo;
-}  // namespace syncer
-
-class RenderViewContextMenuProxy;
-class SharedClipboardUiController;
-
-class SharedClipboardContextMenuObserver
-    : public RenderViewContextMenuObserver {
- public:
-  class SubMenuDelegate : public ui::SimpleMenuModel::Delegate {
-   public:
-    explicit SubMenuDelegate(SharedClipboardContextMenuObserver* parent);
-
-    SubMenuDelegate(const SubMenuDelegate&) = delete;
-    SubMenuDelegate& operator=(const SubMenuDelegate&) = delete;
-
-    ~SubMenuDelegate() override;
-
-    bool IsCommandIdEnabled(int command_id) const override;
-    void ExecuteCommand(int command_id, int event_flags) override;
-
-   private:
-    const raw_ptr<SharedClipboardContextMenuObserver> parent_;
-  };
-
-  explicit SharedClipboardContextMenuObserver(
-      RenderViewContextMenuProxy* proxy);
-
-  SharedClipboardContextMenuObserver(
-      const SharedClipboardContextMenuObserver&) = delete;
-  SharedClipboardContextMenuObserver& operator=(
-      const SharedClipboardContextMenuObserver&) = delete;
-
-  ~SharedClipboardContextMenuObserver() override;
-
-  // RenderViewContextMenuObserver implementation.
-  void InitMenu(const content::ContextMenuParams& params) override;
-  bool IsCommandIdSupported(int command_id) override;
-  bool IsCommandIdEnabled(int command_id) override;
-  void ExecuteCommand(int command_id) override;
-
- private:
-  FRIEND_TEST_ALL_PREFIXES(SharedClipboardContextMenuObserverTest,
-                           SingleDevice_ShowMenu);
-  FRIEND_TEST_ALL_PREFIXES(SharedClipboardContextMenuObserverTest,
-                           MultipleDevices_ShowMenu);
-  FRIEND_TEST_ALL_PREFIXES(SharedClipboardContextMenuObserverTest,
-                           MultipleDevices_MoreThanMax_ShowMenu);
-
-  void BuildSubMenu();
-
-  void SendSharedClipboardMessage(int chosen_device_index);
-
-  raw_ptr<RenderViewContextMenuProxy> proxy_ = nullptr;
-
-  raw_ptr<SharedClipboardUiController> controller_ = nullptr;
-
-  std::vector<std::unique_ptr<syncer::DeviceInfo>> devices_;
-
-  SubMenuDelegate sub_menu_delegate_{this};
-
-  std::u16string text_;
-
-  std::unique_ptr<ui::SimpleMenuModel> sub_menu_model_;
-};
-
-#endif  // CHROME_BROWSER_SHARING_SHARED_CLIPBOARD_SHARED_CLIPBOARD_CONTEXT_MENU_OBSERVER_H_
diff --git a/chrome/browser/sharing/shared_clipboard/shared_clipboard_context_menu_observer_unittest.cc b/chrome/browser/sharing/shared_clipboard/shared_clipboard_context_menu_observer_unittest.cc
deleted file mode 100644
index f267218..0000000
--- a/chrome/browser/sharing/shared_clipboard/shared_clipboard_context_menu_observer_unittest.cc
+++ /dev/null
@@ -1,226 +0,0 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chrome/browser/sharing/shared_clipboard/shared_clipboard_context_menu_observer.h"
-
-#include <memory>
-#include <string>
-#include <vector>
-
-#include "base/strings/strcat.h"
-#include "base/strings/string_number_conversions.h"
-#include "base/strings/utf_string_conversions.h"
-#include "chrome/app/chrome_command_ids.h"
-#include "chrome/browser/renderer_context_menu/mock_render_view_context_menu.h"
-#include "chrome/browser/sharing/fake_device_info.h"
-#include "chrome/browser/sharing/features.h"
-#include "chrome/browser/sharing/mock_sharing_service.h"
-#include "chrome/browser/sharing/sharing_constants.h"
-#include "chrome/browser/sharing/sharing_service_factory.h"
-#include "chrome/browser/sharing/vapid_key_manager.h"
-#include "components/sync_device_info/device_info.h"
-#include "content/public/browser/context_menu_params.h"
-#include "content/public/test/browser_task_environment.h"
-#include "content/public/test/test_renderer_host.h"
-#include "content/public/test/web_contents_tester.h"
-#include "testing/gmock/include/gmock/gmock.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-using ::testing::_;
-using ::testing::ByMove;
-using ::testing::Eq;
-using ::testing::NiceMock;
-using ::testing::Property;
-using ::testing::Return;
-
-using SharingMessage = chrome_browser_sharing::SharingMessage;
-
-namespace {
-
-const char16_t kText[] = u"Some random text to be copied.";
-
-class SharedClipboardContextMenuObserverTest : public testing::Test {
- public:
-  SharedClipboardContextMenuObserverTest() = default;
-
-  SharedClipboardContextMenuObserverTest(
-      const SharedClipboardContextMenuObserverTest&) = delete;
-  SharedClipboardContextMenuObserverTest& operator=(
-      const SharedClipboardContextMenuObserverTest&) = delete;
-
-  ~SharedClipboardContextMenuObserverTest() override = default;
-
-  void SetUp() override {
-    web_contents_ = content::WebContentsTester::CreateTestWebContents(
-        menu_.GetBrowserContext(), nullptr);
-    menu_.set_web_contents(web_contents_.get());
-    SharingServiceFactory::GetInstance()->SetTestingFactory(
-        menu_.GetBrowserContext(),
-        base::BindRepeating([](content::BrowserContext* context)
-                                -> std::unique_ptr<KeyedService> {
-          return std::make_unique<NiceMock<MockSharingService>>();
-        }));
-    observer_ = std::make_unique<SharedClipboardContextMenuObserver>(&menu_);
-    menu_.SetObserver(observer_.get());
-  }
-
-  void InitMenu(const std::u16string text) {
-    content::ContextMenuParams params;
-    params.selection_text = text;
-    observer_->InitMenu(params);
-    sharing_message.mutable_shared_clipboard_message()->set_text(
-        base::UTF16ToUTF8(text));
-  }
-
-  std::vector<std::unique_ptr<syncer::DeviceInfo>> CreateFakeDevices(
-      int count) {
-    std::vector<std::unique_ptr<syncer::DeviceInfo>> devices;
-    for (int i = 0; i < count; i++) {
-      devices.emplace_back(CreateFakeDeviceInfo(
-          base::StrCat({"guid", base::NumberToString(i)}), "name"));
-    }
-    return devices;
-  }
-
- protected:
-  NiceMock<MockSharingService>* service() {
-    return static_cast<NiceMock<MockSharingService>*>(
-        SharingServiceFactory::GetForBrowserContext(menu_.GetBrowserContext()));
-  }
-
-  content::BrowserTaskEnvironment task_environment_;
-  content::RenderViewHostTestEnabler test_render_host_factories_;
-  MockRenderViewContextMenu menu_{/* incognito= */ false};
-  std::unique_ptr<content::WebContents> web_contents_;
-  std::unique_ptr<SharedClipboardContextMenuObserver> observer_;
-  SharingMessage sharing_message;
-};
-
-}  // namespace
-
-MATCHER_P(ProtoEquals, message, "") {
-  std::string expected_serialized, actual_serialized;
-  message.SerializeToString(&expected_serialized);
-  arg.SerializeToString(&actual_serialized);
-  return expected_serialized == actual_serialized;
-}
-
-TEST_F(SharedClipboardContextMenuObserverTest, NoDevices_DoNotShowMenu) {
-  auto devices = CreateFakeDevices(0);
-
-  EXPECT_CALL(*service(), GetDeviceCandidates(_))
-      .WillOnce(Return(ByMove(std::move(devices))));
-
-  InitMenu(kText);
-
-  EXPECT_EQ(0U, menu_.GetMenuSize());
-}
-
-TEST_F(SharedClipboardContextMenuObserverTest, SingleDevice_ShowMenu) {
-  auto devices = CreateFakeDevices(1);
-  auto guid = devices[0]->guid();
-
-  EXPECT_CALL(*service(), GetDeviceCandidates(_))
-      .WillOnce(Return(ByMove(std::move(devices))));
-
-  InitMenu(kText);
-  ASSERT_EQ(1U, menu_.GetMenuSize());
-
-  MockRenderViewContextMenu::MockMenuItem item;
-  ASSERT_TRUE(menu_.GetMenuItem(0, &item));
-  EXPECT_EQ(IDC_CONTENT_CONTEXT_SHARING_SHARED_CLIPBOARD_SINGLE_DEVICE,
-            item.command_id);
-
-  // Emulate click on the device.
-  EXPECT_CALL(*service(),
-              SendMessageToDevice(Property(&syncer::DeviceInfo::guid, guid),
-                                  Eq(kSharingMessageTTL),
-                                  ProtoEquals(sharing_message), _))
-      .Times(1);
-  menu_.ExecuteCommand(
-      IDC_CONTENT_CONTEXT_SHARING_SHARED_CLIPBOARD_SINGLE_DEVICE, 0);
-}
-
-TEST_F(SharedClipboardContextMenuObserverTest, MultipleDevices_ShowMenu) {
-  constexpr int device_count = 3;
-  auto devices = CreateFakeDevices(device_count);
-  std::vector<std::string> guids;
-  for (auto& device : devices)
-    guids.push_back(device->guid());
-
-  EXPECT_CALL(*service(), GetDeviceCandidates(_))
-      .WillOnce(Return(ByMove(std::move(devices))));
-
-  InitMenu(kText);
-  ASSERT_EQ(device_count + 1U, menu_.GetMenuSize());
-
-  // Assert item ordering.
-  MockRenderViewContextMenu::MockMenuItem item;
-  ASSERT_TRUE(menu_.GetMenuItem(0, &item));
-  EXPECT_EQ(IDC_CONTENT_CONTEXT_SHARING_SHARED_CLIPBOARD_MULTIPLE_DEVICES,
-            item.command_id);
-
-  for (int i = 0; i < device_count; i++) {
-    ASSERT_TRUE(menu_.GetMenuItem(i + 1, &item));
-    EXPECT_EQ(kSubMenuFirstDeviceCommandId + i, item.command_id);
-  }
-
-  // Emulate clicks on all commands to check for commands with no device
-  // assigned.
-  for (int i = 0; i < kMaxDevicesShown; i++) {
-    if (i < device_count) {
-      EXPECT_CALL(*service(),
-                  SendMessageToDevice(
-                      Property(&syncer::DeviceInfo::guid, guids[i]),
-                      Eq(kSharingMessageTTL), ProtoEquals(sharing_message), _))
-          .Times(1);
-    } else {
-      EXPECT_CALL(*service(), SendMessageToDevice(_, _, _, _)).Times(0);
-    }
-    observer_->sub_menu_delegate_.ExecuteCommand(
-        kSubMenuFirstDeviceCommandId + i, 0);
-  }
-}
-
-TEST_F(SharedClipboardContextMenuObserverTest,
-       MultipleDevices_MoreThanMax_ShowMenu) {
-  int device_count = kMaxDevicesShown + 1;
-  auto devices = CreateFakeDevices(device_count);
-  std::vector<std::string> guids;
-  for (auto& device : devices)
-    guids.push_back(device->guid());
-
-  EXPECT_CALL(*service(), GetDeviceCandidates(_))
-      .WillOnce(Return(ByMove(std::move(devices))));
-
-  InitMenu(kText);
-  ASSERT_EQ(kMaxDevicesShown + 1U, menu_.GetMenuSize());
-
-  // Assert item ordering.
-  MockRenderViewContextMenu::MockMenuItem item;
-  ASSERT_TRUE(menu_.GetMenuItem(0, &item));
-  EXPECT_EQ(IDC_CONTENT_CONTEXT_SHARING_SHARED_CLIPBOARD_MULTIPLE_DEVICES,
-            item.command_id);
-
-  for (int i = 0; i < kMaxDevicesShown; i++) {
-    ASSERT_TRUE(menu_.GetMenuItem(i + 1, &item));
-    EXPECT_EQ(kSubMenuFirstDeviceCommandId + i, item.command_id);
-  }
-
-  // Emulate clicks on all device commands to check for commands outside valid
-  // range too.
-  for (int i = 0; i < device_count; i++) {
-    if (i < kMaxDevicesShown) {
-      EXPECT_CALL(*service(),
-                  SendMessageToDevice(
-                      Property(&syncer::DeviceInfo::guid, guids[i]),
-                      Eq(kSharingMessageTTL), ProtoEquals(sharing_message), _))
-          .Times(1);
-    } else {
-      EXPECT_CALL(*service(), SendMessageToDevice(_, _, _, _)).Times(0);
-    }
-    observer_->sub_menu_delegate_.ExecuteCommand(
-        kSubMenuFirstDeviceCommandId + i, 0);
-  }
-}
diff --git a/chrome/browser/sharing/shared_clipboard/shared_clipboard_ui_controller.cc b/chrome/browser/sharing/shared_clipboard/shared_clipboard_ui_controller.cc
deleted file mode 100644
index 3fea8e32..0000000
--- a/chrome/browser/sharing/shared_clipboard/shared_clipboard_ui_controller.cc
+++ /dev/null
@@ -1,118 +0,0 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chrome/browser/sharing/shared_clipboard/shared_clipboard_ui_controller.h"
-
-#include <utility>
-
-#include "base/callback.h"
-#include "base/strings/utf_string_conversions.h"
-#include "chrome/app/vector_icons/vector_icons.h"
-#include "chrome/browser/sharing/sharing_constants.h"
-#include "chrome/browser/sharing/sharing_dialog.h"
-#include "chrome/browser/ui/browser.h"
-#include "chrome/browser/ui/browser_finder.h"
-#include "chrome/grit/generated_resources.h"
-#include "components/sync_device_info/device_info.h"
-#include "content/public/browser/web_contents.h"
-#include "ui/base/l10n/l10n_util.h"
-#include "ui/strings/grit/ui_strings.h"
-
-// static
-SharedClipboardUiController*
-SharedClipboardUiController::GetOrCreateFromWebContents(
-    content::WebContents* web_contents) {
-  // Use active WebContents if available.
-  Browser* browser = chrome::FindBrowserWithWebContents(web_contents);
-  if (browser)
-    web_contents = browser->tab_strip_model()->GetActiveWebContents();
-  SharedClipboardUiController::CreateForWebContents(web_contents);
-  return SharedClipboardUiController::FromWebContents(web_contents);
-}
-
-SharedClipboardUiController::SharedClipboardUiController(
-    content::WebContents* web_contents)
-    : SharingUiController(web_contents),
-      content::WebContentsUserData<SharedClipboardUiController>(*web_contents) {
-}
-
-SharedClipboardUiController::~SharedClipboardUiController() = default;
-
-void SharedClipboardUiController::OnDeviceSelected(
-    const std::u16string& text,
-    const syncer::DeviceInfo& device) {
-  text_ = text;
-  OnDeviceChosen(device);
-}
-
-std::u16string SharedClipboardUiController::GetTitle(
-    SharingDialogType dialog_type) {
-  // Shared clipboard only shows error dialogs.
-  DCHECK_EQ(SharingDialogType::kErrorDialog, dialog_type);
-
-  if (send_result() == SharingSendMessageResult::kPayloadTooLarge) {
-    return l10n_util::GetStringUTF16(
-        IDS_BROWSER_SHARING_SHARED_CLIPBOARD_ERROR_DIALOG_TITLE_PAYLOAD_TOO_LARGE);
-  }
-
-  return SharingUiController::GetTitle(dialog_type);
-}
-
-PageActionIconType SharedClipboardUiController::GetIconType() {
-  return PageActionIconType::kSharedClipboard;
-}
-
-sync_pb::SharingSpecificFields::EnabledFeatures
-SharedClipboardUiController::GetRequiredFeature() const {
-  return sync_pb::SharingSpecificFields::SHARED_CLIPBOARD_V2;
-}
-
-// No need for apps for shared clipboard feature
-void SharedClipboardUiController::DoUpdateApps(UpdateAppsCallback callback) {
-  std::move(callback).Run(std::vector<SharingApp>());
-}
-
-void SharedClipboardUiController::OnDeviceChosen(
-    const syncer::DeviceInfo& device) {
-  chrome_browser_sharing::SharingMessage sharing_message;
-  sharing_message.mutable_shared_clipboard_message()->set_text(
-      base::UTF16ToUTF8(text_));
-
-  SendMessageToDevice(device, /*response_timeout=*/absl::nullopt,
-                      std::move(sharing_message),
-                      /*callback=*/absl::nullopt);
-}
-
-void SharedClipboardUiController::OnAppChosen(const SharingApp& app) {
-  // Do nothing - there is no apps
-}
-
-std::u16string SharedClipboardUiController::GetContentType() const {
-  return l10n_util::GetStringUTF16(IDS_BROWSER_SHARING_CONTENT_TYPE_TEXT);
-}
-
-std::u16string SharedClipboardUiController::GetErrorDialogText() const {
-  if (send_result() == SharingSendMessageResult::kPayloadTooLarge) {
-    return l10n_util::GetStringUTF16(
-        IDS_BROWSER_SHARING_SHARED_CLIPBOARD_ERROR_DIALOG_TEXT_PAYLOAD_TOO_LARGE);
-  }
-
-  return SharingUiController::GetErrorDialogText();
-}
-
-const gfx::VectorIcon& SharedClipboardUiController::GetVectorIcon() const {
-  return kCopyIcon;
-}
-
-std::u16string SharedClipboardUiController::GetTextForTooltipAndAccessibleName()
-    const {
-  return l10n_util::GetStringUTF16(IDS_OMNIBOX_TOOLTIP_SHARED_CLIPBOARD);
-}
-
-SharingFeatureName SharedClipboardUiController::GetFeatureMetricsPrefix()
-    const {
-  return SharingFeatureName::kSharedClipboard;
-}
-
-WEB_CONTENTS_USER_DATA_KEY_IMPL(SharedClipboardUiController);
diff --git a/chrome/browser/sharing/shared_clipboard/shared_clipboard_ui_controller.h b/chrome/browser/sharing/shared_clipboard/shared_clipboard_ui_controller.h
deleted file mode 100644
index f60cb2a..0000000
--- a/chrome/browser/sharing/shared_clipboard/shared_clipboard_ui_controller.h
+++ /dev/null
@@ -1,66 +0,0 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CHROME_BROWSER_SHARING_SHARED_CLIPBOARD_SHARED_CLIPBOARD_UI_CONTROLLER_H_
-#define CHROME_BROWSER_SHARING_SHARED_CLIPBOARD_SHARED_CLIPBOARD_UI_CONTROLLER_H_
-
-#include <memory>
-#include <string>
-#include <vector>
-
-#include "base/memory/weak_ptr.h"
-#include "chrome/browser/sharing/sharing_ui_controller.h"
-#include "chrome/browser/ui/page_action/page_action_icon_type.h"
-#include "content/public/browser/web_contents_user_data.h"
-
-namespace content {
-class WebContents;
-}  // namespace content
-
-class SharedClipboardUiController
-    : public SharingUiController,
-      public content::WebContentsUserData<SharedClipboardUiController> {
- public:
-  static SharedClipboardUiController* GetOrCreateFromWebContents(
-      content::WebContents* web_contents);
-
-  SharedClipboardUiController(const SharedClipboardUiController&) = delete;
-  SharedClipboardUiController& operator=(const SharedClipboardUiController&) =
-      delete;
-
-  ~SharedClipboardUiController() override;
-
-  void OnDeviceSelected(const std::u16string& text,
-                        const syncer::DeviceInfo& device);
-
-  // Overridden from SharingUiController:
-  std::u16string GetTitle(SharingDialogType dialog_type) override;
-  PageActionIconType GetIconType() override;
-  sync_pb::SharingSpecificFields::EnabledFeatures GetRequiredFeature()
-      const override;
-  void OnDeviceChosen(const syncer::DeviceInfo& device) override;
-  void OnAppChosen(const SharingApp& app) override;
-  std::u16string GetContentType() const override;
-  std::u16string GetErrorDialogText() const override;
-  const gfx::VectorIcon& GetVectorIcon() const override;
-  std::u16string GetTextForTooltipAndAccessibleName() const override;
-  SharingFeatureName GetFeatureMetricsPrefix() const override;
-
- protected:
-  explicit SharedClipboardUiController(content::WebContents* web_contents);
-
-  // Overridden from SharingUiController:
-  void DoUpdateApps(UpdateAppsCallback callback) override;
-
- private:
-  friend class content::WebContentsUserData<SharedClipboardUiController>;
-
-  std::u16string text_;
-
-  base::WeakPtrFactory<SharedClipboardUiController> weak_ptr_factory_{this};
-
-  WEB_CONTENTS_USER_DATA_KEY_DECL();
-};
-
-#endif  // CHROME_BROWSER_SHARING_SHARED_CLIPBOARD_SHARED_CLIPBOARD_UI_CONTROLLER_H_
diff --git a/chrome/browser/sharing/shared_clipboard/shared_clipboard_ui_controller_unittest.cc b/chrome/browser/sharing/shared_clipboard/shared_clipboard_ui_controller_unittest.cc
deleted file mode 100644
index 09a1758..0000000
--- a/chrome/browser/sharing/shared_clipboard/shared_clipboard_ui_controller_unittest.cc
+++ /dev/null
@@ -1,103 +0,0 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chrome/browser/sharing/shared_clipboard/shared_clipboard_ui_controller.h"
-
-#include <memory>
-
-#include "base/guid.h"
-#include "base/memory/ptr_util.h"
-#include "base/memory/raw_ptr.h"
-#include "base/strings/strcat.h"
-#include "base/strings/utf_string_conversions.h"
-#include "chrome/browser/sharing/fake_device_info.h"
-#include "chrome/browser/sharing/features.h"
-#include "chrome/browser/sharing/mock_sharing_service.h"
-#include "chrome/browser/sharing/sharing_constants.h"
-#include "chrome/browser/sharing/sharing_service_factory.h"
-#include "chrome/test/base/testing_profile.h"
-#include "components/gcm_driver/fake_gcm_driver.h"
-#include "components/gcm_driver/instance_id/instance_id_driver.h"
-#include "components/sync/protocol/device_info_specifics.pb.h"
-#include "components/sync_device_info/fake_device_info_tracker.h"
-#include "components/sync_preferences/testing_pref_service_syncable.h"
-#include "content/public/test/browser_task_environment.h"
-#include "content/public/test/test_renderer_host.h"
-#include "content/public/test/web_contents_tester.h"
-#include "testing/gmock/include/gmock/gmock.h"
-#include "testing/gtest/include/gtest/gtest.h"
-#include "url/gurl.h"
-
-using ::testing::Eq;
-using ::testing::Property;
-
-namespace {
-
-const char16_t kText[] = u"Text to be copied";
-const char kExpectedText[] = "Text to be copied";
-const char kReceiverGuid[] = "test_receiver_guid";
-const char kReceiverName[] = "test_receiver_name";
-
-class SharedClipboardUiControllerTest : public testing::Test {
- public:
-  SharedClipboardUiControllerTest() = default;
-
-  void SetUp() override {
-    web_contents_ =
-        content::WebContentsTester::CreateTestWebContents(&profile_, nullptr);
-    SharingServiceFactory::GetInstance()->SetTestingFactory(
-        &profile_, base::BindRepeating([](content::BrowserContext* context)
-                                           -> std::unique_ptr<KeyedService> {
-          return std::make_unique<testing::NiceMock<MockSharingService>>();
-        }));
-    std::unique_ptr<syncer::DeviceInfo> device_info =
-        CreateFakeDeviceInfo(kReceiverGuid, kReceiverName);
-    controller_ = SharedClipboardUiController::GetOrCreateFromWebContents(
-        web_contents_.get());
-    controller_->OnDeviceSelected(kText, *device_info.get());
-  }
-
- protected:
-  testing::NiceMock<MockSharingService>* service() {
-    return static_cast<testing::NiceMock<MockSharingService>*>(
-        SharingServiceFactory::GetForBrowserContext(&profile_));
-  }
-
-  content::BrowserTaskEnvironment task_environment_;
-  content::RenderViewHostTestEnabler test_render_host_factories_;
-  TestingProfile profile_;
-  std::unique_ptr<content::WebContents> web_contents_;
-  raw_ptr<SharedClipboardUiController> controller_ = nullptr;
-};
-}  // namespace
-
-MATCHER_P(ProtoEquals, message, "") {
-  std::string expected_serialized, actual_serialized;
-  message.SerializeToString(&expected_serialized);
-  arg.SerializeToString(&actual_serialized);
-  return expected_serialized == actual_serialized;
-}
-
-// Check the call to sharing service when a device is chosen.
-TEST_F(SharedClipboardUiControllerTest, OnDeviceChosen) {
-  std::unique_ptr<syncer::DeviceInfo> device_info =
-      CreateFakeDeviceInfo(kReceiverGuid, kReceiverName);
-
-  chrome_browser_sharing::SharingMessage sharing_message;
-  sharing_message.mutable_shared_clipboard_message()->set_text(kExpectedText);
-  EXPECT_CALL(
-      *service(),
-      SendMessageToDevice(Property(&syncer::DeviceInfo::guid, kReceiverGuid),
-                          Eq(kSharingMessageTTL), ProtoEquals(sharing_message),
-                          testing::_));
-  controller_->OnDeviceChosen(*device_info.get());
-}
-
-// Check the call to sharing service to get all synced devices.
-TEST_F(SharedClipboardUiControllerTest, GetSyncedDevices) {
-  EXPECT_CALL(*service(),
-              GetDeviceCandidates(
-                  Eq(sync_pb::SharingSpecificFields::SHARED_CLIPBOARD_V2)));
-  controller_->GetDevices();
-}
diff --git a/chrome/browser/sharing/shared_clipboard/shared_clipboard_utils.cc b/chrome/browser/sharing/shared_clipboard/shared_clipboard_utils.cc
deleted file mode 100644
index 25f5e29a..0000000
--- a/chrome/browser/sharing/shared_clipboard/shared_clipboard_utils.cc
+++ /dev/null
@@ -1,19 +0,0 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chrome/browser/sharing/shared_clipboard/shared_clipboard_utils.h"
-
-#include "chrome/browser/profiles/profile.h"
-#include "chrome/browser/sharing/sharing_service.h"
-#include "chrome/browser/sharing/sharing_service_factory.h"
-#include "chrome/common/pref_names.h"
-#include "components/prefs/pref_service.h"
-#include "content/public/browser/browser_context.h"
-
-bool ShouldOfferSharedClipboard(content::BrowserContext* browser_context,
-                                const std::u16string& text) {
-  // TODO(https://crbug.com/1311675): Remove this function and all its call
-  // sites.
-  return false;
-}
diff --git a/chrome/browser/sharing/shared_clipboard/shared_clipboard_utils.h b/chrome/browser/sharing/shared_clipboard/shared_clipboard_utils.h
deleted file mode 100644
index e2d0c3fc..0000000
--- a/chrome/browser/sharing/shared_clipboard/shared_clipboard_utils.h
+++ /dev/null
@@ -1,18 +0,0 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CHROME_BROWSER_SHARING_SHARED_CLIPBOARD_SHARED_CLIPBOARD_UTILS_H_
-#define CHROME_BROWSER_SHARING_SHARED_CLIPBOARD_SHARED_CLIPBOARD_UTILS_H_
-
-#include <string>
-
-
-namespace content {
-class BrowserContext;
-}  // namespace content
-
-bool ShouldOfferSharedClipboard(content::BrowserContext* browser_context,
-                                const std::u16string& text);
-
-#endif  // CHROME_BROWSER_SHARING_SHARED_CLIPBOARD_SHARED_CLIPBOARD_UTILS_H_
diff --git a/chrome/browser/sharing/shared_clipboard/shared_clipboard_utils_unittest.cc b/chrome/browser/sharing/shared_clipboard/shared_clipboard_utils_unittest.cc
deleted file mode 100644
index 3e77479..0000000
--- a/chrome/browser/sharing/shared_clipboard/shared_clipboard_utils_unittest.cc
+++ /dev/null
@@ -1,89 +0,0 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chrome/browser/sharing/shared_clipboard/shared_clipboard_context_menu_observer.h"
-
-#include <memory>
-
-#include "base/strings/utf_string_conversions.h"
-#include "base/test/scoped_feature_list.h"
-#include "chrome/browser/sharing/mock_sharing_service.h"
-#include "chrome/browser/sharing/shared_clipboard/shared_clipboard_utils.h"
-#include "chrome/browser/sharing/sharing_device_source.h"
-#include "chrome/browser/sharing/sharing_fcm_handler.h"
-#include "chrome/browser/sharing/sharing_fcm_sender.h"
-#include "chrome/browser/sharing/sharing_handler_registry.h"
-#include "chrome/browser/sharing/sharing_service.h"
-#include "chrome/browser/sharing/sharing_service_factory.h"
-#include "chrome/browser/sharing/sharing_sync_preference.h"
-#include "chrome/browser/sharing/vapid_key_manager.h"
-#include "chrome/common/pref_names.h"
-#include "chrome/test/base/testing_profile.h"
-#include "components/prefs/pref_service.h"
-#include "content/public/test/browser_task_environment.h"
-#include "testing/gmock/include/gmock/gmock.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-using ::testing::_;
-using ::testing::ByMove;
-using ::testing::Eq;
-using ::testing::NiceMock;
-using ::testing::Return;
-
-namespace {
-
-const char16_t kText[] = u"Some text to copy to phone device.";
-
-class MockSharingDeviceRegistration : public SharingDeviceRegistration {
- public:
-  MockSharingDeviceRegistration()
-      : SharingDeviceRegistration(/*pref_service=*/nullptr,
-                                  /*sharing_sync_preference=*/nullptr,
-                                  /*vapid_key_manager=*/nullptr,
-                                  /*instance_id_driver=*/nullptr,
-                                  /*sync_service=*/nullptr) {}
-
-  MockSharingDeviceRegistration(const MockSharingDeviceRegistration&) = delete;
-  MockSharingDeviceRegistration& operator=(
-      const MockSharingDeviceRegistration&) = delete;
-
-  ~MockSharingDeviceRegistration() override = default;
-
-  MOCK_CONST_METHOD0(IsSharedClipboardSupported, bool());
-};
-
-class SharedClipboardUtilsTest : public testing::Test {
- public:
-  SharedClipboardUtilsTest() = default;
-
-  SharedClipboardUtilsTest(const SharedClipboardUtilsTest&) = delete;
-  SharedClipboardUtilsTest& operator=(const SharedClipboardUtilsTest&) = delete;
-
-  ~SharedClipboardUtilsTest() override = default;
-
-  void SetUp() override {
-    SharingServiceFactory::GetInstance()->SetTestingFactory(
-        &profile_, base::BindRepeating(&SharedClipboardUtilsTest::CreateService,
-                                       base::Unretained(this)));
-  }
-
- protected:
-  std::unique_ptr<KeyedService> CreateService(
-      content::BrowserContext* context) {
-    return create_service_ ? std::make_unique<MockSharingService>() : nullptr;
-  }
-
-  base::test::ScopedFeatureList scoped_feature_list_;
-  content::BrowserTaskEnvironment task_environment_;
-  TestingProfile profile_;
-  bool create_service_ = true;
-};
-
-}  // namespace
-
-// TODO(https://crbug.com/1311675): Remove this test and file once all the
-// shared clipboard implementation is gone.
-TEST_F(SharedClipboardUtilsTest, UIFlagDisabled_DoNotShowMenu) {
-  EXPECT_FALSE(ShouldOfferSharedClipboard(&profile_, kText));
-}
diff --git a/chrome/browser/sync/sync_service_factory_unittest.cc b/chrome/browser/sync/sync_service_factory_unittest.cc
index c5ee7e8..e83102b 100644
--- a/chrome/browser/sync/sync_service_factory_unittest.cc
+++ b/chrome/browser/sync/sync_service_factory_unittest.cc
@@ -82,7 +82,7 @@
 
   // Returns the collection of default datatypes.
   syncer::ModelTypeSet DefaultDatatypes() {
-    static_assert(39 == syncer::GetNumModelTypes(),
+    static_assert(40 == syncer::GetNumModelTypes(),
                   "When adding a new type, you probably want to add it here as "
                   "well (assuming it is already enabled).");
 
@@ -126,6 +126,7 @@
       datatypes.Put(syncer::OS_PRIORITY_PREFERENCES);
     }
     datatypes.Put(syncer::PRINTERS);
+    // TODO(pawliczek): Add PRINTERS_AUTHORIZATION_SERVERS when ready.
     datatypes.Put(syncer::WIFI_CONFIGURATIONS);
     datatypes.Put(syncer::WORKSPACE_DESK);
 #endif  // BUILDFLAG(IS_CHROMEOS_ASH)
diff --git a/chrome/browser/sync/test/integration/single_client_standalone_transport_sync_test.cc b/chrome/browser/sync/test/integration/single_client_standalone_transport_sync_test.cc
index b1a8e7c..bbcf676 100644
--- a/chrome/browser/sync/test/integration/single_client_standalone_transport_sync_test.cc
+++ b/chrome/browser/sync/test/integration/single_client_standalone_transport_sync_test.cc
@@ -30,7 +30,7 @@
 namespace {
 
 syncer::ModelTypeSet AllowedTypesInStandaloneTransportMode() {
-  static_assert(39 == syncer::GetNumModelTypes(),
+  static_assert(40 == syncer::GetNumModelTypes(),
                 "Add new types below if they run in transport mode");
   // Only some special allowlisted types (and control types) are allowed in
   // standalone transport mode.
@@ -42,11 +42,12 @@
 #if BUILDFLAG(IS_CHROMEOS_ASH)
   // OS sync types run in transport mode.
   if (chromeos::features::IsSyncSettingsCategorizationEnabled()) {
-    allowed_types.PutAll({syncer::APPS, syncer::APP_SETTINGS, syncer::APP_LIST,
-                          syncer::APP_SETTINGS, syncer::ARC_PACKAGE,
-                          syncer::PRINTERS, syncer::OS_PREFERENCES,
-                          syncer::OS_PRIORITY_PREFERENCES, syncer::WEB_APPS,
-                          syncer::WORKSPACE_DESK});
+    allowed_types.PutAll(
+        {syncer::APPS, syncer::APP_SETTINGS, syncer::APP_LIST,
+         syncer::APP_SETTINGS, syncer::ARC_PACKAGE, syncer::PRINTERS,
+         syncer::PRINTERS_AUTHORIZATION_SERVERS, syncer::OS_PREFERENCES,
+         syncer::OS_PRIORITY_PREFERENCES, syncer::WEB_APPS,
+         syncer::WORKSPACE_DESK});
   }
   allowed_types.Put(syncer::WIFI_CONFIGURATIONS);
 #endif  // BUILDFLAG(IS_CHROMEOS_ASH)
diff --git a/chrome/browser/task_manager/providers/web_contents/back_forward_cache_task.cc b/chrome/browser/task_manager/providers/web_contents/back_forward_cache_task.cc
index 7ad5ee3..778cb1e 100644
--- a/chrome/browser/task_manager/providers/web_contents/back_forward_cache_task.cc
+++ b/chrome/browser/task_manager/providers/web_contents/back_forward_cache_task.cc
@@ -7,10 +7,12 @@
 #include <string>
 
 #include "base/strings/utf_string_conversions.h"
+#include "chrome/browser/task_manager/providers/web_contents/web_contents_task_provider.h"
 #include "chrome/grit/generated_resources.h"
 #include "content/public/browser/browser_context.h"
 #include "content/public/browser/render_frame_host.h"
 #include "content/public/browser/site_instance.h"
+#include "content/public/browser/web_contents.h"
 #include "ui/base/l10n/l10n_util.h"
 
 namespace {
@@ -47,16 +49,20 @@
 
 BackForwardCacheTask::BackForwardCacheTask(
     content::RenderFrameHost* render_frame_host,
-    RendererTask* parent_task)
+    RendererTask* parent_task,
+    WebContentsTaskProvider* task_provider)
     : RendererTask(
           GetTaskTitle(render_frame_host, parent_task),
           nullptr,  // TODO(crbug.com/1225508): Set Favicon for main frames.
           render_frame_host),
-      parent_task_(parent_task) {}
+      parent_task_(parent_task),
+      task_provider_(task_provider) {}
 
 // For the top level BackForwardCacheTask |parent_task_| is nullptr.
 Task* BackForwardCacheTask::GetParentTask() const {
-  return parent_task_;
+  return parent_task_ ? parent_task_
+                      : task_provider_->GetTaskOfFrame(
+                            web_contents()->GetPrimaryMainFrame());
 }
 
 // The top page calls the default Activate().
diff --git a/chrome/browser/task_manager/providers/web_contents/back_forward_cache_task.h b/chrome/browser/task_manager/providers/web_contents/back_forward_cache_task.h
index 3d0abc90..602e967 100644
--- a/chrome/browser/task_manager/providers/web_contents/back_forward_cache_task.h
+++ b/chrome/browser/task_manager/providers/web_contents/back_forward_cache_task.h
@@ -14,13 +14,16 @@
 
 namespace task_manager {
 
+class WebContentsTaskProvider;
+
 // Defines a concrete renderer task that can represent processes stored in the
 // BackForwardCache. Tasks are added for each cached main frame, as well as for
 // each separate SiteInstance for subframes within a cached page.
 class BackForwardCacheTask : public RendererTask {
  public:
   BackForwardCacheTask(content::RenderFrameHost* render_frame_host,
-                       RendererTask* parent_task);
+                       RendererTask* parent_task,
+                       WebContentsTaskProvider* task_provider);
   BackForwardCacheTask(const BackForwardCacheTask&) = delete;
   BackForwardCacheTask& operator=(const BackForwardCacheTask&) = delete;
   ~BackForwardCacheTask() override = default;
@@ -41,6 +44,8 @@
   // task is not guaranteed.
   // For cached main frame tasks |parent_task_| is nullptr.
   raw_ptr<RendererTask> parent_task_;
+  // The provider has the same lifespan as the task manager.
+  const raw_ptr<WebContentsTaskProvider> task_provider_;
 };
 
 }  // namespace task_manager
diff --git a/chrome/browser/task_manager/providers/web_contents/fenced_frame_task.cc b/chrome/browser/task_manager/providers/web_contents/fenced_frame_task.cc
index 9244ac5..a75663f 100644
--- a/chrome/browser/task_manager/providers/web_contents/fenced_frame_task.cc
+++ b/chrome/browser/task_manager/providers/web_contents/fenced_frame_task.cc
@@ -8,6 +8,7 @@
 #include "chrome/grit/generated_resources.h"
 #include "content/public/browser/browser_context.h"
 #include "content/public/browser/render_frame_host.h"
+#include "content/public/browser/site_instance.h"
 #include "ui/base/l10n/l10n_util.h"
 #include "url/gurl.h"
 
@@ -19,28 +20,28 @@
           /*title=*/u"",
           /*icon=*/nullptr,
           /*subframe=*/render_frame_host),
-      render_frame_host_(render_frame_host),
+      site_instance_(render_frame_host->GetSiteInstance()),
       embedder_task_(embedder_task) {
   set_title(GetTitle());
 }
 
 void FencedFrameTask::Activate() {
-  DCHECK(embedder_task_);
   embedder_task_->Activate();
 }
 
+const task_manager::Task* FencedFrameTask::GetParentTask() const {
+  return embedder_task_;
+}
+
 void FencedFrameTask::UpdateTitle() {
   set_title(GetTitle());
 }
 
 std::u16string FencedFrameTask::GetTitle() const {
-  DCHECK(render_frame_host_);
-  const auto message_id =
-      render_frame_host_->GetBrowserContext()->IsOffTheRecord()
-          ? IDS_TASK_MANAGER_FENCED_FRAME_INCOGNITO_PREFIX
-          : IDS_TASK_MANAGER_FENCED_FRAME_PREFIX;
-  const auto title =
-      base::UTF8ToUTF16(render_frame_host_->GetLastCommittedURL().spec());
+  const auto message_id = site_instance_->GetBrowserContext()->IsOffTheRecord()
+                              ? IDS_TASK_MANAGER_FENCED_FRAME_INCOGNITO_PREFIX
+                              : IDS_TASK_MANAGER_FENCED_FRAME_PREFIX;
+  const auto title = base::UTF8ToUTF16(site_instance_->GetSiteURL().spec());
   return l10n_util::GetStringFUTF16(message_id, title);
 }
 
diff --git a/chrome/browser/task_manager/providers/web_contents/fenced_frame_task.h b/chrome/browser/task_manager/providers/web_contents/fenced_frame_task.h
index b8b79e9..fad12c1 100644
--- a/chrome/browser/task_manager/providers/web_contents/fenced_frame_task.h
+++ b/chrome/browser/task_manager/providers/web_contents/fenced_frame_task.h
@@ -10,6 +10,7 @@
 
 namespace content {
 class RenderFrameHost;
+class SiteInstance;
 }  // namespace content
 
 namespace task_manager {
@@ -25,6 +26,7 @@
 
   // task_manager::Task:
   void Activate() override;
+  const task_manager::Task* GetParentTask() const override;
   // task_manager::RendererTask:
   void UpdateTitle() override;
   void UpdateFavicon() override {}
@@ -32,22 +34,17 @@
  private:
   std::u16string GetTitle() const;
 
+  // The site instance is owned by the RFH and the RFH always outlives the task.
   // A FencedFrame task is deleted when:
-  // 1, a navigation happens to the fenced frame, so
-  //    |WebContentsEntry::RenderFrameHostChanged| is fired. Clearly in this
-  //    case both the embedder frame (hence the task) as well as the FF's RFH
-  //    are always alive.
-  // 2, when the user closes the tab / terminates the embedder process from task
-  //    manager/ terminates the FF's process from task manager. In the three
-  //    cases the task will be deleted from
-  //    |WebContentsEntry::RenderFrameDeleted|. When the user closes the tab or
-  //    terminates the embedder process, the tasks will be deleted in a
-  //    bottom-up fashion, from FF to embedder FF; when the user terminates the
-  //    process of the FF, FF's frame is deleted. So in the three cases the RFH
-  //    and embedder task are also guaranteed to outlive the FF task.
-  raw_ptr<content::RenderFrameHost> render_frame_host_;
+  // 1, a navigation happens to the FencedFrame (FF), so
+  //    |WebContentsEntry::RenderFrameHostChanged| is fired. Clearly the FF's
+  //    RFH is alive (thus the site instance).
+  // 2, when the fenced frame is destroyed (by either terminating the embedder
+  //    or FF's process). The |RenderFrameDeleted| will be triggered to delete
+  //    the task. At that point the RFH is still alive.
+  const raw_ptr<content::SiteInstance> site_instance_;
   // Allows us to focus on the embedder's tab.
-  raw_ptr<RendererTask> embedder_task_;
+  const raw_ptr<RendererTask> embedder_task_;
 };
 
 }  // namespace task_manager
diff --git a/chrome/browser/task_manager/providers/web_contents/prerender_task.cc b/chrome/browser/task_manager/providers/web_contents/prerender_task.cc
index bb644c90..0ac0a662 100644
--- a/chrome/browser/task_manager/providers/web_contents/prerender_task.cc
+++ b/chrome/browser/task_manager/providers/web_contents/prerender_task.cc
@@ -6,20 +6,23 @@
 
 #include "base/check.h"
 #include "base/strings/utf_string_conversions.h"
+#include "chrome/browser/task_manager/providers/web_contents/web_contents_task_provider.h"
 #include "chrome/grit/generated_resources.h"
-#include "chrome/grit/theme_resources.h"
-#include "content/public/browser/render_frame_host.h"
+#include "content/public/browser/web_contents.h"
+#include "content/public/browser/web_contents_delegate.h"
 #include "ui/base/l10n/l10n_util.h"
 #include "url/gurl.h"
 
 namespace task_manager {
 
-PrerenderTask::PrerenderTask(content::RenderFrameHost* render_frame_host)
+PrerenderTask::PrerenderTask(content::RenderFrameHost* render_frame_host,
+                             WebContentsTaskProvider* task_provider)
     : RendererTask(
           /*title=*/u"",
           /*icon=*/nullptr,
           /*subframe=*/render_frame_host),
-      render_frame_host_(render_frame_host) {
+      render_frame_host_(render_frame_host),
+      task_provider_(task_provider) {
   DCHECK(render_frame_host_);
   DCHECK_EQ(render_frame_host->GetLifecycleState(),
             content::RenderFrameHost::LifecycleState::kPrerendering);
@@ -28,12 +31,15 @@
 
 PrerenderTask::~PrerenderTask() = default;
 
+const Task* PrerenderTask::GetParentTask() const {
+  return task_provider_->GetTaskOfFrame(web_contents()->GetPrimaryMainFrame());
+}
+
 void PrerenderTask::UpdateTitle() {
   set_title(GetTitle());
 }
 
 std::u16string PrerenderTask::GetTitle() const {
-  DCHECK(render_frame_host_);
   const auto title =
       base::UTF8ToUTF16(render_frame_host_->GetLastCommittedURL().spec());
   return l10n_util::GetStringFUTF16(IDS_TASK_MANAGER_PRERENDER_PREFIX, title);
diff --git a/chrome/browser/task_manager/providers/web_contents/prerender_task.h b/chrome/browser/task_manager/providers/web_contents/prerender_task.h
index bc3aedf..3741a7cf6 100644
--- a/chrome/browser/task_manager/providers/web_contents/prerender_task.h
+++ b/chrome/browser/task_manager/providers/web_contents/prerender_task.h
@@ -14,14 +14,19 @@
 
 namespace task_manager {
 
+class WebContentsTaskProvider;
+
 // Defines a task manager entry for a Prerender2 rule.
 class PrerenderTask : public RendererTask {
  public:
-  explicit PrerenderTask(content::RenderFrameHost* render_frame_host);
+  explicit PrerenderTask(content::RenderFrameHost* render_frame_host,
+                         WebContentsTaskProvider* task_provider);
   PrerenderTask(const PrerenderTask&) = delete;
   PrerenderTask& operator=(const PrerenderTask&) = delete;
   ~PrerenderTask() override;
 
+  // task_manager::Task:
+  const Task* GetParentTask() const override;
   // task_manager::RendererTask:
   void UpdateTitle() override;
   void UpdateFavicon() override {}
@@ -32,7 +37,9 @@
   // prerender2 task is deleted at |WebContentsEntry::RenderFrameDeleted| and
   // |WebContentsEntry::RenderFrameHostStateChange|, and at both occasions the
   // RFH is still alive.
-  raw_ptr<content::RenderFrameHost> render_frame_host_;
+  const raw_ptr<content::RenderFrameHost> render_frame_host_;
+  // The provider has the same lifespan as the task manager.
+  const raw_ptr<WebContentsTaskProvider> task_provider_;
 };
 
 }  // namespace task_manager
diff --git a/chrome/browser/task_manager/providers/web_contents/subframe_task_browsertest.cc b/chrome/browser/task_manager/providers/web_contents/subframe_task_browsertest.cc
index 9ad8dec9..7eef369b 100644
--- a/chrome/browser/task_manager/providers/web_contents/subframe_task_browsertest.cc
+++ b/chrome/browser/task_manager/providers/web_contents/subframe_task_browsertest.cc
@@ -39,6 +39,14 @@
                                     std::u16string());
 }
 
+std::u16string PrefixExpectedBFCacheTitle(const std::string& title,
+                                          bool is_subframe) {
+  const auto msg_id = is_subframe
+                          ? IDS_TASK_MANAGER_BACK_FORWARD_CACHE_SUBFRAME_PREFIX
+                          : IDS_TASK_MANAGER_BACK_FORWARD_CACHE_PREFIX;
+  return l10n_util::GetStringFUTF16(msg_id, base::UTF8ToUTF16(title));
+}
+
 std::u16string PrefixExpectedTabTitle(const std::string& title) {
   return l10n_util::GetStringFUTF16(IDS_TASK_MANAGER_TAB_PREFIX,
                                     base::UTF8ToUTF16(title));
@@ -133,7 +141,25 @@
           ? 4U
           : 1U,
       task_manager.tasks().size());
-  const Task* simple_page_task = task_manager.tasks().front();
+
+  const auto& tasks = task_manager.tasks();
+  // Main page and two cross-origin iframes.
+  if (content::BackForwardCache::IsSameSiteBackForwardCacheFeatureEnabled()) {
+    EXPECT_EQ(
+        PrefixExpectedBFCacheTitle("http://a.com/", /*is_subframe=*/false),
+        tasks[0]->title());
+    EXPECT_EQ(PrefixExpectedBFCacheTitle("http://b.com/",
+                                         /*is_subframe=*/true),
+              tasks[1]->title());
+    EXPECT_EQ(PrefixExpectedBFCacheTitle("http://c.com/",
+                                         /*is_subframe=*/true),
+              tasks[2]->title());
+  }
+  // When navigation to |kSimplePageUrl| happens, tasks are first created for
+  // page a.com and two cross-origin iframes b.com and c.com from
+  // |RenderFrameHostStateChange|, then the task for |kSimplePageUrl| is created
+  // from |DidFinishNavigation| when the navigation completes. Thus |.back()|.
+  const Task* simple_page_task = tasks.back();
   EXPECT_EQ(Task::RENDERER, simple_page_task->GetType());
   EXPECT_EQ(PrefixExpectedTabTitle("Title Of Awesomeness"),
             simple_page_task->title());
diff --git a/chrome/browser/task_manager/providers/web_contents/tab_contents_tag_browsertest.cc b/chrome/browser/task_manager/providers/web_contents/tab_contents_tag_browsertest.cc
index 8e9521a5..b1bea20 100644
--- a/chrome/browser/task_manager/providers/web_contents/tab_contents_tag_browsertest.cc
+++ b/chrome/browser/task_manager/providers/web_contents/tab_contents_tag_browsertest.cc
@@ -24,6 +24,7 @@
 #include "content/public/browser/back_forward_cache.h"
 #include "content/public/browser/favicon_status.h"
 #include "content/public/browser/navigation_entry.h"
+#include "content/public/browser/site_instance.h"
 #include "content/public/test/browser_test.h"
 #include "content/public/test/fenced_frame_test_util.h"
 #include "content/public/test/test_utils.h"
@@ -134,7 +135,7 @@
   TabContentsTagTest() { EXPECT_TRUE(embedded_test_server()->Start()); }
   TabContentsTagTest(const TabContentsTagTest&) = delete;
   TabContentsTagTest& operator=(const TabContentsTagTest&) = delete;
-  ~TabContentsTagTest() override {}
+  ~TabContentsTagTest() override = default;
 
   void AddNewTestTabAt(int index, const char* test_page_file) {
     int tabs_count_before = tabs_count();
@@ -297,6 +298,11 @@
           browser()->tab_strip_model()->GetActiveWebContents());
   FaviconWaiter waiter(favicon_driver);
   waiter.WaitForFaviconWithURL(GetUrlOfFile("/favicon/icon.png"));
+  const auto favicon_url = browser()
+                               ->tab_strip_model()
+                               ->GetActiveWebContents()
+                               ->GetSiteInstance()
+                               ->GetSiteURL();
 
   // Check that the task manager uses the specified favicon for the page.
   base::FilePath test_dir;
@@ -324,13 +330,18 @@
     // same-site main frame navigations, we'll get a new task because we are
     // changing RenderFrameHosts. Note that the previous page's task might still
     // be around if the previous page is saved in the back-forward cache.
-    ASSERT_EQ(
-        content::BackForwardCache::IsSameSiteBackForwardCacheFeatureEnabled()
-            ? 2U
-            : 1U,
-        task_manager.tasks().size());
-    task = task_manager.tasks().front();
+    if (content::BackForwardCache::IsSameSiteBackForwardCacheFeatureEnabled()) {
+      ASSERT_EQ(2U, task_manager.tasks().size());
+      ASSERT_EQ(
+          l10n_util::GetStringFUTF16(IDS_TASK_MANAGER_BACK_FORWARD_CACHE_PREFIX,
+                                     base::UTF8ToUTF16(favicon_url.spec())),
+          task_manager.tasks().front()->title());
+    } else {
+      ASSERT_EQ(1U, task_manager.tasks().size());
+    }
   }
+
+  task = task_manager.tasks().back();
   ASSERT_EQ(GetDefaultTitleForUrl(no_favicon_page_url), task->title());
 
   // Check that the task manager uses the default favicon for the page.
diff --git a/chrome/browser/task_manager/providers/web_contents/web_contents_task_provider.cc b/chrome/browser/task_manager/providers/web_contents/web_contents_task_provider.cc
index 3a3d7a2..612001f2 100644
--- a/chrome/browser/task_manager/providers/web_contents/web_contents_task_provider.cc
+++ b/chrome/browser/task_manager/providers/web_contents/web_contents_task_provider.cc
@@ -12,7 +12,10 @@
 #include "base/containers/flat_set.h"
 #include "base/memory/raw_ptr.h"
 #include "base/memory/weak_ptr.h"
+#include "base/notreached.h"
 #include "chrome/browser/task_manager/providers/web_contents/back_forward_cache_task.h"
+#include "chrome/browser/task_manager/providers/web_contents/fenced_frame_task.h"
+#include "chrome/browser/task_manager/providers/web_contents/prerender_task.h"
 #include "chrome/browser/task_manager/providers/web_contents/subframe_task.h"
 #include "chrome/browser/task_manager/providers/web_contents/web_contents_tags_manager.h"
 #include "content/public/browser/navigation_handle.h"
@@ -64,7 +67,6 @@
       RenderFrameHost* render_frame_host,
       RenderFrameHost::LifecycleState old_state,
       RenderFrameHost::LifecycleState new_state) override;
-  void RenderFrameCreated(RenderFrameHost*) override;
   void WebContentsDestroyed() override;
   void OnRendererUnresponsive(RenderProcessHost* render_process_host) override;
   void DidFinishNavigation(
@@ -74,7 +76,7 @@
   void RenderFrameReady(int process_routing_id, int frame_routing_id);
 
  private:
-  // Defines a callback for WebContents::ForEachFrame() to create a
+  // Defines a callback for WebContents::ForEachRenderFrameHost() to create a
   // corresponding task for the given |render_frame_host| and notifying the
   // provider's observer of the new task.
   void CreateTaskForFrame(RenderFrameHost* render_frame_host);
@@ -104,7 +106,7 @@
   base::flat_map<SiteInstance*, SiteInstanceInfo> site_instance_infos_;
 
   // States whether we did record a main frame for this entry.
-  raw_ptr<SiteInstance> main_frame_site_instance_ = nullptr;
+  raw_ptr<SiteInstance> primary_main_frame_site_instance_ = nullptr;
 
   base::WeakPtrFactory<WebContentsEntry> weak_factory_{this};
 };
@@ -122,7 +124,7 @@
 
 void WebContentsTaskProvider::WebContentsEntry::CreateAllTasks() {
   DCHECK(web_contents()->GetPrimaryMainFrame());
-  web_contents()->ForEachFrame(base::BindRepeating(
+  web_contents()->ForEachRenderFrameHost(base::BindRepeating(
       &WebContentsEntry::CreateTaskForFrame, base::Unretained(this)));
 }
 
@@ -138,7 +140,7 @@
   }
 
   site_instance_infos_.clear();
-  main_frame_site_instance_ = nullptr;
+  primary_main_frame_site_instance_ = nullptr;
 }
 
 RendererTask* WebContentsTaskProvider::WebContentsEntry::GetTaskForFrame(
@@ -175,8 +177,6 @@
 void WebContentsTaskProvider::WebContentsEntry::RenderFrameHostChanged(
     RenderFrameHost* old_host,
     RenderFrameHost* new_host) {
-  DCHECK(new_host->IsActive());
-
   // The navigating frame and its subframes are now pending deletion. Stop
   // tracking them immediately rather than when they are destroyed. The order of
   // deletion is important. The children must be removed first.
@@ -184,12 +184,11 @@
     ClearTasksForDescendantsOf(old_host);
     ClearTaskForFrame(old_host);
   }
-
-  CreateTaskForFrame(new_host);
+  // Tasks creation for |new_host| is delayed to |DidFinishNavigation|.
 }
 
-// Only handle frames entering or exiting
-// RenderFrameHost::LifecycleState::kInBackForwardCache.
+// Handles creation and deletion of BFCache tasks for pages entering and leaving
+// the BFCache, and deletion of prerender tasks after prerendering activation.
 void WebContentsTaskProvider::WebContentsEntry::RenderFrameHostStateChanged(
     RenderFrameHost* render_frame_host,
     RenderFrameHost::LifecycleState old_state,
@@ -199,18 +198,14 @@
 
   if (new_state == RenderFrameHost::LifecycleState::kInBackForwardCache)
     CreateTaskForFrame(render_frame_host);
-}
 
-void WebContentsTaskProvider::WebContentsEntry::RenderFrameCreated(
-    RenderFrameHost* render_frame_host) {
-  DCHECK(render_frame_host->IsRenderFrameLive());
-
-  // Skip pending/speculative hosts. We'll create tasks for these if the
-  // navigation commits, at which point RenderFrameHostChanged() will fire.
-  if (!render_frame_host->IsActive())
-    return;
-
-  CreateTaskForFrame(render_frame_host);
+  // |RenderFrameDeleted| is not fired for prerender page activation so we need
+  // to delete prerender task. |RenderFrameHostStateChanged| is the earliest
+  // event of the prerendering activation flow.
+  if (old_state == RenderFrameHost::LifecycleState::kPrerendering &&
+      new_state == RenderFrameHost::LifecycleState::kActive) {
+    ClearTaskForFrame(render_frame_host);
+  }
 }
 
 void WebContentsTaskProvider::WebContentsEntry::RenderFrameReady(
@@ -252,9 +247,47 @@
 
 void WebContentsTaskProvider::WebContentsEntry::DidFinishNavigation(
     content::NavigationHandle* navigation_handle) {
-  // We only need to update tasks for primary main frame navigations.
-  if (!navigation_handle->IsInPrimaryMainFrame())
+  // No task creation/update for downloads and HTTP204/205.
+  if (!navigation_handle->HasCommitted()) {
     return;
+  }
+
+  // Creation.
+  //
+  // Create a task if we have not encountered this site instance before, or it
+  // is a new RenderFrameHost for this site instance. For same-document
+  // navigation, since neither |RenderFrameDeleted| nor |RenderFrameHostChanged|
+  // is fired to delete the existing task, we do not recreate them.
+  //
+  // TODO(crbug.com/1183630): DidFinishNavigation is not called when we create
+  // initial empty documents, and as a result, we will not create new tasks for
+  // these empty documents if they are in a different process from their
+  // embedder/opener (eg: an empty fenced frame or a blank tab created by
+  // window.open('', '_blank', 'noopener')). Ideally, we would call
+  // CreateTaskForFrame inside RenderFrameCreated instead (which is called for
+  // initial documents), but CreateTaskForFrame uses RFH::GetLifecycleState,
+  // which cannot currently be called inside RenderFrameCreated (due to a DCHECK
+  // which doesn't allow the method to be called when the state is
+  // 'kSpeculative').
+  auto* rfh = navigation_handle->GetRenderFrameHost();
+  auto* site_instance = rfh->GetSiteInstance();
+  auto it = site_instance_infos_.find(site_instance);
+  if (!navigation_handle->IsSameDocument() &&
+      (it == site_instance_infos_.end() ||
+       it->second.frames.find(rfh) == it->second.frames.end())) {
+    CreateTaskForFrame(rfh);
+  }
+
+  // Update.
+  //
+  // We only need to update tasks for primary main frame navigations.
+  // FencedFrame task gets the title from |SiteInstance::GetSiteURL()| which
+  // does not change for the same site instance, thus no need to update;
+  // prerender does not support multiple navigations thus no need to update its
+  // title.
+  if (!navigation_handle->IsInPrimaryMainFrame()) {
+    return;
+  }
 
   RendererTask* main_frame_task =
       GetTaskForFrame(web_contents()->GetPrimaryMainFrame());
@@ -288,21 +321,39 @@
 
 void WebContentsTaskProvider::WebContentsEntry::CreateTaskForFrame(
     RenderFrameHost* render_frame_host) {
+  DCHECK(render_frame_host);
   // Currently we do not track speculative RenderFrameHosts or RenderFrameHosts
   // which are pending deletion.
-  DCHECK(render_frame_host);
-  DCHECK(render_frame_host->IsActive() ||
-         render_frame_host->GetLifecycleState() ==
-             RenderFrameHost::LifecycleState::kInBackForwardCache);
+  const auto rfh_state = render_frame_host->GetLifecycleState();
+  switch (rfh_state) {
+    case RenderFrameHost::LifecycleState::kPrerendering:
+    case RenderFrameHost::LifecycleState::kInBackForwardCache:
+    case RenderFrameHost::LifecycleState::kActive:
+      break;
+    default:
+      NOTREACHED();
+      break;
+  }
 
-  // Exclude sad tabs and sad OOPIFs.
-  if (!render_frame_host->IsRenderFrameLive())
+  // Exclude sad tabs, sad OOPIFs.
+  if (!render_frame_host->IsRenderFrameLive()) {
     return;
+  }
+
+  // Another instance of this class will be created for inner WebContents. If we
+  // iterate into an inner WebContents that is not associated with `this`, skip
+  // it, so we don't create duplicated tasks. Task creation for RenderFrameHosts
+  // not associated with a WebContents should be handled by a different type of
+  // TaskProvider.
+  if (content::WebContents::FromRenderFrameHost(render_frame_host) !=
+      web_contents()) {
+    return;
+  }
 
   // Exclude frames in the same SiteInstance or same process as their parent;
   // |site_instance_infos_| only contains local roots.
   content::SiteInstance* site_instance = render_frame_host->GetSiteInstance();
-  auto* parent = render_frame_host->GetParent();
+  auto* parent = render_frame_host->GetParentOrOuterDocument();
   if (parent && (site_instance == parent->GetSiteInstance() ||
                  site_instance->GetProcess() ==
                      parent->GetSiteInstance()->GetProcess())) {
@@ -310,9 +361,10 @@
   }
 
   bool site_instance_exists = site_instance_infos_.count(site_instance) != 0;
-  bool is_primary_main_frame =
-      (render_frame_host == web_contents()->GetPrimaryMainFrame());
-  bool site_instance_is_main = (site_instance == main_frame_site_instance_);
+  auto* primary_main_rfh = web_contents()->GetPrimaryMainFrame();
+  bool is_primary_main_frame = (render_frame_host == primary_main_rfh);
+  bool site_instance_is_main =
+      (site_instance == primary_main_frame_site_instance_);
 
   std::unique_ptr<RendererTask> new_task;
 
@@ -321,22 +373,26 @@
   // represented by a SubframeTask.
   if (!site_instance_exists ||
       (is_primary_main_frame && !site_instance_is_main)) {
-    if (render_frame_host->GetLifecycleState() ==
-        RenderFrameHost::LifecycleState::kInBackForwardCache) {
-      // Use RFH::GetMainFrame instead web_contents()->GetMainFrame() because
-      // the BFCached frames are not the currently active main frame.
+    auto* primary_main_frame_task = GetTaskForFrame(primary_main_rfh);
+    if (rfh_state == RenderFrameHost::LifecycleState::kInBackForwardCache) {
+      // Use RFH::GetMainFrame instead web_contents()->GetPrimaryMainFrame()
+      // because the BFCached frames are not the currently active main frame.
       RenderFrameHost* main_frame = render_frame_host->GetMainFrame();
       new_task = std::make_unique<BackForwardCacheTask>(
-          render_frame_host, GetTaskForFrame(main_frame));
+          render_frame_host, GetTaskForFrame(main_frame), provider_);
+    } else if (rfh_state == RenderFrameHost::LifecycleState::kPrerendering) {
+      new_task = std::make_unique<PrerenderTask>(render_frame_host, provider_);
     } else if (is_primary_main_frame) {
       const WebContentsTag* tag =
           WebContentsTag::FromWebContents(web_contents());
       new_task = tag->CreateTask(provider_);
-      main_frame_site_instance_ = site_instance;
+      primary_main_frame_site_instance_ = site_instance;
+    } else if (render_frame_host->IsFencedFrameRoot()) {
+      new_task = std::make_unique<FencedFrameTask>(render_frame_host,
+                                                   primary_main_frame_task);
     } else {
-      new_task = std::make_unique<SubframeTask>(
-          render_frame_host,
-          GetTaskForFrame(web_contents()->GetPrimaryMainFrame()));
+      new_task = std::make_unique<SubframeTask>(render_frame_host,
+                                                primary_main_frame_task);
     }
   }
 
@@ -385,27 +441,30 @@
     site_instance_infos_.erase(itr);
     provider_->NotifyObserverTaskRemoved(renderer_task.get());
 
-    if (site_instance == main_frame_site_instance_)
-      main_frame_site_instance_ = nullptr;
+    if (site_instance == primary_main_frame_site_instance_)
+      primary_main_frame_site_instance_ = nullptr;
   }
 
 #if DCHECK_IS_ON()
   // Whenever we have a task, we should have a main frame site instance.
-  // However, when a tab is destroyed and there was a BFCached Task, the main
-  // task may be cleaned up before the BFCached Task.
+  // However, when a tab is destroyed and there was a BFCached Task or a
+  // prerender task, the main task may be cleaned up before the
+  // BFCached/prerender Task. BFCache or prerender tasks will be deleted
+  // asynchronously after the main frame is deleted.
 
-  bool only_bfcache_rfhs = true;
-  for (auto& entry : site_instance_infos_) {
-    for (auto* rfh : entry.second.frames) {
-      if (rfh->GetLifecycleState() !=
-          RenderFrameHost::LifecycleState::kInBackForwardCache) {
-        only_bfcache_rfhs = false;
+  bool only_bfcache_or_prerender_rfhs = true;
+  for (auto& [site_instance, site_instance_info] : site_instance_infos_) {
+    for (auto* rfh : site_instance_info.frames) {
+      const auto state = rfh->GetLifecycleState();
+      if (state != RenderFrameHost::LifecycleState::kInBackForwardCache &&
+          state != RenderFrameHost::LifecycleState::kPrerendering) {
+        only_bfcache_or_prerender_rfhs = false;
       }
     }
   }
-
-  DCHECK(only_bfcache_rfhs || site_instance_infos_.empty() ==
-                                  (main_frame_site_instance_ == nullptr));
+  DCHECK(only_bfcache_or_prerender_rfhs ||
+         site_instance_infos_.empty() ==
+             (primary_main_frame_site_instance_ == nullptr));
 #endif
 }
 
diff --git a/chrome/browser/task_manager/task_manager_browsertest.cc b/chrome/browser/task_manager/task_manager_browsertest.cc
index cb8c7c0b..1b3782d 100644
--- a/chrome/browser/task_manager/task_manager_browsertest.cc
+++ b/chrome/browser/task_manager/task_manager_browsertest.cc
@@ -10,6 +10,8 @@
 #include "base/files/file_path.h"
 #include "base/path_service.h"
 #include "base/run_loop.h"
+#include "base/strings/strcat.h"
+#include "base/strings/string_piece_forward.h"
 #include "base/strings/stringprintf.h"
 #include "base/strings/utf_string_conversions.h"
 #include "build/build_config.h"
@@ -18,6 +20,8 @@
 #include "chrome/browser/devtools/devtools_window_testing.h"
 #include "chrome/browser/extensions/extension_browsertest.h"
 #include "chrome/browser/notifications/notification_test_util.h"
+#include "chrome/browser/predictors/autocomplete_action_predictor.h"
+#include "chrome/browser/predictors/autocomplete_action_predictor_factory.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/task_manager/task_manager_browsertest_util.h"
 #include "chrome/browser/task_manager/task_manager_interface.h"
@@ -28,6 +32,8 @@
 #include "chrome/browser/ui/browser_window.h"
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
 #include "chrome/browser/web_applications/extensions/web_app_extension_shortcut.h"
+#include "chrome/common/chrome_features.h"
+#include "chrome/common/chrome_paths.h"
 #include "chrome/common/chrome_switches.h"
 #include "chrome/grit/generated_resources.h"
 #include "chrome/test/base/in_process_browser_test.h"
@@ -35,6 +41,7 @@
 #include "components/infobars/content/content_infobar_manager.h"
 #include "components/infobars/core/confirm_infobar_delegate.h"
 #include "components/infobars/core/infobar.h"
+#include "components/url_formatter/url_formatter.h"
 #include "content/public/browser/child_process_security_policy.h"
 #include "content/public/browser/notification_service.h"
 #include "content/public/browser/page_navigator.h"
@@ -44,7 +51,9 @@
 #include "content/public/test/browser_test.h"
 #include "content/public/test/browser_test_utils.h"
 #include "content/public/test/content_browser_test_utils.h"
+#include "content/public/test/fenced_frame_test_util.h"
 #include "content/public/test/no_renderer_crashes_assertion.h"
+#include "content/public/test/prerender_test_util.h"
 #include "content/public/test/test_navigation_observer.h"
 #include "extensions/browser/extension_registry.h"
 #include "extensions/common/extension.h"
@@ -53,20 +62,32 @@
 #include "services/strings/grit/services_strings.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/public/common/features.h"
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/base/page_transition_types.h"
 #include "url/gurl.h"
+#include "url/url_constants.h"
 
 using content::WebContents;
 using task_manager::browsertest_util::ColumnSpecifier;
 using task_manager::browsertest_util::MatchAboutBlankTab;
 using task_manager::browsertest_util::MatchAnyApp;
+using task_manager::browsertest_util::MatchAnyBFCache;
 using task_manager::browsertest_util::MatchAnyExtension;
+using task_manager::browsertest_util::MatchAnyFencedFrame;
+using task_manager::browsertest_util::MatchAnyIncognitoFencedFrame;
+using task_manager::browsertest_util::MatchAnyIncognitoTab;
+using task_manager::browsertest_util::MatchAnyPrerender;
 using task_manager::browsertest_util::MatchAnySubframe;
 using task_manager::browsertest_util::MatchAnyTab;
 using task_manager::browsertest_util::MatchAnyUtility;
 using task_manager::browsertest_util::MatchApp;
+using task_manager::browsertest_util::MatchBFCache;
 using task_manager::browsertest_util::MatchExtension;
+using task_manager::browsertest_util::MatchFencedFrame;
+using task_manager::browsertest_util::MatchIncognitoFencedFrame;
+using task_manager::browsertest_util::MatchIncognitoTab;
+using task_manager::browsertest_util::MatchPrerender;
 using task_manager::browsertest_util::MatchSubframe;
 using task_manager::browsertest_util::MatchTab;
 using task_manager::browsertest_util::MatchUtility;
@@ -1374,3 +1395,632 @@
               subframe_b_index < subframe_d_index);
   }
 }
+
+//==============================================================================
+// Prerender tasks test.
+namespace {
+// Prerender trigger page URL.
+const char kMainPageUrl[] = "/title2.html";
+// The prerendered URL.
+const char kPrerenderURL[] = "/title1.html";
+
+class AutocompleteActionPredictorObserverImpl
+    : public predictors::AutocompleteActionPredictor::Observer {
+ public:
+  explicit AutocompleteActionPredictorObserverImpl(
+      predictors::AutocompleteActionPredictor* predictor) {
+    observation_.Observe(predictor);
+  }
+
+  ~AutocompleteActionPredictorObserverImpl() override = default;
+
+  void WaitForInitialization() {
+    base::RunLoop loop;
+    waiting_ = loop.QuitClosure();
+    loop.Run();
+  }
+
+  // predictors::AutocompleteActionPredictor::Observer:
+  void OnInitialized() override {
+    DCHECK(waiting_);
+    std::move(waiting_).Run();
+  }
+
+  base::ScopedObservation<predictors::AutocompleteActionPredictor,
+                          predictors::AutocompleteActionPredictor::Observer>
+      observation_{this};
+  base::OnceClosure waiting_;
+};
+
+class PrerenderTaskBrowserTest : public TaskManagerBrowserTest {
+ public:
+  PrerenderTaskBrowserTest() {
+    // `blink::features::kPrerender2` and
+    // `blink::features::kPrerender2MemoryControls` are enabled in
+    // |prerender_helper_|.
+    prerender_helper_ = std::make_unique<content::test::PrerenderTestHelper>(
+        base::BindRepeating(&PrerenderTaskBrowserTest::GetActiveWebContents,
+                            base::Unretained(this)));
+    EXPECT_TRUE(blink::features::IsPrerender2Enabled());
+    feature_list_.InitWithFeaturesAndParameters(
+        /*enabled_features=*/
+        {
+            {features::kBackForwardCache,
+             {{"enable_same_site", "true"},
+              {"TimeToLiveInBackForwardCacheInSeconds", "3600"}}},
+            {features::kOmniboxTriggerForPrerender2, {}},
+        },
+        /*disabled_features=*/{});
+    EXPECT_TRUE(content::BackForwardCache::IsBackForwardCacheFeatureEnabled());
+  }
+  PrerenderTaskBrowserTest(const PrerenderTaskBrowserTest&) = delete;
+  PrerenderTaskBrowserTest& operator=(const PrerenderTaskBrowserTest&) = delete;
+  ~PrerenderTaskBrowserTest() override = default;
+
+  void SetUpCommandLine(base::CommandLine* command_line) override {
+    content::IsolateAllSitesForTesting(command_line);
+    ASSERT_TRUE(content::AreAllSitesIsolatedForTesting());
+    TaskManagerBrowserTest::SetUpCommandLine(command_line);
+  }
+
+  void SetUpOnMainThread() override {
+    host_resolver()->AddRule("*", "127.0.0.1");
+    embedded_test_server()->ServeFilesFromDirectory(
+        base::PathService::CheckedGet(chrome::DIR_TEST_DATA));
+    ASSERT_TRUE(embedded_test_server()->InitializeAndListen());
+    embedded_test_server()->StartAcceptingConnections();
+  }
+
+  void NavigateTo(base::StringPiece page_url) const {
+    ASSERT_TRUE(ui_test_utils::NavigateToURL(
+        browser(), embedded_test_server()->GetURL(page_url)));
+  }
+
+  WebContents* GetActiveWebContents() {
+    return browser()->tab_strip_model()->GetActiveWebContents();
+  }
+
+  predictors::AutocompleteActionPredictor* GetAutocompleteActionPredictor() {
+    return predictors::AutocompleteActionPredictorFactory::GetForProfile(
+        browser()->profile());
+  }
+
+  void WaitForAutocompleteActionPredictorInitialization() {
+    if (GetAutocompleteActionPredictor()->initialized()) {
+      return;
+    }
+    AutocompleteActionPredictorObserverImpl predictor_observer(
+        GetAutocompleteActionPredictor());
+    predictor_observer.WaitForInitialization();
+  }
+
+  WebContents* NavigateToURLWithDispositionAndTransition(
+      const GURL& url,
+      WindowOpenDisposition disposition,
+      ui::PageTransition transition) {
+    return GetActiveWebContents()->OpenURL(content::OpenURLParams(
+        url, content::Referrer(), disposition, transition,
+        /*is_renderer_initiated=*/false));
+  }
+
+  content::test::PrerenderTestHelper* prerender_helper() {
+    return prerender_helper_.get();
+  }
+
+  // Prerender's task title is constructed from |RFH->GetLastCommittedURL|,
+  // which contains the port of the testing webserver.
+  std::string port() const {
+    return base::NumberToString(embedded_test_server()->port());
+  }
+
+ private:
+  std::unique_ptr<content::test::PrerenderTestHelper> prerender_helper_;
+  base::test::ScopedFeatureList feature_list_;
+};
+
+}  // namespace
+
+// Tests that the task manager properly:
+// 1. shows the Prerender entry when the speculation rule is injected;
+// 2. shows the Prerender entry when the manager is closed and reopened.
+// 3. deletes the Prerender entry when the prerendered page is activated.
+IN_PROC_BROWSER_TEST_F(PrerenderTaskBrowserTest, ProperlyShowsTasks) {
+  ShowTaskManager();
+  ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(1, MatchAboutBlankTab()));
+
+  NavigateTo(kMainPageUrl);
+
+  const auto prerender_gurl = embedded_test_server()->GetURL(kPrerenderURL);
+
+  // Inject the speculation rule and wait for prerender to complete.
+  prerender_helper()->AddPrerender(prerender_gurl);
+
+  // Must have one tab task, one prerender task.
+  ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(1, MatchAnyTab()));
+  ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(1, MatchAnyPrerender()));
+  ASSERT_NO_FATAL_FAILURE(
+      WaitForTaskManagerRows(1, MatchTab("Title Of Awesomeness")));
+  ASSERT_NO_FATAL_FAILURE(
+      WaitForTaskManagerRows(1, MatchPrerender(prerender_gurl.spec())));
+
+  // "Close" the task manager and "reopen" it. We should see the same tasks.
+  HideTaskManager();
+  ShowTaskManager();
+  ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(1, MatchAnyTab()));
+  ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(1, MatchAnyPrerender()));
+  ASSERT_NO_FATAL_FAILURE(
+      WaitForTaskManagerRows(1, MatchTab("Title Of Awesomeness")));
+  ASSERT_NO_FATAL_FAILURE(
+      WaitForTaskManagerRows(1, MatchPrerender(prerender_gurl.spec())));
+
+  // Activate the prerender page. The triggering page is placed in BFCache,
+  // and the prerendered page is activated.
+  content::test::PrerenderHostObserver obs(*GetActiveWebContents(),
+                                           prerender_gurl);
+  content::test::PrerenderTestHelper::NavigatePrimaryPage(
+      *GetActiveWebContents(), prerender_gurl);
+  ASSERT_TRUE(obs.was_activated());
+
+  ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(1, MatchAnyTab()));
+  ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(1, MatchAnyBFCache()));
+  // Take out the "http://".
+  const auto tab_title =
+      url_formatter::FormatUrl(embedded_test_server()->GetURL(kPrerenderURL));
+  ASSERT_NO_FATAL_FAILURE(
+      WaitForTaskManagerRows(1, MatchTab(base::UTF16ToUTF8(tab_title))));
+  ASSERT_NO_FATAL_FAILURE(
+      WaitForTaskManagerRows(1, MatchBFCache("http://127.0.0.1/")));
+}
+
+// Tests that the task manager properly deletes the prerender task once the
+// prerender is cancelled.
+IN_PROC_BROWSER_TEST_F(PrerenderTaskBrowserTest,
+                       DeletesTaskAfterPrerenderKilled) {
+  ShowTaskManager();
+  ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(1, MatchAboutBlankTab()));
+
+  NavigateTo(kMainPageUrl);
+
+  const auto prerender_gurl = embedded_test_server()->GetURL(kPrerenderURL);
+  prerender_helper()->AddPrerender(prerender_gurl);
+
+  // Must have one tab task, one prerender task.
+  ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(1, MatchAnyTab()));
+  ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(1, MatchAnyPrerender()));
+  ASSERT_NO_FATAL_FAILURE(
+      WaitForTaskManagerRows(1, MatchTab("Title Of Awesomeness")));
+  ASSERT_NO_FATAL_FAILURE(
+      WaitForTaskManagerRows(1, MatchPrerender(prerender_gurl.spec())));
+
+  // Terminate the prerender task, which should signal the task manager to
+  // remove the prerender task entry.
+  {
+    content::ScopedAllowRendererCrashes scoped_allow_renderer_crashes;
+    int prerender_row =
+        FindResourceIndex(MatchPrerender(prerender_gurl.spec()));
+    ASSERT_NE(-1, prerender_row);
+    ASSERT_TRUE(model()->GetTabId(prerender_row).is_valid());
+    model()->Kill(prerender_row);
+    ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(1, MatchAnyTab()));
+    ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(0, MatchAnyPrerender()));
+    ASSERT_NO_FATAL_FAILURE(
+        WaitForTaskManagerRows(1, MatchTab("Title Of Awesomeness")));
+  }
+}
+
+// Tests that the task manager properly deletes the task of the trigger tab and
+// prerender when the trigger is terminated.
+IN_PROC_BROWSER_TEST_F(PrerenderTaskBrowserTest,
+                       DeletesTaskAfterTriggerPageKilled) {
+  ShowTaskManager();
+  ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(1, MatchAboutBlankTab()));
+
+  NavigateTo(kMainPageUrl);
+
+  const auto prerender_gurl = embedded_test_server()->GetURL(kPrerenderURL);
+  prerender_helper()->AddPrerender(prerender_gurl);
+
+  // Must have one tab task, one prerender task.
+  ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(1, MatchAnyTab()));
+  ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(1, MatchAnyPrerender()));
+  ASSERT_NO_FATAL_FAILURE(
+      WaitForTaskManagerRows(1, MatchTab("Title Of Awesomeness")));
+  ASSERT_NO_FATAL_FAILURE(
+      WaitForTaskManagerRows(1, MatchPrerender(prerender_gurl.spec())));
+
+  // Terminate the prerender task, which should signal the task manager to
+  // remove the prerender task entry.
+  {
+    content::ScopedAllowRendererCrashes scoped_allow_renderer_crashes;
+    int trigger_row = FindResourceIndex(MatchTab("Title Of Awesomeness"));
+    ASSERT_NE(-1, trigger_row);
+    ASSERT_TRUE(model()->GetTabId(trigger_row).is_valid());
+    model()->Kill(trigger_row);
+    ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(0, MatchAnyTab()));
+    ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(0, MatchAnyPrerender()));
+  }
+}
+
+// Test that the autocomplete action predictor trigger Prerender tasks are
+// properly displayed. Such predictor is used to trigger Omnibox Prerender.
+IN_PROC_BROWSER_TEST_F(PrerenderTaskBrowserTest,
+                       ProperlyShowsPrerenderTaskByAutocompletePredictor) {
+  ShowTaskManager();
+  ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(1, MatchAboutBlankTab()));
+
+  NavigateTo(kMainPageUrl);
+
+  ASSERT_TRUE(GetAutocompleteActionPredictor());
+  WaitForAutocompleteActionPredictorInitialization();
+  const auto prerender_gurl = embedded_test_server()->GetURL(kPrerenderURL);
+  GetAutocompleteActionPredictor()->StartPrerendering(
+      prerender_gurl, *(browser()->tab_strip_model()->GetActiveWebContents()),
+      gfx::Size(50, 50));
+
+  // One task for main page and one for the prerendered page.
+  ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(1, MatchAnyTab()));
+  ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(1, MatchAnyPrerender()));
+  ASSERT_NO_FATAL_FAILURE(
+      WaitForTaskManagerRows(1, MatchTab("Title Of Awesomeness")));
+  ASSERT_NO_FATAL_FAILURE(
+      WaitForTaskManagerRows(1, MatchPrerender(prerender_gurl.spec())));
+  // Main task stays after prerendered task is terminated.
+  {
+    content::ScopedAllowRendererCrashes scoped_allow_renderer_crashes;
+    int prerender_row =
+        FindResourceIndex(MatchPrerender(prerender_gurl.spec()));
+    ASSERT_NE(-1, prerender_row);
+    ASSERT_TRUE(model()->GetTabId(prerender_row).is_valid());
+    model()->Kill(prerender_row);
+    ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(1, MatchAnyTab()));
+    ASSERT_NO_FATAL_FAILURE(
+        WaitForTaskManagerRows(1, MatchTab("Title Of Awesomeness")));
+    ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(0, MatchAnyPrerender()));
+  }
+  // Both tasks are deleted after main task is terminated.
+  {
+    // Use a different URL because re-using the same URL does not trigger new
+    // prerendering:
+    // https://crsrc.org/c/chrome/browser/predictors/autocomplete_action_predictor.cc;l=208;drc=a08a4e1c3f6862b3b1385b8a040a4fdb524e509d
+    const char kNewPrerenderURL[] = "/title3.html";
+    const auto new_prerender_gurl =
+        embedded_test_server()->GetURL(kNewPrerenderURL);
+    GetAutocompleteActionPredictor()->StartPrerendering(
+        embedded_test_server()->GetURL(kNewPrerenderURL),
+        *GetActiveWebContents(), gfx::Size(50, 50));
+    ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(1, MatchAnyPrerender()));
+    ASSERT_NO_FATAL_FAILURE(
+        WaitForTaskManagerRows(1, MatchPrerender(new_prerender_gurl.spec())));
+
+    content::ScopedAllowRendererCrashes scoped_allow_renderer_crashes;
+    int trigger_row = FindResourceIndex(MatchTab("Title Of Awesomeness"));
+    ASSERT_NE(-1, trigger_row);
+    ASSERT_TRUE(model()->GetTabId(trigger_row).is_valid());
+    model()->Kill(trigger_row);
+    ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(0, MatchAnyTab()));
+    ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(0, MatchAnyPrerender()));
+  }
+}
+
+// Test that the Omnibox-triggered prerender activation clears the prerender
+// entry in the task manager.
+IN_PROC_BROWSER_TEST_F(PrerenderTaskBrowserTest,
+                       OmniboxPrerenderActivationClearsTask) {
+  ShowTaskManager();
+  ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(1, MatchAboutBlankTab()));
+
+  NavigateTo(kMainPageUrl);
+
+  ASSERT_TRUE(GetAutocompleteActionPredictor());
+  WaitForAutocompleteActionPredictorInitialization();
+  const auto prerender_gurl = embedded_test_server()->GetURL(kPrerenderURL);
+  GetAutocompleteActionPredictor()->StartPrerendering(
+      prerender_gurl, *GetActiveWebContents(), gfx::Size(50, 50));
+
+  // One task for main page and one for the prerendered page.
+  ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(1, MatchAnyTab()));
+  ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(1, MatchAnyPrerender()));
+  ASSERT_NO_FATAL_FAILURE(
+      WaitForTaskManagerRows(1, MatchTab("Title Of Awesomeness")));
+  ASSERT_NO_FATAL_FAILURE(
+      WaitForTaskManagerRows(1, MatchPrerender(prerender_gurl.spec())));
+
+  // Activate the Omnibox prerender, after which the prerender task should
+  // disappear.
+  content::test::PrerenderHostObserver obs(*GetActiveWebContents(),
+                                           prerender_gurl);
+  // |ui::PAGE_TRANSITION_FROM_ADDRESS_BAR| augmentation is required for omnibox
+  // activation.
+  auto* web_contents = NavigateToURLWithDispositionAndTransition(
+      prerender_gurl, WindowOpenDisposition::CURRENT_TAB,
+      ui::PageTransitionFromInt(ui::PAGE_TRANSITION_TYPED |
+                                ui::PAGE_TRANSITION_FROM_ADDRESS_BAR));
+  obs.WaitForActivation();
+  ASSERT_TRUE(obs.was_activated());
+  ASSERT_EQ(web_contents, GetActiveWebContents());  // Current tab.
+  ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(1, MatchAnyTab()));
+  ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(0, MatchAnyPrerender()));
+  // Take out the "http://".
+  ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(
+      1,
+      MatchTab(base::UTF16ToUTF8(url_formatter::FormatUrl(prerender_gurl)))));
+}
+
+//==============================================================================
+// FencedFrame tasks test.
+namespace {
+
+class FencedFrameTaskBrowserTest : public TaskManagerBrowserTest {
+ public:
+  FencedFrameTaskBrowserTest() {
+    EXPECT_TRUE(blink::features::IsFencedFramesEnabled());
+    EXPECT_TRUE(blink::features::IsFencedFramesMPArchBased());
+  }
+  FencedFrameTaskBrowserTest(const FencedFrameTaskBrowserTest&) = delete;
+  FencedFrameTaskBrowserTest& operator=(const FencedFrameTaskBrowserTest&) =
+      delete;
+  ~FencedFrameTaskBrowserTest() override = default;
+
+  void SetUpCommandLine(base::CommandLine* command_line) override {
+    content::IsolateAllSitesForTesting(command_line);
+    ASSERT_TRUE(content::AreAllSitesIsolatedForTesting());
+    TaskManagerBrowserTest::SetUpCommandLine(command_line);
+  }
+
+  void SetUpOnMainThread() override {
+    host_resolver()->AddRule("*", "127.0.0.1");
+    https_server()->ServeFilesFromDirectory(
+        base::PathService::CheckedGet(chrome::DIR_TEST_DATA));
+    https_server()->SetSSLConfig(net::EmbeddedTestServer::CERT_TEST_NAMES);
+    content::SetupCrossSiteRedirector(https_server());
+    ASSERT_TRUE(https_server()->InitializeAndListen());
+    https_server()->StartAcceptingConnections();
+  }
+
+  void NavigateTo(Browser* browser,
+                  base::StringPiece host,
+                  base::StringPiece rel_url) {
+    ASSERT_TRUE(ui_test_utils::NavigateToURL(
+        browser, https_server()->GetURL(host, rel_url)));
+  }
+
+  std::string GetFencedFrameTitle(base::StringPiece host) const {
+    return base::StrCat({"https://", host, "/"});
+  }
+
+  net::EmbeddedTestServer* https_server() { return &https_server_; }
+
+  content::test::FencedFrameTestHelper* helper() { return helper_.get(); }
+
+ private:
+  base::test::ScopedFeatureList feature_list_;
+  net::EmbeddedTestServer https_server_{net::EmbeddedTestServer::TYPE_HTTPS};
+  std::unique_ptr<content::test::FencedFrameTestHelper> helper_ =
+      std::make_unique<content::test::FencedFrameTestHelper>();
+};
+
+}  // namespace
+
+// Testing that the task manager properly displays fenced frame tasks with
+// re-opening task manager, and with fenced frame navigations.
+IN_PROC_BROWSER_TEST_F(FencedFrameTaskBrowserTest, ProperlyShowsTasks) {
+  ShowTaskManager();
+  ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(1, MatchAboutBlankTab()));
+
+  NavigateTo(browser(), "a.test", "/title2.html");
+  // Create two fenced frames.
+  auto* main_frame = browser()
+                         ->tab_strip_model()
+                         ->GetActiveWebContents()
+                         ->GetPrimaryMainFrame();
+  const auto initial_gurl =
+      https_server()->GetURL("a.test", "/fenced_frames/title1.html");
+  content::RenderFrameHostWrapper fenced_frame_rfh(
+      helper()->CreateFencedFrame(main_frame, initial_gurl));
+  ASSERT_TRUE(fenced_frame_rfh);
+
+  // One task for the embedder. Same origin fenced frame does not show up in the
+  // task manager.
+  ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(1, MatchAnyTab()));
+  ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(0, MatchAnyFencedFrame()));
+  ASSERT_NO_FATAL_FAILURE(
+      WaitForTaskManagerRows(1, MatchTab("Title Of Awesomeness")));
+
+  // Navigate the same-site FF to a cross-site url. The changes should be
+  // reflected in the task manager.
+  const auto cross_site_gurl =
+      https_server()->GetURL("b.test", "/fenced_frames/title2.html");
+  helper()->NavigateFrameInFencedFrameTree(fenced_frame_rfh.get(),
+                                           cross_site_gurl);
+  ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(1, MatchAnyTab()));
+  ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(1, MatchAnyFencedFrame()));
+  ASSERT_NO_FATAL_FAILURE(
+      WaitForTaskManagerRows(1, MatchTab("Title Of Awesomeness")));
+  ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(
+      1, MatchFencedFrame(GetFencedFrameTitle("b.test"))));
+
+  // Close the task manager and re-open it, all tasks should be re-created.
+  HideTaskManager();
+  ShowTaskManager();
+  ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(1, MatchAnyTab()));
+  ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(1, MatchAnyFencedFrame()));
+  ASSERT_NO_FATAL_FAILURE(
+      WaitForTaskManagerRows(1, MatchTab("Title Of Awesomeness")));
+  ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(
+      1, MatchFencedFrame(GetFencedFrameTitle("b.test"))));
+
+  // Terminate the fenced frame. The embedder frame remains intact.
+  {
+    content::ScopedAllowRendererCrashes scoped_allow_renderer_crashes;
+    int fenced_frame_row =
+        FindResourceIndex(MatchFencedFrame(GetFencedFrameTitle("b.test")));
+    ASSERT_NE(-1, fenced_frame_row);
+    ASSERT_TRUE(model()->GetTabId(fenced_frame_row).is_valid());
+    model()->Kill(fenced_frame_row);
+    ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(1, MatchAnyTab()));
+    ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(0, MatchAnyFencedFrame()));
+    ASSERT_NO_FATAL_FAILURE(
+        WaitForTaskManagerRows(1, MatchTab("Title Of Awesomeness")));
+  }
+  // Re-create the fenced frame and terminate the embedding frame. The
+  // embedder's task and the remaining fenced frame tasks are destroyed.
+  {
+    helper()->CreateFencedFrame(main_frame, initial_gurl);
+    content::ScopedAllowRendererCrashes scoped_allow_renderer_crashes;
+    int embedder_row = FindResourceIndex(MatchTab("Title Of Awesomeness"));
+    ASSERT_NE(-1, embedder_row);
+    ASSERT_TRUE(model()->GetTabId(embedder_row).is_valid());
+    model()->Kill(embedder_row);
+    ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(0, MatchAnyTab()));
+    ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(0, MatchAnyFencedFrame()));
+  }
+}
+
+// Test that the empty fenced frame (one without a `src`) is not shown in the
+// task manager. Not shown because we cannot observe any navigation events for
+// fenced frame creation (only |RenderFrameCreated| is triggered).
+IN_PROC_BROWSER_TEST_F(FencedFrameTaskBrowserTest, EmptyFencedFrameNotShown) {
+  const std::string kEmptyFencedFrameSnippet = R"(
+    const ff = document.createElement("fencedframe");
+    document.body.appendChild(ff);
+  )";
+
+  ShowTaskManager();
+  ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(1, MatchAboutBlankTab()));
+
+  NavigateTo(browser(), "a.test", "/title2.html");
+
+  auto* main_frame = browser()
+                         ->tab_strip_model()
+                         ->GetActiveWebContents()
+                         ->GetPrimaryMainFrame();
+  ASSERT_TRUE(content::ExecJs(main_frame, kEmptyFencedFrameSnippet));
+  ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(1, MatchAnyTab()));
+  ASSERT_NO_FATAL_FAILURE(
+      WaitForTaskManagerRows(1, MatchTab("Title Of Awesomeness")));
+  ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(0, MatchAnyFencedFrame()));
+
+  // Navigation on the empty fenced frame should create an entry.
+  auto* fenced_frame_rfh =
+      content::test::FencedFrameTestHelper::GetMostRecentlyAddedFencedFrame(
+          main_frame);
+  ASSERT_NE(fenced_frame_rfh, nullptr);
+  const auto fenced_frame_gurl =
+      https_server()->GetURL("b.test", "/fenced_frames/title1.html");
+  helper()->NavigateFrameInFencedFrameTree(fenced_frame_rfh, fenced_frame_gurl);
+  ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(1, MatchAnyTab()));
+  ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(1, MatchAnyFencedFrame()));
+  ASSERT_NO_FATAL_FAILURE(
+      WaitForTaskManagerRows(1, MatchTab("Title Of Awesomeness")));
+  ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(
+      1, MatchFencedFrame(GetFencedFrameTitle("b.test"))));
+}
+
+// Tests that the task manager properly shows tasks in Incognito mode.
+IN_PROC_BROWSER_TEST_F(FencedFrameTaskBrowserTest, ShowsIncognitoTask) {
+  auto* incognito_browser = CreateIncognitoBrowser();
+  ASSERT_NE(incognito_browser, nullptr);
+  ShowTaskManager();
+  ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(1, MatchAboutBlankTab()));
+
+  NavigateTo(incognito_browser, "a.test", "/title2.html");
+  auto* main_frame = incognito_browser->tab_strip_model()
+                         ->GetActiveWebContents()
+                         ->GetPrimaryMainFrame();
+  const auto fenced_frame_gurl =
+      https_server()->GetURL("b.test", "/fenced_frames/title1.html");
+  content::RenderFrameHostWrapper ff_rfh(
+      helper()->CreateFencedFrame(main_frame, fenced_frame_gurl));
+  ASSERT_TRUE(ff_rfh);
+  // Two tasks: one for the incognito main frame and another for the incognito
+  // fenced frames.
+  ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(1, MatchAnyIncognitoTab()));
+  ASSERT_NO_FATAL_FAILURE(
+      WaitForTaskManagerRows(1, MatchAnyIncognitoFencedFrame()));
+  ASSERT_NO_FATAL_FAILURE(
+      WaitForTaskManagerRows(1, MatchIncognitoTab("Title Of Awesomeness")));
+  ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(
+      1, MatchIncognitoFencedFrame(GetFencedFrameTitle("b.test"))));
+}
+
+// Test that clicking on the task manager fenced frame task row brings the focus
+// to the embedder page.
+IN_PROC_BROWSER_TEST_F(FencedFrameTaskBrowserTest, TaskActivationChangesFocus) {
+  ShowTaskManager();
+  ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(1, MatchAboutBlankTab()));
+
+  NavigateTo(browser(), "a.test", "/title2.html");
+  // Create one fenced frame.
+  auto* main_frame = browser()
+                         ->tab_strip_model()
+                         ->GetActiveWebContents()
+                         ->GetPrimaryMainFrame();
+  const auto fenced_frame_gurl =
+      https_server()->GetURL("b.test", "/fenced_frames/title1.html");
+  content::RenderFrameHostWrapper ff_rfh(
+      helper()->CreateFencedFrame(main_frame, fenced_frame_gurl));
+  ASSERT_TRUE(ff_rfh);
+
+  // One main tab task, one fenced frame task.
+  ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(1, MatchAnyTab()));
+  ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(1, MatchAnyFencedFrame()));
+  ASSERT_NO_FATAL_FAILURE(
+      WaitForTaskManagerRows(1, MatchTab("Title Of Awesomeness")));
+  ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(
+      1, MatchFencedFrame(GetFencedFrameTitle("b.test"))));
+
+  // Open a new tab of "about:blank". This appends an active WebContents at
+  // index 1.
+  ui_test_utils::NavigateToURLWithDisposition(
+      browser(), GURL(url::kAboutBlankURL),
+      WindowOpenDisposition::NEW_FOREGROUND_TAB,
+      ui_test_utils::BROWSER_TEST_WAIT_FOR_LOAD_STOP);
+
+  // The WebContents of "about:blank" is active.
+  ASSERT_EQ(browser()->tab_strip_model()->active_index(), 1);
+
+  const int fenced_frame_task_row =
+      FindResourceIndex(MatchFencedFrame(GetFencedFrameTitle("b.test")));
+  model()->Activate(fenced_frame_task_row);
+
+  // The WebContents of the embedder page is active.
+  ASSERT_EQ(browser()->tab_strip_model()->active_index(), 0);
+}
+
+// Test that same-document navigation does not change the task's title.
+IN_PROC_BROWSER_TEST_F(FencedFrameTaskBrowserTest,
+                       NoTitleChangeForSameDocNavigation) {
+  ShowTaskManager();
+  ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(1, MatchAboutBlankTab()));
+
+  NavigateTo(browser(), "a.test", "/title2.html");
+  // Create one fenced frame.
+  auto* main_frame = browser()
+                         ->tab_strip_model()
+                         ->GetActiveWebContents()
+                         ->GetPrimaryMainFrame();
+  const auto fenced_frame_gurl =
+      https_server()->GetURL("b.test", "/fenced_frames/title1.html");
+  content::RenderFrameHostWrapper ff_rfh(
+      helper()->CreateFencedFrame(main_frame, fenced_frame_gurl));
+  ASSERT_TRUE(ff_rfh);
+
+  // One main tab task, one fenced frame task.
+  ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(1, MatchAnyTab()));
+  ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(1, MatchAnyFencedFrame()));
+  ASSERT_NO_FATAL_FAILURE(
+      WaitForTaskManagerRows(1, MatchTab("Title Of Awesomeness")));
+  ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(
+      1, MatchFencedFrame(GetFencedFrameTitle("b.test"))));
+
+  // Same-doc navigation of the fenced frame.
+  const auto same_doc_navi_gurl = https_server()->GetURL(
+      "b.test", base::StrCat({"/fenced_frames/title1.html", "#same_doc_navi"}));
+  helper()->NavigateFrameInFencedFrameTree(ff_rfh.get(), same_doc_navi_gurl);
+  ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(1, MatchAnyTab()));
+  ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(1, MatchAnyFencedFrame()));
+  ASSERT_NO_FATAL_FAILURE(
+      WaitForTaskManagerRows(1, MatchTab("Title Of Awesomeness")));
+  ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(
+      1, MatchFencedFrame(GetFencedFrameTitle("b.test"))));
+}
diff --git a/chrome/browser/task_manager/task_manager_browsertest_util.cc b/chrome/browser/task_manager/task_manager_browsertest_util.cc
index 89d7309..52cb54a 100644
--- a/chrome/browser/task_manager/task_manager_browsertest_util.cc
+++ b/chrome/browser/task_manager/task_manager_browsertest_util.cc
@@ -187,9 +187,9 @@
   observer.RunUntilSatisfied();
 }
 
-std::u16string MatchTab(const char* title) {
+std::u16string MatchTab(base::StringPiece title) {
   return l10n_util::GetStringFUTF16(IDS_TASK_MANAGER_TAB_PREFIX,
-                                    base::ASCIIToUTF16(title));
+                                    base::UTF8ToUTF16(title));
 }
 
 std::u16string MatchAnyTab() {
@@ -200,6 +200,15 @@
   return MatchTab("about:blank");
 }
 
+std::u16string MatchIncognitoTab(base::StringPiece title) {
+  return l10n_util::GetStringFUTF16(IDS_TASK_MANAGER_TAB_INCOGNITO_PREFIX,
+                                    base::UTF8ToUTF16(title));
+}
+
+std::u16string MatchAnyIncognitoTab() {
+  return MatchIncognitoTab("*");
+}
+
 std::u16string MatchExtension(const char* title) {
   return l10n_util::GetStringFUTF16(IDS_TASK_MANAGER_EXTENSION_PREFIX,
                                     base::ASCIIToUTF16(title));
@@ -262,5 +271,40 @@
   return MatchUtility(u"*");
 }
 
+std::u16string MatchBFCache(base::StringPiece title) {
+  return l10n_util::GetStringFUTF16(IDS_TASK_MANAGER_BACK_FORWARD_CACHE_PREFIX,
+                                    base::UTF8ToUTF16(title));
+}
+
+std::u16string MatchAnyBFCache() {
+  return MatchBFCache("*");
+}
+
+std::u16string MatchPrerender(base::StringPiece title) {
+  return l10n_util::GetStringFUTF16(IDS_TASK_MANAGER_PRERENDER_PREFIX,
+                                    base::UTF8ToUTF16(title));
+}
+
+std::u16string MatchAnyPrerender() {
+  return MatchPrerender("*");
+}
+
+std::u16string MatchFencedFrame(base::StringPiece title) {
+  return l10n_util::GetStringFUTF16(IDS_TASK_MANAGER_FENCED_FRAME_PREFIX,
+                                    base::UTF8ToUTF16(title));
+}
+
+std::u16string MatchAnyFencedFrame() {
+  return MatchFencedFrame("*");
+}
+
+std::u16string MatchIncognitoFencedFrame(base::StringPiece title) {
+  return l10n_util::GetStringFUTF16(
+      IDS_TASK_MANAGER_FENCED_FRAME_INCOGNITO_PREFIX, base::UTF8ToUTF16(title));
+}
+
+std::u16string MatchAnyIncognitoFencedFrame() {
+  return MatchIncognitoFencedFrame("*");
+}
 }  // namespace browsertest_util
 }  // namespace task_manager
diff --git a/chrome/browser/task_manager/task_manager_browsertest_util.h b/chrome/browser/task_manager/task_manager_browsertest_util.h
index fa4a85e..40d80e6 100644
--- a/chrome/browser/task_manager/task_manager_browsertest_util.h
+++ b/chrome/browser/task_manager/task_manager_browsertest_util.h
@@ -15,6 +15,7 @@
 
 #include <string>
 
+#include "base/strings/string_piece_forward.h"
 
 namespace task_manager {
 namespace browsertest_util {
@@ -60,9 +61,11 @@
                                     size_t min_column_value);
 
 // ASCII matcher convenience functions for use with WaitForTaskManagerRows()
-std::u16string MatchTab(const char* title);         // "Tab: " + title
+std::u16string MatchTab(base::StringPiece title);   // "Tab: " + title
 std::u16string MatchAnyTab();                       // "Tab: *"
 std::u16string MatchAboutBlankTab();                // "Tab: about:blank"
+std::u16string MatchIncognitoTab(base::StringPiece title);
+std::u16string MatchAnyIncognitoTab();
 std::u16string MatchExtension(const char* title);   // "Extension: " + title
 std::u16string MatchAnyExtension();                 // "Extension: *"
 std::u16string MatchApp(const char* title);         // "App: " + title
@@ -78,7 +81,14 @@
 // "Utility: " + title
 std::u16string MatchUtility(const std::u16string& title);
 std::u16string MatchAnyUtility();  // "Utility: *"
-
+std::u16string MatchBFCache(base::StringPiece title);
+std::u16string MatchAnyBFCache();
+std::u16string MatchPrerender(base::StringPiece title);
+std::u16string MatchAnyPrerender();
+std::u16string MatchFencedFrame(base::StringPiece title);
+std::u16string MatchAnyFencedFrame();
+std::u16string MatchIncognitoFencedFrame(base::StringPiece title);
+std::u16string MatchAnyIncognitoFencedFrame();
 }  // namespace browsertest_util
 }  // namespace task_manager
 
diff --git a/chrome/browser/task_manager/task_manager_tester.cc b/chrome/browser/task_manager/task_manager_tester.cc
index 14b9adf9..34d7128 100644
--- a/chrome/browser/task_manager/task_manager_tester.cc
+++ b/chrome/browser/task_manager/task_manager_tester.cc
@@ -180,6 +180,10 @@
   model_->KillTask(row);
 }
 
+void TaskManagerTester::Activate(int row) {
+  model_->ActivateTask(row);
+}
+
 void TaskManagerTester::GetRowsGroupRange(int row,
                                           int* out_start,
                                           int* out_length) {
diff --git a/chrome/browser/task_manager/task_manager_tester.h b/chrome/browser/task_manager/task_manager_tester.h
index e7dd24d..ef7df21 100644
--- a/chrome/browser/task_manager/task_manager_tester.h
+++ b/chrome/browser/task_manager/task_manager_tester.h
@@ -55,6 +55,9 @@
   // Kill the process of |row|.
   void Kill(int row);
 
+  // Activate the task of |row|.
+  void Activate(int row);
+
   // Gets the start index and length of the group to which the task at
   // |row_index| belongs.
   void GetRowsGroupRange(int row, int* out_start, int* out_length);
diff --git a/chrome/browser/ui/BUILD.gn b/chrome/browser/ui/BUILD.gn
index 3f1321a6..a9210cc2 100644
--- a/chrome/browser/ui/BUILD.gn
+++ b/chrome/browser/ui/BUILD.gn
@@ -2237,6 +2237,8 @@
       "ash/default_pinned_apps.h",
       "ash/desks/chrome_desks_templates_delegate.cc",
       "ash/desks/chrome_desks_templates_delegate.h",
+      "ash/desks/chrome_desks_util.cc",
+      "ash/desks/chrome_desks_util.h",
       "ash/desks/desks_client.cc",
       "ash/desks/desks_client.h",
       "ash/desks/desks_templates_app_launch_handler.cc",
diff --git a/chrome/browser/ui/android/omnibox/BUILD.gn b/chrome/browser/ui/android/omnibox/BUILD.gn
index 5f1c6af..f4dae72 100644
--- a/chrome/browser/ui/android/omnibox/BUILD.gn
+++ b/chrome/browser/ui/android/omnibox/BUILD.gn
@@ -63,6 +63,7 @@
     "java/src/org/chromium/chrome/browser/omnibox/suggestions/DropdownItemViewInfo.java",
     "java/src/org/chromium/chrome/browser/omnibox/suggestions/DropdownItemViewInfoListBuilder.java",
     "java/src/org/chromium/chrome/browser/omnibox/suggestions/DropdownItemViewInfoListManager.java",
+    "java/src/org/chromium/chrome/browser/omnibox/suggestions/FaviconFetcher.java",
     "java/src/org/chromium/chrome/browser/omnibox/suggestions/OmniboxPedalDelegate.java",
     "java/src/org/chromium/chrome/browser/omnibox/suggestions/OmniboxQueryTileCoordinator.java",
     "java/src/org/chromium/chrome/browser/omnibox/suggestions/OmniboxSuggestionUiType.java",
@@ -369,6 +370,8 @@
     "java/src/org/chromium/chrome/browser/omnibox/suggestions/CachedZeroSuggestionsManagerUnitTest.java",
     "java/src/org/chromium/chrome/browser/omnibox/suggestions/DropdownItemViewInfoListBuilderUnitTest.java",
     "java/src/org/chromium/chrome/browser/omnibox/suggestions/DropdownItemViewInfoListManagerUnitTest.java",
+    "java/src/org/chromium/chrome/browser/omnibox/suggestions/FaviconFetcherUnitTest.java",
+    "java/src/org/chromium/chrome/browser/omnibox/suggestions/answer/AnswerSuggestionProcessorUnitTest.java",
     "java/src/org/chromium/chrome/browser/omnibox/suggestions/base/BaseSuggestionViewBinderUnitTest.java",
     "java/src/org/chromium/chrome/browser/omnibox/suggestions/base/BaseSuggestionViewProcessorUnitTest.java",
     "java/src/org/chromium/chrome/browser/omnibox/suggestions/base/BaseSuggestionViewTest.java",
@@ -379,6 +382,7 @@
     "java/src/org/chromium/chrome/browser/omnibox/suggestions/carousel/BaseCarouselSuggestionSelectionManagerUnitTest.java",
     "java/src/org/chromium/chrome/browser/omnibox/suggestions/carousel/BaseCarouselSuggestionViewBinderUnitTest.java",
     "java/src/org/chromium/chrome/browser/omnibox/suggestions/clipboard/ClipboardSuggestionProcessorUnitTest.java",
+    "java/src/org/chromium/chrome/browser/omnibox/suggestions/entity/EntitySuggestionProcessorUnitTest.java",
     "java/src/org/chromium/chrome/browser/omnibox/suggestions/header/HeaderViewBinderUnitTest.java",
     "java/src/org/chromium/chrome/browser/omnibox/suggestions/mostvisited/MostVisitedTilesProcessorUnitTest.java",
     "java/src/org/chromium/chrome/browser/omnibox/suggestions/tail/AlignmentManagerUnitTest.java",
@@ -415,6 +419,7 @@
     "//components/embedder_support/android:util_java",
     "//components/externalauth/android:java",
     "//components/favicon/android:java",
+    "//components/image_fetcher:java",
     "//components/omnibox/browser:browser_java",
     "//components/omnibox/browser:test_util_java",
     "//components/search_engines/android:java",
@@ -430,6 +435,7 @@
     "//third_party/androidx:androidx_recyclerview_recyclerview_java",
     "//third_party/androidx:androidx_test_core_java",
     "//third_party/androidx:androidx_test_runner_java",
+    "//third_party/hamcrest:hamcrest_core_java",
     "//third_party/hamcrest:hamcrest_library_java",
     "//third_party/junit",
     "//third_party/mockito:mockito_java",
diff --git a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/LocationBarCoordinator.java b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/LocationBarCoordinator.java
index ce044c9..1ac7e25a 100644
--- a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/LocationBarCoordinator.java
+++ b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/LocationBarCoordinator.java
@@ -65,9 +65,9 @@
  * <p>The coordinator creates and owns elements within this component.
  */
 
-public final class LocationBarCoordinator implements LocationBar, NativeInitObserver,
-                                                     OmniboxSuggestionsDropdownEmbedder,
-                                                     AutocompleteDelegate {
+public class LocationBarCoordinator implements LocationBar, NativeInitObserver,
+                                               OmniboxSuggestionsDropdownEmbedder,
+                                               AutocompleteDelegate {
     /** Identifies coordinators with methods specific to a device type. */
     public interface SubCoordinator {
         /** Destroys SubCoordinator. */
diff --git a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/LocationBarLayout.java b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/LocationBarLayout.java
index ad9a743..0466d02 100644
--- a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/LocationBarLayout.java
+++ b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/LocationBarLayout.java
@@ -295,6 +295,11 @@
         return mStatusCoordinator;
     }
 
+    @VisibleForTesting
+    public void setStatusCoordinatorForTesting(StatusCoordinator statusCoordinator) {
+        mStatusCoordinator = statusCoordinator;
+    }
+
     /* package */ void setUrlActionContainerVisibility(int visibility) {
         mUrlActionContainer.setVisibility(visibility);
     }
diff --git a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/DropdownItemViewInfoListBuilder.java b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/DropdownItemViewInfoListBuilder.java
index 5a499a0..44714c8 100644
--- a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/DropdownItemViewInfoListBuilder.java
+++ b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/DropdownItemViewInfoListBuilder.java
@@ -55,6 +55,7 @@
     private @Nullable HeaderProcessor mHeaderProcessor;
     private @Nullable Supplier<ShareDelegate> mShareDelegateSupplier;
     private @Nullable ImageFetcher mImageFetcher;
+    private @Nullable FaviconFetcher mFaviconFetcher;
     private @Nullable LargeIconBridge mIconBridge;
     private @NonNull BookmarkState mBookmarkState;
     @Px
@@ -87,22 +88,23 @@
         final Supplier<ShareDelegate> shareSupplier =
                 () -> mShareDelegateSupplier == null ? null : mShareDelegateSupplier.get();
 
+        mFaviconFetcher = new FaviconFetcher(context, iconBridgeSupplier);
+
         mHeaderProcessor = new HeaderProcessor(context, host, delegate);
         registerSuggestionProcessor(new EditUrlSuggestionProcessor(
-                context, host, delegate, iconBridgeSupplier, mActivityTabSupplier, shareSupplier));
+                context, host, delegate, mFaviconFetcher, mActivityTabSupplier, shareSupplier));
         registerSuggestionProcessor(
                 new AnswerSuggestionProcessor(context, host, textProvider, imageFetcherSupplier));
         registerSuggestionProcessor(
-                new ClipboardSuggestionProcessor(context, host, iconBridgeSupplier));
+                new ClipboardSuggestionProcessor(context, host, mFaviconFetcher));
         registerSuggestionProcessor(
                 new EntitySuggestionProcessor(context, host, imageFetcherSupplier));
         registerSuggestionProcessor(new TailSuggestionProcessor(context, host));
-        registerSuggestionProcessor(
-                new MostVisitedTilesProcessor(context, host, iconBridgeSupplier));
+        registerSuggestionProcessor(new MostVisitedTilesProcessor(context, host, mFaviconFetcher));
         registerSuggestionProcessor(new PedalSuggestionProcessor(context, host, textProvider,
-                iconBridgeSupplier, mBookmarkState, mOmniboxPedalDelegate, delegate));
+                mFaviconFetcher, mBookmarkState, mOmniboxPedalDelegate, delegate));
         registerSuggestionProcessor(new BasicSuggestionProcessor(
-                context, host, textProvider, iconBridgeSupplier, mBookmarkState));
+                context, host, textProvider, mFaviconFetcher, mBookmarkState));
     }
 
     void destroy() {
@@ -153,6 +155,10 @@
             mImageFetcher = null;
         }
 
+        if (mFaviconFetcher != null) {
+            mFaviconFetcher.clearCache();
+        }
+
         mIconBridge = new LargeIconBridge(profile);
         mImageFetcher = ImageFetcherFactory.createImageFetcher(ImageFetcherConfig.IN_MEMORY_ONLY,
                 profile.getProfileKey(), GlobalDiscardableReferencePool.getReferencePool(),
@@ -195,11 +201,9 @@
      * @param hasFocus Indicates whether URL bar is now focused.
      */
     void onUrlFocusChange(boolean hasFocus) {
-        if (!hasFocus && mImageFetcher != null) {
-            mImageFetcher.clear();
-        }
-
         if (!hasFocus) {
+            if (mImageFetcher != null) mImageFetcher.clear();
+            if (mFaviconFetcher != null) mFaviconFetcher.clearCache();
             mBuiltListHasFullyConcealedElements = false;
         }
 
diff --git a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/FaviconFetcher.java b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/FaviconFetcher.java
new file mode 100644
index 0000000..5c018db1
--- /dev/null
+++ b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/FaviconFetcher.java
@@ -0,0 +1,235 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.browser.omnibox.suggestions;
+
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.util.LruCache;
+
+import androidx.annotation.IntDef;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.annotation.VisibleForTesting;
+
+import org.chromium.base.supplier.Supplier;
+import org.chromium.base.task.PostTask;
+import org.chromium.chrome.browser.omnibox.R;
+import org.chromium.components.browser_ui.widget.RoundedIconGenerator;
+import org.chromium.components.favicon.LargeIconBridge;
+import org.chromium.content_public.browser.UiThreadTaskTraits;
+import org.chromium.url.GURL;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Favicon fetching mechanism with multi-step fallback functionality.
+ * The class supports very simple caching mechanism which remembers what type of icon is available
+ * for a given URL to reduce number of JNI and object creation calls.
+ */
+public class FaviconFetcher {
+    /**
+     * Maximum number of IconAvailability records to keep.
+     * The cache used here is cheap (cost of one GURL and one int per entry), but
+     * allows us to reduce number of JNI calls for URLs we already confirmed to have
+     * no icons.
+     */
+    private static final int MAX_ICON_AVAILABILITY_RECORDS = 256;
+
+    /** Variants of available Favicons. */
+    @IntDef({FaviconType.NONE, FaviconType.REGULAR, FaviconType.SMALL, FaviconType.GENERATED})
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface FaviconType {
+        // No favicon or fallback icon is available.
+        int NONE = 0;
+        // A regular size favicon is available.
+        int REGULAR = 1;
+        // Regular size favicon is not available, but small favicon is.
+        int SMALL = 2;
+        // Neither regular nor small favicon is available - fall back to RoundedIconGenerator.
+        int GENERATED = 3;
+    }
+
+    /** Receiver of the Favicon fetch. */
+    public interface FaviconFetchCompleteListener {
+        /**
+         * Invoked when a favicon fetch is complete.
+         * @param bitmap The resulting bitmap; may be null if favicon could not be retrieved for
+         *         the supplied URL (reported with FaviconType.NONE).
+         * @param type The type of a favicon that was retrieved, or FaviconType.NONE if favicon
+         *         could not be retrieved/acquired from any source.
+         */
+        void onFaviconFetchComplete(@Nullable Bitmap bitmap, @FaviconType int type);
+    }
+
+    private final @NonNull LruCache<GURL, Integer> mFaviconTypeCache;
+    private final @NonNull Supplier<LargeIconBridge> mIconBridgeSupplier;
+    private int mDesiredFaviconWidthPx;
+    private @NonNull RoundedIconGenerator mIconGenerator;
+
+    /**
+     * Constructor.
+     *
+     * @param context An Android context.
+     * @param iconBridgeSupplier Supplier of the LargeIconBridge used to fetch site favicons.
+     */
+    public FaviconFetcher(
+            @NonNull Context context, @NonNull Supplier<LargeIconBridge> iconBridgeSupplier) {
+        mIconBridgeSupplier = iconBridgeSupplier;
+        mDesiredFaviconWidthPx = context.getResources().getDimensionPixelSize(
+                R.dimen.omnibox_suggestion_favicon_size);
+
+        int fallbackIconSize =
+                context.getResources().getDimensionPixelSize(R.dimen.tile_view_icon_size);
+        int fallbackIconColor = context.getColor(R.color.default_favicon_background_color);
+        int fallbackIconTextSize =
+                context.getResources().getDimensionPixelSize(R.dimen.tile_view_icon_text_size);
+        mIconGenerator = new RoundedIconGenerator(fallbackIconSize, fallbackIconSize,
+                fallbackIconSize / 2, fallbackIconColor, fallbackIconTextSize);
+
+        mFaviconTypeCache = new LruCache(MAX_ICON_AVAILABILITY_RECORDS);
+    }
+
+    /**
+     * Retrieve a favicon for a particular URL, retrying the fetch request with a smaller size if
+     * the requested size does not yield any results.
+     *
+     * @param url The url to retrieve a favicon for.
+     * @param allowGeneratedIcon When true, Fetcher will generate a favicon if all fetch attempts
+     *         have failed.
+     * @param callback The callback to invoke with a favicon, once the result is known.
+     *         The callback will be invoked with fallback bitmap if favicon cannot be retrieved.
+     */
+    public void fetchFaviconWithBackoff(@NonNull GURL url, boolean allowGeneratedIcon,
+            @NonNull FaviconFetchCompleteListener callback) {
+        // Check for possible former records on the favicon to determine where to look for it.
+        @FaviconType
+        Integer faviconType = mFaviconTypeCache.get(url);
+        if (faviconType == null) {
+            faviconType = FaviconType.REGULAR;
+        }
+
+        // If there are no regular favicons, and the caller rejects generated ones, abort.
+        if ((faviconType == FaviconType.GENERATED) && !allowGeneratedIcon) {
+            callback.onFaviconFetchComplete(null, FaviconType.NONE);
+            return;
+        }
+
+        FaviconFetchCompleteListener backoffListener = new FaviconFetchCompleteListener() {
+            @Override
+            public void onFaviconFetchComplete(@Nullable Bitmap icon, @FaviconType int type) {
+                // Pass an icon directly to the callee if the fetch succeeded.
+                if (icon != null) {
+                    // Remember the type of an icon to avoid backoff the next time we need this
+                    // icon.
+                    mFaviconTypeCache.put(url, type);
+                    callback.onFaviconFetchComplete(icon, type);
+                    return;
+                }
+
+                // Back off and try again otherwise.
+                switch (type) {
+                    case FaviconType.REGULAR:
+                        fetchFaviconType(url, FaviconType.SMALL, this);
+                        break;
+
+                    case FaviconType.SMALL:
+                        // At this point it's safe to assume the only type we can offer is a
+                        // generated icon. This is because REGULAR and SMALL icon fetches have
+                        // already failed. This is a minor optimization that helps us reduce number
+                        // of calls to LargeIconBridge. Note this is safe even if we don't want
+                        // generated icons. This is because we won't be generating an icon in such
+                        // case.
+                        mFaviconTypeCache.put(url, FaviconType.GENERATED);
+
+                        if (allowGeneratedIcon) {
+                            fetchFaviconType(url, FaviconType.GENERATED, this);
+                            break;
+                        }
+                        // fallthrough: we don't allow generated icons.
+
+                    case FaviconType.GENERATED:
+                    default:
+                        callback.onFaviconFetchComplete(null, FaviconType.NONE);
+                        break;
+                }
+            }
+        };
+
+        fetchFaviconType(url, faviconType, backoffListener);
+    }
+
+    /**
+     * For a supplied url, retrieve a favicon of desired faviconType and deliver the result via the
+     * supplied callback.
+     * All fetches are done asynchronously, but the caller should expect a synchronous call in some
+     * cases, eg if an icon cannot be retrieved because a corresponding provider is not available.
+     *
+     * @param url The url to retrieve a favicon for.
+     * @param faviconType The specific type of favicon to retrieve.
+     * @param callback The callback that will be invoked with the result.
+     */
+    private void fetchFaviconType(@NonNull GURL url, @FaviconType int faviconType,
+            @NonNull FaviconFetchCompleteListener callback) {
+        LargeIconBridge iconBridge = mIconBridgeSupplier.get();
+
+        // Determine if we need LargeIconBridge for this fetch, and abort early if we can't fulfill
+        // the request.
+        if ((faviconType == FaviconType.REGULAR || faviconType == FaviconType.SMALL)
+                && (iconBridge == null)) {
+            callback.onFaviconFetchComplete(null, faviconType);
+            return;
+        }
+
+        switch (faviconType) {
+            case FaviconType.REGULAR:
+                iconBridge.getLargeIconForUrl(url, mDesiredFaviconWidthPx,
+                        (icon, fallbackColor, isFallbackColorDefault,
+                                iconType) -> callback.onFaviconFetchComplete(icon, faviconType));
+                return;
+
+            case FaviconType.SMALL:
+                iconBridge.getLargeIconForUrl(url, mDesiredFaviconWidthPx / 2,
+                        (icon, fallbackColor, isFallbackColorDefault,
+                                iconType) -> callback.onFaviconFetchComplete(icon, faviconType));
+                return;
+
+            case FaviconType.GENERATED:
+                PostTask.postTask(UiThreadTaskTraits.DEFAULT, () -> {
+                    Bitmap icon = mIconGenerator.generateIconForUrl(url);
+                    callback.onFaviconFetchComplete(icon, faviconType);
+                });
+                return;
+
+            default:
+                assert false : "Invalid favicon type requested: " + faviconType;
+                callback.onFaviconFetchComplete(null, faviconType);
+                return;
+        }
+    }
+
+    /** Clear all cached entries. */
+    public void clearCache() {
+        mFaviconTypeCache.evictAll();
+    }
+
+    /**
+     * Overrides RoundedIconGenerator for testing.
+     * @param generator RoundedIconGenerator to use.
+     */
+    @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
+    void setRoundedIconGeneratorForTesting(@NonNull RoundedIconGenerator generator) {
+        mIconGenerator = generator;
+    }
+
+    /**
+     * Overrides desired favicon size for testing.
+     * @param desiredFaviconSizePx Desired favicon size in pixels.
+     */
+    @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
+    void setDesiredFaviconSizeForTesting(int desiredFaviconSizePx) {
+        mDesiredFaviconWidthPx = desiredFaviconSizePx;
+    }
+}
diff --git a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/FaviconFetcherUnitTest.java b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/FaviconFetcherUnitTest.java
new file mode 100644
index 0000000..e7a86091
--- /dev/null
+++ b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/FaviconFetcherUnitTest.java
@@ -0,0 +1,276 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.browser.omnibox.suggestions;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.clearInvocations;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+import static org.mockito.Mockito.when;
+
+import android.app.Activity;
+import android.graphics.Bitmap;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.annotation.Px;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+import org.robolectric.Robolectric;
+import org.robolectric.annotation.Config;
+import org.robolectric.shadows.ShadowLog;
+import org.robolectric.shadows.ShadowLooper;
+
+import org.chromium.base.test.BaseRobolectricTestRunner;
+import org.chromium.chrome.R;
+import org.chromium.chrome.browser.omnibox.suggestions.FaviconFetcher.FaviconFetchCompleteListener;
+import org.chromium.chrome.browser.omnibox.suggestions.FaviconFetcher.FaviconType;
+import org.chromium.components.browser_ui.widget.RoundedIconGenerator;
+import org.chromium.components.favicon.LargeIconBridge;
+import org.chromium.components.favicon.LargeIconBridge.LargeIconCallback;
+import org.chromium.url.GURL;
+import org.chromium.url.JUnitTestGURLs;
+
+/**
+ * Tests for {@link FaviconFetcher}.
+ */
+@RunWith(BaseRobolectricTestRunner.class)
+@Config(manifest = Config.NONE, shadows = {ShadowLooper.class})
+public final class FaviconFetcherUnitTest {
+    private static final GURL NAV_URL = JUnitTestGURLs.getGURL(JUnitTestGURLs.URL_1);
+    private static final GURL NAV_URL_2 = JUnitTestGURLs.getGURL(JUnitTestGURLs.URL_2);
+    private static final int FALLBACK_COLOR = 0xACE0BA5E;
+    private static final int REGULAR_FAVICON_SIZE_PX = 100;
+    private static final int SMALL_FAVICON_SIZE_PX = REGULAR_FAVICON_SIZE_PX / 2;
+
+    public @Rule MockitoRule mockitoRule = MockitoJUnit.rule();
+
+    private Activity mActivity;
+    private ArgumentCaptor<LargeIconCallback> mIconCallbackCaptor =
+            ArgumentCaptor.forClass(LargeIconCallback.class);
+
+    private FaviconFetcher mFetcher;
+
+    private @Mock Bitmap mGeneratedIconBitmap;
+    private @Mock Bitmap mFavIconBitmap;
+    private @Mock LargeIconBridge mLargeIconBridge;
+    private @Mock RoundedIconGenerator mIconGenerator;
+    private @Mock FaviconFetchCompleteListener mCallback;
+
+    @Before
+    public void setUp() {
+        // Enable logs to be printed along with possible test failures.
+        ShadowLog.stream = System.out;
+
+        mActivity = Robolectric.buildActivity(Activity.class).setup().get();
+        mActivity.setTheme(R.style.Theme_BrowserUI_DayNight);
+
+        mFetcher = new FaviconFetcher(mActivity, () -> mLargeIconBridge);
+        mFetcher.setRoundedIconGeneratorForTesting(mIconGenerator);
+        mFetcher.setDesiredFaviconSizeForTesting(REGULAR_FAVICON_SIZE_PX);
+
+        when(mIconGenerator.generateIconForUrl(any(GURL.class))).thenReturn(mGeneratedIconBitmap);
+        when(mLargeIconBridge.getLargeIconForUrl(any(), anyInt(), mIconCallbackCaptor.capture()))
+                .thenReturn(true);
+    }
+
+    /**
+     * Confirm that icon of expected size was requested from LargeIconBridge, and report a
+     * supplied bitmap back to the caller.
+     *
+     * @param bitmap The bitmap to return to the caller (may be null).
+     */
+    private void verifyLargeIconBridgeRequest(
+            @NonNull GURL url, @Px int size, @Nullable Bitmap bitmap) {
+        ShadowLooper.runUiThreadTasks();
+        verify(mLargeIconBridge, times(1))
+                .getLargeIconForUrl(eq(url), eq(size), mIconCallbackCaptor.capture());
+        mIconCallbackCaptor.getValue().onLargeIconAvailable(bitmap, FALLBACK_COLOR, true, 0);
+    }
+
+    /**
+     * Confirm that icon was requested from RoundedIconGenerator, and report a
+     * supplied bitmap back to the caller.
+     *
+     * @param bitmap The bitmap to return to the caller (may be null).
+     */
+    private void verifyRoundedIconRequest(@NonNull GURL url, @Nullable Bitmap bitmap) {
+        doReturn(bitmap).when(mIconGenerator).generateIconForUrl(eq(url));
+        ShadowLooper.runUiThreadTasks();
+        verify(mIconGenerator, times(1)).generateIconForUrl(eq(url));
+    }
+
+    /**
+     * Confirm the type of icon reported to the caller.
+     *
+     * @param bitmap The expected bitmap.
+     * @param type The expected favicon type.
+     */
+    private void verifyReturnedIcon(@Nullable Bitmap bitmap, @FaviconType int type) {
+        verify(mCallback, times(1)).onFaviconFetchComplete(eq(bitmap), eq(type));
+    }
+
+    /**
+     * Confirm no unexpected calls were made to any of our data producers or consumers and
+     * clear all counters.
+     */
+    private void verifyNoOtherInteractionsAndClearInteractions() {
+        if (mLargeIconBridge != null) {
+            verifyNoMoreInteractions(mLargeIconBridge);
+            clearInvocations(mLargeIconBridge);
+        }
+        verifyNoMoreInteractions(mIconGenerator, mCallback);
+        clearInvocations(mIconGenerator, mCallback);
+    }
+
+    @Test
+    public void testIconRetrieval_noLargeIconBridge() {
+        // Favicon service does not exist, so we should expect a _single_ call to
+        // RoundedIconGenerator.
+        mLargeIconBridge = null;
+        mFetcher.fetchFaviconWithBackoff(NAV_URL, true, mCallback);
+        verifyRoundedIconRequest(NAV_URL, mGeneratedIconBitmap);
+        verifyReturnedIcon(mGeneratedIconBitmap, FaviconType.GENERATED);
+        verifyNoOtherInteractionsAndClearInteractions();
+    }
+
+    @Test
+    public void testIconRetrieval_largeIconAvailableWithNoBackoff() {
+        mFetcher.fetchFaviconWithBackoff(NAV_URL, true, mCallback);
+        verifyLargeIconBridgeRequest(NAV_URL, REGULAR_FAVICON_SIZE_PX, mFavIconBitmap);
+        verifyReturnedIcon(mFavIconBitmap, FaviconType.REGULAR);
+        verifyNoOtherInteractionsAndClearInteractions();
+
+        // Try again, blocking generated icons.
+        mFetcher.fetchFaviconWithBackoff(NAV_URL, false, mCallback);
+        verifyLargeIconBridgeRequest(NAV_URL, REGULAR_FAVICON_SIZE_PX, mFavIconBitmap);
+        verifyReturnedIcon(mFavIconBitmap, FaviconType.REGULAR);
+        verifyNoOtherInteractionsAndClearInteractions();
+    }
+
+    @Test
+    public void testIconRetrieval_backOffToSmallIcon() {
+        mFetcher.fetchFaviconWithBackoff(NAV_URL, true, mCallback);
+
+        verifyLargeIconBridgeRequest(NAV_URL, REGULAR_FAVICON_SIZE_PX, null);
+        verifyLargeIconBridgeRequest(NAV_URL, SMALL_FAVICON_SIZE_PX, mFavIconBitmap);
+        verifyReturnedIcon(mFavIconBitmap, FaviconType.SMALL);
+        verifyNoOtherInteractionsAndClearInteractions();
+
+        // Try again, blocking generated icons. Observe we go directly for small icon.
+        mFetcher.fetchFaviconWithBackoff(NAV_URL, false, mCallback);
+        verifyLargeIconBridgeRequest(NAV_URL, SMALL_FAVICON_SIZE_PX, mFavIconBitmap);
+        verifyReturnedIcon(mFavIconBitmap, FaviconType.SMALL);
+        verifyNoOtherInteractionsAndClearInteractions();
+    }
+
+    @Test
+    public void testIconRetrieval_backOffToGeneratedIcon() {
+        mFetcher.fetchFaviconWithBackoff(NAV_URL, true, mCallback);
+
+        verifyLargeIconBridgeRequest(NAV_URL, REGULAR_FAVICON_SIZE_PX, null);
+        verifyLargeIconBridgeRequest(NAV_URL, SMALL_FAVICON_SIZE_PX, null);
+        verifyRoundedIconRequest(NAV_URL, mGeneratedIconBitmap);
+        verifyReturnedIcon(mGeneratedIconBitmap, FaviconType.GENERATED);
+        verifyNoOtherInteractionsAndClearInteractions();
+
+        // Try again, observe we go directly for generated icon.
+        mFetcher.fetchFaviconWithBackoff(NAV_URL, true, mCallback);
+        verifyRoundedIconRequest(NAV_URL, mGeneratedIconBitmap);
+        verifyReturnedIcon(mGeneratedIconBitmap, FaviconType.GENERATED);
+        verifyNoOtherInteractionsAndClearInteractions();
+    }
+
+    @Test
+    public void testIconRetrieval_backOffWithNoGeneratedIcons() {
+        mFetcher.fetchFaviconWithBackoff(NAV_URL, false, mCallback);
+
+        // Expect _no_ calls to icon generator.
+        verifyLargeIconBridgeRequest(NAV_URL, REGULAR_FAVICON_SIZE_PX, null);
+        verifyLargeIconBridgeRequest(NAV_URL, SMALL_FAVICON_SIZE_PX, null);
+        verifyReturnedIcon(null, FaviconType.NONE);
+        verifyNoOtherInteractionsAndClearInteractions();
+
+        // Try again, allowing generated icons. Observe we go directly for generated icon.
+        mFetcher.fetchFaviconWithBackoff(NAV_URL, true, mCallback);
+        verifyRoundedIconRequest(NAV_URL, mGeneratedIconBitmap);
+        verifyReturnedIcon(mGeneratedIconBitmap, FaviconType.GENERATED);
+        verifyNoOtherInteractionsAndClearInteractions();
+
+        // Try again, rejecting generated icons. Observe we go directly for generated icon.
+        mFetcher.fetchFaviconWithBackoff(NAV_URL, false, mCallback);
+        verifyReturnedIcon(null, FaviconType.NONE);
+        verifyNoOtherInteractionsAndClearInteractions();
+    }
+
+    @Test
+    public void testIconRetrieval_nullBitmapWhenAllMechanismFail() {
+        mFetcher.fetchFaviconWithBackoff(NAV_URL, true, mCallback);
+
+        verifyLargeIconBridgeRequest(NAV_URL, REGULAR_FAVICON_SIZE_PX, null);
+        verifyLargeIconBridgeRequest(NAV_URL, SMALL_FAVICON_SIZE_PX, null);
+        verifyRoundedIconRequest(NAV_URL, null);
+        verifyReturnedIcon(null, FaviconType.NONE);
+        verifyNoOtherInteractionsAndClearInteractions();
+
+        // Note: re-trying with no generated icons should result with immediate stop.
+        mFetcher.fetchFaviconWithBackoff(NAV_URL, false, mCallback);
+        verifyReturnedIcon(null, FaviconType.NONE);
+        verifyNoOtherInteractionsAndClearInteractions();
+    }
+
+    @Test
+    public void testIconRetrieval_differentUrlsDontCollide() {
+        mFetcher.fetchFaviconWithBackoff(NAV_URL, true, mCallback);
+        verifyLargeIconBridgeRequest(NAV_URL, REGULAR_FAVICON_SIZE_PX, null);
+        verifyLargeIconBridgeRequest(NAV_URL, SMALL_FAVICON_SIZE_PX, null);
+        verifyRoundedIconRequest(NAV_URL, mGeneratedIconBitmap);
+        verifyReturnedIcon(mGeneratedIconBitmap, FaviconType.GENERATED);
+        verifyNoOtherInteractionsAndClearInteractions();
+
+        // Try another URL and confirm we're looking for a regular icon first.
+        mFetcher.fetchFaviconWithBackoff(NAV_URL_2, true, mCallback);
+        verifyLargeIconBridgeRequest(NAV_URL_2, REGULAR_FAVICON_SIZE_PX, mFavIconBitmap);
+        verifyReturnedIcon(mFavIconBitmap, FaviconType.REGULAR);
+        verifyNoOtherInteractionsAndClearInteractions();
+
+        // Try the previous URL again and see we already fall back to generated icon.
+        mFetcher.fetchFaviconWithBackoff(NAV_URL, true, mCallback);
+        verifyRoundedIconRequest(NAV_URL, mGeneratedIconBitmap);
+        verifyReturnedIcon(mGeneratedIconBitmap, FaviconType.GENERATED);
+        verifyNoOtherInteractionsAndClearInteractions();
+    }
+
+    @Test
+    public void testIconRetrieval_clearingCacheRestartsEntireFlow() {
+        mFetcher.fetchFaviconWithBackoff(NAV_URL, true, mCallback);
+        verifyLargeIconBridgeRequest(NAV_URL, REGULAR_FAVICON_SIZE_PX, null);
+        verifyLargeIconBridgeRequest(NAV_URL, SMALL_FAVICON_SIZE_PX, null);
+        verifyRoundedIconRequest(NAV_URL, null);
+        verifyReturnedIcon(null, FaviconType.NONE);
+        verifyNoOtherInteractionsAndClearInteractions();
+
+        mFetcher.clearCache();
+
+        // Retry. Expect the exact same flow with that same URL.
+        mFetcher.fetchFaviconWithBackoff(NAV_URL, true, mCallback);
+        verifyLargeIconBridgeRequest(NAV_URL, REGULAR_FAVICON_SIZE_PX, null);
+        verifyLargeIconBridgeRequest(NAV_URL, SMALL_FAVICON_SIZE_PX, null);
+        verifyRoundedIconRequest(NAV_URL, null);
+        verifyReturnedIcon(null, FaviconType.NONE);
+        verifyNoOtherInteractionsAndClearInteractions();
+    }
+}
diff --git a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/answer/AnswerSuggestionProcessor.java b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/answer/AnswerSuggestionProcessor.java
index fb8133c..4e4b8e5 100644
--- a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/answer/AnswerSuggestionProcessor.java
+++ b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/answer/AnswerSuggestionProcessor.java
@@ -45,7 +45,7 @@
     public AnswerSuggestionProcessor(Context context, SuggestionHost suggestionHost,
             UrlBarEditingTextStateProvider editingTextProvider,
             Supplier<ImageFetcher> imageFetcherSupplier) {
-        super(context, suggestionHost);
+        super(context, suggestionHost, null);
         mSuggestionHost = suggestionHost;
         mPendingAnswerRequestUrls = new HashMap<>();
         mUrlBarEditingTextProvider = editingTextProvider;
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/omnibox/suggestions/answer/AnswerSuggestionProcessorUnitTest.java b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/answer/AnswerSuggestionProcessorUnitTest.java
similarity index 93%
rename from chrome/android/javatests/src/org/chromium/chrome/browser/omnibox/suggestions/answer/AnswerSuggestionProcessorUnitTest.java
rename to chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/answer/AnswerSuggestionProcessorUnitTest.java
index aea7307..78a95e7 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/omnibox/suggestions/answer/AnswerSuggestionProcessorUnitTest.java
+++ b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/answer/AnswerSuggestionProcessorUnitTest.java
@@ -12,7 +12,6 @@
 import static org.mockito.Mockito.when;
 
 import android.graphics.Bitmap;
-import android.graphics.Bitmap.Config;
 import android.graphics.drawable.BitmapDrawable;
 import android.graphics.drawable.Drawable;
 import android.text.Spannable;
@@ -21,17 +20,18 @@
 
 import org.junit.Assert;
 import org.junit.Before;
+import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.ArgumentCaptor;
 import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+import org.robolectric.annotation.Config;
 
 import org.chromium.base.Callback;
 import org.chromium.base.ContextUtils;
-import org.chromium.base.test.BaseJUnit4ClassRunner;
-import org.chromium.base.test.UiThreadTest;
-import org.chromium.base.test.util.Batch;
+import org.chromium.base.test.BaseRobolectricTestRunner;
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.omnibox.OmniboxSuggestionType;
 import org.chromium.chrome.browser.omnibox.UrlBarEditingTextStateProvider;
@@ -47,33 +47,30 @@
 import org.chromium.components.omnibox.SuggestionAnswer;
 import org.chromium.components.omnibox.SuggestionAnswer.ImageLine;
 import org.chromium.components.omnibox.SuggestionAnswer.TextField;
-import org.chromium.content_public.browser.test.NativeLibraryTestUtils;
 import org.chromium.ui.modelutil.PropertyModel;
 import org.chromium.ui.modelutil.PropertyModel.WritableIntPropertyKey;
 import org.chromium.ui.modelutil.PropertyModel.WritableObjectPropertyKey;
+import org.chromium.url.ShadowGURL;
 
 import java.util.Arrays;
 
 /**
  * Tests for {@link AnswerSuggestionProcessor}.
  */
-@RunWith(BaseJUnit4ClassRunner.class)
-@Batch(Batch.UNIT_TESTS)
+@RunWith(BaseRobolectricTestRunner.class)
+@Config(manifest = Config.NONE, shadows = {ShadowGURL.class})
 public class AnswerSuggestionProcessorUnitTest {
     private static final @AnswerType int ANSWER_TYPES[] = {AnswerType.DICTIONARY,
             AnswerType.FINANCE, AnswerType.KNOWLEDGE_GRAPH, AnswerType.SPORTS, AnswerType.SUNRISE,
             AnswerType.TRANSLATION, AnswerType.WEATHER, AnswerType.WHEN_IS, AnswerType.CURRENCY};
 
-    @Mock
-    SuggestionHost mSuggestionHost;
+    public @Rule MockitoRule mMockitoRule = MockitoJUnit.rule();
 
-    @Mock
-    UrlBarEditingTextStateProvider mUrlStateProvider;
+    private @Mock SuggestionHost mSuggestionHost;
+    private @Mock UrlBarEditingTextStateProvider mUrlStateProvider;
+    private @Mock ImageFetcher mImageFetcher;
+    private @Mock Bitmap mBitmap;
 
-    @Mock
-    ImageFetcher mImageFetcher;
-
-    private Bitmap mBitmap;
     private AnswerSuggestionProcessor mProcessor;
 
     /**
@@ -170,17 +167,8 @@
                 /* additionalText */ null, /* statusText */ null, url);
     }
 
-    public AnswerSuggestionProcessorUnitTest() {
-        // SetUp runs on the UI thread because we're using UiThreadTestRule, so do native library
-        // loading here, which happens on the Instrumentation thread.
-        NativeLibraryTestUtils.loadNativeLibraryNoBrowserProcess();
-    }
-
     @Before
     public void setUp() {
-        MockitoAnnotations.initMocks(this);
-        mBitmap = Bitmap.createBitmap(1, 1, Config.ALPHA_8);
-
         mProcessor = new AnswerSuggestionProcessor(ContextUtils.getApplicationContext(),
                 mSuggestionHost, mUrlStateProvider, () -> mImageFetcher);
     }
@@ -202,7 +190,6 @@
 
     @Test
     @SmallTest
-    @UiThreadTest
     public void regularAnswer_order() {
         final SuggestionTestHelper suggHelper =
                 createAnswerSuggestion(AnswerType.KNOWLEDGE_GRAPH, "Query", 1, "Answer", 1, null);
@@ -216,7 +203,6 @@
 
     @Test
     @SmallTest
-    @UiThreadTest
     public void dictionaryAnswer_order() {
         final SuggestionTestHelper suggHelper =
                 createAnswerSuggestion(AnswerType.DICTIONARY, "Query", 1, "Answer", 1, null);
@@ -229,7 +215,6 @@
 
     @Test
     @SmallTest
-    @UiThreadTest
     public void calculationAnswer_order() {
         final SuggestionTestHelper suggHelper = createCalculationSuggestion("12345", "123 + 45");
         processSuggestion(suggHelper);
@@ -240,7 +225,6 @@
 
     @Test
     @SmallTest
-    @UiThreadTest
     public void regularAnswer_shortMultiline() {
         final SuggestionTestHelper suggHelper =
                 createAnswerSuggestion(AnswerType.KNOWLEDGE_GRAPH, "", 1, "", 3, null);
@@ -252,7 +236,6 @@
 
     @Test
     @SmallTest
-    @UiThreadTest
     public void dictionaryAnswer_shortMultiline() {
         final SuggestionTestHelper suggHelper =
                 createAnswerSuggestion(AnswerType.DICTIONARY, "", 1, "", 3, null);
@@ -266,7 +249,6 @@
     // Check that multiline titles are truncated to a single line.
     @Test
     @SmallTest
-    @UiThreadTest
     public void regularAnswer_truncatedMultiline() {
         final SuggestionTestHelper suggHelper =
                 createAnswerSuggestion(AnswerType.KNOWLEDGE_GRAPH, "", 3, "", 10, null);
@@ -278,7 +260,6 @@
 
     @Test
     @SmallTest
-    @UiThreadTest
     public void dictionaryAnswer_truncatedMultiline() {
         final SuggestionTestHelper suggHelper =
                 createAnswerSuggestion(AnswerType.DICTIONARY, "", 3, "", 10, null);
@@ -291,7 +272,6 @@
     // Image fetching and icon association tests.
     @Test
     @SmallTest
-    @UiThreadTest
     public void answerImage_fallbackIcons() {
         for (@AnswerType int type : ANSWER_TYPES) {
             SuggestionTestHelper suggHelper = createAnswerSuggestion(type, "", 1, "", 1, null);
@@ -303,7 +283,6 @@
 
     @Test
     @SmallTest
-    @UiThreadTest
     public void answerImage_iconAssociation() {
         SuggestionTestHelper suggHelper =
                 createAnswerSuggestion(AnswerType.DICTIONARY, "", 1, "", 1, null);
@@ -349,7 +328,6 @@
 
     @Test
     @SmallTest
-    @UiThreadTest
     public void answerImage_repeatedUrlsAreFetchedOnlyOnce() {
         final String url1 = "http://site1.com";
         final String url2 = "http://site2.com";
@@ -373,7 +351,6 @@
 
     @Test
     @SmallTest
-    @UiThreadTest
     public void answerImage_bitmapReplacesIconForAllSuggestionsWithSameUrl() {
         final String url = "http://site.com";
         final SuggestionTestHelper sugg1 =
@@ -413,7 +390,6 @@
 
     @Test
     @SmallTest
-    @UiThreadTest
     public void answerImage_failedBitmapFetchDoesNotClearIcons() {
         final String url = "http://site.com";
         final ArgumentCaptor<Callback<Bitmap>> callback = ArgumentCaptor.forClass(Callback.class);
@@ -433,7 +409,6 @@
 
     @Test
     @SmallTest
-    @UiThreadTest
     public void answerImage_noImageFetchWhenFetcherIsUnavailable() {
         final String url = "http://site.com";
         mImageFetcher = null;
@@ -445,7 +420,6 @@
 
     @Test
     @SmallTest
-    @UiThreadTest
     public void answerImage_associatedModelsAreErasedFromPendingListAfterImageFetch() {
         ArgumentCaptor<Callback<Bitmap>> callback = ArgumentCaptor.forClass(Callback.class);
         final String url = "http://site1.com";
diff --git a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/base/BaseSuggestionViewProcessor.java b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/base/BaseSuggestionViewProcessor.java
index 2579766..b07be211 100644
--- a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/base/BaseSuggestionViewProcessor.java
+++ b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/base/BaseSuggestionViewProcessor.java
@@ -5,7 +5,6 @@
 package org.chromium.chrome.browser.omnibox.suggestions.base;
 
 import android.content.Context;
-import android.graphics.Bitmap;
 import android.graphics.Typeface;
 import android.text.Spannable;
 import android.text.style.StyleSpan;
@@ -16,10 +15,10 @@
 
 import org.chromium.chrome.browser.omnibox.MatchClassificationStyle;
 import org.chromium.chrome.browser.omnibox.R;
+import org.chromium.chrome.browser.omnibox.suggestions.FaviconFetcher;
 import org.chromium.chrome.browser.omnibox.suggestions.SuggestionHost;
 import org.chromium.chrome.browser.omnibox.suggestions.SuggestionProcessor;
 import org.chromium.chrome.browser.omnibox.suggestions.base.BaseSuggestionViewProperties.Action;
-import org.chromium.components.favicon.LargeIconBridge;
 import org.chromium.components.omnibox.AutocompleteMatch;
 import org.chromium.components.omnibox.AutocompleteMatch.MatchClassification;
 import org.chromium.ui.modelutil.PropertyModel;
@@ -32,8 +31,9 @@
  * A class that handles base properties and model for most suggestions.
  */
 public abstract class BaseSuggestionViewProcessor implements SuggestionProcessor {
-    private final Context mContext;
-    private final SuggestionHost mSuggestionHost;
+    private final @NonNull Context mContext;
+    private final @NonNull SuggestionHost mSuggestionHost;
+    private final @Nullable FaviconFetcher mFaviconFetcher;
     private final int mDesiredFaviconWidthPx;
     private final int mDecorationImageSizePx;
     private final int mSuggestionSizePx;
@@ -41,8 +41,10 @@
     /**
      * @param context Current context.
      * @param host A handle to the object using the suggestions.
+     * @param faviconFetcher A mechanism to use to retrieve favicons.
      */
-    public BaseSuggestionViewProcessor(Context context, SuggestionHost host) {
+    public BaseSuggestionViewProcessor(@NonNull Context context, @NonNull SuggestionHost host,
+            @Nullable FaviconFetcher faviconFetcher) {
         mContext = context;
         mSuggestionHost = host;
         mDesiredFaviconWidthPx = mContext.getResources().getDimensionPixelSize(
@@ -51,6 +53,7 @@
                 R.dimen.omnibox_suggestion_decoration_image_size);
         mSuggestionSizePx = mContext.getResources().getDimensionPixelSize(
                 R.dimen.omnibox_suggestion_semicompact_height);
+        mFaviconFetcher = faviconFetcher;
     }
 
     /**
@@ -201,24 +204,15 @@
      *
      * @param model Model representing current suggestion.
      * @param url Target URL the suggestion points to.
-     * @param iconBridge A {@link LargeIconBridge} supplies site favicons.
-     * @param onIconFetched Optional callback that will be invoked after successful fetch of a
-     *         favicon.
      */
-    protected void fetchSuggestionFavicon(PropertyModel model, GURL url, LargeIconBridge iconBridge,
-            @Nullable Runnable onIconFetched) {
-        if (url == null || iconBridge == null) return;
-
-        iconBridge.getLargeIconForUrl(url, mDesiredFaviconWidthPx,
-                (Bitmap icon, int fallbackColor, boolean isFallbackColorDefault, int iconType) -> {
-                    if (icon == null) return;
-
-                    setSuggestionDrawableState(model,
-                            SuggestionDrawableState.Builder.forBitmap(mContext, icon).build());
-                    if (onIconFetched != null) {
-                        onIconFetched.run();
-                    }
-                });
+    protected void fetchSuggestionFavicon(PropertyModel model, GURL url) {
+        assert mFaviconFetcher != null : "You must supply the FaviconFetcher in order to use it";
+        mFaviconFetcher.fetchFaviconWithBackoff(url, false, (icon, type) -> {
+            if (icon != null) {
+                setSuggestionDrawableState(
+                        model, SuggestionDrawableState.Builder.forBitmap(mContext, icon).build());
+            }
+        });
     }
 
     /**
diff --git a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/basic/BasicSuggestionProcessor.java b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/basic/BasicSuggestionProcessor.java
index af599e7d..71ecdce 100644
--- a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/basic/BasicSuggestionProcessor.java
+++ b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/basic/BasicSuggestionProcessor.java
@@ -10,19 +10,17 @@
 import androidx.annotation.DrawableRes;
 import androidx.annotation.NonNull;
 
-import org.chromium.base.supplier.Supplier;
 import org.chromium.chrome.browser.omnibox.MatchClassificationStyle;
 import org.chromium.chrome.browser.omnibox.OmniboxSuggestionType;
 import org.chromium.chrome.browser.omnibox.R;
 import org.chromium.chrome.browser.omnibox.UrlBarData;
 import org.chromium.chrome.browser.omnibox.UrlBarEditingTextStateProvider;
+import org.chromium.chrome.browser.omnibox.suggestions.FaviconFetcher;
 import org.chromium.chrome.browser.omnibox.suggestions.OmniboxSuggestionUiType;
 import org.chromium.chrome.browser.omnibox.suggestions.SuggestionHost;
 import org.chromium.chrome.browser.omnibox.suggestions.base.BaseSuggestionViewProcessor;
 import org.chromium.chrome.browser.omnibox.suggestions.base.SuggestionDrawableState;
 import org.chromium.chrome.browser.omnibox.suggestions.base.SuggestionSpannable;
-import org.chromium.chrome.browser.omnibox.suggestions.basic.SuggestionViewProperties.SuggestionIcon;
-import org.chromium.components.favicon.LargeIconBridge;
 import org.chromium.components.omnibox.AutocompleteMatch;
 import org.chromium.ui.modelutil.PropertyModel;
 import org.chromium.url.GURL;
@@ -41,9 +39,7 @@
         boolean isBookmarked(GURL url);
     }
     private final @NonNull UrlBarEditingTextStateProvider mUrlBarEditingTextProvider;
-    private final @NonNull Supplier<LargeIconBridge> mIconBridgeSupplier;
     private final @NonNull BookmarkState mBookmarkState;
-    private final int mDesiredFaviconWidthPx;
 
     /**
      * @param context An Android context.
@@ -55,14 +51,10 @@
     public BasicSuggestionProcessor(@NonNull Context context,
             @NonNull SuggestionHost suggestionHost,
             @NonNull UrlBarEditingTextStateProvider editingTextProvider,
-            @NonNull Supplier<LargeIconBridge> iconBridgeSupplier,
-            @NonNull BookmarkState bookmarkState) {
-        super(context, suggestionHost);
+            @NonNull FaviconFetcher faviconFetcher, @NonNull BookmarkState bookmarkState) {
+        super(context, suggestionHost, faviconFetcher);
 
-        mDesiredFaviconWidthPx = getContext().getResources().getDimensionPixelSize(
-                R.dimen.omnibox_suggestion_favicon_size);
         mUrlBarEditingTextProvider = editingTextProvider;
-        mIconBridgeSupplier = iconBridgeSupplier;
         mBookmarkState = bookmarkState;
     }
 
@@ -88,74 +80,31 @@
      * Note that the stock icons do not include Favicon - Favicon is only declared
      * when we know we have a valid and large enough site favicon to present.
      */
-    private @SuggestionIcon int getSuggestionIconType(AutocompleteMatch suggestion) {
+    private @DrawableRes int getSuggestionIcon(AutocompleteMatch suggestion) {
         if (suggestion.isSearchSuggestion()) {
             switch (suggestion.getType()) {
                 case OmniboxSuggestionType.VOICE_SUGGEST:
-                    return SuggestionIcon.VOICE;
+                    return R.drawable.btn_mic;
 
                 case OmniboxSuggestionType.SEARCH_SUGGEST_PERSONALIZED:
                 case OmniboxSuggestionType.SEARCH_HISTORY:
-                    return SuggestionIcon.HISTORY;
+                    return R.drawable.ic_history_googblue_24dp;
 
                 default:
                     if (suggestion.getSubtypes().contains(/* SUBTYPE_TRENDS = */ 143)) {
-                        return SuggestionIcon.TRENDS;
+                        return R.drawable.trending_up_black_24dp;
                     }
-                    return SuggestionIcon.MAGNIFIER;
+                    return R.drawable.ic_suggestion_magnifier;
             }
         } else {
             if (mBookmarkState.isBookmarked(suggestion.getUrl())) {
-                return SuggestionIcon.BOOKMARK;
+                return R.drawable.btn_star;
             } else {
-                return SuggestionIcon.GLOBE;
+                return R.drawable.ic_globe_24dp;
             }
         }
     }
 
-    private void updateSuggestionIcon(AutocompleteMatch suggestion, PropertyModel model) {
-        @SuggestionIcon
-        int type = getSuggestionIconType(suggestion);
-        @DrawableRes
-        int icon = R.drawable.ic_suggestion_magnifier;
-
-        switch (type) {
-            case SuggestionIcon.BOOKMARK:
-                icon = R.drawable.btn_star;
-                break;
-
-            case SuggestionIcon.HISTORY:
-                icon = R.drawable.ic_history_googblue_24dp;
-                break;
-
-            case SuggestionIcon.GLOBE:
-                icon = R.drawable.ic_globe_24dp;
-                break;
-
-            case SuggestionIcon.MAGNIFIER:
-                icon = R.drawable.ic_suggestion_magnifier;
-                break;
-
-            case SuggestionIcon.VOICE:
-                icon = R.drawable.btn_mic;
-                break;
-
-            case SuggestionIcon.TRENDS:
-                icon = R.drawable.trending_up_black_24dp;
-                break;
-
-            default:
-                // All other cases are invalid.
-                assert false : "Suggestion type " + type + " is not valid.";
-        }
-
-        model.set(SuggestionViewProperties.SUGGESTION_ICON_TYPE, type);
-        setSuggestionDrawableState(model,
-                SuggestionDrawableState.Builder.forDrawableRes(getContext(), icon)
-                        .setAllowTint(true)
-                        .build());
-    }
-
     @Override
     public void populateModel(AutocompleteMatch suggestion, PropertyModel model, int position) {
         super.populateModel(suggestion, model, position);
@@ -179,13 +128,16 @@
         final SuggestionSpannable textLine1 =
                 getSuggestedQuery(suggestion, !isSearchSuggestion, !urlHighlighted);
 
-        updateSuggestionIcon(suggestion, model);
+        setSuggestionDrawableState(model,
+                SuggestionDrawableState.Builder
+                        .forDrawableRes(getContext(), getSuggestionIcon(suggestion))
+                        .setAllowTint(true)
+                        .build());
+
         model.set(SuggestionViewProperties.IS_SEARCH_SUGGESTION, isSearchSuggestion);
         model.set(SuggestionViewProperties.TEXT_LINE_1_TEXT, textLine1);
         model.set(SuggestionViewProperties.TEXT_LINE_2_TEXT, textLine2);
-        fetchSuggestionFavicon(model, suggestion.getUrl(), mIconBridgeSupplier.get(), () -> {
-            model.set(SuggestionViewProperties.SUGGESTION_ICON_TYPE, SuggestionIcon.FAVICON);
-        });
+        fetchSuggestionFavicon(model, suggestion.getUrl());
         model.set(SuggestionViewProperties.ALLOW_WRAP_AROUND, isSearchSuggestion);
 
         if (!mUrlBarEditingTextProvider.getTextWithoutAutocomplete().trim().equalsIgnoreCase(
diff --git a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/basic/BasicSuggestionProcessorUnitTest.java b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/basic/BasicSuggestionProcessorUnitTest.java
index e78ca074..70a75e3 100644
--- a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/basic/BasicSuggestionProcessorUnitTest.java
+++ b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/basic/BasicSuggestionProcessorUnitTest.java
@@ -4,14 +4,15 @@
 
 package org.chromium.chrome.browser.omnibox.suggestions.basic;
 
-import static org.mockito.Mockito.anyInt;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.eq;
 import static org.mockito.Mockito.verify;
+import static org.robolectric.Shadows.shadowOf;
 
 import android.graphics.Bitmap;
 import android.graphics.drawable.BitmapDrawable;
 
+import androidx.annotation.DrawableRes;
 import androidx.test.filters.SmallTest;
 
 import org.junit.Assert;
@@ -28,17 +29,17 @@
 
 import org.chromium.base.ContextUtils;
 import org.chromium.base.test.BaseRobolectricTestRunner;
-import org.chromium.chrome.R;
 import org.chromium.chrome.browser.omnibox.OmniboxSuggestionType;
+import org.chromium.chrome.browser.omnibox.R;
 import org.chromium.chrome.browser.omnibox.ShadowUrlBarData;
 import org.chromium.chrome.browser.omnibox.UrlBarEditingTextStateProvider;
+import org.chromium.chrome.browser.omnibox.suggestions.FaviconFetcher;
+import org.chromium.chrome.browser.omnibox.suggestions.FaviconFetcher.FaviconFetchCompleteListener;
+import org.chromium.chrome.browser.omnibox.suggestions.FaviconFetcher.FaviconType;
 import org.chromium.chrome.browser.omnibox.suggestions.SuggestionHost;
 import org.chromium.chrome.browser.omnibox.suggestions.base.BaseSuggestionViewProperties;
 import org.chromium.chrome.browser.omnibox.suggestions.base.SuggestionDrawableState;
-import org.chromium.chrome.browser.omnibox.suggestions.basic.SuggestionViewProperties.SuggestionIcon;
 import org.chromium.chrome.test.util.browser.Features;
-import org.chromium.components.favicon.LargeIconBridge;
-import org.chromium.components.favicon.LargeIconBridge.LargeIconCallback;
 import org.chromium.components.omnibox.AutocompleteMatch;
 import org.chromium.components.omnibox.AutocompleteMatchBuilder;
 import org.chromium.ui.modelutil.PropertyModel;
@@ -58,19 +59,24 @@
 public class BasicSuggestionProcessorUnitTest {
     private static final GURL EXTERNAL_URL = JUnitTestGURLs.getGURL(JUnitTestGURLs.URL_1);
     private static final GURL INTERNAL_URL = JUnitTestGURLs.getGURL(JUnitTestGURLs.NTP_URL);
+    private static final @DrawableRes int ICON_BOOKMARK = R.drawable.btn_star;
+    private static final @DrawableRes int ICON_GLOBE = R.drawable.ic_globe_24dp;
+    private static final @DrawableRes int ICON_HISTORY = R.drawable.ic_history_googblue_24dp;
+    private static final @DrawableRes int ICON_MAGNIFIER = R.drawable.ic_suggestion_magnifier;
+    private static final @DrawableRes int ICON_TRENDS = R.drawable.trending_up_black_24dp;
+    private static final @DrawableRes int ICON_VOICE = R.drawable.btn_mic;
+    private static final @DrawableRes int ICON_FAVICON = 0; // Favicons do not come from resources.
 
-    private static final Map<Integer, String> ICON_TYPE_NAMES =
-            new HashMap<Integer, String>(SuggestionIcon.TOTAL_COUNT) {
-                {
-                    put(SuggestionIcon.UNSET, "UNSET");
-                    put(SuggestionIcon.BOOKMARK, "BOOKMARK");
-                    put(SuggestionIcon.HISTORY, "HISTORY");
-                    put(SuggestionIcon.GLOBE, "GLOBE");
-                    put(SuggestionIcon.MAGNIFIER, "MAGNIFIER");
-                    put(SuggestionIcon.VOICE, "VOICE");
-                    put(SuggestionIcon.FAVICON, "FAVICON");
-                }
-            };
+    private static final Map<Integer, String> ICON_TYPE_NAMES = new HashMap<Integer, String>() {
+        {
+            put(ICON_BOOKMARK, "BOOKMARK");
+            put(ICON_HISTORY, "HISTORY");
+            put(ICON_GLOBE, "GLOBE");
+            put(ICON_MAGNIFIER, "MAGNIFIER");
+            put(ICON_VOICE, "VOICE");
+            put(ICON_FAVICON, "FAVICON");
+        }
+    };
 
     private static final Map<Integer, String> SUGGESTION_TYPE_NAMES = new HashMap<Integer, String>(
             OmniboxSuggestionType.NUM_TYPES) {
@@ -101,9 +107,9 @@
     public @Rule MockitoRule mMockitoRule = MockitoJUnit.rule();
 
     private @Mock SuggestionHost mSuggestionHost;
-    private @Mock LargeIconBridge mIconBridge;
     private @Mock UrlBarEditingTextStateProvider mUrlBarText;
     private @Mock Bitmap mBitmap;
+    private @Mock FaviconFetcher mIconFetcher;
 
     private BasicSuggestionProcessor mProcessor;
     private AutocompleteMatch mSuggestion;
@@ -124,7 +130,7 @@
     public void setUp() {
         doReturn("").when(mUrlBarText).getTextWithoutAutocomplete();
         mProcessor = new BasicSuggestionProcessor(ContextUtils.getApplicationContext(),
-                mSuggestionHost, mUrlBarText, () -> mIconBridge, mIsBookmarked);
+                mSuggestionHost, mUrlBarText, mIconFetcher, mIsBookmarked);
     }
 
     /**
@@ -162,35 +168,37 @@
     }
 
     private void assertSuggestionTypeAndIcon(
-            @OmniboxSuggestionType int expectedType, @SuggestionIcon int expectedIcon) {
-        int actualIcon = mModel.get(SuggestionViewProperties.SUGGESTION_ICON_TYPE);
+            @OmniboxSuggestionType int expectedType, @DrawableRes int expectedIconRes) {
+        SuggestionDrawableState sds = mModel.get(BaseSuggestionViewProperties.ICON);
+        @DrawableRes
+        int actualIconRes = shadowOf(sds.drawable).getCreatedFromResId();
         Assert.assertEquals(
                 String.format("%s: Want Icon %s, Got %s", SUGGESTION_TYPE_NAMES.get(expectedType),
-                        ICON_TYPE_NAMES.get(expectedIcon), ICON_TYPE_NAMES.get(actualIcon)),
-                expectedIcon, actualIcon);
+                        ICON_TYPE_NAMES.get(expectedIconRes), ICON_TYPE_NAMES.get(actualIconRes)),
+                expectedIconRes, actualIconRes);
     }
 
     @Test
     @SmallTest
     public void getSuggestionIconTypeForSearch_Default() {
         int[][] testCases = {
-                {OmniboxSuggestionType.URL_WHAT_YOU_TYPED, SuggestionIcon.MAGNIFIER},
-                {OmniboxSuggestionType.HISTORY_URL, SuggestionIcon.MAGNIFIER},
-                {OmniboxSuggestionType.HISTORY_TITLE, SuggestionIcon.MAGNIFIER},
-                {OmniboxSuggestionType.HISTORY_BODY, SuggestionIcon.MAGNIFIER},
-                {OmniboxSuggestionType.HISTORY_KEYWORD, SuggestionIcon.MAGNIFIER},
-                {OmniboxSuggestionType.NAVSUGGEST, SuggestionIcon.MAGNIFIER},
-                {OmniboxSuggestionType.SEARCH_WHAT_YOU_TYPED, SuggestionIcon.MAGNIFIER},
-                {OmniboxSuggestionType.SEARCH_HISTORY, SuggestionIcon.HISTORY},
-                {OmniboxSuggestionType.SEARCH_SUGGEST, SuggestionIcon.MAGNIFIER},
-                {OmniboxSuggestionType.SEARCH_SUGGEST_ENTITY, SuggestionIcon.MAGNIFIER},
-                {OmniboxSuggestionType.SEARCH_SUGGEST_TAIL, SuggestionIcon.MAGNIFIER},
-                {OmniboxSuggestionType.SEARCH_SUGGEST_PERSONALIZED, SuggestionIcon.HISTORY},
-                {OmniboxSuggestionType.SEARCH_SUGGEST_PROFILE, SuggestionIcon.MAGNIFIER},
-                {OmniboxSuggestionType.SEARCH_OTHER_ENGINE, SuggestionIcon.MAGNIFIER},
-                {OmniboxSuggestionType.NAVSUGGEST_PERSONALIZED, SuggestionIcon.MAGNIFIER},
-                {OmniboxSuggestionType.VOICE_SUGGEST, SuggestionIcon.VOICE},
-                {OmniboxSuggestionType.DOCUMENT_SUGGESTION, SuggestionIcon.MAGNIFIER},
+                {OmniboxSuggestionType.URL_WHAT_YOU_TYPED, ICON_MAGNIFIER},
+                {OmniboxSuggestionType.HISTORY_URL, ICON_MAGNIFIER},
+                {OmniboxSuggestionType.HISTORY_TITLE, ICON_MAGNIFIER},
+                {OmniboxSuggestionType.HISTORY_BODY, ICON_MAGNIFIER},
+                {OmniboxSuggestionType.HISTORY_KEYWORD, ICON_MAGNIFIER},
+                {OmniboxSuggestionType.NAVSUGGEST, ICON_MAGNIFIER},
+                {OmniboxSuggestionType.SEARCH_WHAT_YOU_TYPED, ICON_MAGNIFIER},
+                {OmniboxSuggestionType.SEARCH_HISTORY, ICON_HISTORY},
+                {OmniboxSuggestionType.SEARCH_SUGGEST, ICON_MAGNIFIER},
+                {OmniboxSuggestionType.SEARCH_SUGGEST_ENTITY, ICON_MAGNIFIER},
+                {OmniboxSuggestionType.SEARCH_SUGGEST_TAIL, ICON_MAGNIFIER},
+                {OmniboxSuggestionType.SEARCH_SUGGEST_PERSONALIZED, ICON_HISTORY},
+                {OmniboxSuggestionType.SEARCH_SUGGEST_PROFILE, ICON_MAGNIFIER},
+                {OmniboxSuggestionType.SEARCH_OTHER_ENGINE, ICON_MAGNIFIER},
+                {OmniboxSuggestionType.NAVSUGGEST_PERSONALIZED, ICON_MAGNIFIER},
+                {OmniboxSuggestionType.VOICE_SUGGEST, ICON_VOICE},
+                {OmniboxSuggestionType.DOCUMENT_SUGGESTION, ICON_MAGNIFIER},
         };
 
         mProcessor.onNativeInitialized();
@@ -205,23 +213,23 @@
     @SmallTest
     public void getSuggestionIconTypeForUrl_Default() {
         int[][] testCases = {
-                {OmniboxSuggestionType.URL_WHAT_YOU_TYPED, SuggestionIcon.GLOBE},
-                {OmniboxSuggestionType.HISTORY_URL, SuggestionIcon.GLOBE},
-                {OmniboxSuggestionType.HISTORY_TITLE, SuggestionIcon.GLOBE},
-                {OmniboxSuggestionType.HISTORY_BODY, SuggestionIcon.GLOBE},
-                {OmniboxSuggestionType.HISTORY_KEYWORD, SuggestionIcon.GLOBE},
-                {OmniboxSuggestionType.NAVSUGGEST, SuggestionIcon.GLOBE},
-                {OmniboxSuggestionType.SEARCH_WHAT_YOU_TYPED, SuggestionIcon.GLOBE},
-                {OmniboxSuggestionType.SEARCH_HISTORY, SuggestionIcon.GLOBE},
-                {OmniboxSuggestionType.SEARCH_SUGGEST, SuggestionIcon.GLOBE},
-                {OmniboxSuggestionType.SEARCH_SUGGEST_ENTITY, SuggestionIcon.GLOBE},
-                {OmniboxSuggestionType.SEARCH_SUGGEST_TAIL, SuggestionIcon.GLOBE},
-                {OmniboxSuggestionType.SEARCH_SUGGEST_PERSONALIZED, SuggestionIcon.GLOBE},
-                {OmniboxSuggestionType.SEARCH_SUGGEST_PROFILE, SuggestionIcon.GLOBE},
-                {OmniboxSuggestionType.SEARCH_OTHER_ENGINE, SuggestionIcon.GLOBE},
-                {OmniboxSuggestionType.NAVSUGGEST_PERSONALIZED, SuggestionIcon.GLOBE},
-                {OmniboxSuggestionType.VOICE_SUGGEST, SuggestionIcon.GLOBE},
-                {OmniboxSuggestionType.DOCUMENT_SUGGESTION, SuggestionIcon.GLOBE},
+                {OmniboxSuggestionType.URL_WHAT_YOU_TYPED, ICON_GLOBE},
+                {OmniboxSuggestionType.HISTORY_URL, ICON_GLOBE},
+                {OmniboxSuggestionType.HISTORY_TITLE, ICON_GLOBE},
+                {OmniboxSuggestionType.HISTORY_BODY, ICON_GLOBE},
+                {OmniboxSuggestionType.HISTORY_KEYWORD, ICON_GLOBE},
+                {OmniboxSuggestionType.NAVSUGGEST, ICON_GLOBE},
+                {OmniboxSuggestionType.SEARCH_WHAT_YOU_TYPED, ICON_GLOBE},
+                {OmniboxSuggestionType.SEARCH_HISTORY, ICON_GLOBE},
+                {OmniboxSuggestionType.SEARCH_SUGGEST, ICON_GLOBE},
+                {OmniboxSuggestionType.SEARCH_SUGGEST_ENTITY, ICON_GLOBE},
+                {OmniboxSuggestionType.SEARCH_SUGGEST_TAIL, ICON_GLOBE},
+                {OmniboxSuggestionType.SEARCH_SUGGEST_PERSONALIZED, ICON_GLOBE},
+                {OmniboxSuggestionType.SEARCH_SUGGEST_PROFILE, ICON_GLOBE},
+                {OmniboxSuggestionType.SEARCH_OTHER_ENGINE, ICON_GLOBE},
+                {OmniboxSuggestionType.NAVSUGGEST_PERSONALIZED, ICON_GLOBE},
+                {OmniboxSuggestionType.VOICE_SUGGEST, ICON_GLOBE},
+                {OmniboxSuggestionType.DOCUMENT_SUGGESTION, ICON_GLOBE},
         };
 
         mProcessor.onNativeInitialized();
@@ -236,23 +244,23 @@
     @SmallTest
     public void getSuggestionIconTypeForBookmarks_Default() {
         int[][] testCases = {
-                {OmniboxSuggestionType.URL_WHAT_YOU_TYPED, SuggestionIcon.BOOKMARK},
-                {OmniboxSuggestionType.HISTORY_URL, SuggestionIcon.BOOKMARK},
-                {OmniboxSuggestionType.HISTORY_TITLE, SuggestionIcon.BOOKMARK},
-                {OmniboxSuggestionType.HISTORY_BODY, SuggestionIcon.BOOKMARK},
-                {OmniboxSuggestionType.HISTORY_KEYWORD, SuggestionIcon.BOOKMARK},
-                {OmniboxSuggestionType.NAVSUGGEST, SuggestionIcon.BOOKMARK},
-                {OmniboxSuggestionType.SEARCH_WHAT_YOU_TYPED, SuggestionIcon.BOOKMARK},
-                {OmniboxSuggestionType.SEARCH_HISTORY, SuggestionIcon.BOOKMARK},
-                {OmniboxSuggestionType.SEARCH_SUGGEST, SuggestionIcon.BOOKMARK},
-                {OmniboxSuggestionType.SEARCH_SUGGEST_ENTITY, SuggestionIcon.BOOKMARK},
-                {OmniboxSuggestionType.SEARCH_SUGGEST_TAIL, SuggestionIcon.BOOKMARK},
-                {OmniboxSuggestionType.SEARCH_SUGGEST_PERSONALIZED, SuggestionIcon.BOOKMARK},
-                {OmniboxSuggestionType.SEARCH_SUGGEST_PROFILE, SuggestionIcon.BOOKMARK},
-                {OmniboxSuggestionType.SEARCH_OTHER_ENGINE, SuggestionIcon.BOOKMARK},
-                {OmniboxSuggestionType.NAVSUGGEST_PERSONALIZED, SuggestionIcon.BOOKMARK},
-                {OmniboxSuggestionType.VOICE_SUGGEST, SuggestionIcon.BOOKMARK},
-                {OmniboxSuggestionType.DOCUMENT_SUGGESTION, SuggestionIcon.BOOKMARK},
+                {OmniboxSuggestionType.URL_WHAT_YOU_TYPED, ICON_BOOKMARK},
+                {OmniboxSuggestionType.HISTORY_URL, ICON_BOOKMARK},
+                {OmniboxSuggestionType.HISTORY_TITLE, ICON_BOOKMARK},
+                {OmniboxSuggestionType.HISTORY_BODY, ICON_BOOKMARK},
+                {OmniboxSuggestionType.HISTORY_KEYWORD, ICON_BOOKMARK},
+                {OmniboxSuggestionType.NAVSUGGEST, ICON_BOOKMARK},
+                {OmniboxSuggestionType.SEARCH_WHAT_YOU_TYPED, ICON_BOOKMARK},
+                {OmniboxSuggestionType.SEARCH_HISTORY, ICON_BOOKMARK},
+                {OmniboxSuggestionType.SEARCH_SUGGEST, ICON_BOOKMARK},
+                {OmniboxSuggestionType.SEARCH_SUGGEST_ENTITY, ICON_BOOKMARK},
+                {OmniboxSuggestionType.SEARCH_SUGGEST_TAIL, ICON_BOOKMARK},
+                {OmniboxSuggestionType.SEARCH_SUGGEST_PERSONALIZED, ICON_BOOKMARK},
+                {OmniboxSuggestionType.SEARCH_SUGGEST_PROFILE, ICON_BOOKMARK},
+                {OmniboxSuggestionType.SEARCH_OTHER_ENGINE, ICON_BOOKMARK},
+                {OmniboxSuggestionType.NAVSUGGEST_PERSONALIZED, ICON_BOOKMARK},
+                {OmniboxSuggestionType.VOICE_SUGGEST, ICON_BOOKMARK},
+                {OmniboxSuggestionType.DOCUMENT_SUGGESTION, ICON_BOOKMARK},
         };
 
         mIsBookmarked.mState = true;
@@ -269,12 +277,12 @@
     @SmallTest
     public void getSuggestionIconTypeForTrendingQueries() {
         int[][] testCases = {
-                {OmniboxSuggestionType.URL_WHAT_YOU_TYPED, SuggestionIcon.TRENDS},
-                {OmniboxSuggestionType.SEARCH_HISTORY, SuggestionIcon.HISTORY},
-                {OmniboxSuggestionType.SEARCH_SUGGEST, SuggestionIcon.TRENDS},
-                {OmniboxSuggestionType.SEARCH_SUGGEST_TAIL, SuggestionIcon.TRENDS},
-                {OmniboxSuggestionType.SEARCH_SUGGEST_PERSONALIZED, SuggestionIcon.HISTORY},
-                {OmniboxSuggestionType.VOICE_SUGGEST, SuggestionIcon.VOICE},
+                {OmniboxSuggestionType.URL_WHAT_YOU_TYPED, ICON_TRENDS},
+                {OmniboxSuggestionType.SEARCH_HISTORY, ICON_HISTORY},
+                {OmniboxSuggestionType.SEARCH_SUGGEST, ICON_TRENDS},
+                {OmniboxSuggestionType.SEARCH_SUGGEST_TAIL, ICON_TRENDS},
+                {OmniboxSuggestionType.SEARCH_SUGGEST_PERSONALIZED, ICON_HISTORY},
+                {OmniboxSuggestionType.VOICE_SUGGEST, ICON_VOICE},
         };
 
         mProcessor.onNativeInitialized();
@@ -343,16 +351,16 @@
     @Test
     @SmallTest
     public void suggestionFavicons_showFaviconWhenAvailable() {
-        final ArgumentCaptor<LargeIconCallback> callback =
-                ArgumentCaptor.forClass(LargeIconCallback.class);
+        final ArgumentCaptor<FaviconFetchCompleteListener> callback =
+                ArgumentCaptor.forClass(FaviconFetchCompleteListener.class);
         mProcessor.onNativeInitialized();
         createUrlSuggestion(OmniboxSuggestionType.URL_WHAT_YOU_TYPED, "");
         SuggestionDrawableState icon1 = mModel.get(BaseSuggestionViewProperties.ICON);
         Assert.assertNotNull(icon1);
 
-        verify(mIconBridge)
-                .getLargeIconForUrl(eq(mSuggestion.getUrl()), anyInt(), callback.capture());
-        callback.getValue().onLargeIconAvailable(mBitmap, 0, false, 0);
+        verify(mIconFetcher)
+                .fetchFaviconWithBackoff(eq(mSuggestion.getUrl()), eq(false), callback.capture());
+        callback.getValue().onFaviconFetchComplete(mBitmap, FaviconType.REGULAR);
         SuggestionDrawableState icon2 = mModel.get(BaseSuggestionViewProperties.ICON);
         Assert.assertNotNull(icon2);
 
@@ -363,16 +371,16 @@
     @Test
     @SmallTest
     public void suggestionFavicons_doNotReplaceFallbackIconWhenNoFaviconIsAvailable() {
-        final ArgumentCaptor<LargeIconCallback> callback =
-                ArgumentCaptor.forClass(LargeIconCallback.class);
+        final ArgumentCaptor<FaviconFetchCompleteListener> callback =
+                ArgumentCaptor.forClass(FaviconFetchCompleteListener.class);
         mProcessor.onNativeInitialized();
         createUrlSuggestion(OmniboxSuggestionType.URL_WHAT_YOU_TYPED, "");
         SuggestionDrawableState icon1 = mModel.get(BaseSuggestionViewProperties.ICON);
         Assert.assertNotNull(icon1);
 
-        verify(mIconBridge)
-                .getLargeIconForUrl(eq(mSuggestion.getUrl()), anyInt(), callback.capture());
-        callback.getValue().onLargeIconAvailable(null, 0, false, 0);
+        verify(mIconFetcher)
+                .fetchFaviconWithBackoff(eq(mSuggestion.getUrl()), eq(false), callback.capture());
+        callback.getValue().onFaviconFetchComplete(null, FaviconType.NONE);
         SuggestionDrawableState icon2 = mModel.get(BaseSuggestionViewProperties.ICON);
         Assert.assertNotNull(icon2);
 
diff --git a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/basic/SuggestionViewProperties.java b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/basic/SuggestionViewProperties.java
index a7d807ea..e4fe147 100644
--- a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/basic/SuggestionViewProperties.java
+++ b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/basic/SuggestionViewProperties.java
@@ -4,42 +4,17 @@
 
 package org.chromium.chrome.browser.omnibox.suggestions.basic;
 
-import androidx.annotation.IntDef;
-
 import org.chromium.chrome.browser.omnibox.suggestions.base.BaseSuggestionViewProperties;
 import org.chromium.chrome.browser.omnibox.suggestions.base.SuggestionSpannable;
 import org.chromium.ui.modelutil.PropertyKey;
 import org.chromium.ui.modelutil.PropertyModel;
 import org.chromium.ui.modelutil.PropertyModel.WritableBooleanPropertyKey;
-import org.chromium.ui.modelutil.PropertyModel.WritableIntPropertyKey;
 import org.chromium.ui.modelutil.PropertyModel.WritableObjectPropertyKey;
 
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-
 /**
  * The properties associated with rendering the default suggestion view.
  */
 public class SuggestionViewProperties {
-    @IntDef({SuggestionIcon.UNSET, SuggestionIcon.BOOKMARK, SuggestionIcon.HISTORY,
-            SuggestionIcon.GLOBE, SuggestionIcon.MAGNIFIER, SuggestionIcon.VOICE,
-            SuggestionIcon.FAVICON, SuggestionIcon.TRENDS, SuggestionIcon.TOTAL_COUNT})
-    @Retention(RetentionPolicy.SOURCE)
-    public @interface SuggestionIcon {
-        int UNSET = 0;
-        int BOOKMARK = 1;
-        int HISTORY = 2;
-        int GLOBE = 3;
-        int MAGNIFIER = 4;
-        int VOICE = 5;
-        int FAVICON = 6;
-        int TRENDS = 7;
-        int TOTAL_COUNT = 8;
-    }
-
-    /** The suggestion icon type shown. @see SuggestionIcon. Used for metric collection purposes. */
-    public static final WritableIntPropertyKey SUGGESTION_ICON_TYPE = new WritableIntPropertyKey();
-
     /** Whether suggestion is a search suggestion. */
     public static final WritableBooleanPropertyKey IS_SEARCH_SUGGESTION =
             new WritableBooleanPropertyKey();
@@ -56,8 +31,8 @@
     public static final WritableBooleanPropertyKey ALLOW_WRAP_AROUND =
             new WritableBooleanPropertyKey();
 
-    public static final PropertyKey[] ALL_UNIQUE_KEYS = new PropertyKey[] {IS_SEARCH_SUGGESTION,
-            SUGGESTION_ICON_TYPE, TEXT_LINE_1_TEXT, TEXT_LINE_2_TEXT, ALLOW_WRAP_AROUND};
+    public static final PropertyKey[] ALL_UNIQUE_KEYS = new PropertyKey[] {
+            IS_SEARCH_SUGGESTION, TEXT_LINE_1_TEXT, TEXT_LINE_2_TEXT, ALLOW_WRAP_AROUND};
 
     public static final PropertyKey[] ALL_KEYS =
             PropertyModel.concatKeys(ALL_UNIQUE_KEYS, BaseSuggestionViewProperties.ALL_KEYS);
diff --git a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/clipboard/ClipboardSuggestionProcessor.java b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/clipboard/ClipboardSuggestionProcessor.java
index 71a94209..5ea721a 100644
--- a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/clipboard/ClipboardSuggestionProcessor.java
+++ b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/clipboard/ClipboardSuggestionProcessor.java
@@ -13,9 +13,9 @@
 import androidx.annotation.VisibleForTesting;
 
 import org.chromium.base.metrics.RecordUserAction;
-import org.chromium.base.supplier.Supplier;
 import org.chromium.chrome.browser.omnibox.OmniboxSuggestionType;
 import org.chromium.chrome.browser.omnibox.R;
+import org.chromium.chrome.browser.omnibox.suggestions.FaviconFetcher;
 import org.chromium.chrome.browser.omnibox.suggestions.OmniboxSuggestionUiType;
 import org.chromium.chrome.browser.omnibox.suggestions.SuggestionHost;
 import org.chromium.chrome.browser.omnibox.suggestions.base.BaseSuggestionViewProcessor;
@@ -23,7 +23,6 @@
 import org.chromium.chrome.browser.omnibox.suggestions.base.SuggestionDrawableState;
 import org.chromium.chrome.browser.omnibox.suggestions.base.SuggestionSpannable;
 import org.chromium.chrome.browser.omnibox.suggestions.basic.SuggestionViewProperties;
-import org.chromium.components.favicon.LargeIconBridge;
 import org.chromium.components.omnibox.AutocompleteMatch;
 import org.chromium.ui.modelutil.PropertyModel;
 
@@ -31,17 +30,14 @@
 
 /** A class that handles model and view creation for the clipboard suggestions. */
 public class ClipboardSuggestionProcessor extends BaseSuggestionViewProcessor {
-    private final Supplier<LargeIconBridge> mIconBridgeSupplier;
-
     /**
      * @param context An Android context.
      * @param suggestionHost A handle to the object using the suggestions.
-     * @param iconBridgeSupplier A {@link LargeIconBridge} supplies site favicons.
+     * @param faviconFetcher Mechanism used to retrieve favicons.
      */
-    public ClipboardSuggestionProcessor(Context context, SuggestionHost suggestionHost,
-            Supplier<LargeIconBridge> iconBridgeSupplier) {
-        super(context, suggestionHost);
-        mIconBridgeSupplier = iconBridgeSupplier;
+    public ClipboardSuggestionProcessor(
+            Context context, SuggestionHost suggestionHost, FaviconFetcher faviconFetcher) {
+        super(context, suggestionHost, faviconFetcher);
     }
 
     @Override
@@ -145,7 +141,7 @@
 
         if (isUrlSuggestion) {
             // Update favicon for URL if it is available.
-            fetchSuggestionFavicon(model, suggestion.getUrl(), mIconBridgeSupplier.get(), null);
+            fetchSuggestionFavicon(model, suggestion.getUrl());
         }
     }
 
diff --git a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/clipboard/ClipboardSuggestionProcessorUnitTest.java b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/clipboard/ClipboardSuggestionProcessorUnitTest.java
index 6b5aa19..162206c 100644
--- a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/clipboard/ClipboardSuggestionProcessorUnitTest.java
+++ b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/clipboard/ClipboardSuggestionProcessorUnitTest.java
@@ -4,7 +4,6 @@
 
 package org.chromium.chrome.browser.omnibox.suggestions.clipboard;
 
-import static org.mockito.Mockito.anyInt;
 import static org.mockito.Mockito.eq;
 import static org.mockito.Mockito.verify;
 
@@ -34,6 +33,9 @@
 import org.chromium.base.test.BaseRobolectricTestRunner;
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.omnibox.OmniboxSuggestionType;
+import org.chromium.chrome.browser.omnibox.suggestions.FaviconFetcher;
+import org.chromium.chrome.browser.omnibox.suggestions.FaviconFetcher.FaviconFetchCompleteListener;
+import org.chromium.chrome.browser.omnibox.suggestions.FaviconFetcher.FaviconType;
 import org.chromium.chrome.browser.omnibox.suggestions.SuggestionCommonProperties;
 import org.chromium.chrome.browser.omnibox.suggestions.SuggestionHost;
 import org.chromium.chrome.browser.omnibox.suggestions.base.BaseSuggestionViewProperties;
@@ -41,8 +43,6 @@
 import org.chromium.chrome.browser.omnibox.suggestions.base.SuggestionSpannable;
 import org.chromium.chrome.browser.omnibox.suggestions.basic.SuggestionViewProperties;
 import org.chromium.chrome.browser.omnibox.suggestions.basic.SuggestionViewViewBinder;
-import org.chromium.components.favicon.LargeIconBridge;
-import org.chromium.components.favicon.LargeIconBridge.LargeIconCallback;
 import org.chromium.components.omnibox.AutocompleteMatch;
 import org.chromium.components.omnibox.AutocompleteMatchBuilder;
 import org.chromium.ui.modelutil.PropertyModel;
@@ -63,7 +63,7 @@
     public @Rule MockitoRule mMockitoRule = MockitoJUnit.rule();
 
     private @Mock SuggestionHost mSuggestionHost;
-    private @Mock LargeIconBridge mIconBridge;
+    private @Mock FaviconFetcher mIconFetcher;
     private @Mock Resources mResources;
 
     private Context mContext;
@@ -81,7 +81,7 @@
         mContext = new ContextThemeWrapper(
                 ContextUtils.getApplicationContext(), R.style.Theme_BrowserUI_DayNight);
         mBitmap = Bitmap.createBitmap(10, 5, Bitmap.Config.ARGB_8888);
-        mProcessor = new ClipboardSuggestionProcessor(mContext, mSuggestionHost, () -> mIconBridge);
+        mProcessor = new ClipboardSuggestionProcessor(mContext, mSuggestionHost, mIconFetcher);
         mRootView = new LinearLayout(mContext);
         mTitleTextView = new TextView(mContext);
         mTitleTextView.setId(R.id.line_1);
@@ -149,14 +149,14 @@
     @Test
     @SmallTest
     public void clipboardSuggestion_showsFaviconWhenAvailable() {
-        final ArgumentCaptor<LargeIconCallback> callback =
-                ArgumentCaptor.forClass(LargeIconCallback.class);
+        final ArgumentCaptor<FaviconFetchCompleteListener> callback =
+                ArgumentCaptor.forClass(FaviconFetchCompleteListener.class);
         createClipboardSuggestionAndClickReveal(OmniboxSuggestionType.CLIPBOARD_URL, TEST_URL);
         SuggestionDrawableState icon1 = mModel.get(BaseSuggestionViewProperties.ICON);
         Assert.assertNotNull(icon1);
 
-        verify(mIconBridge).getLargeIconForUrl(eq(TEST_URL), anyInt(), callback.capture());
-        callback.getValue().onLargeIconAvailable(mBitmap, 0, false, 0);
+        verify(mIconFetcher).fetchFaviconWithBackoff(eq(TEST_URL), eq(false), callback.capture());
+        callback.getValue().onFaviconFetchComplete(mBitmap, FaviconType.REGULAR);
         SuggestionDrawableState icon2 = mModel.get(BaseSuggestionViewProperties.ICON);
         Assert.assertNotNull(icon2);
 
@@ -167,14 +167,14 @@
     @Test
     @SmallTest
     public void clipboardSuggestion_showsFallbackIconWhenNoFaviconIsAvailable() {
-        final ArgumentCaptor<LargeIconCallback> callback =
-                ArgumentCaptor.forClass(LargeIconCallback.class);
+        final ArgumentCaptor<FaviconFetchCompleteListener> callback =
+                ArgumentCaptor.forClass(FaviconFetchCompleteListener.class);
         createClipboardSuggestionAndClickReveal(OmniboxSuggestionType.CLIPBOARD_URL, TEST_URL);
         SuggestionDrawableState icon1 = mModel.get(BaseSuggestionViewProperties.ICON);
         Assert.assertNotNull(icon1);
 
-        verify(mIconBridge).getLargeIconForUrl(eq(TEST_URL), anyInt(), callback.capture());
-        callback.getValue().onLargeIconAvailable(null, 0, false, 0);
+        verify(mIconFetcher).fetchFaviconWithBackoff(eq(TEST_URL), eq(false), callback.capture());
+        callback.getValue().onFaviconFetchComplete(null, FaviconType.NONE);
         SuggestionDrawableState icon2 = mModel.get(BaseSuggestionViewProperties.ICON);
         Assert.assertNotNull(icon2);
 
@@ -184,13 +184,13 @@
     @Test
     @SmallTest
     public void clipobardSuggestion_urlAndTextDirection() {
-        final ArgumentCaptor<LargeIconCallback> callback =
-                ArgumentCaptor.forClass(LargeIconCallback.class);
+        final ArgumentCaptor<FaviconFetchCompleteListener> callback =
+                ArgumentCaptor.forClass(FaviconFetchCompleteListener.class);
         // URL
         createClipboardSuggestionAndClickReveal(OmniboxSuggestionType.CLIPBOARD_URL, TEST_URL);
         Assert.assertFalse(mModel.get(SuggestionViewProperties.IS_SEARCH_SUGGESTION));
-        verify(mIconBridge).getLargeIconForUrl(eq(TEST_URL), anyInt(), callback.capture());
-        callback.getValue().onLargeIconAvailable(null, 0, false, 0);
+        verify(mIconFetcher).fetchFaviconWithBackoff(eq(TEST_URL), eq(false), callback.capture());
+        callback.getValue().onFaviconFetchComplete(null, FaviconType.NONE);
         Assert.assertEquals(TextView.TEXT_DIRECTION_LTR, mLastSetTextDirection);
 
         // Text
diff --git a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/editurl/EditUrlSuggestionProcessor.java b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/editurl/EditUrlSuggestionProcessor.java
index 7e273415..005283b 100644
--- a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/editurl/EditUrlSuggestionProcessor.java
+++ b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/editurl/EditUrlSuggestionProcessor.java
@@ -11,6 +11,7 @@
 import org.chromium.chrome.browser.history_clusters.HistoryClustersTabHelper;
 import org.chromium.chrome.browser.omnibox.OmniboxSuggestionType;
 import org.chromium.chrome.browser.omnibox.R;
+import org.chromium.chrome.browser.omnibox.suggestions.FaviconFetcher;
 import org.chromium.chrome.browser.omnibox.suggestions.OmniboxSuggestionUiType;
 import org.chromium.chrome.browser.omnibox.suggestions.SuggestionHost;
 import org.chromium.chrome.browser.omnibox.suggestions.UrlBarDelegate;
@@ -23,7 +24,6 @@
 import org.chromium.chrome.browser.share.ShareDelegate.ShareOrigin;
 import org.chromium.chrome.browser.tab.SadTab;
 import org.chromium.chrome.browser.tab.Tab;
-import org.chromium.components.favicon.LargeIconBridge;
 import org.chromium.components.omnibox.AutocompleteMatch;
 import org.chromium.components.ukm.UkmRecorder;
 import org.chromium.ui.base.Clipboard;
@@ -42,9 +42,6 @@
     /** The delegate for accessing the location bar for observation and modification. */
     private final UrlBarDelegate mUrlBarDelegate;
 
-    /** Supplies site favicons. */
-    private final Supplier<LargeIconBridge> mIconBridgeSupplier;
-
     /** The delegate for accessing the sharing feature. */
     private final Supplier<ShareDelegate> mShareDelegateSupplier;
 
@@ -58,13 +55,12 @@
      * @param locationBarDelegate A means of modifying the location bar.
      */
     public EditUrlSuggestionProcessor(Context context, SuggestionHost suggestionHost,
-            UrlBarDelegate locationBarDelegate, Supplier<LargeIconBridge> iconBridgeSupplier,
+            UrlBarDelegate locationBarDelegate, FaviconFetcher faviconFetcher,
             Supplier<Tab> tabSupplier, Supplier<ShareDelegate> shareDelegateSupplier) {
-        super(context, suggestionHost);
+        super(context, suggestionHost, faviconFetcher);
 
         mContext = context;
         mUrlBarDelegate = locationBarDelegate;
-        mIconBridgeSupplier = iconBridgeSupplier;
         mTabSupplier = tabSupplier;
         mShareDelegateSupplier = shareDelegateSupplier;
     }
@@ -145,7 +141,7 @@
                                         .build(),
                                 R.string.bookmark_item_edit, () -> onEditLink(suggestion))));
 
-        fetchSuggestionFavicon(model, suggestion.getUrl(), mIconBridgeSupplier.get(), null);
+        fetchSuggestionFavicon(model, suggestion.getUrl());
     }
 
     @Override
diff --git a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/entity/EntitySuggestionProcessor.java b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/entity/EntitySuggestionProcessor.java
index 18e5a90..69cc7ec 100644
--- a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/entity/EntitySuggestionProcessor.java
+++ b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/entity/EntitySuggestionProcessor.java
@@ -54,7 +54,7 @@
      */
     public EntitySuggestionProcessor(Context context, SuggestionHost suggestionHost,
             Supplier<ImageFetcher> imageFetcherSupplier) {
-        super(context, suggestionHost);
+        super(context, suggestionHost, null);
         mSuggestionHost = suggestionHost;
         mPendingImageRequests = new HashMap<>();
         mImageFetcherSupplier = imageFetcherSupplier;
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/omnibox/suggestions/entity/EntitySuggestionProcessorUnitTest.java b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/entity/EntitySuggestionProcessorUnitTest.java
similarity index 81%
rename from chrome/android/javatests/src/org/chromium/chrome/browser/omnibox/suggestions/entity/EntitySuggestionProcessorUnitTest.java
rename to chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/entity/EntitySuggestionProcessorUnitTest.java
index 8bd979eb..dae972a 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/omnibox/suggestions/entity/EntitySuggestionProcessorUnitTest.java
+++ b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/entity/EntitySuggestionProcessorUnitTest.java
@@ -10,7 +10,6 @@
 import static org.mockito.Mockito.verify;
 
 import android.graphics.Bitmap;
-import android.graphics.Bitmap.Config;
 import android.graphics.Color;
 import android.graphics.drawable.BitmapDrawable;
 import android.graphics.drawable.ColorDrawable;
@@ -20,18 +19,19 @@
 
 import org.junit.Assert;
 import org.junit.Before;
+import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.ArgumentCaptor;
 import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+import org.robolectric.annotation.Config;
 
 import org.chromium.base.BaseSwitches;
 import org.chromium.base.Callback;
 import org.chromium.base.ContextUtils;
-import org.chromium.base.test.BaseJUnit4ClassRunner;
-import org.chromium.base.test.UiThreadTest;
-import org.chromium.base.test.util.Batch;
+import org.chromium.base.test.BaseRobolectricTestRunner;
 import org.chromium.base.test.util.CommandLineFlags;
 import org.chromium.chrome.browser.omnibox.OmniboxSuggestionType;
 import org.chromium.chrome.browser.omnibox.suggestions.SuggestionHost;
@@ -40,24 +40,28 @@
 import org.chromium.components.image_fetcher.ImageFetcher;
 import org.chromium.components.omnibox.AutocompleteMatch;
 import org.chromium.components.omnibox.AutocompleteMatchBuilder;
-import org.chromium.content_public.browser.test.NativeLibraryTestUtils;
 import org.chromium.ui.modelutil.PropertyModel;
 import org.chromium.url.GURL;
+import org.chromium.url.JUnitTestGURLs;
+import org.chromium.url.ShadowGURL;
 
 /**
  * Tests for {@link EntitySuggestionProcessor}.
  */
-@RunWith(BaseJUnit4ClassRunner.class)
-@Batch(Batch.UNIT_TESTS)
+@RunWith(BaseRobolectricTestRunner.class)
+@Config(manifest = Config.NONE, shadows = {ShadowGURL.class})
 @CommandLineFlags.Add(BaseSwitches.DISABLE_LOW_END_DEVICE_MODE)
 public class EntitySuggestionProcessorUnitTest {
-    @Mock
-    SuggestionHost mSuggestionHost;
+    private static final GURL WEB_URL = JUnitTestGURLs.getGURL(JUnitTestGURLs.URL_1);
+    private static final GURL WEB_URL_2 = JUnitTestGURLs.getGURL(JUnitTestGURLs.URL_2);
+    private static final GURL SEARCH_URL = JUnitTestGURLs.getGURL(JUnitTestGURLs.SEARCH_URL);
 
-    @Mock
-    ImageFetcher mImageFetcher;
+    public @Rule MockitoRule mMockitoRule = MockitoJUnit.rule();
 
-    private Bitmap mBitmap;
+    private @Mock SuggestionHost mSuggestionHost;
+    private @Mock ImageFetcher mImageFetcher;
+    private @Mock Bitmap mBitmap;
+
     private EntitySuggestionProcessor mProcessor;
 
     /**
@@ -103,11 +107,6 @@
 
     @Before
     public void setUp() {
-        MockitoAnnotations.initMocks(this);
-        NativeLibraryTestUtils.loadNativeLibraryNoBrowserProcess();
-
-        mBitmap = Bitmap.createBitmap(1, 1, Config.ALPHA_8);
-
         mProcessor = new EntitySuggestionProcessor(
                 ContextUtils.getApplicationContext(), mSuggestionHost, () -> mImageFetcher);
     }
@@ -118,10 +117,8 @@
 
     @Test
     @SmallTest
-    @UiThreadTest
     public void contentTest_basicContent() {
-        SuggestionTestHelper suggHelper =
-                createSuggestion("subject", "details", null, GURL.emptyGURL());
+        SuggestionTestHelper suggHelper = createSuggestion("subject", "details", null, SEARCH_URL);
         processSuggestion(suggHelper);
         Assert.assertEquals(
                 "subject", suggHelper.mModel.get(EntitySuggestionViewProperties.SUBJECT_TEXT));
@@ -131,9 +128,8 @@
 
     @Test
     @SmallTest
-    @UiThreadTest
     public void decorationTest_noColorOrImage() {
-        SuggestionTestHelper suggHelper = createSuggestion("", "", null, GURL.emptyGURL());
+        SuggestionTestHelper suggHelper = createSuggestion("", "", null, SEARCH_URL);
         processSuggestion(suggHelper);
 
         Assert.assertNotNull(suggHelper.getIcon());
@@ -142,9 +138,8 @@
 
     @Test
     @SmallTest
-    @UiThreadTest
     public void decorationTest_validHexColor() {
-        SuggestionTestHelper suggHelper = createSuggestion("", "", "#fedcba", GURL.emptyGURL());
+        SuggestionTestHelper suggHelper = createSuggestion("", "", "#fedcba", SEARCH_URL);
         processSuggestion(suggHelper);
 
         Assert.assertThat(suggHelper.getIcon(), instanceOf(ColorDrawable.class));
@@ -154,9 +149,8 @@
 
     @Test
     @SmallTest
-    @UiThreadTest
     public void decorationTest_validNamedColor() {
-        SuggestionTestHelper suggHelper = createSuggestion("", "", "red", GURL.emptyGURL());
+        SuggestionTestHelper suggHelper = createSuggestion("", "", "red", SEARCH_URL);
         processSuggestion(suggHelper);
 
         Assert.assertThat(suggHelper.getIcon(), instanceOf(ColorDrawable.class));
@@ -166,32 +160,29 @@
 
     @Test
     @SmallTest
-    @UiThreadTest
     public void decorationTest_invalidColor() {
         // Note, fallback is the bitmap drawable representing a search loupe.
-        SuggestionTestHelper suggHelper = createSuggestion("", "", "", GURL.emptyGURL());
+        SuggestionTestHelper suggHelper = createSuggestion("", "", "", SEARCH_URL);
         processSuggestion(suggHelper);
         Assert.assertThat(suggHelper.getIcon(), instanceOf(BitmapDrawable.class));
 
-        suggHelper = createSuggestion("", "", "#", GURL.emptyGURL());
+        suggHelper = createSuggestion("", "", "#", SEARCH_URL);
         processSuggestion(suggHelper);
         Assert.assertThat(suggHelper.getIcon(), instanceOf(BitmapDrawable.class));
 
-        suggHelper = createSuggestion("", "", "invalid", GURL.emptyGURL());
+        suggHelper = createSuggestion("", "", "invalid", SEARCH_URL);
         processSuggestion(suggHelper);
         Assert.assertThat(suggHelper.getIcon(), instanceOf(BitmapDrawable.class));
     }
 
     @Test
     @SmallTest
-    @UiThreadTest
     public void decorationTest_basicSuccessfulBitmapFetch() {
-        final GURL url = new GURL("http://site.com");
-        SuggestionTestHelper suggHelper = createSuggestion("", "", "red", url);
+        SuggestionTestHelper suggHelper = createSuggestion("", "", "red", WEB_URL);
         processSuggestion(suggHelper);
 
         final ArgumentCaptor<Callback<Bitmap>> callback = ArgumentCaptor.forClass(Callback.class);
-        verify(mImageFetcher).fetchImage(eq(createParams(url.getSpec())), callback.capture());
+        verify(mImageFetcher).fetchImage(eq(createParams(WEB_URL.getSpec())), callback.capture());
 
         Assert.assertThat(suggHelper.getIcon(), instanceOf(ColorDrawable.class));
         callback.getValue().onResult(mBitmap);
@@ -201,39 +192,34 @@
 
     @Test
     @SmallTest
-    @UiThreadTest
     public void decorationTest_repeatedUrlsAreFetchedOnlyOnce() {
-        final GURL url1 = new GURL("http://site1.com");
-        final GURL url2 = new GURL("http://site2.com");
-        final SuggestionTestHelper sugg1 = createSuggestion("", "", "", url1);
-        final SuggestionTestHelper sugg2 = createSuggestion("", "", "", url1);
-        final SuggestionTestHelper sugg3 = createSuggestion("", "", "", url2);
-        final SuggestionTestHelper sugg4 = createSuggestion("", "", "", url2);
+        final SuggestionTestHelper sugg1 = createSuggestion("", "", "", WEB_URL);
+        final SuggestionTestHelper sugg2 = createSuggestion("", "", "", WEB_URL);
+        final SuggestionTestHelper sugg3 = createSuggestion("", "", "", WEB_URL_2);
+        final SuggestionTestHelper sugg4 = createSuggestion("", "", "", WEB_URL_2);
 
         processSuggestion(sugg1);
         processSuggestion(sugg2);
         processSuggestion(sugg3);
         processSuggestion(sugg4);
 
-        verify(mImageFetcher).fetchImage(eq(createParams(url1.getSpec())), any());
-        verify(mImageFetcher).fetchImage(eq(createParams(url2.getSpec())), any());
+        verify(mImageFetcher).fetchImage(eq(createParams(WEB_URL.getSpec())), any());
+        verify(mImageFetcher).fetchImage(eq(createParams(WEB_URL_2.getSpec())), any());
     }
 
     @Test
     @SmallTest
-    @UiThreadTest
     public void decorationTest_bitmapReplacesIconForAllSuggestionsWithSameUrl() {
-        final GURL url = new GURL("http://site.com");
-        final SuggestionTestHelper sugg1 = createSuggestion("", "", "", url);
-        final SuggestionTestHelper sugg2 = createSuggestion("", "", "", url);
-        final SuggestionTestHelper sugg3 = createSuggestion("", "", "", url);
+        final SuggestionTestHelper sugg1 = createSuggestion("", "", "", WEB_URL);
+        final SuggestionTestHelper sugg2 = createSuggestion("", "", "", WEB_URL);
+        final SuggestionTestHelper sugg3 = createSuggestion("", "", "", WEB_URL);
 
         processSuggestion(sugg1);
         processSuggestion(sugg2);
         processSuggestion(sugg3);
 
         final ArgumentCaptor<Callback<Bitmap>> callback = ArgumentCaptor.forClass(Callback.class);
-        verify(mImageFetcher).fetchImage(eq(createParams(url.getSpec())), callback.capture());
+        verify(mImageFetcher).fetchImage(eq(createParams(WEB_URL.getSpec())), callback.capture());
 
         final Drawable icon1 = sugg1.getIcon();
         final Drawable icon2 = sugg2.getIcon();
@@ -259,14 +245,12 @@
 
     @Test
     @SmallTest
-    @UiThreadTest
     public void decorationTest_failedBitmapFetchDoesNotReplaceIcon() {
-        final GURL url = new GURL("http://site.com");
-        final SuggestionTestHelper suggHelper = createSuggestion("", "", null, url);
+        final SuggestionTestHelper suggHelper = createSuggestion("", "", null, WEB_URL);
         processSuggestion(suggHelper);
 
         final ArgumentCaptor<Callback<Bitmap>> callback = ArgumentCaptor.forClass(Callback.class);
-        verify(mImageFetcher).fetchImage(eq(createParams(url.getSpec())), callback.capture());
+        verify(mImageFetcher).fetchImage(eq(createParams(WEB_URL.getSpec())), callback.capture());
 
         final Drawable oldIcon = suggHelper.getIcon();
         callback.getValue().onResult(null);
@@ -278,14 +262,12 @@
 
     @Test
     @SmallTest
-    @UiThreadTest
     public void decorationTest_failedBitmapFetchDoesNotReplaceColor() {
-        final GURL url = new GURL("http://site.com");
-        final SuggestionTestHelper suggHelper = createSuggestion("", "", "red", url);
+        final SuggestionTestHelper suggHelper = createSuggestion("", "", "red", WEB_URL);
         processSuggestion(suggHelper);
 
         final ArgumentCaptor<Callback<Bitmap>> callback = ArgumentCaptor.forClass(Callback.class);
-        verify(mImageFetcher).fetchImage(eq(createParams(url.getSpec())), callback.capture());
+        verify(mImageFetcher).fetchImage(eq(createParams(WEB_URL.getSpec())), callback.capture());
 
         final Drawable oldIcon = suggHelper.getIcon();
         callback.getValue().onResult(null);
@@ -297,17 +279,15 @@
 
     @Test
     @SmallTest
-    @UiThreadTest
     public void decorationTest_updatedModelsAreRemovedFromPendingRequestsList() {
-        final GURL url = new GURL("http://site1.com");
-        final SuggestionTestHelper sugg1 = createSuggestion("", "", "", url);
-        final SuggestionTestHelper sugg2 = createSuggestion("", "", "", url);
+        final SuggestionTestHelper sugg1 = createSuggestion("", "", "", WEB_URL);
+        final SuggestionTestHelper sugg2 = createSuggestion("", "", "", WEB_URL);
 
         processSuggestion(sugg1);
         processSuggestion(sugg2);
 
         final ArgumentCaptor<Callback<Bitmap>> callback = ArgumentCaptor.forClass(Callback.class);
-        verify(mImageFetcher).fetchImage(eq(createParams(url.getSpec())), callback.capture());
+        verify(mImageFetcher).fetchImage(eq(createParams(WEB_URL.getSpec())), callback.capture());
         verify(mImageFetcher).fetchImage(any(), any());
 
         final Drawable icon1 = sugg1.getIcon();
diff --git a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/mostvisited/MostVisitedTilesProcessor.java b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/mostvisited/MostVisitedTilesProcessor.java
index 2b3a888..4b36b543 100644
--- a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/mostvisited/MostVisitedTilesProcessor.java
+++ b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/mostvisited/MostVisitedTilesProcessor.java
@@ -5,16 +5,14 @@
 package org.chromium.chrome.browser.omnibox.suggestions.mostvisited;
 
 import android.content.Context;
-import android.graphics.Bitmap;
 import android.graphics.drawable.BitmapDrawable;
-import android.graphics.drawable.Drawable;
 
 import androidx.annotation.NonNull;
-import androidx.annotation.VisibleForTesting;
+import androidx.appcompat.content.res.AppCompatResources;
 
-import org.chromium.base.supplier.Supplier;
 import org.chromium.chrome.browser.omnibox.OmniboxSuggestionType;
 import org.chromium.chrome.browser.omnibox.R;
+import org.chromium.chrome.browser.omnibox.suggestions.FaviconFetcher;
 import org.chromium.chrome.browser.omnibox.suggestions.OmniboxSuggestionUiType;
 import org.chromium.chrome.browser.omnibox.suggestions.SuggestionCommonProperties;
 import org.chromium.chrome.browser.omnibox.suggestions.SuggestionHost;
@@ -23,10 +21,7 @@
 import org.chromium.chrome.browser.omnibox.suggestions.carousel.BaseCarouselSuggestionViewProperties;
 import org.chromium.chrome.browser.ui.theme.BrandedColorScheme;
 import org.chromium.components.browser_ui.styles.ChromeColors;
-import org.chromium.components.browser_ui.widget.RoundedIconGenerator;
-import org.chromium.components.browser_ui.widget.TintedDrawable;
 import org.chromium.components.browser_ui.widget.tile.TileViewProperties;
-import org.chromium.components.favicon.LargeIconBridge;
 import org.chromium.components.omnibox.AutocompleteMatch;
 import org.chromium.ui.modelutil.MVCListAdapter.ListItem;
 import org.chromium.ui.modelutil.PropertyModel;
@@ -41,36 +36,24 @@
 public class MostVisitedTilesProcessor extends BaseCarouselSuggestionProcessor {
     private final @NonNull Context mContext;
     private final @NonNull SuggestionHost mSuggestionHost;
-    private final @NonNull Supplier<LargeIconBridge> mIconBridgeSupplier;
+    private final @NonNull FaviconFetcher mFaviconFetcher;
     private final int mMinCarouselItemViewHeight;
-    private final int mDesiredFaviconWidthPx;
-    private @NonNull RoundedIconGenerator mIconGenerator;
 
     /**
      * Constructor.
      *
      * @param context An Android context.
      * @param host SuggestionHost receiving notifications about user actions.
-     * @param iconBridgeSupplier Supplier of the LargeIconBridge used to fetch site favicons.
+     * @param faviconFetcher Class retrieving favicons for the MV Tiles.
      */
     public MostVisitedTilesProcessor(@NonNull Context context, @NonNull SuggestionHost host,
-            @NonNull Supplier<LargeIconBridge> iconBridgeSupplier) {
+            @NonNull FaviconFetcher faviconFetcher) {
         super(context);
         mContext = context;
         mSuggestionHost = host;
-        mIconBridgeSupplier = iconBridgeSupplier;
+        mFaviconFetcher = faviconFetcher;
         mMinCarouselItemViewHeight =
                 mContext.getResources().getDimensionPixelSize(R.dimen.tile_view_min_height);
-        mDesiredFaviconWidthPx = mContext.getResources().getDimensionPixelSize(
-                R.dimen.omnibox_suggestion_favicon_size);
-
-        int fallbackIconSize =
-                mContext.getResources().getDimensionPixelSize(R.dimen.tile_view_icon_size);
-        int fallbackIconColor = mContext.getColor(R.color.default_favicon_background_color);
-        int fallbackIconTextSize =
-                mContext.getResources().getDimensionPixelSize(R.dimen.tile_view_icon_text_size);
-        mIconGenerator = new RoundedIconGenerator(fallbackIconSize, fallbackIconSize,
-                fallbackIconSize / 2, fallbackIconColor, fallbackIconTextSize);
     }
 
     @Override
@@ -98,7 +81,6 @@
         final List<AutocompleteMatch.SuggestTile> tiles = suggestion.getSuggestTiles();
         final int tilesCount = tiles.size();
         final List<ListItem> tileList = new ArrayList<>(tilesCount);
-        final LargeIconBridge iconBridge = mIconBridgeSupplier.get();
 
         for (int elementIndex = 0; elementIndex < tilesCount; elementIndex++) {
             final PropertyModel tileModel = new PropertyModel(TileViewProperties.ALL_KEYS);
@@ -119,41 +101,31 @@
             });
 
             if (tiles.get(elementIndex).isSearch) {
-                Drawable drawable = TintedDrawable.constructTintedDrawable(
-                        mContext, R.drawable.ic_suggestion_magnifier);
                 // Note: we should never show most visited tiles in incognito mode. Catch this early
                 // if we ever do.
                 assert model.get(SuggestionCommonProperties.COLOR_SCHEME)
                         != BrandedColorScheme.INCOGNITO;
-                drawable.setTintList(
+                tileModel.set(TileViewProperties.ICON_TINT,
                         ChromeColors.getSecondaryIconTint(mContext, /* isIncognito= */ false));
-                tileModel.set(TileViewProperties.ICON, drawable);
+                tileModel.set(TileViewProperties.ICON,
+                        AppCompatResources.getDrawable(
+                                mContext, R.drawable.ic_suggestion_magnifier));
                 tileModel.set(TileViewProperties.CONTENT_DESCRIPTION,
                         mContext.getString(
                                 R.string.accessibility_omnibox_most_visited_tile_search, title));
             } else {
+                tileModel.set(TileViewProperties.ICON_TINT, null);
                 tileModel.set(TileViewProperties.CONTENT_DESCRIPTION,
                         mContext.getString(
                                 R.string.accessibility_omnibox_most_visited_tile_navigate, title,
                                 url.getHost()));
 
-                if (iconBridge != null) {
-                    // TODO(https://crbug.com/1335507): Cache the generated bitmaps.
-                    // This is not needed for the experimentation purposes (this block is currently
-                    // used very infrequently - only to offer zero-prefix URL suggestions on URL
-                    // visit). This must be addressed before MV Carousel can be offered in more
-                    // contexts. Consider unifying cache with LargeIconBridge if possible.
-                    Bitmap fallbackIcon = mIconGenerator.generateIconForUrl(url);
-                    tileModel.set(TileViewProperties.ICON, new BitmapDrawable(fallbackIcon));
-
-                    iconBridge.getLargeIconForUrl(tiles.get(elementIndex).url,
-                            mDesiredFaviconWidthPx,
-                            (Bitmap icon, int fallbackColor, boolean isFallbackColorDefault,
-                                    int iconType) -> {
-                                if (icon == null) return;
-                                setIcon(tileModel, icon);
-                            });
-                }
+                tileModel.set(TileViewProperties.SMALL_ICON_ROUNDING_RADIUS,
+                        mContext.getResources().getDimensionPixelSize(
+                                R.dimen.omnibox_carousel_icon_rounding_radius));
+                mFaviconFetcher.fetchFaviconWithBackoff(url, true, (icon, type) -> {
+                    tileModel.set(TileViewProperties.ICON, new BitmapDrawable(icon));
+                });
             }
 
             tileList.add(new ListItem(
@@ -163,26 +135,4 @@
         model.set(BaseCarouselSuggestionViewProperties.TILES, tileList);
         model.set(BaseCarouselSuggestionViewProperties.SHOW_TITLE, false);
     }
-
-    /**
-     * Sets the large icon to the supplied tile's model.
-     *
-     * @param tileModel The model for the specific tile view to be modified.
-     * @param icon The icon to apply.
-     */
-    private void setIcon(@NonNull PropertyModel tileModel, @NonNull Bitmap icon) {
-        tileModel.set(TileViewProperties.SMALL_ICON_ROUNDING_RADIUS,
-                mContext.getResources().getDimensionPixelSize(
-                        R.dimen.omnibox_carousel_icon_rounding_radius));
-        tileModel.set(TileViewProperties.ICON, new BitmapDrawable(icon));
-    }
-
-    /**
-     * Overrides RoundedIconGenerator for testing.
-     * @param RoundedIconGenerator Generator to use.
-     */
-    @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
-    void setRoundedIconGeneratorForTesting(@NonNull RoundedIconGenerator generator) {
-        mIconGenerator = generator;
-    }
 }
diff --git a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/mostvisited/MostVisitedTilesProcessorUnitTest.java b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/mostvisited/MostVisitedTilesProcessorUnitTest.java
index a99be51..1cb8ef5 100644
--- a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/mostvisited/MostVisitedTilesProcessorUnitTest.java
+++ b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/mostvisited/MostVisitedTilesProcessorUnitTest.java
@@ -8,13 +8,13 @@
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertThat;
 import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyBoolean;
 import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doNothing;
 import static org.mockito.Mockito.inOrder;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.verifyNoMoreInteractions;
-import static org.mockito.Mockito.when;
 import static org.robolectric.Shadows.shadowOf;
 
 import android.app.Activity;
@@ -33,16 +33,16 @@
 import org.mockito.junit.MockitoRule;
 import org.robolectric.Robolectric;
 import org.robolectric.annotation.Config;
+import org.robolectric.shadows.ShadowLog;
 
 import org.chromium.base.test.BaseRobolectricTestRunner;
 import org.chromium.chrome.R;
+import org.chromium.chrome.browser.omnibox.suggestions.FaviconFetcher;
+import org.chromium.chrome.browser.omnibox.suggestions.FaviconFetcher.FaviconFetchCompleteListener;
 import org.chromium.chrome.browser.omnibox.suggestions.SuggestionHost;
 import org.chromium.chrome.browser.omnibox.suggestions.carousel.BaseCarouselSuggestionItemViewBuilder;
 import org.chromium.chrome.browser.omnibox.suggestions.carousel.BaseCarouselSuggestionViewProperties;
-import org.chromium.components.browser_ui.widget.RoundedIconGenerator;
 import org.chromium.components.browser_ui.widget.tile.TileViewProperties;
-import org.chromium.components.favicon.LargeIconBridge;
-import org.chromium.components.favicon.LargeIconBridge.LargeIconCallback;
 import org.chromium.components.omnibox.AutocompleteMatch;
 import org.chromium.components.omnibox.AutocompleteMatch.SuggestTile;
 import org.chromium.components.omnibox.AutocompleteMatchBuilder;
@@ -64,34 +64,33 @@
     private static final GURL NAV_URL_2 = JUnitTestGURLs.getGURL(JUnitTestGURLs.URL_2);
     private static final GURL SEARCH_URL = JUnitTestGURLs.getGURL(JUnitTestGURLs.SEARCH_URL);
     private static final int FALLBACK_COLOR = 0xACE0BA5E;
+    private static final int DESIRED_FAVICON_SIZE_PX = 100;
 
     public @Rule MockitoRule mockitoRule = MockitoJUnit.rule();
 
     private Activity mActivity;
     private PropertyModel mPropertyModel;
     private MostVisitedTilesProcessor mProcessor;
-    private ArgumentCaptor<LargeIconCallback> mIconCallbackCaptor =
-            ArgumentCaptor.forClass(LargeIconCallback.class);
     private AutocompleteMatch mMatch;
 
+    private ArgumentCaptor<FaviconFetchCompleteListener> mIconCallbackCaptor =
+            ArgumentCaptor.forClass(FaviconFetchCompleteListener.class);
+    private @Mock Bitmap mFaviconBitmap;
     private @Mock SuggestionHost mSuggestionHost;
-    private @Mock LargeIconBridge mLargeIconBridge;
-    private @Mock RoundedIconGenerator mIconGenerator;
-    private @Mock Bitmap mGeneratedIconBitmap;
-    private @Mock Bitmap mLargeIconBitmap;
+    private @Mock FaviconFetcher mFaviconFetcher;
 
     @Before
     public void setUp() {
+        // Enable logs to be printed along with possible test failures.
+        ShadowLog.stream = System.out;
         mActivity = Robolectric.buildActivity(Activity.class).setup().get();
         mActivity.setTheme(R.style.Theme_BrowserUI_DayNight);
-        mProcessor =
-                new MostVisitedTilesProcessor(mActivity, mSuggestionHost, () -> mLargeIconBridge);
-        mProcessor.setRoundedIconGeneratorForTesting(mIconGenerator);
 
-        when(mIconGenerator.generateIconForUrl(any(GURL.class))).thenReturn(mGeneratedIconBitmap);
-        when(mLargeIconBridge.getLargeIconForUrl(any(), anyInt(), mIconCallbackCaptor.capture()))
-                .thenReturn(true);
+        doNothing()
+                .when(mFaviconFetcher)
+                .fetchFaviconWithBackoff(any(), anyBoolean(), mIconCallbackCaptor.capture());
 
+        mProcessor = new MostVisitedTilesProcessor(mActivity, mSuggestionHost, mFaviconFetcher);
         mPropertyModel = mProcessor.createModel();
     }
 
@@ -111,8 +110,7 @@
     public void testDecorations_searchTile() {
         List<ListItem> tileList =
                 populateTilePropertiesForTiles(0, new SuggestTile("title", SEARCH_URL, true));
-        verifyNoMoreInteractions(mIconGenerator);
-        verifyNoMoreInteractions(mLargeIconBridge);
+        verifyNoMoreInteractions(mFaviconFetcher);
 
         assertEquals(1, tileList.size());
         ListItem tileItem = tileList.get(0);
@@ -125,12 +123,13 @@
     }
 
     @Test
-    public void testDecorations_navTile_generatedIconOnly() {
+    public void testDecorations_navTile() {
         List<ListItem> tileList =
                 populateTilePropertiesForTiles(0, new SuggestTile("title", NAV_URL, false));
-        verify(mIconGenerator, times(1)).generateIconForUrl(eq(NAV_URL));
-        verify(mLargeIconBridge, times(1)).getLargeIconForUrl(eq(NAV_URL), anyInt(), any());
+        verify(mFaviconFetcher, times(1)).fetchFaviconWithBackoff(eq(NAV_URL), anyBoolean(), any());
+        mIconCallbackCaptor.getValue().onFaviconFetchComplete(mFaviconBitmap, 0);
 
+        // Since we "retrieved" an icon from LargeIconBridge, we should not generate a fallback.
         assertEquals(1, tileList.size());
         ListItem tileItem = tileList.get(0);
         PropertyModel tileModel = tileItem.model;
@@ -140,52 +139,7 @@
         assertEquals(BaseCarouselSuggestionItemViewBuilder.ViewType.TILE_VIEW, tileItem.type);
         assertThat(drawable, instanceOf(BitmapDrawable.class));
         Bitmap bitmap = ((BitmapDrawable) drawable).getBitmap();
-        assertEquals(mGeneratedIconBitmap, bitmap);
-    }
-
-    @Test
-    public void testDecorations_navTile_fallbackColor() {
-        List<ListItem> tileList =
-                populateTilePropertiesForTiles(0, new SuggestTile("title", NAV_URL, false));
-        verify(mIconGenerator, times(1)).generateIconForUrl(eq(NAV_URL));
-        verify(mLargeIconBridge, times(1)).getLargeIconForUrl(eq(NAV_URL), anyInt(), any());
-        // Report no icon, only color.
-        mIconCallbackCaptor.getValue().onLargeIconAvailable(null, FALLBACK_COLOR, true, 0);
-
-        // The logic should ignore the fallback color and focus only on the presence of an
-        // actual icon. If no icon is available, we should retain the original generated icon.
-        assertEquals(1, tileList.size());
-        ListItem tileItem = tileList.get(0);
-        PropertyModel tileModel = tileItem.model;
-        Drawable drawable = tileModel.get(TileViewProperties.ICON);
-
-        assertEquals(BaseCarouselSuggestionItemViewBuilder.ViewType.TILE_VIEW, tileItem.type);
-        assertThat(drawable, instanceOf(BitmapDrawable.class));
-        Bitmap bitmap = ((BitmapDrawable) drawable).getBitmap();
-        assertEquals(mGeneratedIconBitmap, bitmap);
-    }
-
-    @Test
-    public void testDecorations_navTile_actualIcon() {
-        List<ListItem> tileList =
-                populateTilePropertiesForTiles(0, new SuggestTile("title", NAV_URL, false));
-        verify(mIconGenerator, times(1)).generateIconForUrl(eq(NAV_URL));
-        verify(mLargeIconBridge, times(1)).getLargeIconForUrl(eq(NAV_URL), anyInt(), any());
-        // Report no icon, only color.
-        mIconCallbackCaptor.getValue().onLargeIconAvailable(
-                mLargeIconBitmap, FALLBACK_COLOR, true, 0);
-
-        // In the presence of actual icon we should expect to see that icon being applied to the
-        // model.
-        assertEquals(1, tileList.size());
-        ListItem tileItem = tileList.get(0);
-        PropertyModel tileModel = tileItem.model;
-        Drawable drawable = tileModel.get(TileViewProperties.ICON);
-
-        assertEquals(BaseCarouselSuggestionItemViewBuilder.ViewType.TILE_VIEW, tileItem.type);
-        assertThat(drawable, instanceOf(BitmapDrawable.class));
-        Bitmap bitmap = ((BitmapDrawable) drawable).getBitmap();
-        assertEquals(mLargeIconBitmap, bitmap);
+        assertEquals(mFaviconBitmap, bitmap);
     }
 
     @Test
@@ -214,8 +168,7 @@
                 .onSuggestionClicked(eq(mMatch), eq(3), eq(SEARCH_URL));
 
         verifyNoMoreInteractions(mSuggestionHost);
-        verifyNoMoreInteractions(mIconGenerator);
-        verifyNoMoreInteractions(mLargeIconBridge);
+        verifyNoMoreInteractions(mFaviconFetcher);
     }
 
     @Test
@@ -244,8 +197,7 @@
                 .onDeleteMatchElement(eq(mMatch), eq("search1"), eq(1), eq(0));
 
         verifyNoMoreInteractions(mSuggestionHost);
-        verifyNoMoreInteractions(mIconGenerator);
-        verifyNoMoreInteractions(mLargeIconBridge);
+        verifyNoMoreInteractions(mFaviconFetcher);
     }
 
     @Test
@@ -270,8 +222,7 @@
         ordered.verify(mSuggestionHost, times(1)).setOmniboxEditingText(eq(SEARCH_URL.getSpec()));
 
         verifyNoMoreInteractions(mSuggestionHost);
-        verifyNoMoreInteractions(mIconGenerator);
-        verifyNoMoreInteractions(mLargeIconBridge);
+        verifyNoMoreInteractions(mFaviconFetcher);
     }
 
     @Test
diff --git a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/pedal/PedalSuggestionProcessor.java b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/pedal/PedalSuggestionProcessor.java
index 4afe9a8..a2dbe80 100644
--- a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/pedal/PedalSuggestionProcessor.java
+++ b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/pedal/PedalSuggestionProcessor.java
@@ -10,9 +10,9 @@
 import androidx.annotation.VisibleForTesting;
 import androidx.collection.ArraySet;
 
-import org.chromium.base.supplier.Supplier;
 import org.chromium.chrome.browser.omnibox.UrlBarEditingTextStateProvider;
 import org.chromium.chrome.browser.omnibox.suggestions.AutocompleteDelegate;
+import org.chromium.chrome.browser.omnibox.suggestions.FaviconFetcher;
 import org.chromium.chrome.browser.omnibox.suggestions.OmniboxPedalDelegate;
 import org.chromium.chrome.browser.omnibox.suggestions.OmniboxSuggestionUiType;
 import org.chromium.chrome.browser.omnibox.suggestions.SuggestionHost;
@@ -20,7 +20,6 @@
 import org.chromium.chrome.browser.omnibox.suggestions.base.BaseSuggestionViewProperties;
 import org.chromium.chrome.browser.omnibox.suggestions.basic.BasicSuggestionProcessor;
 import org.chromium.chrome.browser.omnibox.suggestions.pedal.PedalSuggestionViewProperties.PedalIcon;
-import org.chromium.components.favicon.LargeIconBridge;
 import org.chromium.components.omnibox.AutocompleteMatch;
 import org.chromium.components.omnibox.action.OmniboxPedal;
 import org.chromium.ui.modelutil.PropertyModel;
@@ -42,18 +41,17 @@
      * @param context An Android context.
      * @param suggestionHost A handle to the object using the suggestions.
      * @param editingTextProvider A means of accessing the text in the omnibox.
-     * @param iconBridgeSupplier A means of accessing the large icon bridge.
+     * @param faviconFetcher A means of accessing the large icon bridge.
      * @param bookmarkBridgeSupplier A means of accessing the bookmark information.
      * @param omniboxPedalDelegate A delegate that will responsible for pedals.
      */
     public PedalSuggestionProcessor(@NonNull Context context,
             @NonNull SuggestionHost suggestionHost,
             @NonNull UrlBarEditingTextStateProvider editingTextProvider,
-            @NonNull Supplier<LargeIconBridge> iconBridgeSupplier,
-            @NonNull BookmarkState bookmarkState,
+            @NonNull FaviconFetcher faviconFetcher, @NonNull BookmarkState bookmarkState,
             @NonNull OmniboxPedalDelegate omniboxPedalDelegate,
             @NonNull AutocompleteDelegate autocompleteDelegate) {
-        super(context, suggestionHost, editingTextProvider, iconBridgeSupplier, bookmarkState);
+        super(context, suggestionHost, editingTextProvider, faviconFetcher, bookmarkState);
         mOmniboxPedalDelegate = omniboxPedalDelegate;
         mAutocompleteDelegate = autocompleteDelegate;
     }
@@ -128,4 +126,4 @@
         }
         mLastVisiblePedals.clear();
     }
-}
\ No newline at end of file
+}
diff --git a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/tail/TailSuggestionProcessor.java b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/tail/TailSuggestionProcessor.java
index 1ceb3fe..cc1ffb61 100644
--- a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/tail/TailSuggestionProcessor.java
+++ b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/tail/TailSuggestionProcessor.java
@@ -27,7 +27,7 @@
      * @param suggestionHost A handle to the object using the suggestions.
      */
     public TailSuggestionProcessor(Context context, SuggestionHost suggestionHost) {
-        super(context, suggestionHost);
+        super(context, suggestionHost, null);
         mAlignTailSuggestions = DeviceFormFactor.isNonMultiDisplayContextOnTablet(context);
     }
 
diff --git a/chrome/browser/ui/android/signin/java/src/org/chromium/chrome/browser/ui/signin/ConfirmManagedSyncDataDialogTest.java b/chrome/browser/ui/android/signin/java/src/org/chromium/chrome/browser/ui/signin/ConfirmManagedSyncDataDialogTest.java
index 37ea209b..37dc481 100644
--- a/chrome/browser/ui/android/signin/java/src/org/chromium/chrome/browser/ui/signin/ConfirmManagedSyncDataDialogTest.java
+++ b/chrome/browser/ui/android/signin/java/src/org/chromium/chrome/browser/ui/signin/ConfirmManagedSyncDataDialogTest.java
@@ -32,6 +32,7 @@
 import org.chromium.base.test.BaseActivityTestRule;
 import org.chromium.base.test.util.Batch;
 import org.chromium.base.test.util.CommandLineFlags;
+import org.chromium.base.test.util.DisableIf;
 import org.chromium.chrome.browser.flags.ChromeSwitches;
 import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
 import org.chromium.ui.test.util.BlankUiTestActivity;
@@ -101,6 +102,7 @@
 
     @Test
     @LargeTest
+    @DisableIf.Build(sdk_is_greater_than = 25, message = "https://crbug.com/1336718 - failing on O")
     public void testDialogIsDismissedWhenRecreated() throws Exception {
         showManagedSyncDataDialog();
         onView(withText(R.string.sign_in_managed_account))
diff --git a/chrome/browser/ui/android/toolbar/BUILD.gn b/chrome/browser/ui/android/toolbar/BUILD.gn
index 5b37441..ede8733 100644
--- a/chrome/browser/ui/android/toolbar/BUILD.gn
+++ b/chrome/browser/ui/android/toolbar/BUILD.gn
@@ -287,6 +287,7 @@
     "java/src/org/chromium/chrome/browser/toolbar/top/ToggleTabStackButtonCoordinatorTest.java",
     "java/src/org/chromium/chrome/browser/toolbar/top/ToolbarControlContainerTest.java",
     "java/src/org/chromium/chrome/browser/toolbar/top/ToolbarSnapshotStateTest.java",
+    "java/src/org/chromium/chrome/browser/toolbar/top/ToolbarTabletUnitTest.java",
     "java/src/org/chromium/chrome/browser/toolbar/top/TopToolbarInteractabilityManagerTest.java",
     "java/src/org/chromium/chrome/browser/toolbar/top/TopToolbarOverlayMediatorTest.java",
   ]
diff --git a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/ToolbarTablet.java b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/ToolbarTablet.java
index 6d4051e..a10283a 100644
--- a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/ToolbarTablet.java
+++ b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/ToolbarTablet.java
@@ -71,15 +71,19 @@
         extends ToolbarLayout implements OnClickListener, View.OnLongClickListener {
     private ObjectAnimator mTabSwitcherModeAnimation;
 
-    /** Downloads page for offline access. */
+    /**
+     * Downloads page for offline access.
+     */
     public interface OfflineDownloader {
         /**
          * Trigger the download of a page.
+         *
          * @param context Context to pull resources from.
          * @param tab Tab containing the page to download.
          */
         void downloadPage(Context context, Tab tab);
     }
+
     private HomeButton mHomeButton;
     private ImageButton mBackButton;
     private ImageButton mForwardButton;
@@ -113,6 +117,7 @@
 
     /**
      * Constructs a ToolbarTablet object.
+     *
      * @param context The Context in which this View object is created.
      * @param attrs The AttributeSet that was specified with this View.
      */
@@ -172,8 +177,8 @@
     }
 
     /**
-     * Sets up key listeners after native initialization is complete, so that we can invoke
-     * native functions.
+     * Sets up key listeners after native initialization is complete, so that we can invoke native
+     * functions.
      */
     @Override
     public void onNativeLibraryReady() {
@@ -600,6 +605,11 @@
 
     @Override
     void onAccessibilityStatusChanged(boolean enabled) {
+        enableTabStackButton(enabled);
+    }
+
+    @VisibleForTesting
+    void enableTabStackButton(boolean enabled) {
         mShowTabStack = (enabled && isAccessibilityTabSwitcherPreferenceEnabled())
                 || isGridTabSwitcherEnabled();
         updateSwitcherButtonVisibility(mShowTabStack);
@@ -714,6 +724,7 @@
 
     /**
      * Sets the toolbar start padding based on whether the buttons are visible.
+     *
      * @param buttonsVisible Whether the toolbar buttons are visible.
      */
     private void setStartPaddingBasedOnButtonVisibility(boolean buttonsVisible) {
@@ -726,7 +737,7 @@
 
     /**
      * @return The difference in start padding when the buttons are visible and when they are not
-     *         visible.
+     * visible.
      */
     public int getStartPaddingDifferenceForButtonVisibilityAnimation() {
         // If the home button is visible then the padding doesn't change.
@@ -828,4 +839,24 @@
         return ChromeFeatureList.getFieldTrialParamByFeatureAsBoolean(
                 ChromeFeatureList.GRID_TAB_SWITCHER_FOR_TABLETS, "enable_launch_polish", false);
     }
+
+    @VisibleForTesting
+    ImageButton[] getToolbarButtons() {
+        return mToolbarButtons;
+    }
+
+    @VisibleForTesting
+    ObjectAnimator getTabSwitcherModeAnimation() {
+        return mTabSwitcherModeAnimation;
+    }
+
+    @VisibleForTesting
+    void enableButtonVisibilityChangeAnimationForTesting() {
+        mShouldAnimateButtonVisibilityChange = true;
+    }
+
+    @VisibleForTesting
+    void setToolbarButtonsVisibleForTesting(boolean value) {
+        mToolbarButtonsVisible = value;
+    }
 }
diff --git a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/ToolbarTabletUnitTest.java b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/ToolbarTabletUnitTest.java
new file mode 100644
index 0000000..4785147
--- /dev/null
+++ b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/ToolbarTabletUnitTest.java
@@ -0,0 +1,304 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+package org.chromium.chrome.browser.toolbar.top;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.animation.ObjectAnimator;
+import android.app.Activity;
+import android.graphics.drawable.Drawable;
+import android.os.Looper;
+import android.view.View;
+import android.widget.ImageButton;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TestRule;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.Robolectric;
+import org.robolectric.Shadows;
+import org.robolectric.annotation.LooperMode;
+import org.robolectric.shadows.ShadowToast;
+
+import org.chromium.base.FeatureList;
+import org.chromium.base.test.BaseRobolectricTestRunner;
+import org.chromium.chrome.R;
+import org.chromium.chrome.browser.flags.ChromeFeatureList;
+import org.chromium.chrome.browser.omnibox.LocationBarCoordinator;
+import org.chromium.chrome.browser.omnibox.LocationBarCoordinatorTablet;
+import org.chromium.chrome.browser.omnibox.LocationBarLayout;
+import org.chromium.chrome.browser.omnibox.status.StatusCoordinator;
+import org.chromium.chrome.browser.toolbar.menu_button.MenuButtonCoordinator;
+import org.chromium.chrome.test.util.browser.Features;
+import org.chromium.chrome.test.util.browser.Features.EnableFeatures;
+
+import java.util.ArrayList;
+
+/**
+ * Unit tests for @{@link ToolbarTablet}
+ */
+@LooperMode(LooperMode.Mode.PAUSED)
+@RunWith(BaseRobolectricTestRunner.class)
+public final class ToolbarTabletUnitTest {
+    @Rule
+    public TestRule mFeaturesProcessorRule = new Features.JUnitProcessor();
+    @Mock
+    private LocationBarCoordinator mLocationBar;
+    @Mock
+    private Drawable mDrawable;
+    @Mock
+    private LocationBarCoordinatorTablet mLocationBarTablet;
+    @Mock
+    private StatusCoordinator mStatusCoordinator;
+    @Mock
+    private MenuButtonCoordinator mMenuButtonCoordinator;
+    @Mock
+    private View mContainerView;
+    private Activity mActivity;
+    private ToolbarTablet mToolbarTablet;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+        mActivity = Robolectric.buildActivity(Activity.class).setup().get();
+        mActivity.setTheme(org.chromium.chrome.tab_ui.R.style.Theme_BrowserUI_DayNight);
+        mToolbarTablet = (ToolbarTablet) mActivity.getLayoutInflater().inflate(
+                org.chromium.chrome.R.layout.toolbar_tablet, null);
+        when(mLocationBar.getTabletCoordinator()).thenReturn(mLocationBarTablet);
+        when(mLocationBarTablet.getBackground()).thenReturn(mDrawable);
+        mToolbarTablet.setLocationBarCoordinator(mLocationBar);
+        LocationBarLayout locationBarLayout = mToolbarTablet.findViewById(R.id.location_bar);
+        locationBarLayout.setStatusCoordinatorForTesting(mStatusCoordinator);
+        mToolbarTablet.setMenuButtonCoordinatorForTesting(mMenuButtonCoordinator);
+    }
+
+    @After
+    public void tearDown() {
+        disableGridTabSwitcher();
+    }
+
+    @Test
+    public void onMeasureShortWidth_hidesToolbarButtons() {
+        mToolbarTablet.measure(300, 300);
+
+        ImageButton[] btns = mToolbarTablet.getToolbarButtons();
+        for (ImageButton btn : btns) {
+            assertEquals(
+                    "Toolbar button visibility is not as expected", View.GONE, btn.getVisibility());
+        }
+    }
+
+    @Test
+    public void onMeasureLargeWidth_showsToolbarButtons() {
+        mToolbarTablet.measure(700, 300);
+
+        ImageButton[] btns = mToolbarTablet.getToolbarButtons();
+        for (ImageButton btn : btns) {
+            assertEquals("Toolbar button visibility is not as expected", View.VISIBLE,
+                    btn.getVisibility());
+        }
+    }
+
+    @Test
+    public void onMeasureSmallWidthWithAnimation_hidesToolbarButtons() {
+        for (ImageButton btn : mToolbarTablet.getToolbarButtons()) {
+            when(mLocationBar.createHideButtonAnimatorForTablet(btn))
+                    .thenReturn(ObjectAnimator.ofFloat(btn, View.ALPHA, 0.f));
+        }
+        when(mLocationBar.getHideButtonsWhenUnfocusedAnimatorsForTablet(anyInt()))
+                .thenReturn(new ArrayList<>());
+
+        mToolbarTablet.enableButtonVisibilityChangeAnimationForTesting();
+        // Call
+        mToolbarTablet.measure(300, 300);
+        Shadows.shadowOf(Looper.getMainLooper()).idle();
+        // Verify
+        ImageButton[] btns = mToolbarTablet.getToolbarButtons();
+        for (ImageButton btn : btns) {
+            assertEquals(
+                    "Toolbar button visibility is not as expected", View.GONE, btn.getVisibility());
+        }
+    }
+
+    @Test
+    public void onMeasureLargeWidthWithAnimation_showsToolbarButtons() {
+        mToolbarTablet.setToolbarButtonsVisibleForTesting(false);
+        mToolbarTablet.enableButtonVisibilityChangeAnimationForTesting();
+        for (ImageButton btn : mToolbarTablet.getToolbarButtons()) {
+            when(mLocationBar.createShowButtonAnimatorForTablet(btn))
+                    .thenReturn(ObjectAnimator.ofFloat(btn, View.ALPHA, 1.f));
+        }
+        when(mLocationBar.getShowButtonsWhenUnfocusedAnimatorsForTablet(anyInt()))
+                .thenReturn(new ArrayList<>());
+        // Call
+        mToolbarTablet.measure(700, 300);
+        Shadows.shadowOf(Looper.getMainLooper()).idle();
+        // Verify
+        ImageButton[] btns = mToolbarTablet.getToolbarButtons();
+        for (ImageButton btn : btns) {
+            assertEquals("Toolbar button visibility is not as expected", View.VISIBLE,
+                    btn.getVisibility());
+        }
+    }
+
+    @EnableFeatures(ChromeFeatureList.GRID_TAB_SWITCHER_FOR_TABLETS)
+    @Test
+    public void testSetTabSwitcherModeOn_hidesToolbar() {
+        assertEquals("Initial Toolbar visibility is not as expected", View.VISIBLE,
+                mToolbarTablet.getVisibility());
+        // Call
+        mToolbarTablet.setTabSwitcherMode(true, false, false, mMenuButtonCoordinator);
+        mToolbarTablet.getTabSwitcherModeAnimation().end();
+        assertEquals(
+                "Toolbar visibility is not as expected", View.GONE, mToolbarTablet.getVisibility());
+        verify(mLocationBar).setUrlBarFocusable(false);
+    }
+
+    @EnableFeatures(ChromeFeatureList.GRID_TAB_SWITCHER_FOR_TABLETS)
+    @Test
+    public void testSetTabSwitcherModeOff_showsToolbar() {
+        // Hide toolbar as initial state.
+        mToolbarTablet.setVisibility(View.GONE);
+        // Call
+        mToolbarTablet.setTabSwitcherMode(false, false, false, mMenuButtonCoordinator);
+        mToolbarTablet.getTabSwitcherModeAnimation().end();
+        assertEquals("Toolbar visibility is not as expected", View.VISIBLE,
+                mToolbarTablet.getVisibility());
+        verify(mLocationBar).setUrlBarFocusable(true);
+    }
+
+    @EnableFeatures(ChromeFeatureList.GRID_TAB_SWITCHER_FOR_TABLETS)
+    @Test
+    public void testSetTabSwitcherPolishModeOff_toolbarStillVisible() {
+        enableGridTabSwitcher(true);
+        assertEquals("Initial Toolbar visibility is not as expected", View.VISIBLE,
+                mToolbarTablet.getVisibility());
+        // Call
+        mToolbarTablet.setTabSwitcherMode(false, false, false, mMenuButtonCoordinator);
+        assertEquals("Toolbar visibility is not as expected", View.VISIBLE,
+                mToolbarTablet.getVisibility());
+        verify(mLocationBar).setUrlBarFocusable(true);
+    }
+
+    @EnableFeatures(ChromeFeatureList.GRID_TAB_SWITCHER_FOR_TABLETS)
+    @Test
+    public void testSetTabSwitcherPolishModeOn_toolbarStillVisible() {
+        enableGridTabSwitcher(true);
+        assertEquals("Initial Toolbar visibility is not as expected", View.VISIBLE,
+                mToolbarTablet.getVisibility());
+        // Call
+        mToolbarTablet.setTabSwitcherMode(true, false, false, mMenuButtonCoordinator);
+        assertEquals("Toolbar visibility is not as expected", View.VISIBLE,
+                mToolbarTablet.getVisibility());
+        verify(mLocationBar).setUrlBarFocusable(false);
+    }
+
+    @Test
+    public void testSetTabSwitcherOnGTSDisabled_hidesViews() {
+        // Enable tab stack button when GTS is disabled
+        mToolbarTablet.enableTabStackButton(true);
+        when(mLocationBar.getContainerView()).thenReturn(mContainerView);
+        assertEquals("Initial Toolbar visibility is not as expected", View.VISIBLE,
+                mToolbarTablet.getVisibility());
+        // Call
+        mToolbarTablet.setTabSwitcherMode(true, false, false, mMenuButtonCoordinator);
+        assertFalse("Button should not be enabled",
+                mToolbarTablet.findViewById(R.id.back_button).isEnabled());
+        assertFalse("Button should not be enabled",
+                mToolbarTablet.findViewById(R.id.forward_button).isEnabled());
+        assertFalse("Button should not be enabled",
+                mToolbarTablet.findViewById(R.id.refresh_button).isEnabled());
+        verify(mContainerView).setVisibility(View.INVISIBLE);
+        verify(mMenuButtonCoordinator).setAppMenuUpdateBadgeSuppressed(true);
+    }
+
+    @Test
+    public void testSetTabSwitcherOffGTSDisabled_showsViews() {
+        // Enable tab stack button when GTS is disabled
+        mToolbarTablet.enableTabStackButton(true);
+        when(mLocationBar.getContainerView()).thenReturn(mContainerView);
+        assertEquals("Initial Toolbar visibility is not as expected", View.VISIBLE,
+                mToolbarTablet.getVisibility());
+        // Call
+        mToolbarTablet.setTabSwitcherMode(false, false, false, mMenuButtonCoordinator);
+        verify(mContainerView).setVisibility(View.VISIBLE);
+        verify(mMenuButtonCoordinator).setAppMenuUpdateBadgeSuppressed(false);
+    }
+
+    @Test
+    public void testOnLongClick() {
+        longClickAndVerifyToast(R.id.refresh_button, R.string.refresh);
+        longClickAndVerifyToast(R.id.bookmark_button, R.string.menu_bookmark);
+        longClickAndVerifyToast(R.id.save_offline_button, R.string.menu_download);
+    }
+
+    @Test
+    public void testUpdateBackButtonVisibility() {
+        ImageButton btn = mToolbarTablet.findViewById(R.id.back_button);
+        mToolbarTablet.updateBackButtonVisibility(true);
+        assertTrue("Button should be enabled", btn.isEnabled());
+        assertTrue("Button should be focused", btn.isFocusable());
+        mToolbarTablet.updateBackButtonVisibility(false);
+        assertFalse("Button should not be enabled", btn.isEnabled());
+        assertFalse("Button should not be focused", btn.isFocusable());
+    }
+
+    @Test
+    public void testUpdateForwardButtonVisibility() {
+        ImageButton btn = mToolbarTablet.findViewById(R.id.forward_button);
+        mToolbarTablet.updateForwardButtonVisibility(true);
+        assertTrue("Button should be enabled", btn.isEnabled());
+        assertTrue("Button should be focused", btn.isFocusable());
+        mToolbarTablet.updateForwardButtonVisibility(false);
+        assertFalse("Button should not be enabled", btn.isEnabled());
+        assertFalse("Button should not be focused", btn.isFocusable());
+    }
+
+    @Test
+    public void testUpdateReloadButtonVisibility() {
+        ImageButton btn = mToolbarTablet.findViewById(R.id.refresh_button);
+        mToolbarTablet.updateReloadButtonVisibility(true);
+        assertTrue("Button should be enabled", btn.isEnabled());
+        assertEquals("Button drawable level is not as expected", 1, btn.getDrawable().getLevel());
+        assertEquals("Button description is not as expected",
+                mActivity.getResources().getString(R.string.accessibility_btn_stop_loading),
+                btn.getContentDescription());
+        mToolbarTablet.updateReloadButtonVisibility(false);
+        assertEquals("Button drawable level is not as expected", 0, btn.getDrawable().getLevel());
+        assertEquals("Button description is not as expected",
+                mActivity.getResources().getString(R.string.accessibility_btn_refresh),
+                btn.getContentDescription());
+        assertTrue("Button should be enabled", btn.isEnabled());
+    }
+
+    private void longClickAndVerifyToast(int viewId, int stringId) {
+        mToolbarTablet.onLongClick(mToolbarTablet.findViewById(viewId));
+        assertTrue("Toast is not as expected",
+                ShadowToast.showedCustomToast(
+                        mActivity.getResources().getString(stringId), R.id.toast_text));
+    }
+
+    private void enableGridTabSwitcher(boolean enablePolish) {
+        FeatureList.TestValues testValues = new FeatureList.TestValues();
+        testValues.addFeatureFlagOverride(ChromeFeatureList.GRID_TAB_SWITCHER_FOR_TABLETS, true);
+        testValues.addFieldTrialParamOverride(ChromeFeatureList.GRID_TAB_SWITCHER_FOR_TABLETS,
+                "enable_launch_polish", String.valueOf(enablePolish));
+        FeatureList.setTestValues(testValues);
+    }
+
+    private void disableGridTabSwitcher() {
+        FeatureList.TestValues testValues = new FeatureList.TestValues();
+        testValues.addFeatureFlagOverride(ChromeFeatureList.GRID_TAB_SWITCHER_FOR_TABLETS, false);
+        FeatureList.setTestValues(testValues);
+    }
+}
diff --git a/chrome/browser/ui/ash/desks/chrome_desks_templates_delegate.cc b/chrome/browser/ui/ash/desks/chrome_desks_templates_delegate.cc
index f4666b2..4914236 100644
--- a/chrome/browser/ui/ash/desks/chrome_desks_templates_delegate.cc
+++ b/chrome/browser/ui/ash/desks/chrome_desks_templates_delegate.cc
@@ -27,6 +27,7 @@
 #include "chrome/browser/favicon/favicon_service_factory.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/profiles/profile_manager.h"
+#include "chrome/browser/ui/ash/desks/chrome_desks_util.h"
 #include "chrome/browser/ui/ash/desks/desks_client.h"
 #include "chrome/browser/ui/browser_commands.h"
 #include "chrome/browser/ui/browser_tabstrip.h"
@@ -39,6 +40,7 @@
 #include "components/app_restore/full_restore_save_handler.h"
 #include "components/app_restore/full_restore_utils.h"
 #include "components/app_restore/restore_data.h"
+#include "components/app_restore/tab_group_info.h"
 #include "components/app_restore/window_properties.h"
 #include "components/favicon/core/favicon_service.h"
 #include "components/favicon_base/favicon_util.h"
@@ -278,11 +280,17 @@
   if (tab_strip_model) {
     app_launch_info->urls = GetURLsIfApplicable(tab_strip_model);
     app_launch_info->active_tab_index = tab_strip_model->active_index();
+    if (tab_strip_model->SupportsTabGroups()) {
+      app_launch_info->tab_group_infos =
+          chrome_desks_util::ConvertTabGroupsToTabGroupInfos(
+              tab_strip_model->group_model());
+    }
     std::move(callback).Run(std::move(app_launch_info));
     return;
   }
 
   if (app_id == app_constants::kLacrosAppId) {
+    // TODO(avynn): Add lacros support for tab groups.
     const std::string* lacros_window_id =
         window->GetProperty(app_restore::kLacrosWindowId);
     DCHECK(lacros_window_id);
diff --git a/chrome/browser/ui/ash/desks/chrome_desks_util.cc b/chrome/browser/ui/ash/desks/chrome_desks_util.cc
new file mode 100644
index 0000000..de003bb
--- /dev/null
+++ b/chrome/browser/ui/ash/desks/chrome_desks_util.cc
@@ -0,0 +1,64 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/ui/browser.h"
+#include "chrome/browser/ui/browser_tabstrip.h"
+#include "chrome/browser/ui/tabs/tab_group.h"
+#include "chrome/browser/ui/tabs/tab_group_model.h"
+#include "components/app_restore/tab_group_info.h"
+#include "components/tab_groups/tab_group_id.h"
+#include "components/tab_groups/tab_group_visual_data.h"
+
+namespace {
+
+const std::vector<int> ConvertRangeToTabGroupIndices(const gfx::Range& range) {
+  std::vector<int> indices;
+
+  for (uint32_t index = range.start(); index < range.end(); ++index) {
+    indices.push_back(static_cast<int>(index));
+  }
+
+  return indices;
+}
+
+}  // namespace
+
+namespace chrome_desks_util {
+
+absl::optional<std::vector<app_restore::TabGroupInfo>>
+ConvertTabGroupsToTabGroupInfos(const TabGroupModel* group_model) {
+  DCHECK(group_model);
+  const std::vector<tab_groups::TabGroupId>& listed_group_ids =
+      group_model->ListTabGroups();
+
+  if (listed_group_ids.size() == 0) {
+    return absl::nullopt;
+  }
+
+  std::vector<app_restore::TabGroupInfo> tab_groups;
+  for (const tab_groups::TabGroupId& group_id : listed_group_ids) {
+    const TabGroup* tab_group = group_model->GetTabGroup(group_id);
+    tab_groups.emplace_back(
+        gfx::Range(tab_group->ListTabs()),
+        tab_groups::TabGroupVisualData(*(tab_group->visual_data())));
+  }
+
+  return tab_groups;
+}
+
+void AttachTabGroupsToBrowserInstance(
+    const std::vector<app_restore::TabGroupInfo>& tab_groups,
+    Browser* browser) {
+  TabStripModel* tab_strip_model = browser->tab_strip_model();
+
+  for (const app_restore::TabGroupInfo& tab_group : tab_groups) {
+    tab_groups::TabGroupId new_group_id = tab_strip_model->AddToNewGroup(
+        ConvertRangeToTabGroupIndices(tab_group.tab_range));
+    tab_strip_model->group_model()
+        ->GetTabGroup(new_group_id)
+        ->SetVisualData(tab_group.visual_data);
+  }
+}
+
+}  // namespace chrome_desks_util
diff --git a/chrome/browser/ui/ash/desks/chrome_desks_util.h b/chrome/browser/ui/ash/desks/chrome_desks_util.h
new file mode 100644
index 0000000..a730ef7
--- /dev/null
+++ b/chrome/browser/ui/ash/desks/chrome_desks_util.h
@@ -0,0 +1,32 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_UI_ASH_DESKS_CHROME_DESKS_UTIL_H_
+#define CHROME_BROWSER_UI_ASH_DESKS_CHROME_DESKS_UTIL_H_
+
+#include <memory>
+
+class TabGroupModel;
+
+namespace app_restore {
+struct TabGroupInfo;
+}  // namespace app_restore
+
+namespace chrome_desks_util {
+
+// Given a TabGroupModel that contains at least a single TabGroup this method
+// returns a populated optional vector that contains app_Restore::TabGroupInfo
+// representations of the TabGroups contained within the model.
+absl::optional<std::vector<app_restore::TabGroupInfo>>
+ConvertTabGroupsToTabGroupInfos(const TabGroupModel* group_model);
+
+// Given a vector of TabGroupInfo this function attaches tab groups to the
+// out_browser instance passed as the second parameter.
+void AttachTabGroupsToBrowserInstance(
+    const std::vector<app_restore::TabGroupInfo>& tab_groups,
+    Browser* browser);
+
+}  // namespace chrome_desks_util
+
+#endif
diff --git a/chrome/browser/ui/ash/desks/desks_client_browsertest.cc b/chrome/browser/ui/ash/desks/desks_client_browsertest.cc
index 75d0538..187d342 100644
--- a/chrome/browser/ui/ash/desks/desks_client_browsertest.cc
+++ b/chrome/browser/ui/ash/desks/desks_client_browsertest.cc
@@ -49,12 +49,16 @@
 #include "chrome/browser/profiles/keep_alive/profile_keep_alive_types.h"
 #include "chrome/browser/profiles/keep_alive/scoped_profile_keep_alive.h"
 #include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/ui/ash/desks/chrome_desks_templates_delegate.h"
+#include "chrome/browser/ui/ash/desks/chrome_desks_util.h"
 #include "chrome/browser/ui/ash/desks/desks_templates_app_launch_handler.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/browser_finder.h"
 #include "chrome/browser/ui/browser_list.h"
 #include "chrome/browser/ui/browser_tabstrip.h"
 #include "chrome/browser/ui/browser_window.h"
+#include "chrome/browser/ui/tabs/tab_group.h"
+#include "chrome/browser/ui/tabs/tab_group_model.h"
 #include "chrome/browser/ui/web_applications/system_web_app_ui_utils.h"
 #include "chrome/browser/ui/web_applications/test/web_app_browsertest_util.h"
 #include "chrome/browser/web_applications/test/web_app_install_test_utils.h"
@@ -69,11 +73,14 @@
 #include "components/app_restore/full_restore_save_handler.h"
 #include "components/app_restore/full_restore_utils.h"
 #include "components/app_restore/restore_data.h"
+#include "components/app_restore/tab_group_info.h"
 #include "components/app_restore/window_properties.h"
 #include "components/keep_alive_registry/keep_alive_types.h"
 #include "components/keep_alive_registry/scoped_keep_alive.h"
 #include "components/policy/policy_constants.h"
 #include "components/services/app_service/public/cpp/app_types.h"
+#include "components/tab_groups/tab_group_color.h"
+#include "components/tab_groups/tab_group_visual_data.h"
 #include "content/public/test/browser_test.h"
 #include "content/public/test/test_navigation_observer.h"
 #include "content/public/test/test_utils.h"
@@ -86,6 +93,7 @@
 #include "ui/display/screen.h"
 #include "ui/display/test/display_manager_test_api.h"
 #include "ui/events/test/event_generator.h"
+#include "ui/gfx/range/range.h"
 #include "ui/views/controls/button/button.h"
 #include "ui/views/controls/button/label_button.h"
 #include "url/gurl.h"
@@ -111,6 +119,7 @@
     "[{\"version\":1,\"uuid\":\"%s\",\"name\": \"test admin template\","
     "\"created_time_usec\": \"1633535632\",\"updated_time_usec\": "
     "\"1633535632\",\"desk\":{}}]";
+constexpr char kTestTabGroupNameFormat[] = "test_tab_group_%u";
 
 Browser* FindBrowser(int32_t window_id) {
   for (auto* browser : *BrowserList::GetInstance()) {
@@ -310,6 +319,25 @@
   return templates;
 }
 
+// Creates a vector of tab groups based on the vector of GURLs passed into it.
+// A tab group will be created for each individual tab.
+std::vector<app_restore::TabGroupInfo> MakeExpectedTabGroupsBasedOnTabVector(
+    const std::vector<GURL>& urls) {
+  std::vector<app_restore::TabGroupInfo> tab_groups;
+
+  for (uint32_t index = 0; index < urls.size(); ++index) {
+    tab_groups.emplace_back(
+        gfx::Range(index, index + 1),
+        tab_groups::TabGroupVisualData(
+            base::UTF8ToUTF16(
+                base::StringPrintf(kTestTabGroupNameFormat, index)),
+            tab_groups::TabGroupColorId(
+                static_cast<tab_groups::TabGroupColorId>(index % 8))));
+  }
+
+  return tab_groups;
+}
+
 class MockDesksTemplatesAppLaunchHandler
     : public DesksTemplatesAppLaunchHandler {
  public:
@@ -399,18 +427,17 @@
   Browser* CreateBrowser(
       const std::vector<GURL>& urls,
       absl::optional<size_t> active_url_index = absl::nullopt) {
-    Browser::CreateParams params(Browser::TYPE_NORMAL, profile(),
-                                 /*user_gesture=*/false);
-    Browser* browser = Browser::Create(params);
-    // Create a new tab and make sure the urls have loaded.
-    for (size_t i = 0; i < urls.size(); i++) {
-      content::TestNavigationObserver navigation_observer(urls[i]);
-      navigation_observer.StartWatchingNewWebContents();
-      chrome::AddTabAt(
-          browser, urls[i], /*index=*/-1,
-          /*foreground=*/!active_url_index || active_url_index.value() == i);
-      navigation_observer.Wait();
-    }
+    Browser* browser = CreateBrowserImpl(urls, active_url_index);
+    browser->window()->Show();
+    return browser;
+  }
+
+  Browser* CreateBrowserWithTabGroups(
+      const std::vector<GURL>& urls,
+      const std::vector<app_restore::TabGroupInfo>& tab_groups) {
+    Browser* browser = CreateBrowserImpl(urls, absl::nullopt);
+
+    chrome_desks_util::AttachTabGroupsToBrowserInstance(tab_groups, browser);
     browser->window()->Show();
     return browser;
   }
@@ -443,6 +470,23 @@
   }
 
  private:
+  Browser* CreateBrowserImpl(const std::vector<GURL>& urls,
+                             absl::optional<size_t> active_url_index) {
+    Browser::CreateParams params(Browser::TYPE_NORMAL, profile(),
+                                 /*user_gesture=*/false);
+    Browser* browser = Browser::Create(params);
+    // Create a new tab and make sure the urls have loaded.
+    for (size_t i = 0; i < urls.size(); i++) {
+      content::TestNavigationObserver navigation_observer(urls[i]);
+      navigation_observer.StartWatchingNewWebContents();
+      chrome::AddTabAt(
+          browser, urls[i], /*index=*/-1,
+          /*foreground=*/!active_url_index || active_url_index.value() == i);
+      navigation_observer.Wait();
+    }
+    return browser;
+  }
+
   base::test::ScopedFeatureList scoped_feature_list_;
 };
 
@@ -474,6 +518,49 @@
   EXPECT_EQ(data->urls.value(), urls);
 }
 
+// Tests that a browser's tab groups can be captured correctly in a saved desk.
+IN_PROC_BROWSER_TEST_F(DesksTemplatesClientTest, CaptureBrowserTabGroupsTest) {
+  std::vector<GURL> tabs = {GURL(kExampleUrl1), GURL(kExampleUrl2)};
+  std::vector<app_restore::TabGroupInfo> expected_tab_groups =
+      MakeExpectedTabGroupsBasedOnTabVector(tabs);
+
+  // Create a new browser and add a few tabs to it.
+  Browser* browser = CreateBrowserWithTabGroups(tabs, expected_tab_groups);
+  aura::Window* window = browser->window()->GetNativeWindow();
+
+  const int32_t browser_window_id =
+      window->GetProperty(app_restore::kWindowIdKey);
+  // Get current tabs from browser.
+  std::vector<GURL> urls = GetURLsForBrowserWindow(browser);
+
+  ash::ToggleOverview();
+  ash::WaitForOverviewEnterAnimation();
+
+  ClickSaveDeskAsTemplateButton();
+
+  std::vector<const ash::DeskTemplate*> templates = GetAllEntries();
+  ASSERT_EQ(1u, templates.size());
+
+  const ash::DeskTemplate* desk_template = templates.front();
+  const app_restore::RestoreData* restore_data =
+      desk_template->desk_restore_data();
+  const auto& app_id_to_launch_list = restore_data->app_id_to_launch_list();
+  EXPECT_EQ(1u, app_id_to_launch_list.size());
+
+  // Find `browser` window's app restore data.
+  auto iter = app_id_to_launch_list.find(app_constants::kChromeAppId);
+  ASSERT_TRUE(iter != app_id_to_launch_list.end());
+  auto app_restore_data_iter = iter->second.find(browser_window_id);
+  ASSERT_TRUE(app_restore_data_iter != iter->second.end());
+  const auto& data = app_restore_data_iter->second;
+  // Check the urls are captured correctly in the `desk_template`.
+  EXPECT_EQ(urls, data->urls.value());
+
+  // We don't care about the order of the tab groups.
+  EXPECT_THAT(expected_tab_groups, testing::UnorderedElementsAreArray(
+                                       data->tab_group_infos.value()));
+}
+
 // Tests that incognito browser windows will NOT be captured in the desk
 // template.
 IN_PROC_BROWSER_TEST_F(DesksTemplatesClientTest, CaptureIncognitoBrowserTest) {
@@ -832,6 +919,51 @@
             browser_window->parent());
 }
 
+// Tests that launching a template that contains a browser window with tab
+// groups works as expected.
+IN_PROC_BROWSER_TEST_F(DesksTemplatesClientTest,
+                       LaunchTemplateWithBrowserWindowTabGroups) {
+  ASSERT_TRUE(DesksClient::Get());
+
+  // Create a new browser and add a few tabs to it, and specify the active tab
+  // index.
+  std::vector<GURL> creation_urls = {GURL(kExampleUrl1), GURL(kExampleUrl2),
+                                     GURL(kExampleUrl3)};
+  std::vector<app_restore::TabGroupInfo> expected_tab_groups =
+      MakeExpectedTabGroupsBasedOnTabVector(creation_urls);
+
+  Browser* browser =
+      CreateBrowserWithTabGroups(creation_urls, expected_tab_groups);
+
+  // Get current tabs from browser.
+  const std::vector<GURL> urls = GetURLsForBrowserWindow(browser);
+
+  // Enter overview and save the current desk as a template.
+  ash::ToggleOverview();
+  ash::WaitForOverviewEnterAnimation();
+
+  ClickSaveDeskAsTemplateButton();
+
+  ClickFirstTemplateItem();
+
+  // Wait for the tabs to load.
+  content::RunAllTasksUntilIdle();
+
+  // Verify that the browser was launched with the correct urls and active tab.
+  Browser* new_browser = FindLaunchedBrowserByURLs(urls);
+  ASSERT_TRUE(new_browser);
+
+  absl::optional<std::vector<app_restore::TabGroupInfo>> got_tab_groups =
+      chrome_desks_util::ConvertTabGroupsToTabGroupInfos(
+          new_browser->tab_strip_model()->group_model());
+
+  EXPECT_TRUE(got_tab_groups.has_value());
+
+  // We don't care about the order of the tab groups.
+  EXPECT_THAT(expected_tab_groups,
+              testing::UnorderedElementsAreArray(got_tab_groups.value()));
+}
+
 // Tests that browser session restore isn't triggered when we launch a template
 // that contains a browser window.
 IN_PROC_BROWSER_TEST_F(DesksTemplatesClientTest,
diff --git a/chrome/browser/ui/ash/desks/desks_templates_app_launch_handler.cc b/chrome/browser/ui/ash/desks/desks_templates_app_launch_handler.cc
index e33ed88..5c80f14e 100644
--- a/chrome/browser/ui/ash/desks/desks_templates_app_launch_handler.cc
+++ b/chrome/browser/ui/ash/desks/desks_templates_app_launch_handler.cc
@@ -19,11 +19,13 @@
 #include "chrome/browser/ash/app_restore/arc_app_launch_handler.h"
 #include "chrome/browser/ash/system_web_apps/system_web_app_manager.h"
 #include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/ui/ash/desks/chrome_desks_util.h"
 #include "chrome/browser/ui/ash/desks/desks_client.h"
 #include "chrome/browser/ui/ash/shelf/chrome_shelf_controller_util.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/browser_tabstrip.h"
 #include "chrome/browser/ui/browser_window.h"
+#include "chrome/browser/ui/tabs/tab_group_model.h"
 #include "chrome/browser/ui/web_applications/system_web_app_ui_utils.h"
 #include "chrome/browser/web_applications/web_app_provider.h"
 #include "components/app_constants/constants.h"
@@ -52,7 +54,6 @@
              ? maybe_app_name.value()
              : app_id;
 }
-
 }  // namespace
 
 DesksTemplatesAppLaunchHandler::DesksTemplatesAppLaunchHandler(Profile* profile)
@@ -214,6 +215,11 @@
                           base::checked_cast<int32_t>(i) == *active_tab_index));
       }
 
+      if (app_restore_data->tab_group_infos.has_value()) {
+        chrome_desks_util::AttachTabGroupsToBrowserInstance(
+            app_restore_data->tab_group_infos.value(), browser);
+      }
+
       // We need to handle minimized windows separately since unlike other
       // window types, it's not shown.
       if (window_state_type &&
diff --git a/chrome/browser/ui/page_action/page_action_icon_type.h b/chrome/browser/ui/page_action/page_action_icon_type.h
index 9cf8822e..779cbe4 100644
--- a/chrome/browser/ui/page_action/page_action_icon_type.h
+++ b/chrome/browser/ui/page_action/page_action_icon_type.h
@@ -21,7 +21,6 @@
   kSaveAutofillAddress,
   kSaveCard,
   kSendTabToSelf,
-  kSharedClipboard,
   kSharingHub,
   kSideSearch,
   kSmsRemoteFetcher,
diff --git a/chrome/browser/ui/views/accessibility/caption_bubble_controller_views_browsertest.cc b/chrome/browser/ui/views/accessibility/caption_bubble_controller_views_browsertest.cc
index 8737a1ba..f466ec3 100644
--- a/chrome/browser/ui/views/accessibility/caption_bubble_controller_views_browsertest.cc
+++ b/chrome/browser/ui/views/accessibility/caption_bubble_controller_views_browsertest.cc
@@ -174,7 +174,7 @@
 
   void OnError(CaptionBubbleContext* caption_bubble_context) {
     GetController()->OnError(
-        caption_bubble_context, CaptionBubbleErrorType::GENERIC,
+        caption_bubble_context, CaptionBubbleErrorType::kGeneric,
         base::RepeatingClosure(),
         base::BindRepeating(
             [](CaptionBubbleErrorType error_type, bool checked) {}));
@@ -187,7 +187,7 @@
   void OnMediaFoundationError(CaptionBubbleContext* caption_bubble_context) {
     GetController()->OnError(
         caption_bubble_context,
-        CaptionBubbleErrorType::MEDIA_FOUNDATION_RENDERER_UNSUPPORTED,
+        CaptionBubbleErrorType::kMediaFoundationRendererUnsupported,
         base::RepeatingClosure(),
         base::BindRepeating(
             [](CaptionBubbleErrorType error_type, bool checked) {}));
diff --git a/chrome/browser/ui/views/crostini/crostini_ansible_software_config_view.cc b/chrome/browser/ui/views/crostini/crostini_ansible_software_config_view.cc
index 820f44a..d68c15a4 100644
--- a/chrome/browser/ui/views/crostini/crostini_ansible_software_config_view.cc
+++ b/chrome/browser/ui/views/crostini/crostini_ansible_software_config_view.cc
@@ -57,8 +57,8 @@
     OnStateChanged();
 
     ansible_management_service_->ConfigureContainer(
-        crostini::ContainerId::GetDefault(),
-        default_container_ansible_filepath_, base::DoNothing());
+        crostini::DefaultContainerId(), default_container_ansible_filepath_,
+        base::DoNothing());
     return false;
   }
   DCHECK_EQ(state_, State::ERROR);
diff --git a/chrome/browser/ui/views/crostini/crostini_ansible_software_config_view_browsertest.cc b/chrome/browser/ui/views/crostini/crostini_ansible_software_config_view_browsertest.cc
index bb8d31b..3f3cd5a 100644
--- a/chrome/browser/ui/views/crostini/crostini_ansible_software_config_view_browsertest.cc
+++ b/chrome/browser/ui/views/crostini/crostini_ansible_software_config_view_browsertest.cc
@@ -59,9 +59,8 @@
       vm_tools::cicerone::ApplyAnsiblePlaybookProgressSignal signal;
       signal.set_status(
           vm_tools::cicerone::ApplyAnsiblePlaybookProgressSignal::SUCCEEDED);
-      signal.set_vm_name(crostini::ContainerId::GetDefault().vm_name);
-      signal.set_container_name(
-          crostini::ContainerId::GetDefault().container_name);
+      signal.set_vm_name(crostini::DefaultContainerId().vm_name);
+      signal.set_container_name(crostini::DefaultContainerId().container_name);
       ansible_management_service()->OnApplyAnsiblePlaybookProgress(signal);
     } else {
       EXPECT_NE(nullptr, ActiveView());
@@ -69,9 +68,8 @@
       vm_tools::cicerone::ApplyAnsiblePlaybookProgressSignal signal;
       signal.set_status(
           vm_tools::cicerone::ApplyAnsiblePlaybookProgressSignal::FAILED);
-      signal.set_vm_name(crostini::ContainerId::GetDefault().vm_name);
-      signal.set_container_name(
-          crostini::ContainerId::GetDefault().container_name);
+      signal.set_vm_name(crostini::DefaultContainerId().vm_name);
+      signal.set_container_name(crostini::DefaultContainerId().container_name);
       signal.set_failure_details("apple");
       ansible_management_service()->OnApplyAnsiblePlaybookProgress(signal);
     }
@@ -289,7 +287,7 @@
 IN_PROC_BROWSER_TEST_F(CrostiniAnsibleSoftwareConfigViewBrowserTest,
                        AnsibleConfigFlow_Successful) {
   ansible_management_service()->ConfigureContainer(
-      crostini::ContainerId::GetDefault(),
+      crostini::DefaultContainerId(),
       browser()->profile()->GetPrefs()->GetFilePath(
           crostini::prefs::kCrostiniAnsiblePlaybookFilePath),
       base::BindLambdaForTesting([&](bool success) { run_loop()->Quit(); }));
@@ -305,7 +303,7 @@
   // there.
   SetInstallAnsibleStatus(false);
   ansible_management_service()->ConfigureContainer(
-      crostini::ContainerId::GetDefault(),
+      crostini::DefaultContainerId(),
       browser()->profile()->GetPrefs()->GetFilePath(
           crostini::prefs::kCrostiniAnsiblePlaybookFilePath),
       base::BindLambdaForTesting([&](bool success) { run_loop()->Quit(); }));
@@ -321,7 +319,7 @@
   // Set apply failure
   SetApplyAnsibleStatus(false);
   ansible_management_service()->ConfigureContainer(
-      crostini::ContainerId::GetDefault(),
+      crostini::DefaultContainerId(),
       browser()->profile()->GetPrefs()->GetFilePath(
           crostini::prefs::kCrostiniAnsiblePlaybookFilePath),
       base::BindLambdaForTesting(
diff --git a/chrome/browser/ui/views/frame/browser_view.cc b/chrome/browser/ui/views/frame/browser_view.cc
index 2c4fd2aa..5acc434 100644
--- a/chrome/browser/ui/views/frame/browser_view.cc
+++ b/chrome/browser/ui/views/frame/browser_view.cc
@@ -217,7 +217,6 @@
 #include "ui/base/resource/resource_bundle.h"
 #include "ui/base/theme_provider.h"
 #include "ui/base/window_open_disposition.h"
-#include "ui/color/color_provider.h"
 #include "ui/compositor/layer.h"
 #include "ui/compositor/paint_recorder.h"
 #include "ui/content_accelerators/accelerator_util.h"
@@ -454,19 +453,14 @@
   METADATA_HEADER(ContentsSeparator);
 
   ContentsSeparator() {
+    SetBackground(
+        views::CreateThemedSolidBackground(kColorToolbarContentAreaSeparator));
+
     // BrowserViewLayout will respect either the height or width of this,
     // depending on orientation, not simultaneously both.
     SetPreferredSize(
         gfx::Size(views::Separator::kThickness, views::Separator::kThickness));
   }
-
- private:
-  // views::View:
-  void OnThemeChanged() override {
-    SetBackground(views::CreateSolidBackground(
-        GetColorProvider()->GetColor(kColorToolbarContentAreaSeparator)));
-    View::OnThemeChanged();
-  }
 };
 
 BEGIN_METADATA(ContentsSeparator, views::View)
@@ -2386,9 +2380,12 @@
 SharingDialog* BrowserView::ShowSharingDialog(
     content::WebContents* web_contents,
     SharingDialogData data) {
+  // TODO(https://crbug.com/1311680): Remove this altogether. This used to
+  // be hardcoded to anchor off the shared clipboard bubble, but that bubble is
+  // now gone altogether.
   auto* dialog_view =
       new SharingDialogView(toolbar_button_provider()->GetAnchorView(
-                                PageActionIconType::kSharedClipboard),
+                                PageActionIconType::kClickToCall),
                             web_contents, std::move(data));
 
   views::BubbleDialogDelegateView::CreateBubble(dialog_view)->Show();
diff --git a/chrome/browser/ui/views/page_action/page_action_icon_controller.cc b/chrome/browser/ui/views/page_action/page_action_icon_controller.cc
index 23c226e..0e1505f6 100644
--- a/chrome/browser/ui/views/page_action/page_action_icon_controller.cc
+++ b/chrome/browser/ui/views/page_action/page_action_icon_controller.cc
@@ -10,7 +10,6 @@
 #include "base/feature_list.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/sharing/click_to_call/click_to_call_ui_controller.h"
-#include "chrome/browser/sharing/shared_clipboard/shared_clipboard_ui_controller.h"
 #include "chrome/browser/sharing/sms/sms_remote_fetcher_ui_controller.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/views/autofill/payments/local_card_migration_icon_view.h"
@@ -175,19 +174,6 @@
                       params.command_updater, params.icon_label_bubble_delegate,
                       params.page_action_icon_delegate));
         break;
-      case PageActionIconType::kSharedClipboard:
-        add_page_action_icon(
-            type,
-            std::make_unique<SharingIconView>(
-                params.icon_label_bubble_delegate,
-                params.page_action_icon_delegate,
-                base::BindRepeating([](content::WebContents* contents) {
-                  return static_cast<SharingUiController*>(
-                      SharedClipboardUiController::GetOrCreateFromWebContents(
-                          contents));
-                }),
-                base::BindRepeating(SharingDialogView::GetAsBubble)));
-        break;
       case PageActionIconType::kSharingHub:
         add_page_action_icon(
             type, std::make_unique<sharing_hub::SharingHubIconView>(
diff --git a/chrome/browser/ui/views/plugin_vm/plugin_vm_installer_view_browsertest.cc b/chrome/browser/ui/views/plugin_vm/plugin_vm_installer_view_browsertest.cc
index 061c41807..6ff74ada 100644
--- a/chrome/browser/ui/views/plugin_vm/plugin_vm_installer_view_browsertest.cc
+++ b/chrome/browser/ui/views/plugin_vm/plugin_vm_installer_view_browsertest.cc
@@ -23,7 +23,6 @@
 #include "chrome/common/chrome_features.h"
 #include "chrome/grit/generated_resources.h"
 #include "chromeos/ash/components/dbus/concierge/fake_concierge_client.h"
-#include "chromeos/dbus/dbus_thread_manager.h"
 #include "chromeos/dbus/debug_daemon/fake_debug_daemon_client.h"
 #include "chromeos/dbus/vm_plugin_dispatcher/fake_vm_plugin_dispatcher_client.h"
 #include "components/account_id/account_id.h"
@@ -72,7 +71,7 @@
     fake_concierge_client_->set_disk_image_progress_signal_connected(true);
     fake_vm_plugin_dispatcher_client_ =
         static_cast<chromeos::FakeVmPluginDispatcherClient*>(
-            chromeos::DBusThreadManager::Get()->GetVmPluginDispatcherClient());
+            chromeos::VmPluginDispatcherClient::Get());
 
     network_connection_tracker_ =
         network::TestNetworkConnectionTracker::CreateInstance();
diff --git a/chrome/browser/ui/webui/chromeos/crostini_upgrader/crostini_upgrader_dialog.cc b/chrome/browser/ui/webui/chromeos/crostini_upgrader/crostini_upgrader_dialog.cc
index bd57b508..85cebb7 100644
--- a/chrome/browser/ui/webui/chromeos/crostini_upgrader/crostini_upgrader_dialog.cc
+++ b/chrome/browser/ui/webui/chromeos/crostini_upgrader/crostini_upgrader_dialog.cc
@@ -137,7 +137,7 @@
     return;
   }
   crostini_manager->RestartCrostini(
-      crostini::ContainerId::GetDefault(),
+      crostini::DefaultContainerId(),
       base::BindOnce(
           [](base::OnceClosure launch_closure,
              crostini::CrostiniResult result) {
diff --git a/chrome/browser/ui/webui/chromeos/crostini_upgrader/crostini_upgrader_page_handler.cc b/chrome/browser/ui/webui/chromeos/crostini_upgrader/crostini_upgrader_page_handler.cc
index 5f179e1..69d57473 100644
--- a/chrome/browser/ui/webui/chromeos/crostini_upgrader/crostini_upgrader_page_handler.cc
+++ b/chrome/browser/ui/webui/chromeos/crostini_upgrader/crostini_upgrader_page_handler.cc
@@ -59,7 +59,7 @@
   Redisplay();
   CrostiniUpgraderDialog::EmitUpgradeDialogEventHistogram(
       crostini::UpgradeDialogEvent::kDidBackup);
-  upgrader_ui_delegate_->Backup(crostini::ContainerId::GetDefault(),
+  upgrader_ui_delegate_->Backup(crostini::DefaultContainerId(),
                                 show_file_chooser, web_contents_->GetWeakPtr());
 }
 
@@ -69,14 +69,14 @@
 
 void CrostiniUpgraderPageHandler::Upgrade() {
   Redisplay();
-  upgrader_ui_delegate_->Upgrade(crostini::ContainerId::GetDefault());
+  upgrader_ui_delegate_->Upgrade(crostini::DefaultContainerId());
 }
 
 void CrostiniUpgraderPageHandler::Restore() {
   Redisplay();
   CrostiniUpgraderDialog::EmitUpgradeDialogEventHistogram(
       crostini::UpgradeDialogEvent::kDidRestore);
-  upgrader_ui_delegate_->Restore(crostini::ContainerId::GetDefault(),
+  upgrader_ui_delegate_->Restore(crostini::DefaultContainerId(),
                                  web_contents_->GetWeakPtr());
 }
 
diff --git a/chrome/browser/ui/webui/new_tab_page/new_tab_page_ui.cc b/chrome/browser/ui/webui/new_tab_page/new_tab_page_ui.cc
index 22b6025..e7c606c3b 100644
--- a/chrome/browser/ui/webui/new_tab_page/new_tab_page_ui.cc
+++ b/chrome/browser/ui/webui/new_tab_page/new_tab_page_ui.cc
@@ -27,9 +27,9 @@
 #include "chrome/browser/search_provider_logos/logo_service_factory.h"
 #include "chrome/browser/signin/identity_manager_factory.h"
 #include "chrome/browser/sync/sync_service_factory.h"
-#include "chrome/browser/themes/theme_properties.h"
 #include "chrome/browser/themes/theme_service_factory.h"
 #include "chrome/browser/ui/browser_window.h"
+#include "chrome/browser/ui/color/chrome_color_id.h"
 #include "chrome/browser/ui/webui/browser_command/browser_command_handler.h"
 #include "chrome/browser/ui/webui/cr_components/most_visited/most_visited_handler.h"
 #include "chrome/browser/ui/webui/customize_themes/chrome_customize_themes_handler.h"
@@ -73,6 +73,7 @@
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/base/resource/resource_bundle.h"
 #include "ui/base/webui/web_ui_util.h"
+#include "ui/color/color_provider.h"
 #include "ui/native_theme/native_theme.h"
 #include "ui/resources/grit/webui_generated_resources.h"
 #include "url/url_util.h"
@@ -732,18 +733,9 @@
 
 void NewTabPageUI::OnThemeChanged() {
   base::Value::Dict update;
-
-  const ui::ThemeProvider* theme_provider =
-      webui::GetThemeProvider(web_contents_);
-  // TODO(crbug.com/1299925): Always mock theme provider in tests so that
-  // `theme_provider` is never nullptr.
-  if (theme_provider) {
-    auto background_color =
-        theme_provider->GetColor(ThemeProperties::COLOR_NTP_BACKGROUND);
-    update.Set("backgroundColor", skia::SkColorToHexString(background_color));
-  } else {
-    update.Set("backgroundColor", "");
-  }
+  const ui::ColorProvider& color_provider = web_contents_->GetColorProvider();
+  auto background_color = color_provider.GetColor(kColorNewTabPageBackground);
+  update.Set("backgroundColor", skia::SkColorToHexString(background_color));
   content::WebUIDataSource::Update(profile_, chrome::kChromeUINewTabPageHost,
                                    std::move(update));
 }
diff --git a/chrome/browser/ui/webui/new_tab_page_third_party/new_tab_page_third_party_handler.cc b/chrome/browser/ui/webui/new_tab_page_third_party/new_tab_page_third_party_handler.cc
index 77c6201..718ddd9 100644
--- a/chrome/browser/ui/webui/new_tab_page_third_party/new_tab_page_third_party_handler.cc
+++ b/chrome/browser/ui/webui/new_tab_page_third_party/new_tab_page_third_party_handler.cc
@@ -8,12 +8,14 @@
 #include "chrome/browser/themes/theme_properties.h"
 #include "chrome/browser/themes/theme_service.h"
 #include "chrome/browser/themes/theme_service_factory.h"
+#include "chrome/browser/ui/color/chrome_color_id.h"
 #include "chrome/browser/ui/webui/ntp/ntp_resource_cache.h"
 #include "chrome/browser/ui/webui/webui_util.h"
 #include "chrome/common/pref_names.h"
 #include "chrome/grit/theme_resources.h"
 #include "components/prefs/pref_service.h"
 #include "content/public/browser/web_contents.h"
+#include "ui/color/color_provider.h"
 #include "ui/gfx/color_utils.h"
 
 NewTabPageThirdPartyHandler::NewTabPageThirdPartyHandler(
@@ -53,16 +55,17 @@
   const ui::ThemeProvider* theme_provider =
       webui::GetThemeProvider(web_contents_);
   DCHECK(theme_provider);
+  const ui::ColorProvider& color_provider = web_contents_->GetColorProvider();
   most_visited->background_color =
-      theme_provider->GetColor(ThemeProperties::COLOR_NTP_SHORTCUT);
+      color_provider.GetColor(kColorNewTabPageMostVisitedTileBackground);
   most_visited->use_white_tile_icon =
       color_utils::IsDark(most_visited->background_color);
   most_visited->use_title_pill = false;
-  theme->text_color = theme_provider->GetColor(ThemeProperties::COLOR_NTP_TEXT);
+  theme->text_color = color_provider.GetColor(kColorNewTabPageText);
   most_visited->is_dark = !color_utils::IsDark(theme->text_color);
-  theme->color_background = color_utils::SkColorToRgbaString(
-      GetThemeColor(webui::GetNativeTheme(web_contents_), *theme_provider,
-                    ThemeProperties::COLOR_NTP_BACKGROUND));
+  theme->color_background = color_utils::SkColorToRgbaString(GetThemeColor(
+      webui::GetNativeTheme(web_contents_), web_contents_->GetColorProvider(),
+      kColorNewTabPageBackground));
   if (theme_provider->HasCustomImage(IDR_THEME_NTP_BACKGROUND)) {
     theme->background_tiling = GetNewTabBackgroundTilingCSS(*theme_provider);
     theme->background_position =
diff --git a/chrome/browser/ui/webui/new_tab_page_third_party/new_tab_page_third_party_ui.cc b/chrome/browser/ui/webui/new_tab_page_third_party/new_tab_page_third_party_ui.cc
index a69f085..dd334a0 100644
--- a/chrome/browser/ui/webui/new_tab_page_third_party/new_tab_page_third_party_ui.cc
+++ b/chrome/browser/ui/webui/new_tab_page_third_party/new_tab_page_third_party_ui.cc
@@ -10,6 +10,7 @@
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/themes/theme_properties.h"
 #include "chrome/browser/themes/theme_service.h"
+#include "chrome/browser/ui/color/chrome_color_id.h"
 #include "chrome/browser/ui/webui/cr_components/most_visited/most_visited_handler.h"
 #include "chrome/browser/ui/webui/customize_themes/chrome_customize_themes_handler.h"
 #include "chrome/browser/ui/webui/favicon_source.h"
@@ -33,6 +34,7 @@
 #include "content/public/browser/web_ui_data_source.h"
 #include "ui/base/accelerators/accelerator.h"
 #include "ui/base/l10n/l10n_util.h"
+#include "ui/color/color_provider.h"
 #include "ui/gfx/color_utils.h"
 #include "ui/resources/grit/webui_generated_resources.h"
 #include "url/url_util.h"
@@ -66,14 +68,15 @@
   // TODO(crbug.com/1299925): Always mock theme provider in tests so that
   // `theme_provider` is never nullptr.
   if (theme_provider) {
+    const ui::ColorProvider& color_provider = web_contents->GetColorProvider();
     source->AddString("backgroundPosition",
                       GetNewTabBackgroundPositionCSS(*theme_provider));
     source->AddString("backgroundTiling",
                       GetNewTabBackgroundTilingCSS(*theme_provider));
     source->AddString("colorBackground",
                       color_utils::SkColorToRgbaString(GetThemeColor(
-                          webui::GetNativeTheme(web_contents), *theme_provider,
-                          ThemeProperties::COLOR_NTP_BACKGROUND)));
+                          webui::GetNativeTheme(web_contents), color_provider,
+                          kColorNewTabPageBackground)));
     // TODO(crbug.com/1056758): don't get theme id from profile.
     source->AddString("themeId",
                       profile->GetPrefs()->GetString(prefs::kCurrentThemeID));
@@ -81,10 +84,11 @@
                       theme_provider->HasCustomImage(IDR_THEME_NTP_BACKGROUND)
                           ? "has-custom-background"
                           : "");
-    source->AddString("isdark", !color_utils::IsDark(theme_provider->GetColor(
-                                    ThemeProperties::COLOR_NTP_TEXT))
-                                    ? "dark"
-                                    : "");
+    source->AddString(
+        "isdark",
+        !color_utils::IsDark(color_provider.GetColor(kColorNewTabPageText))
+            ? "dark"
+            : "");
   } else {
     source->AddString("backgroundPosition", "");
     source->AddString("backgroundTiling", "");
diff --git a/chrome/browser/ui/webui/ntp/ntp_resource_cache.cc b/chrome/browser/ui/webui/ntp/ntp_resource_cache.cc
index 8048353..65bb8cb 100644
--- a/chrome/browser/ui/webui/ntp/ntp_resource_cache.cc
+++ b/chrome/browser/ui/webui/ntp/ntp_resource_cache.cc
@@ -23,6 +23,7 @@
 #include "chrome/browser/themes/theme_service.h"
 #include "chrome/browser/themes/theme_service_factory.h"
 #include "chrome/browser/ui/browser_window.h"
+#include "chrome/browser/ui/color/chrome_color_id.h"
 #include "chrome/browser/ui/cookie_controls/cookie_controls_service.h"
 #include "chrome/browser/ui/cookie_controls/cookie_controls_service_factory.h"
 #include "chrome/browser/ui/layout_constants.h"
@@ -51,6 +52,7 @@
 #include "ui/base/theme_provider.h"
 #include "ui/base/webui/jstemplate_builder.h"
 #include "ui/base/webui/web_ui_util.h"
+#include "ui/color/color_provider.h"
 #include "ui/gfx/color_utils.h"
 #include "ui/native_theme/native_theme.h"
 
@@ -96,9 +98,9 @@
 }  // namespace
 
 SkColor GetThemeColor(const ui::NativeTheme* native_theme,
-                      const ui::ThemeProvider& tp,
+                      const ui::ColorProvider& cp,
                       int id) {
-  SkColor color = tp.GetColor(id);
+  SkColor color = cp.GetColor(id);
   // If web contents are being inverted because the system is in high-contrast
   // mode, any system theme colors we use must be inverted too to cancel out.
   return native_theme->GetPlatformHighContrastColorScheme() ==
@@ -436,8 +438,9 @@
       profile_->GetPrefs()->GetString(prefs::kCurrentThemeID);
 
   // Colors.
+  const ui::ColorProvider& cp = web_contents->GetColorProvider();
   substitutions["colorBackground"] = color_utils::SkColorToRgbaString(
-      GetThemeColor(native_theme, *tp, ThemeProperties::COLOR_NTP_BACKGROUND));
+      GetThemeColor(native_theme, cp, kColorNewTabPageBackground));
   substitutions["backgroundPosition"] = GetNewTabBackgroundPositionCSS(*tp);
   substitutions["backgroundTiling"] = GetNewTabBackgroundTilingCSS(*tp);
 
@@ -463,17 +466,17 @@
 
   const ui::ThemeProvider* tp = webui::GetThemeProvider(web_contents);
   DCHECK(tp);
+  const ui::ColorProvider& cp = web_contents->GetColorProvider();
 
   // Get our theme colors.
   SkColor color_background =
-      GetThemeColor(native_theme, *tp, ThemeProperties::COLOR_NTP_BACKGROUND);
-  SkColor color_text =
-      GetThemeColor(native_theme, *tp, ThemeProperties::COLOR_NTP_TEXT);
+      GetThemeColor(native_theme, cp, kColorNewTabPageBackground);
+  SkColor color_text = GetThemeColor(native_theme, cp, kColorNewTabPageText);
   SkColor color_text_light =
-      GetThemeColor(native_theme, *tp, ThemeProperties::COLOR_NTP_TEXT_LIGHT);
+      GetThemeColor(native_theme, cp, kColorNewTabPageTextLight);
 
-  SkColor color_section_border = GetThemeColor(
-      native_theme, *tp, ThemeProperties::COLOR_NTP_SECTION_BORDER);
+  SkColor color_section_border =
+      GetThemeColor(native_theme, cp, kColorNewTabPageSectionBorder);
 
   // Generate the replacements.
   ui::TemplateReplacements substitutions;
@@ -486,7 +489,7 @@
   substitutions["colorBackground"] =
       color_utils::SkColorToRgbaString(color_background);
   substitutions["colorLink"] = color_utils::SkColorToRgbString(
-      GetThemeColor(native_theme, *tp, ThemeProperties::COLOR_NTP_LINK));
+      GetThemeColor(native_theme, cp, kColorNewTabPageLink));
   substitutions["backgroundPosition"] = GetNewTabBackgroundPositionCSS(*tp);
   substitutions["backgroundTiling"] = GetNewTabBackgroundTilingCSS(*tp);
   substitutions["colorTextRgba"] = color_utils::SkColorToRgbaString(color_text);
diff --git a/chrome/browser/ui/webui/ntp/ntp_resource_cache.h b/chrome/browser/ui/webui/ntp/ntp_resource_cache.h
index 19fab17d..b7a5780 100644
--- a/chrome/browser/ui/webui/ntp/ntp_resource_cache.h
+++ b/chrome/browser/ui/webui/ntp/ntp_resource_cache.h
@@ -31,11 +31,12 @@
 }
 
 namespace ui {
+class ColorProvider;
 class ThemeProvider;
 }
 
 SkColor GetThemeColor(const ui::NativeTheme* native_theme,
-                      const ui::ThemeProvider& tp,
+                      const ui::ColorProvider& cp,
                       int id);
 std::string GetNewTabBackgroundPositionCSS(
     const ui::ThemeProvider& theme_provider);
diff --git a/chrome/browser/user_education/BUILD.gn b/chrome/browser/user_education/BUILD.gn
index d2b95be8..584f8a1c 100644
--- a/chrome/browser/user_education/BUILD.gn
+++ b/chrome/browser/user_education/BUILD.gn
@@ -30,7 +30,7 @@
   resources_package = "org.chromium.chrome.browser.user_education"
 }
 
-android_library("javatests") {
+android_library("unit_device_javatests") {
   testonly = true
   sources = [ "java/src/org/chromium/chrome/browser/user_education/UserEducationHelperTest.java" ]
   deps = [
diff --git a/chrome/browser/video_tutorials/internal/BUILD.gn b/chrome/browser/video_tutorials/internal/BUILD.gn
index 7cd9672..7064c077 100644
--- a/chrome/browser/video_tutorials/internal/BUILD.gn
+++ b/chrome/browser/video_tutorials/internal/BUILD.gn
@@ -169,7 +169,7 @@
     ]
   }
 
-  android_library("javatests") {
+  android_library("unit_device_javatests") {
     testonly = true
 
     sources = [
@@ -186,7 +186,7 @@
       "//chrome/browser/flags:java",
       "//chrome/browser/video_tutorials:java",
       "//chrome/browser/video_tutorials:test_support_java",
-      "//chrome/test/android:chrome_java_integration_test_support",
+      "//chrome/test/android:chrome_java_unit_test_support",
       "//components/thin_webview:java",
       "//content/public/test/android:content_java_test_support",
       "//third_party/android_deps:espresso_java",
diff --git a/chrome/build/linux.pgo.txt b/chrome/build/linux.pgo.txt
index 3fd7b143..30a9473 100644
--- a/chrome/build/linux.pgo.txt
+++ b/chrome/build/linux.pgo.txt
@@ -1 +1 @@
-chrome-linux-main-1655294346-c876148f524c81c174531ff766e1fa43d90b9791.profdata
+chrome-linux-main-1655315802-81d864008e6376e5b47455e1b42857e9b316eb80.profdata
diff --git a/chrome/build/mac-arm.pgo.txt b/chrome/build/mac-arm.pgo.txt
index 5164718..d81d9305 100644
--- a/chrome/build/mac-arm.pgo.txt
+++ b/chrome/build/mac-arm.pgo.txt
@@ -1 +1 @@
-chrome-mac-arm-main-1655294346-4a5bc99f5b4fe93c7d4763d8221e8218e91122e8.profdata
+chrome-mac-arm-main-1655315802-6327d5236b3274d9db4bcef3b32ca9cf9b5aab2f.profdata
diff --git a/chrome/build/mac.pgo.txt b/chrome/build/mac.pgo.txt
index 5575052d..5678c305 100644
--- a/chrome/build/mac.pgo.txt
+++ b/chrome/build/mac.pgo.txt
@@ -1 +1 @@
-chrome-mac-main-1655294346-f4e35791941cc71d1365cea78981b45b64b6c434.profdata
+chrome-mac-main-1655315802-fa5ce9679a0b26173e4bb435e646fb867684eb4a.profdata
diff --git a/chrome/build/win32.pgo.txt b/chrome/build/win32.pgo.txt
index 14f67552..3a2226eb 100644
--- a/chrome/build/win32.pgo.txt
+++ b/chrome/build/win32.pgo.txt
@@ -1 +1 @@
-chrome-win32-main-1655294346-a80f753b10bef44bf5fb84f9fb2fd5f4d81a5751.profdata
+chrome-win32-main-1655315802-ea1cdc796e9bd324873db95d23f31e04f5ae5db1.profdata
diff --git a/chrome/build/win64.pgo.txt b/chrome/build/win64.pgo.txt
index 71636c83..7480933 100644
--- a/chrome/build/win64.pgo.txt
+++ b/chrome/build/win64.pgo.txt
@@ -1 +1 @@
-chrome-win64-main-1655294346-90cd3cca84f28e326c3c9cd179fadbca0e32005e.profdata
+chrome-win64-main-1655305046-49e31d8168e3841e34a4495d3f79e78d5a3b9592.profdata
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn
index 35795e3..81d2688 100644
--- a/chrome/test/BUILD.gn
+++ b/chrome/test/BUILD.gn
@@ -6457,12 +6457,9 @@
       "../browser/sessions/restore_on_startup_policy_handler_unittest.cc",
       "../browser/sessions/tab_restore_service_unittest.cc",
       "../browser/sharing/shared_clipboard/remote_copy_message_handler_unittest.cc",
-      "../browser/sharing/shared_clipboard/shared_clipboard_context_menu_observer_unittest.cc",
       "../browser/sharing/shared_clipboard/shared_clipboard_message_handler_desktop_unittest.cc",
       "../browser/sharing/shared_clipboard/shared_clipboard_test_base.cc",
       "../browser/sharing/shared_clipboard/shared_clipboard_test_base.h",
-      "../browser/sharing/shared_clipboard/shared_clipboard_ui_controller_unittest.cc",
-      "../browser/sharing/shared_clipboard/shared_clipboard_utils_unittest.cc",
       "../browser/sharing/sms/sms_remote_fetcher_unittest.cc",
       "../browser/signin/signin_promo_unittest.cc",
       "../browser/speech/extension_api/extension_manifests_tts_unittest.cc",
diff --git a/chrome/test/base/chrome_test_launcher.cc b/chrome/test/base/chrome_test_launcher.cc
index b51753c..00e5e6d 100644
--- a/chrome/test/base/chrome_test_launcher.cc
+++ b/chrome/test/base/chrome_test_launcher.cc
@@ -58,6 +58,7 @@
 
 #if BUILDFLAG(IS_WIN)
 #include <Shlobj.h>
+#include "base/debug/handle_hooks_win.h"
 #include "base/win/registry.h"
 #include "base/win/scoped_com_initializer.h"
 #include "chrome/app/chrome_crash_reporter_client_win.h"
@@ -255,7 +256,10 @@
 #if BUILDFLAG(IS_WIN)
   // Create a primordial InstallDetails instance for the test.
   install_static::ScopedInstallDetails install_details;
-#endif
+
+  // Install handle hooks for tests only.
+  base::debug::HandleHooks::PatchLoadedModules();
+#endif  // BUILDFLAG(IS_WIN)
 
   const auto& command_line = *base::CommandLine::ForCurrentProcess();
 
diff --git a/chrome/test/chromedriver/key_converter.cc b/chrome/test/chromedriver/key_converter.cc
index 76c1c2da..46c3fed 100644
--- a/chrome/test/chromedriver/key_converter.cc
+++ b/chrome/test/chromedriver/key_converter.cc
@@ -622,7 +622,7 @@
   if (!action_object->GetString("value", &raw_key))
     return Status(kUnknownError, "missing 'value'");
 
-  int32_t char_index = 0;
+  size_t char_index = 0;
   base_icu::UChar32 code_point;
   base::ReadUnicodeCharacter(raw_key.c_str(), raw_key.size(), &char_index,
                              &code_point);
diff --git a/chrome/test/chromedriver/window_commands.cc b/chrome/test/chromedriver/window_commands.cc
index 87d1551..34f0475 100644
--- a/chrome/test/chromedriver/window_commands.cc
+++ b/chrome/test/chromedriver/window_commands.cc
@@ -1400,12 +1400,11 @@
         bool valid = action_item->GetString("value", &key);
         if (valid) {
           // check if key is a single unicode code point
-          int32_t char_index = 0;
+          size_t char_index = 0;
           base_icu::UChar32 code_point;
-          valid =
-              base::ReadUnicodeCharacter(key.c_str(), key.size(), &char_index,
-                                         &code_point) &&
-              static_cast<std::string::size_type>(char_index + 1) == key.size();
+          valid = base::ReadUnicodeCharacter(key.c_str(), key.size(),
+                                             &char_index, &code_point) &&
+                  char_index + 1 == key.size();
         }
         if (!valid)
           return Status(kInvalidArgument,
diff --git a/chrome/test/data/android/download/get.html b/chrome/test/data/android/download/get.html
index cfcc875..1387f19f 100644
--- a/chrome/test/data/android/download/get.html
+++ b/chrome/test/data/android/download/get.html
@@ -1,3 +1,3 @@
 <html><body style="height:100%;">
-  <a href="test.gzip" style="display:block;position:absolute;top:0;left:0;width:100%;height:100%;">test.gzip</a>
+  <a href="test.gzip" target="_blank" style="display:block;position:absolute;top:0;left:0;width:100%;height:100%;">test.gzip</a>
 </body></html>
diff --git a/chrome/test/data/webui/chromeos/os_feedback_ui/share_data_page_test.js b/chrome/test/data/webui/chromeos/os_feedback_ui/share_data_page_test.js
index d36ba6f..78b16b5 100644
--- a/chrome/test/data/webui/chromeos/os_feedback_ui/share_data_page_test.js
+++ b/chrome/test/data/webui/chromeos/os_feedback_ui/share_data_page_test.js
@@ -9,6 +9,7 @@
 import {FeedbackFlowState} from 'chrome://os-feedback/feedback_flow.js';
 import {ShareDataPageElement} from 'chrome://os-feedback/share_data_page.js';
 import {mojoString16ToString, stringToMojoString16} from 'chrome://resources/ash/common/mojo_utils.js';
+import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 
 import {assertArrayEquals, assertEquals, assertFalse, assertTrue} from '../../chai_assert.js';
 import {eventToPromise, flushTasks, isVisible} from '../../test_util.js';
@@ -89,14 +90,17 @@
 
     // Verify the back button is in the page.
     assertEquals('Back', getElementContent('#buttonBack'));
+    assertTrue(page.i18nExists('backButtonLabel'));
 
     // Verify the send button is in the page.
     assertEquals('Send', getElementContent('#buttonSend'));
 
     // Verify the attach files label is in the page.
+    assertTrue(page.i18nExists('attachFilesLabel'));
     assertEquals('Attach files', getElementContent('#attachFilesLabel'));
 
     // Verify the user email label is in the page.
+    assertTrue(page.i18nExists('userEmailLabel'));
     assertEquals('Email', getElementContent('#userEmailLabel'));
 
     // Verify the share diagnostic data label is in the page.
@@ -365,4 +369,92 @@
         fakeFileData,
         /** @type {!Array<Number>} */ (attachedFile.fileData.bytes));
   });
+
+  /**
+   * Test that when page initially loaded "user-consent" checkbox is false and
+   * expected localize message displayed.
+   */
+  test('UserConsentGrantedCheckbox_StartsFalse', async () => {
+    const expectedUserConsentMessage =
+        'We may email you for more information or updates';
+    await initializePage();
+
+    assertTrue(page.i18nExists('userConsentLabel'));
+
+    const userConsentCheckboxChecked =
+        getElement('#userConsentCheckbox').checked;
+    const userConsentText = getElementContent('#userConsentLabel');
+
+    assertFalse(userConsentCheckboxChecked);
+    assertEquals(expectedUserConsentMessage, userConsentText);
+  });
+
+  /**
+   * Test that report "contact_user_consent_granted" matches "user-consent"
+   * checkbox value.
+   */
+  test(
+      'UserConsentGrantedCheckbox_UpdatesReportContactUserConsentGranted',
+      async () => {
+        await initializePage();
+        page.feedbackContext = fakeFeedbackContext;
+        getElement('#userConsentCheckbox').checked = true;
+        await flushTasks();
+
+        const reportWithConsent = (await clickSendAndWait(page)).report;
+
+        assertTrue(reportWithConsent.contactUserConsentGranted);
+
+        page.reEnableSendReportButton();
+        page.feedbackContext = fakeFeedbackContext;
+        getElement('#userConsentCheckbox').checked = false;
+        await flushTasks();
+
+        const reportWithoutConsent = (await clickSendAndWait(page)).report;
+        assertFalse(reportWithoutConsent.contactUserConsentGranted);
+      });
+
+  /**
+   * Test that when report is anonymous (no email provided), "user-consent"
+   * checkbox is disabled and value is false.
+   */
+  test(
+      'UserConsentGrantedCheckbox_ReportAnonymous_FalseAndDisabled',
+      async () => {
+        await initializePage();
+        page.feedbackContext = fakeFeedbackContext;
+        const disabledInputClass = 'disabled-input-text';
+
+        const consentLabel = getElement('#userConsentLabel');
+        const consentCheckbox = getElement('#userConsentCheckbox');
+        const emailDropdown = getElement('#userEmailDropDown');
+
+        // Select the email.
+        emailDropdown.value = 'test.user2@test.com';
+        await flushTasks();
+
+        // With email selected and consent not granted.
+        assertFalse(consentCheckbox.disabled);
+        assertFalse(consentCheckbox.checked);
+        assertFalse(consentLabel.classList.contains(disabledInputClass));
+
+        // Check checkbox.
+        consentCheckbox.click();
+        await flushTasks();
+
+        // With email selected and consent granted.
+        assertFalse(consentCheckbox.disabled);
+        assertTrue(consentCheckbox.checked);
+        assertFalse(consentLabel.classList.contains(disabledInputClass));
+
+        // Select the "Do Not Provide Email" option.
+        emailDropdown.value = '';
+        emailDropdown.dispatchEvent(new CustomEvent('change'));
+        flush();
+
+        // With anonymous email selected and consent not granted.
+        assertTrue(consentCheckbox.disabled);
+        assertFalse(consentCheckbox.checked);
+        assertTrue(consentLabel.classList.contains(disabledInputClass));
+      });
 }
diff --git a/chrome/test/data/webui/js/cr/ui/BUILD.gn b/chrome/test/data/webui/js/cr/ui/BUILD.gn
index a8a825d..12b5ee8 100644
--- a/chrome/test/data/webui/js/cr/ui/BUILD.gn
+++ b/chrome/test/data/webui/js/cr/ui/BUILD.gn
@@ -2,6 +2,7 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
+import("//build/config/chromeos/ui_mode.gni")
 import("//third_party/closure_compiler/compile_js.gni")
 
 js_library("array_data_model_test") {
@@ -28,36 +29,38 @@
   ]
 }
 
-js_library("grid_test") {
-  deps = [
-    "../../..:chai_assert",
-    "//ui/webui/resources/js/cr/ui:array_data_model.m",
-    "//ui/webui/resources/js/cr/ui:grid.m",
-  ]
-}
+if (is_chromeos_ash) {
+  js_library("grid_test") {
+    deps = [
+      "../../..:chai_assert",
+      "//ui/webui/resources/js/cr/ui:array_data_model.m",
+      "//ui/webui/resources/js/cr/ui:grid.m",
+    ]
+  }
 
-js_library("list_selection_model_test") {
-  deps = [
-    ":list_selection_model_test_util",
-    "../../..:chai_assert",
-    "//ui/webui/resources/js/cr/ui:list_selection_model.m",
-  ]
-}
+  js_library("list_selection_model_test") {
+    deps = [
+      ":list_selection_model_test_util",
+      "../../..:chai_assert",
+      "//ui/webui/resources/js/cr/ui:list_selection_model.m",
+    ]
+  }
 
-js_library("list_selection_model_test_util") {
-  deps = [
-    "../../..:chai_assert",
-    "//ui/webui/resources/js/cr/ui:list_selection_model.m",
-    "//ui/webui/resources/js/cr/ui:list_single_selection_model.m",
-  ]
-}
+  js_library("list_selection_model_test_util") {
+    deps = [
+      "../../..:chai_assert",
+      "//ui/webui/resources/js/cr/ui:list_selection_model.m",
+      "//ui/webui/resources/js/cr/ui:list_single_selection_model.m",
+    ]
+  }
 
-js_library("list_single_selection_model_test") {
-  deps = [
-    ":list_selection_model_test_util",
-    "../../..:chai_assert",
-    "//ui/webui/resources/js/cr/ui:list_single_selection_model.m",
-  ]
+  js_library("list_single_selection_model_test") {
+    deps = [
+      ":list_selection_model_test_util",
+      "../../..:chai_assert",
+      "//ui/webui/resources/js/cr/ui:list_single_selection_model.m",
+    ]
+  }
 }
 
 js_library("list_test") {
@@ -109,14 +112,19 @@
     ":array_data_model_test",
     ":command_test",
     ":context_menu_handler_test",
-    ":grid_test",
-    ":list_selection_model_test",
-    ":list_selection_model_test_util",
-    ":list_single_selection_model_test",
     ":list_test",
     ":menu_button_test",
     ":menu_test",
     ":position_util_test",
     ":splitter_test",
   ]
+
+  if (is_chromeos_ash) {
+    deps += [
+      ":grid_test",
+      ":list_selection_model_test",
+      ":list_selection_model_test_util",
+      ":list_single_selection_model_test",
+    ]
+  }
 }
diff --git a/chrome/test/data/webui/settings/BUILD.gn b/chrome/test/data/webui/settings/BUILD.gn
index 5d13862..3abed204 100644
--- a/chrome/test/data/webui/settings/BUILD.gn
+++ b/chrome/test/data/webui/settings/BUILD.gn
@@ -35,6 +35,7 @@
   preprocessed_files += [
     "test_languages_browser_proxy.ts",
     "languages_page_tests.ts",
+    "languages_page_metrics_test_browser.ts",
   ]
 }
 
@@ -155,7 +156,6 @@
 } else {
   non_preprocessed_files += [
     "fake_language_settings_private.ts",
-    "languages_page_metrics_test_browser.ts",
     "languages_subpage_details_tests.ts",
     "languages_subpage_tests.ts",
     "languages_tests.ts",
diff --git a/chrome/test/data/webui/settings/chromeos/cups_printer_entry_tests.js b/chrome/test/data/webui/settings/chromeos/cups_printer_entry_tests.js
index a0044c0e..dc4e3762d 100644
--- a/chrome/test/data/webui/settings/chromeos/cups_printer_entry_tests.js
+++ b/chrome/test/data/webui/settings/chromeos/cups_printer_entry_tests.js
@@ -72,9 +72,13 @@
   verifyFilteredPrinters(printerEntryListTestElement, searchTerm);
 
   if (expectedVisiblePrinters.length) {
-    assertTrue(printerEntryListTestElement.$$('#no-search-results').hidden);
+    assertTrue(printerEntryListTestElement.shadowRoot
+                   .querySelector('#no-search-results')
+                   .hidden);
   } else {
-    assertFalse(printerEntryListTestElement.$$('#no-search-results').hidden);
+    assertFalse(printerEntryListTestElement.shadowRoot
+                    .querySelector('#no-search-results')
+                    .hidden);
   }
 }
 
@@ -156,13 +160,15 @@
         createPrinterEntry(PrinterType.SAVED);
 
     // Assert that three dot menu is not shown before the dom is updated.
-    assertFalse(!!printerEntryTestElement.$$('.icon-more-vert'));
+    assertFalse(
+        !!printerEntryTestElement.shadowRoot.querySelector('.icon-more-vert'));
 
     flush();
 
     // Three dot menu should be visible when |printerType| is set to
     // PrinterType.SAVED.
-    assertTrue(!!printerEntryTestElement.$$('.icon-more-vert'));
+    assertTrue(
+        !!printerEntryTestElement.shadowRoot.querySelector('.icon-more-vert'));
   });
 
   test('disableButtonWhenSavingPrinterOrDisallowedByPolicy', function() {
@@ -176,7 +182,8 @@
       printerEntryTestElement.printerEntry =
           createPrinterEntry(printerTypes[i]);
       flush();
-      const actionButton = printerEntryTestElement.$$(printerIds[i]);
+      const actionButton =
+          printerEntryTestElement.shadowRoot.querySelector(printerIds[i]);
       printerEntryTestElement.savingPrinter = true;
       printerEntryTestElement.userPrintersAllowed = true;
       assertTrue(actionButton.disabled);
diff --git a/chrome/test/data/webui/settings/chromeos/cups_printer_landing_page_tests.js b/chrome/test/data/webui/settings/chromeos/cups_printer_landing_page_tests.js
index f1e4afa..eace098 100644
--- a/chrome/test/data/webui/settings/chromeos/cups_printer_landing_page_tests.js
+++ b/chrome/test/data/webui/settings/chromeos/cups_printer_landing_page_tests.js
@@ -45,7 +45,7 @@
  * @private
  */
 function initializeEditDialog(page) {
-  const editDialog = page.$$('#editPrinterDialog');
+  const editDialog = page.shadowRoot.querySelector('#editPrinterDialog');
   assertTrue(!!editDialog);
 
   // Edit dialog will reset |ppdModel| when |ppdManufacturer| is set. Must
@@ -146,15 +146,18 @@
  */
 function verifySearchQueryResults(
     printersElement, expectedVisiblePrinters, searchTerm) {
-  const printerEntryListTestElement = printersElement.$$('#printerEntryList');
+  const printerEntryListTestElement =
+      printersElement.shadowRoot.querySelector('#printerEntryList');
 
   verifyVisiblePrinters(printerEntryListTestElement, expectedVisiblePrinters);
   verifyFilteredPrinters(printerEntryListTestElement, searchTerm);
 
   if (expectedVisiblePrinters.length) {
-    assertTrue(printersElement.$$('#no-search-results').hidden);
+    assertTrue(
+        printersElement.shadowRoot.querySelector('#no-search-results').hidden);
   } else {
-    assertFalse(printersElement.$$('#no-search-results').hidden);
+    assertFalse(
+        printersElement.shadowRoot.querySelector('#no-search-results').hidden);
   }
 }
 
@@ -169,8 +172,9 @@
   const printerList = cupsPrintersBrowserProxy.printerList.printerList;
   const savedPrinterEntries = getPrinterEntries(savedPrintersElement);
 
-  clickButton(savedPrinterEntries[index].$$('.icon-more-vert'));
-  clickButton(savedPrintersElement.$$('#removeButton'));
+  clickButton(
+      savedPrinterEntries[index].shadowRoot.querySelector('.icon-more-vert'));
+  clickButton(savedPrintersElement.shadowRoot.querySelector('#removeButton'));
 
   return cupsPrintersBrowserProxy.whenCalled('removeCupsPrinter')
       .then(function() {
@@ -236,7 +240,8 @@
     // |cupsPrinterBrowserProxy| needs to have a list of saved printers before
     // initializing the landing page.
     cupsPrintersBrowserProxy.printerList = {printerList: printerList};
-    CupsPrintersBrowserProxyImpl.instance_ = cupsPrintersBrowserProxy;
+    CupsPrintersBrowserProxyImpl.setInstanceForTesting(
+        cupsPrintersBrowserProxy);
 
     page = document.createElement('settings-cups-printers');
     document.body.appendChild(page);
@@ -276,12 +281,14 @@
           // Wait for saved printers to populate.
           flush();
 
-          savedPrintersElement = page.$$('settings-cups-saved-printers');
+          savedPrintersElement =
+              page.shadowRoot.querySelector('settings-cups-saved-printers');
           assertTrue(!!savedPrintersElement);
 
           // List component contained by CupsSavedPrinters.
           const savedPrintersList =
-              savedPrintersElement.$$('settings-cups-printers-entry-list');
+              savedPrintersElement.shadowRoot.querySelector(
+                  'settings-cups-printers-entry-list');
 
           const printerListEntries = getPrinterEntries(savedPrintersElement);
 
@@ -302,7 +309,8 @@
           // Wait for saved printers to populate.
           flush();
 
-          savedPrintersElement = page.$$('settings-cups-saved-printers');
+          savedPrintersElement =
+              page.shadowRoot.querySelector('settings-cups-saved-printers');
           assertTrue(!!savedPrintersElement);
 
           return removeAllPrinters(
@@ -329,22 +337,23 @@
           // Wait for saved printers to populate.
           flush();
 
-          savedPrintersElement = page.$$('settings-cups-saved-printers');
+          savedPrintersElement =
+              page.shadowRoot.querySelector('settings-cups-saved-printers');
           assertTrue(!!savedPrintersElement);
 
-          savedPrintersList =
-              savedPrintersElement.$$('settings-cups-printers-entry-list');
+          savedPrintersList = savedPrintersElement.shadowRoot.querySelector(
+              'settings-cups-printers-entry-list');
           savedPrinterEntries = getPrinterEntries(savedPrintersElement);
 
           verifyPrintersList(savedPrinterEntries, printerList);
 
-          assertTrue(!!page.$$('#savedPrinters'));
+          assertTrue(!!page.shadowRoot.querySelector('#savedPrinters'));
 
           return removeAllPrinters(
               cupsPrintersBrowserProxy, savedPrintersElement);
         })
         .then(() => {
-          assertFalse(!!page.$$('#savedPrinters'));
+          assertFalse(!!page.shadowRoot.querySelector('#savedPrinters'));
         });
   });
 
@@ -364,28 +373,32 @@
           // Wait for saved printers to populate.
           flush();
 
-          savedPrintersElement = page.$$('settings-cups-saved-printers');
+          savedPrintersElement =
+              page.shadowRoot.querySelector('settings-cups-saved-printers');
           assertTrue(!!savedPrintersElement);
 
           savedPrinterEntries = getPrinterEntries(savedPrintersElement);
 
           // Update the printer name of the first entry.
-          clickButton(savedPrinterEntries[0].$$('.icon-more-vert'));
-          clickButton(savedPrintersElement.$$('#editButton'));
+          clickButton(savedPrinterEntries[0].shadowRoot.querySelector(
+              '.icon-more-vert'));
+          clickButton(
+              savedPrintersElement.shadowRoot.querySelector('#editButton'));
 
           flush();
 
           editDialog = initializeEditDialog(page);
 
           // Change name of printer and save the change.
-          const nameField = editDialog.$$('.printer-name-input');
+          const nameField =
+              editDialog.shadowRoot.querySelector('.printer-name-input');
           assertTrue(!!nameField);
           nameField.value = expectedName;
           nameField.fire('input');
 
           flush();
 
-          clickButton(editDialog.$$('.action-button'));
+          clickButton(editDialog.shadowRoot.querySelector('.action-button'));
 
           return cupsPrintersBrowserProxy.whenCalled('updateCupsPrinter');
         })
@@ -416,35 +429,42 @@
           // Wait for saved printers to populate.
           flush();
 
-          savedPrintersElement = page.$$('settings-cups-saved-printers');
+          savedPrintersElement =
+              page.shadowRoot.querySelector('settings-cups-saved-printers');
           assertTrue(!!savedPrintersElement);
 
           savedPrinterEntries = getPrinterEntries(savedPrintersElement);
 
           // Edit the first entry.
-          clickButton(savedPrinterEntries[0].$$('.icon-more-vert'));
-          clickButton(savedPrintersElement.$$('#editButton'));
+          clickButton(savedPrinterEntries[0].shadowRoot.querySelector(
+              '.icon-more-vert'));
+          clickButton(
+              savedPrintersElement.shadowRoot.querySelector('#editButton'));
 
           flush();
 
           editDialog = initializeEditDialog(page);
 
-          const nameField = editDialog.$$('.printer-name-input');
+          const nameField =
+              editDialog.shadowRoot.querySelector('.printer-name-input');
           assertTrue(!!nameField);
           nameField.value = expectedName;
           nameField.fire('input');
 
-          const addressField = editDialog.$$('#printerAddress');
+          const addressField =
+              editDialog.shadowRoot.querySelector('#printerAddress');
           assertTrue(!!addressField);
           addressField.value = expectedAddress;
           addressField.fire('input');
 
-          assertFalse(editDialog.$$('.cancel-button').hidden);
-          assertFalse(editDialog.$$('.action-button').hidden);
+          assertFalse(
+              editDialog.shadowRoot.querySelector('.cancel-button').hidden);
+          assertFalse(
+              editDialog.shadowRoot.querySelector('.action-button').hidden);
 
           flush();
 
-          clickButton(editDialog.$$('.action-button'));
+          clickButton(editDialog.shadowRoot.querySelector('.action-button'));
 
           return cupsPrintersBrowserProxy.whenCalled('reconfigureCupsPrinter');
         })
@@ -472,7 +492,8 @@
           // Wait for saved printers to populate.
           flush();
 
-          savedPrintersElement = page.$$('settings-cups-saved-printers');
+          savedPrintersElement =
+              page.shadowRoot.querySelector('settings-cups-saved-printers');
           assertTrue(!!savedPrintersElement);
 
           const printerListEntries = getPrinterEntries(savedPrintersElement);
@@ -531,7 +552,8 @@
           // Wait for saved printers to populate.
           flush();
 
-          savedPrintersElement = page.$$('settings-cups-saved-printers');
+          savedPrintersElement =
+              page.shadowRoot.querySelector('settings-cups-saved-printers');
           assertTrue(!!savedPrintersElement);
 
           const printerListEntries = getPrinterEntries(savedPrintersElement);
@@ -578,9 +600,12 @@
         .then(async () => {
           // Wait for saved printers to populate.
           flush();
-          savedPrintersElement = page.$$('settings-cups-saved-printers');
+          savedPrintersElement =
+              page.shadowRoot.querySelector('settings-cups-saved-printers');
           assertTrue(!!savedPrintersElement);
-          const printerEntryList = savedPrintersElement.$$('#printerEntryList');
+          const printerEntryList =
+              savedPrintersElement.shadowRoot.querySelector(
+                  '#printerEntryList');
           const printerListEntries = getPrinterEntries(savedPrintersElement);
           printerEntryList.focus();
           printerEntryList.dispatchEvent(arrowDownEvent);
@@ -614,10 +639,12 @@
 
     flush();
 
-    const savedPrinters = page.$$('settings-cups-saved-printers');
-    const printerEntry =
-        savedPrinters && savedPrinters.$$('settings-cups-printers-entry');
-    const deepLinkElement = printerEntry && printerEntry.$$('#moreActions');
+    const savedPrinters =
+        page.shadowRoot.querySelector('settings-cups-saved-printers');
+    const printerEntry = savedPrinters &&
+        savedPrinters.shadowRoot.querySelector('settings-cups-printers-entry');
+    const deepLinkElement =
+        printerEntry && printerEntry.shadowRoot.querySelector('#moreActions');
     await waitAfterNextRender(deepLinkElement);
     assertEquals(
         deepLinkElement, getDeepActiveElement(),
@@ -635,11 +662,13 @@
           // Wait for saved printers to populate.
           flush();
 
-          savedPrintersElement = page.$$('settings-cups-saved-printers');
+          savedPrintersElement =
+              page.shadowRoot.querySelector('settings-cups-saved-printers');
           assertTrue(!!savedPrintersElement);
 
           const printerEntryListTestElement =
-              savedPrintersElement.$$('#printerEntryList');
+              savedPrintersElement.shadowRoot.querySelector(
+                  '#printerEntryList');
 
           verifyVisiblePrinters(printerEntryListTestElement, [
             createPrinterListEntry('google', '4', 'id4', PrinterType.SAVED),
@@ -648,7 +677,8 @@
           ]);
           // Assert that the Show more button is hidden because printer list
           // length is <= 3.
-          assertFalse(!!savedPrintersElement.$$('#show-more-container'));
+          assertFalse(!!savedPrintersElement.shadowRoot.querySelector(
+              '#show-more-container'));
 
           // Newly added printers will always be visible and inserted to the
           // top of the list.
@@ -663,7 +693,8 @@
               printerEntryListTestElement, expectedVisiblePrinters);
           // Assert that the Show more button is still hidden because all newly
           // added printers are visible.
-          assertFalse(!!savedPrintersElement.$$('#show-more-container'));
+          assertFalse(!!savedPrintersElement.shadowRoot.querySelector(
+              '#show-more-container'));
         });
   });
 
@@ -679,11 +710,13 @@
           // Wait for saved printers to populate.
           flush();
 
-          savedPrintersElement = page.$$('settings-cups-saved-printers');
+          savedPrintersElement =
+              page.shadowRoot.querySelector('settings-cups-saved-printers');
           assertTrue(!!savedPrintersElement);
 
           const printerEntryListTestElement =
-              savedPrintersElement.$$('#printerEntryList');
+              savedPrintersElement.shadowRoot.querySelector(
+                  '#printerEntryList');
 
           // There are 4 total printers but only 3 printers are visible and 1 is
           // hidden underneath the Show more section.
@@ -694,11 +727,14 @@
           ]);
           // Assert that the Show more button is shown since printer list length
           // is > 3.
-          assertTrue(!!savedPrintersElement.$$('#show-more-container'));
+          assertTrue(!!savedPrintersElement.shadowRoot.querySelector(
+              '#show-more-container'));
 
           // Click on the Show more button.
-          clickButton(savedPrintersElement.$$('#show-more-icon'));
-          assertFalse(!!savedPrintersElement.$$('#show-more-container'));
+          clickButton(
+              savedPrintersElement.shadowRoot.querySelector('#show-more-icon'));
+          assertFalse(!!savedPrintersElement.shadowRoot.querySelector(
+              '#show-more-container'));
           // Clicking on the Show more button reveals all hidden printers.
           verifyVisiblePrinters(printerEntryListTestElement, [
             createPrinterListEntry('google', '4', 'id4', PrinterType.SAVED),
@@ -721,11 +757,13 @@
           // Wait for saved printers to populate.
           flush();
 
-          savedPrintersElement = page.$$('settings-cups-saved-printers');
+          savedPrintersElement =
+              page.shadowRoot.querySelector('settings-cups-saved-printers');
           assertTrue(!!savedPrintersElement);
 
           const printerEntryListTestElement =
-              savedPrintersElement.$$('#printerEntryList');
+              savedPrintersElement.shadowRoot.querySelector(
+                  '#printerEntryList');
 
           // There are 4 total printers but only 3 printers are visible and 1 is
           // hidden underneath the Show more section.
@@ -736,7 +774,8 @@
           ]);
           // Assert that the Show more button is shown since printer list length
           // is > 3.
-          assertTrue(!!savedPrintersElement.$$('#show-more-container'));
+          assertTrue(!!savedPrintersElement.shadowRoot.querySelector(
+              '#show-more-container'));
 
           // Newly added printers will always be visible.
           addNewSavedPrinter(createCupsPrinterInfo('test5', '5', 'id5'));
@@ -747,7 +786,8 @@
             createPrinterListEntry('test2', '2', 'id2', PrinterType.SAVED)
           ]);
           // Assert that the Show more button is still shown.
-          assertTrue(!!savedPrintersElement.$$('#show-more-container'));
+          assertTrue(!!savedPrintersElement.shadowRoot.querySelector(
+              '#show-more-container'));
         });
   });
 
@@ -764,11 +804,13 @@
           // Wait for saved printers to populate.
           flush();
 
-          savedPrintersElement = page.$$('settings-cups-saved-printers');
+          savedPrintersElement =
+              page.shadowRoot.querySelector('settings-cups-saved-printers');
           assertTrue(!!savedPrintersElement);
 
           const printerEntryListTestElement =
-              savedPrintersElement.$$('#printerEntryList');
+              savedPrintersElement.shadowRoot.querySelector(
+                  '#printerEntryList');
 
           // There are 5 total printers but only 3 printers are visible and 2
           // are hidden underneath the Show more section.
@@ -779,7 +821,8 @@
           ]);
           // Assert that the Show more button is shown since printer list length
           // is > 3.
-          assertTrue(!!savedPrintersElement.$$('#show-more-container'));
+          assertTrue(!!savedPrintersElement.shadowRoot.querySelector(
+              '#show-more-container'));
 
           // Simulate removing 'google' printer.
           removeSavedPrinter('id3');
@@ -792,7 +835,8 @@
             createPrinterListEntry('google3', '5', 'id5', PrinterType.SAVED),
             createPrinterListEntry('test1', '1', 'id1', PrinterType.SAVED)
           ]);
-          assertTrue(!!savedPrintersElement.$$('#show-more-container'));
+          assertTrue(!!savedPrintersElement.shadowRoot.querySelector(
+              '#show-more-container'));
 
           // Simulate removing 'google2' printer.
           removeSavedPrinter('id4');
@@ -803,7 +847,8 @@
             createPrinterListEntry('test1', '1', 'id1', PrinterType.SAVED),
             createPrinterListEntry('test2', '2', 'id2', PrinterType.SAVED)
           ]);
-          assertFalse(!!savedPrintersElement.$$('#show-more-container'));
+          assertFalse(!!savedPrintersElement.shadowRoot.querySelector(
+              '#show-more-container'));
         });
   });
 
@@ -821,11 +866,13 @@
           // Wait for saved printers to populate.
           flush();
 
-          savedPrintersElement = page.$$('settings-cups-saved-printers');
+          savedPrintersElement =
+              page.shadowRoot.querySelector('settings-cups-saved-printers');
           assertTrue(!!savedPrintersElement);
 
           const printerEntryListTestElement =
-              savedPrintersElement.$$('#printerEntryList');
+              savedPrintersElement.shadowRoot.querySelector(
+                  '#printerEntryList');
 
           // There are 6 total printers but only 3 printers are visible and 3
           // are hidden underneath the Show more section.
@@ -836,7 +883,8 @@
           ]);
           // Assert that the Show more button is shown since printer list length
           // is > 3.
-          assertTrue(!!savedPrintersElement.$$('#show-more-container'));
+          assertTrue(!!savedPrintersElement.shadowRoot.querySelector(
+              '#show-more-container'));
 
           // Set search term to 'google' and expect 4 visible printers.
           let searchTerm = 'google';
@@ -854,7 +902,8 @@
               ],
               searchTerm);
           // Having a search term should hide the Show more button.
-          assertFalse(!!savedPrintersElement.$$('#show-more-container'));
+          assertFalse(!!savedPrintersElement.shadowRoot.querySelector(
+              '#show-more-container'));
 
           // Search for a term with no matching printers. Expect Show more
           // button to still be hidden.
@@ -863,7 +912,8 @@
           flush();
           verifySearchQueryResults(savedPrintersElement, [], searchTerm);
 
-          assertFalse(!!savedPrintersElement.$$('#show-more-container'));
+          assertFalse(!!savedPrintersElement.shadowRoot.querySelector(
+              '#show-more-container'));
 
           // Change search term and expect new set of visible printers.
           searchTerm = 'test';
@@ -876,7 +926,8 @@
                 createPrinterListEntry('test2', '2', 'id2', PrinterType.SAVED)
               ],
               searchTerm);
-          assertFalse(!!savedPrintersElement.$$('#show-more-container'));
+          assertFalse(!!savedPrintersElement.shadowRoot.querySelector(
+              '#show-more-container'));
 
           // Remove the search term and expect the collapsed list to appear
           // again.
@@ -892,7 +943,8 @@
               savedPrintersElement, expectedVisiblePrinters, searchTerm);
           verifyVisiblePrinters(
               printerEntryListTestElement, expectedVisiblePrinters);
-          assertTrue(!!savedPrintersElement.$$('#show-more-container'));
+          assertTrue(!!savedPrintersElement.shadowRoot.querySelector(
+              '#show-more-container'));
         });
   });
 
@@ -908,11 +960,13 @@
           // Wait for saved printers to populate.
           flush();
 
-          savedPrintersElement = page.$$('settings-cups-saved-printers');
+          savedPrintersElement =
+              page.shadowRoot.querySelector('settings-cups-saved-printers');
           assertTrue(!!savedPrintersElement);
 
           const printerEntryListTestElement =
-              savedPrintersElement.$$('#printerEntryList');
+              savedPrintersElement.shadowRoot.querySelector(
+                  '#printerEntryList');
 
           // There are 4 total printers but only 3 printers are visible and 1 is
           // hidden underneath the Show more section.
@@ -923,7 +977,8 @@
           ]);
           // Assert that the Show more button is shown since printer list length
           // is > 3.
-          assertTrue(!!savedPrintersElement.$$('#show-more-container'));
+          assertTrue(!!savedPrintersElement.shadowRoot.querySelector(
+              '#show-more-container'));
 
           // Add a new printer and expect it to be at the top of the list.
           addNewSavedPrinter(createCupsPrinterInfo('newPrinter', '5', 'id5'));
@@ -933,7 +988,8 @@
             createPrinterListEntry('google2', '4', 'id4', PrinterType.SAVED),
             createPrinterListEntry('test1', '1', 'id1', PrinterType.SAVED)
           ]);
-          assertTrue(!!savedPrintersElement.$$('#show-more-container'));
+          assertTrue(!!savedPrintersElement.shadowRoot.querySelector(
+              '#show-more-container'));
 
           // Now simulate removing printer 'test1'.
           removeSavedPrinter('id1');
@@ -946,7 +1002,8 @@
             createPrinterListEntry('google', '3', 'id3', PrinterType.SAVED),
             createPrinterListEntry('google2', '4', 'id4', PrinterType.SAVED)
           ]);
-          assertTrue(!!savedPrintersElement.$$('#show-more-container'));
+          assertTrue(!!savedPrintersElement.shadowRoot.querySelector(
+              '#show-more-container'));
 
           // Remove another printer and assert that we still have 3 visible
           // printers but now 'test2' is our third visible printer.
@@ -957,7 +1014,8 @@
             createPrinterListEntry('test2', '2', 'id2', PrinterType.SAVED)
           ]);
           // Printer list length is <= 3, Show more button should be hidden.
-          assertFalse(!!savedPrintersElement.$$('#show-more-container'));
+          assertFalse(!!savedPrintersElement.shadowRoot.querySelector(
+              '#show-more-container'));
         });
   });
 });
@@ -980,7 +1038,8 @@
     const mojom = chromeos.networkConfig.mojom;
     cupsPrintersBrowserProxy = new TestCupsPrintersBrowserProxy();
 
-    CupsPrintersBrowserProxyImpl.instance_ = cupsPrintersBrowserProxy;
+    CupsPrintersBrowserProxyImpl.setInstanceForTesting(
+        cupsPrintersBrowserProxy);
 
     // Simulate internet connection.
     wifi1 = OncMojo.getDefaultNetworkState(mojom.NetworkType.kWiFi, 'wifi1');
@@ -1015,7 +1074,8 @@
     ];
 
     return flushTasks().then(() => {
-      nearbyPrintersElement = page.$$('settings-cups-nearby-printers');
+      nearbyPrintersElement =
+          page.shadowRoot.querySelector('settings-cups-nearby-printers');
       assertTrue(!!nearbyPrintersElement);
 
       // Assert that no printers have been detected.
@@ -1064,7 +1124,8 @@
     ];
 
     return flushTasks().then(() => {
-      nearbyPrintersElement = page.$$('settings-cups-nearby-printers');
+      nearbyPrintersElement =
+          page.shadowRoot.querySelector('settings-cups-nearby-printers');
       assertTrue(!!nearbyPrintersElement);
 
       // Simuluate finding nearby printers.
@@ -1088,7 +1149,8 @@
 
     return flushTasks()
         .then(() => {
-          nearbyPrintersElement = page.$$('settings-cups-nearby-printers');
+          nearbyPrintersElement =
+              page.shadowRoot.querySelector('settings-cups-nearby-printers');
           assertTrue(!!nearbyPrintersElement);
 
           // Simuluate finding nearby printers.
@@ -1102,11 +1164,13 @@
           // printer has the correct button.
           const nearbyPrinterEntries = getPrinterEntries(nearbyPrintersElement);
           assertEquals(1, nearbyPrinterEntries.length);
-          assertTrue(!!nearbyPrinterEntries[0].$$('.save-printer-button'));
+          assertTrue(!!nearbyPrinterEntries[0].shadowRoot.querySelector(
+              '.save-printer-button'));
 
           // Add an automatic printer and assert that that the toast
           // notification is shown.
-          addButton = nearbyPrinterEntries[0].$$('.save-printer-button');
+          addButton = nearbyPrinterEntries[0].shadowRoot.querySelector(
+              '.save-printer-button');
           clickButton(addButton);
           // Add button should be disabled during setup.
           assertTrue(addButton.disabled);
@@ -1117,7 +1181,9 @@
           assertFalse(addButton.disabled);
           const expectedToastMessage =
               'Added ' + automaticPrinterList[0].printerName;
-          verifyErrorToastMessage(expectedToastMessage, page.$$('#errorToast'));
+          verifyErrorToastMessage(
+              expectedToastMessage,
+              page.shadowRoot.querySelector('#errorToast'));
         });
   });
 
@@ -1128,7 +1194,8 @@
       createCupsPrinterInfo('third', '2', 'id5'),
     ];
     return flushTasks().then(async () => {
-      nearbyPrintersElement = page.$$('settings-cups-nearby-printers');
+      nearbyPrintersElement =
+          page.shadowRoot.querySelector('settings-cups-nearby-printers');
 
       // Block so that FocusRowBehavior.attached can run.
       await waitAfterNextRender(nearbyPrintersElement);
@@ -1143,33 +1210,41 @@
       // executed.
       await waitAfterNextRender(nearbyPrintersElement);
       const nearbyPrinterEntries = getPrinterEntries(nearbyPrintersElement);
-      const printerEntryList = nearbyPrintersElement.$$('#printerEntryList');
+      const printerEntryList =
+          nearbyPrintersElement.shadowRoot.querySelector('#printerEntryList');
 
-      nearbyPrinterEntries[0].$$('#entry').focus();
+      nearbyPrinterEntries[0].shadowRoot.querySelector('#entry').focus();
       assertEquals(
-          nearbyPrinterEntries[0].$$('#entry'), getDeepActiveElement());
+          nearbyPrinterEntries[0].shadowRoot.querySelector('#entry'),
+          getDeepActiveElement());
       // Ensure that we can navigate through items in a row
       getDeepActiveElement().dispatchEvent(arrowRightEvent);
       assertEquals(
-          nearbyPrinterEntries[0].$$('#setupPrinterButton'),
+          nearbyPrinterEntries[0].shadowRoot.querySelector(
+              '#setupPrinterButton'),
           getDeepActiveElement());
       getDeepActiveElement().dispatchEvent(arrowLeftEvent);
       assertEquals(
-          nearbyPrinterEntries[0].$$('#entry'), getDeepActiveElement());
+          nearbyPrinterEntries[0].shadowRoot.querySelector('#entry'),
+          getDeepActiveElement());
 
       // Ensure that we can navigate through printer rows
       printerEntryList.dispatchEvent(arrowDownEvent);
       assertEquals(
-          nearbyPrinterEntries[1].$$('#entry'), getDeepActiveElement());
+          nearbyPrinterEntries[1].shadowRoot.querySelector('#entry'),
+          getDeepActiveElement());
       printerEntryList.dispatchEvent(arrowDownEvent);
       assertEquals(
-          nearbyPrinterEntries[2].$$('#entry'), getDeepActiveElement());
+          nearbyPrinterEntries[2].shadowRoot.querySelector('#entry'),
+          getDeepActiveElement());
       printerEntryList.dispatchEvent(arrowUpEvent);
       assertEquals(
-          nearbyPrinterEntries[1].$$('#entry'), getDeepActiveElement());
+          nearbyPrinterEntries[1].shadowRoot.querySelector('#entry'),
+          getDeepActiveElement());
       printerEntryList.dispatchEvent(arrowUpEvent);
       assertEquals(
-          nearbyPrinterEntries[0].$$('#entry'), getDeepActiveElement());
+          nearbyPrinterEntries[0].shadowRoot.querySelector('#entry'),
+          getDeepActiveElement());
     });
   });
 
@@ -1182,7 +1257,8 @@
 
     return flushTasks()
         .then(() => {
-          nearbyPrintersElement = page.$$('settings-cups-nearby-printers');
+          nearbyPrintersElement =
+              page.shadowRoot.querySelector('settings-cups-nearby-printers');
           assertTrue(!!nearbyPrintersElement);
 
           // Simuluate finding nearby printers.
@@ -1196,7 +1272,8 @@
           // the correct icon button.
           const nearbyPrinterEntries = getPrinterEntries(nearbyPrintersElement);
           assertEquals(1, nearbyPrinterEntries.length);
-          assertTrue(!!nearbyPrinterEntries[0].$$('#setupPrinterButton'));
+          assertTrue(!!nearbyPrinterEntries[0].shadowRoot.querySelector(
+              '#setupPrinterButton'));
 
           // Force a failure with adding a discovered printer.
           cupsPrintersBrowserProxy.setAddDiscoveredPrinterFailure(
@@ -1204,7 +1281,8 @@
 
           // Assert that clicking on the setup button shows the advanced
           // configuration dialog.
-          setupButton = nearbyPrinterEntries[0].$$('#setupPrinterButton');
+          setupButton = nearbyPrinterEntries[0].shadowRoot.querySelector(
+              '#setupPrinterButton');
           clickButton(setupButton);
           // Setup button should be disabled during setup.
           assertTrue(setupButton.disabled);
@@ -1215,22 +1293,25 @@
           assertFalse(setupButton.disabled);
 
           flush();
-          const addDialog = page.$$('#addPrinterDialog');
-          manufacturerDialog =
-              addDialog.$$('add-printer-manufacturer-model-dialog');
+          const addDialog = page.shadowRoot.querySelector('#addPrinterDialog');
+          manufacturerDialog = addDialog.shadowRoot.querySelector(
+              'add-printer-manufacturer-model-dialog');
           assertTrue(!!manufacturerDialog);
 
           return cupsPrintersBrowserProxy.whenCalled(
               'getCupsPrinterManufacturersList');
         })
         .then(() => {
-          const addButton = manufacturerDialog.$$('#addPrinterButton');
+          const addButton =
+              manufacturerDialog.shadowRoot.querySelector('#addPrinterButton');
           assertTrue(addButton.disabled);
 
           // Populate the manufacturer and model fields to enable the Add
           // button.
-          manufacturerDialog.$$('#manufacturerDropdown').value = 'make';
-          const modelDropdown = manufacturerDialog.$$('#modelDropdown');
+          manufacturerDialog.shadowRoot.querySelector('#manufacturerDropdown')
+              .value = 'make';
+          const modelDropdown =
+              manufacturerDialog.shadowRoot.querySelector('#modelDropdown');
           modelDropdown.value = 'model';
 
           clickButton(addButton);
@@ -1241,7 +1322,9 @@
           // message when adding a discovered printer.
           const expectedToastMessage =
               'Added ' + discoveredPrinterList[0].printerName;
-          verifyErrorToastMessage(expectedToastMessage, page.$$('#errorToast'));
+          verifyErrorToastMessage(
+              expectedToastMessage,
+              page.shadowRoot.querySelector('#errorToast'));
         });
   });
 
@@ -1256,9 +1339,10 @@
       // We require internet to be able to add a new printer. Connecting to
       // a network without connectivity should be equivalent to not being
       // connected to a network.
-      assertTrue(!!page.$$('#cloudOffIcon'));
-      assertTrue(!!page.$$('#connectionMessage'));
-      assertTrue(!!page.$$('#addManualPrinterIcon').disabled);
+      assertTrue(!!page.shadowRoot.querySelector('#cloudOffIcon'));
+      assertTrue(!!page.shadowRoot.querySelector('#connectionMessage'));
+      assertTrue(
+          !!page.shadowRoot.querySelector('#addManualPrinterIcon').disabled);
     });
   });
 
@@ -1273,9 +1357,10 @@
         .then(() => {
           // Expect offline text to show up when no internet is
           // connected.
-          assertTrue(!!page.$$('#cloudOffIcon'));
-          assertTrue(!!page.$$('#connectionMessage'));
-          assertTrue(!!page.$$('#addManualPrinterIcon').disabled);
+          assertTrue(!!page.shadowRoot.querySelector('#cloudOffIcon'));
+          assertTrue(!!page.shadowRoot.querySelector('#connectionMessage'));
+          assertTrue(!!page.shadowRoot.querySelector('#addManualPrinterIcon')
+                           .disabled);
 
           // Simulate connecting to a network with connectivity.
           wifi1.connectionState =
@@ -1302,11 +1387,13 @@
 
           flush();
 
-          nearbyPrintersElement = page.$$('settings-cups-nearby-printers');
+          nearbyPrintersElement =
+              page.shadowRoot.querySelector('settings-cups-nearby-printers');
           assertTrue(!!nearbyPrintersElement);
 
           printerEntryListTestElement =
-              nearbyPrintersElement.$$('#printerEntryList');
+              nearbyPrintersElement.shadowRoot.querySelector(
+                  '#printerEntryList');
           assertTrue(!!printerEntryListTestElement);
 
           const nearbyPrinterEntries = getPrinterEntries(nearbyPrintersElement);
@@ -1325,11 +1412,12 @@
     ];
 
     return flushTasks().then(() => {
-      nearbyPrintersElement = page.$$('settings-cups-nearby-printers');
+      nearbyPrintersElement =
+          page.shadowRoot.querySelector('settings-cups-nearby-printers');
       assertTrue(!!nearbyPrintersElement);
 
       printerEntryListTestElement =
-          nearbyPrintersElement.$$('#printerEntryList');
+          nearbyPrintersElement.shadowRoot.querySelector('#printerEntryList');
       assertTrue(!!printerEntryListTestElement);
 
       // Simuluate finding nearby printers.
@@ -1411,11 +1499,12 @@
     ];
 
     return flushTasks().then(() => {
-      nearbyPrintersElement = page.$$('settings-cups-nearby-printers');
+      nearbyPrintersElement =
+          page.shadowRoot.querySelector('settings-cups-nearby-printers');
       assertTrue(!!nearbyPrintersElement);
 
       printerEntryListTestElement =
-          nearbyPrintersElement.$$('#printerEntryList');
+          nearbyPrintersElement.shadowRoot.querySelector('#printerEntryList');
       assertTrue(!!printerEntryListTestElement);
 
       // Simuluate finding nearby printers.
@@ -1492,7 +1581,8 @@
     // |cupsPrinterBrowserProxy| needs to have a list of printers before
     // initializing the landing page.
     cupsPrintersBrowserProxy.printerList = {printerList: printerList};
-    CupsPrintersBrowserProxyImpl.instance_ = cupsPrintersBrowserProxy;
+    CupsPrintersBrowserProxyImpl.setInstanceForTesting(
+        cupsPrintersBrowserProxy);
 
     page = document.createElement('settings-cups-printers');
     document.body.appendChild(page);
@@ -1513,10 +1603,11 @@
           // Wait for saved printers to populate.
           flush();
 
-          enterprisePrintersElement =
-              page.$$('settings-cups-enterprise-printers');
+          enterprisePrintersElement = page.shadowRoot.querySelector(
+              'settings-cups-enterprise-printers');
           printerEntryListTestElement =
-              enterprisePrintersElement.$$('#printerEntryList');
+              enterprisePrintersElement.shadowRoot.querySelector(
+                  '#printerEntryList');
           verifyVisiblePrinters(printerEntryListTestElement, [
             createPrinterListEntry('test1', '1', 'id1', PrinterType.ENTERPRISE),
             createPrinterListEntry('test2', '2', 'id2', PrinterType.ENTERPRISE),
@@ -1537,43 +1628,57 @@
           // Wait for enterprise printers to populate.
           flush();
 
-          enterprisePrintersElement =
-              page.$$('settings-cups-enterprise-printers');
+          enterprisePrintersElement = page.shadowRoot.querySelector(
+              'settings-cups-enterprise-printers');
           assertTrue(!!enterprisePrintersElement);
 
           const enterprisePrinterEntries =
               getPrinterEntries(enterprisePrintersElement);
 
           // Users are not allowed to remove enterprise printers.
-          const removeButton = enterprisePrintersElement.$$('#removeButton');
+          const removeButton =
+              enterprisePrintersElement.shadowRoot.querySelector(
+                  '#removeButton');
           assertTrue(removeButton.disabled);
 
-          clickButton(enterprisePrinterEntries[0].$$('.icon-more-vert'));
-          clickButton(enterprisePrintersElement.$$('#viewButton'));
+          clickButton(enterprisePrinterEntries[0].shadowRoot.querySelector(
+              '.icon-more-vert'));
+          clickButton(enterprisePrintersElement.shadowRoot.querySelector(
+              '#viewButton'));
 
           flush();
 
           editDialog = initializeEditDialog(page);
 
-          const nameField = editDialog.$$('.printer-name-input');
+          const nameField =
+              editDialog.shadowRoot.querySelector('.printer-name-input');
           assertTrue(!!nameField);
           assertEquals('test1', nameField.value);
           assertTrue(nameField.readonly);
 
-          assertTrue(editDialog.$$('#printerAddress').readonly);
-          assertTrue(editDialog.$$('.md-select').disabled);
-          assertTrue(editDialog.$$('#printerQueue').readonly);
-          assertTrue(editDialog.$$('#printerPPDManufacturer').readonly);
+          assertTrue(
+              editDialog.shadowRoot.querySelector('#printerAddress').readonly);
+          assertTrue(
+              editDialog.shadowRoot.querySelector('.md-select').disabled);
+          assertTrue(
+              editDialog.shadowRoot.querySelector('#printerQueue').readonly);
+          assertTrue(
+              editDialog.shadowRoot.querySelector('#printerPPDManufacturer')
+                  .readonly);
 
           // The "specify PDD" section should be hidden.
-          assertTrue(editDialog.$$('.browse-button').parentElement.hidden);
-          assertTrue(editDialog.$$('#ppdLabel').hidden);
+          assertTrue(editDialog.shadowRoot.querySelector('.browse-button')
+                         .parentElement.hidden);
+          assertTrue(editDialog.shadowRoot.querySelector('#ppdLabel').hidden);
 
           // Save and Cancel buttons should be hidden. Close button should be
           // visible.
-          assertTrue(editDialog.$$('.cancel-button').hidden);
-          assertTrue(editDialog.$$('.action-button').hidden);
-          assertFalse(editDialog.$$('.close-button').hidden);
+          assertTrue(
+              editDialog.shadowRoot.querySelector('.cancel-button').hidden);
+          assertTrue(
+              editDialog.shadowRoot.querySelector('.action-button').hidden);
+          assertFalse(
+              editDialog.shadowRoot.querySelector('.close-button').hidden);
         });
   });
 
@@ -1589,12 +1694,13 @@
           // Wait for enterprise printers to populate.
           flush();
 
-          enterprisePrintersElement =
-              page.$$('settings-cups-enterprise-printers');
+          enterprisePrintersElement = page.shadowRoot.querySelector(
+              'settings-cups-enterprise-printers');
           assertTrue(!!enterprisePrintersElement);
 
           const printerEntryListTestElement =
-              enterprisePrintersElement.$$('#printerEntryList');
+              enterprisePrintersElement.shadowRoot.querySelector(
+                  '#printerEntryList');
 
           // There are 4 total printers but only 3 printers are visible and 1 is
           // hidden underneath the Show more section.
@@ -1605,11 +1711,14 @@
           ]);
           // Assert that the Show more button is shown since printer list length
           // is > 3.
-          assertTrue(!!enterprisePrintersElement.$$('#show-more-container'));
+          assertTrue(!!enterprisePrintersElement.shadowRoot.querySelector(
+              '#show-more-container'));
 
           // Click on the Show more button.
-          clickButton(enterprisePrintersElement.$$('#show-more-icon'));
-          assertFalse(!!enterprisePrintersElement.$$('#show-more-container'));
+          clickButton(enterprisePrintersElement.shadowRoot.querySelector(
+              '#show-more-icon'));
+          assertFalse(!!enterprisePrintersElement.shadowRoot.querySelector(
+              '#show-more-container'));
           // Clicking on the Show more button reveals all hidden printers.
           verifyVisiblePrinters(printerEntryListTestElement, [
             createPrinterListEntry('test1', '1', 'id1', PrinterType.ENTERPRISE),
diff --git a/chrome/test/data/webui/settings/chromeos/cups_printer_page_tests.js b/chrome/test/data/webui/settings/chromeos/cups_printer_page_tests.js
index 53133ce..b75225b 100644
--- a/chrome/test/data/webui/settings/chromeos/cups_printer_page_tests.js
+++ b/chrome/test/data/webui/settings/chromeos/cups_printer_page_tests.js
@@ -44,8 +44,8 @@
 
 suite('CupsAddPrinterDialogTests', function() {
   function fillAddManuallyDialog(addDialog) {
-    const name = addDialog.$$('#printerNameInput');
-    const address = addDialog.$$('#printerAddressInput');
+    const name = addDialog.shadowRoot.querySelector('#printerNameInput');
+    const address = addDialog.shadowRoot.querySelector('#printerAddressInput');
 
     assertTrue(!!name);
     name.value = 'Test printer';
@@ -53,21 +53,21 @@
     assertTrue(!!address);
     address.value = '127.0.0.1';
 
-    const addButton = addDialog.$$('#addPrinterButton');
+    const addButton = addDialog.shadowRoot.querySelector('#addPrinterButton');
     assertTrue(!!addButton);
     assertFalse(addButton.disabled);
   }
 
   function clickAddButton(dialog) {
     assertTrue(!!dialog, 'Dialog is null for add');
-    const addButton = dialog.$$('.action-button');
+    const addButton = dialog.shadowRoot.querySelector('.action-button');
     assertTrue(!!addButton, 'Button is null');
     addButton.click();
   }
 
   function clickCancelButton(dialog) {
     assertTrue(!!dialog, 'Dialog is null for cancel');
-    const cancelButton = dialog.$$('.cancel-button');
+    const cancelButton = dialog.shadowRoot.querySelector('.cancel-button');
     assertTrue(!!cancelButton, 'Button is null');
     cancelButton.click();
   }
@@ -80,16 +80,18 @@
 
   function mockAddPrinterInputKeyboardPress(crInputId) {
     // Start in add manual dialog.
-    const addDialog = dialog.$$('add-printer-manually-dialog');
+    const addDialog =
+        dialog.shadowRoot.querySelector('add-printer-manually-dialog');
     assertTrue(!!addDialog);
 
     // Test that pressing Enter before all the fields are populated does not
     // advance to the next dialog.
-    const input = addDialog.$$(crInputId);
+    const input = addDialog.shadowRoot.querySelector(crInputId);
     keyEventOn(input, 'keypress', /*keycode=*/ 13, [], 'Enter');
     flush();
 
-    assertFalse(!!dialog.$$('add-printer-manufacturer-model-dialog'));
+    assertFalse(!!dialog.shadowRoot.querySelector(
+        'add-printer-manufacturer-model-dialog'));
     assertFalse(dialog.showManufacturerDialog_);
     assertTrue(dialog.showManuallyAddDialog_);
 
@@ -101,7 +103,8 @@
     keyEventOn(input, 'keypress', /*keycode=*/ 16, [], 'Shift');
     flush();
 
-    assertFalse(!!dialog.$$('add-printer-manufacturer-model-dialog'));
+    assertFalse(!!dialog.shadowRoot.querySelector(
+        'add-printer-manufacturer-model-dialog'));
     assertFalse(dialog.showManufacturerDialog_);
     assertTrue(dialog.showManuallyAddDialog_);
 
@@ -118,7 +121,8 @@
 
   setup(function() {
     cupsPrintersBrowserProxy = new TestCupsPrintersBrowserProxy();
-    CupsPrintersBrowserProxyImpl.instance_ = cupsPrintersBrowserProxy;
+    CupsPrintersBrowserProxyImpl.setInstanceForTesting(
+        cupsPrintersBrowserProxy);
 
     PolymerTest.clearBody();
     page = document.createElement('settings-cups-printers');
@@ -126,7 +130,7 @@
     page.enableUpdatedUi_ = false;
     document.body.appendChild(page);
     assertTrue(!!page);
-    dialog = page.$$('settings-cups-add-printer-dialog');
+    dialog = page.shadowRoot.querySelector('settings-cups-add-printer-dialog');
     assertTrue(!!dialog);
 
     dialog.open();
@@ -202,12 +206,13 @@
    */
   test('ValidAddOpensModelSelection', function() {
     // Starts in add manual dialog.
-    const addDialog = dialog.$$('add-printer-manually-dialog');
+    const addDialog =
+        dialog.shadowRoot.querySelector('add-printer-manually-dialog');
     assertTrue(!!addDialog);
     flush();
     fillAddManuallyDialog(addDialog);
 
-    addDialog.$$('.action-button').click();
+    addDialog.shadowRoot.querySelector('.action-button').click();
     flush();
 
     // Upon rejection, show model.
@@ -218,7 +223,8 @@
         })
         .then(function() {
           // Showing model selection.
-          assertTrue(!!dialog.$$('add-printer-manufacturer-model-dialog'));
+          assertTrue(!!dialog.shadowRoot.querySelector(
+              'add-printer-manufacturer-model-dialog'));
 
           assertTrue(dialog.showManufacturerDialog_);
           assertFalse(dialog.showManuallyAddDialog_);
@@ -231,7 +237,8 @@
    */
   test('GetPrinterInfoFailsGeneralError', function() {
     // Starts in add manual dialog.
-    const addDialog = dialog.$$('add-printer-manually-dialog');
+    const addDialog =
+        dialog.shadowRoot.querySelector('add-printer-manually-dialog');
     assertTrue(!!addDialog);
     flush();
 
@@ -242,7 +249,7 @@
         PrinterSetupResult.FATAL_ERROR);
 
     // Attempt to add the printer.
-    addDialog.$$('.action-button').click();
+    addDialog.shadowRoot.querySelector('.action-button').click();
     flush();
 
     // Upon rejection, show model.
@@ -250,8 +257,11 @@
         .then(function(result) {
           // The general error should be showing.
           assertTrue(!!addDialog.errorText_);
-          const generalErrorElement = addDialog.$$('printer-dialog-error');
-          assertFalse(generalErrorElement.$$('#error-container').hidden);
+          const generalErrorElement =
+              addDialog.shadowRoot.querySelector('printer-dialog-error');
+          assertFalse(
+              generalErrorElement.shadowRoot.querySelector('#error-container')
+                  .hidden);
         });
   });
 
@@ -262,7 +272,8 @@
    */
   test('GetPrinterInfoFailsUnreachableError', function() {
     // Starts in add manual dialog.
-    const addDialog = dialog.$$('add-printer-manually-dialog');
+    const addDialog =
+        dialog.shadowRoot.querySelector('add-printer-manually-dialog');
     assertTrue(!!addDialog);
     flush();
 
@@ -273,14 +284,15 @@
         PrinterSetupResult.PRINTER_UNREACHABLE);
 
     // Attempt to add the printer.
-    addDialog.$$('.action-button').click();
+    addDialog.shadowRoot.querySelector('.action-button').click();
     flush();
 
     // Upon rejection, show model.
     return cupsPrintersBrowserProxy.whenCalled('getPrinterInfo')
         .then(function(result) {
           // The printer address input should be marked as invalid.
-          assertTrue(addDialog.$$('#printerAddressInput').invalid);
+          assertTrue(addDialog.shadowRoot.querySelector('#printerAddressInput')
+                         .invalid);
         });
   });
 
@@ -290,7 +302,8 @@
    */
   test('NoBlankQueries', function() {
     // Starts in add manual dialog.
-    const addDialog = dialog.$$('add-printer-manually-dialog');
+    const addDialog =
+        dialog.shadowRoot.querySelector('add-printer-manually-dialog');
     assertTrue(!!addDialog);
     flush();
     fillAddManuallyDialog(addDialog);
@@ -304,14 +317,14 @@
 
     cupsPrintersBrowserProxy.manufacturers =
         ['ManufacturerA', 'ManufacturerB', 'Chromites'];
-    addDialog.$$('.action-button').click();
+    addDialog.shadowRoot.querySelector('.action-button').click();
     flush();
 
     return cupsPrintersBrowserProxy
         .whenCalled('getCupsPrinterManufacturersList')
         .then(function() {
-          const modelDialog =
-              dialog.$$('add-printer-manufacturer-model-dialog');
+          const modelDialog = dialog.shadowRoot.querySelector(
+              'add-printer-manufacturer-model-dialog');
           assertTrue(!!modelDialog);
           // Manufacturer dialog has been rendered and the model list was not
           // requested.  We're done.
@@ -358,7 +371,8 @@
     };
 
     // Press the add button to advance dialog.
-    const addDialog = dialog.$$('add-printer-manually-dialog');
+    const addDialog =
+        dialog.shadowRoot.querySelector('add-printer-manually-dialog');
     assertTrue(!!addDialog);
     flush();
     clickAddButton(addDialog);
@@ -370,7 +384,8 @@
         .then(function() {
           flush();
           // Cancel setup with the cancel button.
-          clickCancelButton(dialog.$$('add-printer-manufacturer-model-dialog'));
+          clickCancelButton(dialog.shadowRoot.querySelector(
+              'add-printer-manufacturer-model-dialog'));
           return cupsPrintersBrowserProxy.whenCalled('cancelPrinterSetUp');
         })
         .then(function(printer) {
@@ -385,12 +400,13 @@
    */
   test('getEulaUrlGetsCalledOnModelChange', function() {
     // Start in add manual dialog.
-    const addDialog = dialog.$$('add-printer-manually-dialog');
+    const addDialog =
+        dialog.shadowRoot.querySelector('add-printer-manually-dialog');
     assertTrue(!!addDialog);
     flush();
     fillAddManuallyDialog(addDialog);
 
-    addDialog.$$('.action-button').click();
+    addDialog.shadowRoot.querySelector('.action-button').click();
     flush();
 
     const expectedEulaLink = 'chrome://os-credits/#google';
@@ -406,17 +422,20 @@
     return cupsPrintersBrowserProxy
         .whenCalled('getCupsPrinterManufacturersList')
         .then(function() {
-          modelDialog = dialog.$$('add-printer-manufacturer-model-dialog');
+          modelDialog = dialog.shadowRoot.querySelector(
+              'add-printer-manufacturer-model-dialog');
           assertTrue(!!modelDialog);
 
-          urlElement = modelDialog.$$('#eulaUrl');
+          urlElement = modelDialog.shadowRoot.querySelector('#eulaUrl');
           // Check that the EULA text is not shown.
           assertTrue(urlElement.hidden);
 
           cupsPrintersBrowserProxy.setEulaUrl(expectedEulaLink);
 
-          modelDialog.$$('#manufacturerDropdown').value = expectedManufacturer;
-          modelDropdown = modelDialog.$$('#modelDropdown');
+          modelDialog.shadowRoot.querySelector('#manufacturerDropdown').value =
+              expectedManufacturer;
+          modelDropdown =
+              modelDialog.shadowRoot.querySelector('#modelDropdown');
           modelDropdown.value = expectedModel;
           return verifyGetEulaUrlWasCalled(
               cupsPrintersBrowserProxy, expectedManufacturer, expectedModel);
@@ -457,7 +476,8 @@
   test('AddButtonDisabledAfterClicking', function() {
     // From the add manually dialog, click the add button to advance to the
     // manufacturer dialog.
-    const addDialog = dialog.$$('add-printer-manually-dialog');
+    const addDialog =
+        dialog.shadowRoot.querySelector('add-printer-manually-dialog');
     assertTrue(!!addDialog);
     flush();
     fillAddManuallyDialog(addDialog);
@@ -469,16 +489,19 @@
     return cupsPrintersBrowserProxy
         .whenCalled('getCupsPrinterManufacturersList')
         .then(function() {
-          const manufacturerDialog =
-              dialog.$$('add-printer-manufacturer-model-dialog');
+          const manufacturerDialog = dialog.shadowRoot.querySelector(
+              'add-printer-manufacturer-model-dialog');
           assertTrue(!!manufacturerDialog);
 
           // Populate the manufacturer and model fields to enable the add
           // button.
-          manufacturerDialog.$$('#manufacturerDropdown').value = 'make';
-          manufacturerDialog.$$('#modelDropdown').value = 'model';
+          manufacturerDialog.shadowRoot.querySelector('#manufacturerDropdown')
+              .value = 'make';
+          manufacturerDialog.shadowRoot.querySelector('#modelDropdown').value =
+              'model';
 
-          const addButton = manufacturerDialog.$$('#addPrinterButton');
+          const addButton =
+              manufacturerDialog.shadowRoot.querySelector('#addPrinterButton');
           assertTrue(!!addButton);
           assertFalse(addButton.disabled);
           addButton.click();
@@ -503,7 +526,8 @@
         })
         .then(function() {
           // Showing model selection.
-          assertTrue(!!dialog.$$('add-printer-manufacturer-model-dialog'));
+          assertTrue(!!dialog.shadowRoot.querySelector(
+              'add-printer-manufacturer-model-dialog'));
           assertTrue(dialog.showManufacturerDialog_);
           assertFalse(dialog.showManuallyAddDialog_);
         });
@@ -520,7 +544,8 @@
         })
         .then(function() {
           // Showing model selection.
-          assertFalse(!!dialog.$$('add-printer-configuring-dialog'));
+          assertFalse(!!dialog.shadowRoot.querySelector(
+              'add-printer-configuring-dialog'));
           assertTrue(dialog.showManufacturerDialog_);
           assertFalse(dialog.showManuallyAddDialog_);
         });
@@ -537,7 +562,8 @@
         })
         .then(function() {
           // Showing model selection.
-          assertTrue(!!dialog.$$('add-printer-manufacturer-model-dialog'));
+          assertTrue(!!dialog.shadowRoot.querySelector(
+              'add-printer-manufacturer-model-dialog'));
           assertTrue(dialog.showManufacturerDialog_);
           assertFalse(dialog.showManuallyAddDialog_);
         });
@@ -550,7 +576,8 @@
   test('AddButtonDisabledAfterClicking', function() {
     // From the add manually dialog, click the add button to advance to the
     // manufacturer dialog.
-    const addDialog = dialog.$$('add-printer-manually-dialog');
+    const addDialog =
+        dialog.shadowRoot.querySelector('add-printer-manually-dialog');
     assertTrue(!!addDialog);
     flush();
     fillAddManuallyDialog(addDialog);
@@ -560,15 +587,17 @@
     return cupsPrintersBrowserProxy
         .whenCalled('getCupsPrinterManufacturersList')
         .then(function() {
-          const manufacturerDialog =
-              dialog.$$('add-printer-manufacturer-model-dialog');
+          const manufacturerDialog = dialog.shadowRoot.querySelector(
+              'add-printer-manufacturer-model-dialog');
           assertTrue(!!manufacturerDialog);
 
           const manufacturerDropdown =
-              manufacturerDialog.$$('#manufacturerDropdown');
+              manufacturerDialog.shadowRoot.querySelector(
+                  '#manufacturerDropdown');
           const modelDropdown =
-              manufacturerDialog.$$('#modelDropdown');
-          const addButton = manufacturerDialog.$$('#addPrinterButton');
+              manufacturerDialog.shadowRoot.querySelector('#modelDropdown');
+          const addButton =
+              manufacturerDialog.shadowRoot.querySelector('#addPrinterButton');
 
           // Set the starting values for manufacturer and model dropdown.
           manufacturerDropdown.value = 'make';
@@ -577,33 +606,40 @@
 
           // Mimic typing in random input. Make sure the Add button becomes
           // disabled.
-          manufacturerDropdown.$$('#search').value = 'hlrRkJQkNsh';
-          manufacturerDropdown.$$('#search').fire('input');
+          manufacturerDropdown.shadowRoot.querySelector('#search').value =
+              'hlrRkJQkNsh';
+          manufacturerDropdown.shadowRoot.querySelector('#search').fire(
+              'input');
           assertTrue(addButton.disabled);
 
           // Then mimic typing in the original value to re-enable the Add
           // button.
-          manufacturerDropdown.$$('#search').value = 'make';
-          manufacturerDropdown.$$('#search').fire('input');
+          manufacturerDropdown.shadowRoot.querySelector('#search').value =
+              'make';
+          manufacturerDropdown.shadowRoot.querySelector('#search').fire(
+              'input');
           assertFalse(addButton.disabled);
 
           // Mimic typing in random input. Make sure the Add button becomes
           // disabled.
-          modelDropdown.$$('#search').value = 'hlrRkJQkNsh';
-          modelDropdown.$$('#search').fire('input');
+          modelDropdown.shadowRoot.querySelector('#search').value =
+              'hlrRkJQkNsh';
+          modelDropdown.shadowRoot.querySelector('#search').fire('input');
           assertTrue(addButton.disabled);
 
           // Then mimic typing in the original value to re-enable the Add
           // button.
-          modelDropdown.$$('#search').value = 'model';
-          modelDropdown.$$('#search').fire('input');
+          modelDropdown.shadowRoot.querySelector('#search').value = 'model';
+          modelDropdown.shadowRoot.querySelector('#search').fire('input');
           assertFalse(addButton.disabled);
         });
   });
 
   test('Queue input is hidden when protocol is App Socket', () => {
-    const addDialog = dialog.$$('add-printer-manually-dialog');
-    let printerQueueInput = addDialog.$$('#printerQueueInput');
+    const addDialog =
+        dialog.shadowRoot.querySelector('add-printer-manually-dialog');
+    let printerQueueInput =
+        addDialog.shadowRoot.querySelector('#printerQueueInput');
     const select = addDialog.shadowRoot.querySelector('select');
     assertTrue(!!printerQueueInput);
 
@@ -611,14 +647,16 @@
     select.dispatchEvent(new CustomEvent('change'), {'bubbles': true});
     flush();
 
-    printerQueueInput = addDialog.$$('#printerQueueInput');
+    printerQueueInput =
+        addDialog.shadowRoot.querySelector('#printerQueueInput');
     assertFalse(!!printerQueueInput);
 
     select.value = 'http';
     select.dispatchEvent(new CustomEvent('change'), {'bubbles': true});
     flush();
 
-    printerQueueInput = addDialog.$$('#printerQueueInput');
+    printerQueueInput =
+        addDialog.shadowRoot.querySelector('#printerQueueInput');
     assertTrue(!!printerQueueInput);
   });
 });
@@ -640,7 +678,8 @@
 
     cupsPrintersBrowserProxy = new TestCupsPrintersBrowserProxy();
 
-    CupsPrintersBrowserProxyImpl.instance_ = cupsPrintersBrowserProxy;
+    CupsPrintersBrowserProxyImpl.setInstanceForTesting(
+        cupsPrintersBrowserProxy);
 
     // Simulate internet connection.
     wifi1 = OncMojo.getDefaultNetworkState(mojom.NetworkType.kWiFi, 'wifi1');
@@ -669,7 +708,7 @@
    */
   function clickSaveButton(dialog) {
     assertTrue(!!dialog, 'Dialog is null for save');
-    const saveButton = dialog.$$('.action-button');
+    const saveButton = dialog.shadowRoot.querySelector('.action-button');
     dialog.printerInfoChanged_ = true;
     assertFalse(saveButton.disabled);
     assertTrue(!!saveButton, 'Button is null');
@@ -682,7 +721,7 @@
    */
   function clickCancelButton(dialog) {
     assertTrue(!!dialog, 'Dialog is null for cancel');
-    const cancelButton = dialog.$$('.cancel-button');
+    const cancelButton = dialog.shadowRoot.querySelector('.cancel-button');
     assertTrue(!!cancelButton, 'Button is null');
     cancelButton.click();
   }
@@ -713,7 +752,7 @@
     // Trigger the edit dialog to open.
     page.fire('edit-cups-printer-details');
     flush();
-    dialog = page.$$('settings-cups-edit-printer-dialog');
+    dialog = page.shadowRoot.querySelector('settings-cups-edit-printer-dialog');
     // This proxy function gets called whenever the edit dialog is initialized.
     return cupsPrintersBrowserProxy.whenCalled('getCupsPrinterModelsList');
   }
@@ -728,16 +767,18 @@
                /*model=*/ 'model', /*protocol=*/ 'usb', /*serverAddress=*/ '')
         .then(() => {
           // Assert that the protocol is USB.
-          assertEquals('usb', dialog.$$('.md-select').value);
+          assertEquals(
+              'usb', dialog.shadowRoot.querySelector('.md-select').value);
 
           // Edit the printer name.
-          const nameField = dialog.$$('.printer-name-input');
+          const nameField =
+              dialog.shadowRoot.querySelector('.printer-name-input');
           assertTrue(!!nameField);
           nameField.value = 'edited printer';
           nameField.fire('input');
 
           // Assert that the "Save" button is enabled.
-          const saveButton = dialog.$$('.action-button');
+          const saveButton = dialog.shadowRoot.querySelector('.action-button');
           assertTrue(!!saveButton);
           assertTrue(!saveButton.disabled);
         });
@@ -753,10 +794,10 @@
                /*autoconf=*/ false, /*manufacturer=*/ 'make',
                /*model=*/ 'model', /*protocol=*/ 'ipp', /*serverAddress=*/ '')
         .then(() => {
-          assertTrue(!!dialog.$$('#printerName'));
-          assertTrue(!!dialog.$$('#printerAddress'));
+          assertTrue(!!dialog.shadowRoot.querySelector('#printerName'));
+          assertTrue(!!dialog.shadowRoot.querySelector('#printerAddress'));
 
-          const saveButton = dialog.$$('.action-button');
+          const saveButton = dialog.shadowRoot.querySelector('.action-button');
           assertTrue(!!saveButton);
           assertTrue(saveButton.disabled);
 
@@ -794,15 +835,17 @@
                /*manufacturer=*/ 'make', /*model=*/ 'model',
                /*protocol=*/ expectedProtocol, /*serverAddress=*/ '')
         .then(() => {
-          const nameField = dialog.$$('.printer-name-input');
+          const nameField =
+              dialog.shadowRoot.querySelector('.printer-name-input');
           assertTrue(!!nameField);
           nameField.value = 'edited printer name';
 
-          const addressField = dialog.$$('#printerAddress');
+          const addressField =
+              dialog.shadowRoot.querySelector('#printerAddress');
           assertTrue(!!addressField);
           addressField.value = '9.9.9.9';
 
-          const protocolField = dialog.$$('.md-select');
+          const protocolField = dialog.shadowRoot.querySelector('.md-select');
           assertTrue(!!protocolField);
           protocolField.value = 'http';
 
@@ -825,7 +868,8 @@
                /*autoconf=*/ false, /*manufacturer=*/ 'make',
                /*model=*/ 'model', /*protocol=*/ 'ipp', /*serverAddress=*/ '')
         .then(() => {
-          const nameField = dialog.$$('.printer-name-input');
+          const nameField =
+              dialog.shadowRoot.querySelector('.printer-name-input');
           assertTrue(!!nameField);
           nameField.value = expectedName;
 
@@ -852,11 +896,12 @@
         .then(() => {
           // Editing more than just the printer name requires reconfiguring the
           // printer.
-          const addressField = dialog.$$('#printerAddress');
+          const addressField =
+              dialog.shadowRoot.querySelector('#printerAddress');
           assertTrue(!!addressField);
           addressField.value = expectedAddress;
 
-          const queueField = dialog.$$('#printerQueue');
+          const queueField = dialog.shadowRoot.querySelector('#printerQueue');
           assertTrue(!!queueField);
           queueField.value = expectedQueue;
 
@@ -882,11 +927,12 @@
         .then(() => {
           // Editing more than just the printer name requires reconfiguring the
           // printer.
-          const addressField = dialog.$$('#printerAddress');
+          const addressField =
+              dialog.shadowRoot.querySelector('#printerAddress');
           assertTrue(!!addressField);
           addressField.value = expectedAddress;
 
-          const queueField = dialog.$$('#printerQueue');
+          const queueField = dialog.shadowRoot.querySelector('#printerQueue');
           assertTrue(!!queueField);
           queueField.value = expectedQueue;
 
@@ -909,7 +955,8 @@
                /*model=*/ 'model', /*protocol=*/ 'ipp', /*serverAddress=*/ '')
         .then(() => {
           // Assert that the manufacturer and model drop-downs are shown.
-          assertFalse(dialog.$$('#makeAndModelSection').hidden);
+          assertFalse(
+              dialog.shadowRoot.querySelector('#makeAndModelSection').hidden);
         });
   });
 
@@ -923,7 +970,8 @@
                /*model=*/ 'model', /*protocol=*/ 'ipp', /*serverAddress=*/ '')
         .then(() => {
           // Assert that the manufacturer and model drop-downs are hidden.
-          assertTrue(!dialog.$$('#makeAndModelSection').if);
+          assertTrue(
+              !dialog.shadowRoot.querySelector('#makeAndModelSection').if);
         });
   });
 
@@ -936,11 +984,12 @@
                /*autoconf=*/ false, /*manufacturer=*/ 'make',
                /*model=*/ 'model', /*protocol=*/ 'ipp', /*serverAddress=*/ '')
         .then(() => {
-          const saveButton = dialog.$$('.action-button');
+          const saveButton = dialog.shadowRoot.querySelector('.action-button');
           assertTrue(!!saveButton);
           assertTrue(saveButton.disabled);
 
-          const nameField = dialog.$$('.printer-name-input');
+          const nameField =
+              dialog.shadowRoot.querySelector('.printer-name-input');
           assertTrue(!!nameField);
           nameField.value = 'edited printer';
           nameField.fire('input');
@@ -958,11 +1007,12 @@
                /*autoconf=*/ false, /*manufacturer=*/ 'make',
                /*model=*/ 'model', /*protocol=*/ 'ipp', /*serverAddress=*/ '')
         .then(() => {
-          const saveButton = dialog.$$('.action-button');
+          const saveButton = dialog.shadowRoot.querySelector('.action-button');
           assertTrue(!!saveButton);
           assertTrue(saveButton.disabled);
 
-          const addressField = dialog.$$('#printerAddress');
+          const addressField =
+              dialog.shadowRoot.querySelector('#printerAddress');
           assertTrue(!!addressField);
           addressField.value = 'newAddress:789';
           addressField.fire('input');
@@ -980,11 +1030,11 @@
                /*autoconf=*/ false, /*manufacturer=*/ 'make',
                /*model=*/ 'model', /*protocol=*/ 'ipp', /*serverAddress=*/ '')
         .then(() => {
-          const saveButton = dialog.$$('.action-button');
+          const saveButton = dialog.shadowRoot.querySelector('.action-button');
           assertTrue(!!saveButton);
           assertTrue(saveButton.disabled);
 
-          const queueField = dialog.$$('#printerQueue');
+          const queueField = dialog.shadowRoot.querySelector('#printerQueue');
           assertTrue(!!queueField);
           queueField.value = 'newqueueinfo';
           queueField.fire('input');
@@ -1002,11 +1052,11 @@
                /*autoconf=*/ false, /*manufacturer=*/ 'make',
                /*model=*/ 'model', /*protocol=*/ 'ipp', /*serverAddress=*/ '')
         .then(() => {
-          const saveButton = dialog.$$('.action-button');
+          const saveButton = dialog.shadowRoot.querySelector('.action-button');
           assertTrue(!!saveButton);
           assertTrue(saveButton.disabled);
 
-          const dropDown = dialog.$$('.md-select');
+          const dropDown = dialog.shadowRoot.querySelector('.md-select');
           dropDown.value = 'http';
           dropDown.dispatchEvent(new CustomEvent('change'), {'bubbles': true});
           flush();
@@ -1030,11 +1080,12 @@
                /*autoconf=*/ false, /*manufacturer=*/ 'make',
                /*model=*/ 'model', /*protocol=*/ 'ipp', /*serverAddress=*/ '')
         .then(() => {
-          saveButton = dialog.$$('.action-button');
+          saveButton = dialog.shadowRoot.querySelector('.action-button');
           assertTrue(!!saveButton);
           assertTrue(saveButton.disabled);
 
-          const makeDropDown = dialog.$$('#printerPPDManufacturer');
+          const makeDropDown =
+              dialog.shadowRoot.querySelector('#printerPPDManufacturer');
           makeDropDown.value = 'HP';
           makeDropDown.dispatchEvent(
               new CustomEvent('change'), {'bubbles': true});
@@ -1046,7 +1097,8 @@
           // Saving is disabled until a model is selected.
           assertTrue(saveButton.disabled);
 
-          const modelDropDown = dialog.$$('#printerPPDModel');
+          const modelDropDown =
+              dialog.shadowRoot.querySelector('#printerPPDModel');
           modelDropDown.value = 'HP 910';
           modelDropDown.dispatchEvent(
               new CustomEvent('change'), {'bubbles': true});
@@ -1074,7 +1126,7 @@
                /*autoconf=*/ false, /*manufacturer=*/ 'make',
                /*model=*/ 'model', /*protocol=*/ 'ipp', /*serverAddress=*/ '')
         .then(() => {
-          urlElement = dialog.$$('#eulaUrl');
+          urlElement = dialog.shadowRoot.querySelector('#eulaUrl');
           // Check that the EULA text is hidden.
           assertTrue(urlElement.hidden);
 
@@ -1082,8 +1134,9 @@
           // so we have to reset the resolver before the next call.
           resetGetEulaUrl(cupsPrintersBrowserProxy, eulaLink);
 
-          dialog.$$('#printerPPDManufacturer').value = expectedManufacturer;
-          modelDropdown = dialog.$$('#printerPPDModel');
+          dialog.shadowRoot.querySelector('#printerPPDManufacturer').value =
+              expectedManufacturer;
+          modelDropdown = dialog.shadowRoot.querySelector('#printerPPDModel');
           modelDropdown.value = expectedModel;
 
           return verifyGetEulaUrlWasCalled(
@@ -1132,14 +1185,15 @@
                /*autoconf=*/ false, /*manufacturer=*/ 'make',
                /*model=*/ 'model', /*protocol=*/ 'ipp', /*serverAddress=*/ '')
         .then(() => {
-          const nameField = dialog.$$('.printer-name-input');
+          const nameField =
+              dialog.shadowRoot.querySelector('.printer-name-input');
           assertTrue(!!nameField);
           nameField.value = expectedName;
           nameField.fire('input');
 
           flush();
 
-          const saveButton = dialog.$$('.action-button');
+          const saveButton = dialog.shadowRoot.querySelector('.action-button');
           assertTrue(!!saveButton);
           assertFalse(saveButton.disabled);
 
@@ -1160,11 +1214,13 @@
                /*serverAddress=*/ 'ipp://192.168.1.1:631')
         .then(() => {
           // Verify the only the name field is not disabled.
-          assertTrue(dialog.$$('#printerAddress').disabled);
-          assertTrue(dialog.$$('.md-select').disabled);
-          assertTrue(dialog.$$('#printerQueue').disabled);
+          assertTrue(
+              dialog.shadowRoot.querySelector('#printerAddress').disabled);
+          assertTrue(dialog.shadowRoot.querySelector('.md-select').disabled);
+          assertTrue(dialog.shadowRoot.querySelector('#printerQueue').disabled);
 
-          const nameField = dialog.$$('.printer-name-input');
+          const nameField =
+              dialog.shadowRoot.querySelector('.printer-name-input');
           assertTrue(!!nameField);
           assertFalse(nameField.disabled);
 
@@ -1173,7 +1229,7 @@
 
           flush();
 
-          const saveButton = dialog.$$('.action-button');
+          const saveButton = dialog.shadowRoot.querySelector('.action-button');
           assertTrue(!!saveButton);
           assertFalse(saveButton.disabled);
 
@@ -1205,7 +1261,8 @@
 
     cupsPrintersBrowserProxy = new TestCupsPrintersBrowserProxy();
 
-    CupsPrintersBrowserProxyImpl.instance_ = cupsPrintersBrowserProxy;
+    CupsPrintersBrowserProxyImpl.setInstanceForTesting(
+        cupsPrintersBrowserProxy);
 
     PolymerTest.clearBody();
     Router.getInstance().navigateTo(routes.CUPS_PRINTERS);
@@ -1213,7 +1270,7 @@
     page = document.createElement('settings-cups-printers');
     document.body.appendChild(page);
     assertTrue(!!page);
-    dialog = page.$$('settings-cups-add-printer-dialog');
+    dialog = page.shadowRoot.querySelector('settings-cups-add-printer-dialog');
     assertTrue(!!dialog);
 
     flush();
@@ -1247,8 +1304,8 @@
    */
   function getPrintServerDialog(page) {
     assertTrue(!!page);
-    dialog = page.$$('settings-cups-add-printer-dialog');
-    return dialog.$$('add-print-server-dialog');
+    dialog = page.shadowRoot.querySelector('settings-cups-add-printer-dialog');
+    return dialog.shadowRoot.querySelector('add-print-server-dialog');
   }
 
   /**
@@ -1266,23 +1323,27 @@
     dialog.open();
     flush();
 
-    const addPrinterDialog = dialog.$$('add-printer-manually-dialog');
+    const addPrinterDialog =
+        dialog.shadowRoot.querySelector('add-printer-manually-dialog');
     // Switch to Add print server dialog.
-    addPrinterDialog.$$('#print-server-button').click();
+    addPrinterDialog.shadowRoot.querySelector('#print-server-button').click();
     flush();
-    const printServerDialog = dialog.$$('add-print-server-dialog');
+    const printServerDialog =
+        dialog.shadowRoot.querySelector('add-print-server-dialog');
     assertTrue(!!printServerDialog);
 
     flush();
     cupsPrintersBrowserProxy.setQueryPrintServerResult(error);
     return flushTasks().then(() => {
       // Fill dialog with the server address.
-      const address = printServerDialog.$$('#printServerAddressInput');
+      const address = printServerDialog.shadowRoot.querySelector(
+          '#printServerAddressInput');
       assertTrue(!!address);
       address.value = address;
 
       // Add the print server.
-      const button = printServerDialog.$$('.action-button');
+      const button =
+          printServerDialog.shadowRoot.querySelector('.action-button');
       // Button should not be disabled before clicking on it.
       assertTrue(!button.disabled);
       button.click();
@@ -1300,7 +1361,8 @@
   function verifyErrorMessage(expectedError) {
     // Assert that the dialog did not close on errors.
     const printServerDialog = getPrintServerDialog(page);
-    const dialogError = printServerDialog.$$('#server-dialog-error');
+    const dialogError =
+        printServerDialog.shadowRoot.querySelector('#server-dialog-error');
     // Assert that the dialog error is displayed.
     assertTrue(!dialogError.hidden);
     assertEquals(loadTimeData.getString(expectedError), dialogError.errorText);
@@ -1314,7 +1376,7 @@
   function verifyToastMessage(expectedMessage, numPrinters) {
     // We always display the total number of printers found from a print
     // server.
-    const toast = page.$$('#printServerErrorToast');
+    const toast = page.shadowRoot.querySelector('#printServerErrorToast');
     assertTrue(toast.open);
     assertEquals(
         loadTimeData.getStringF(expectedMessage, numPrinters),
@@ -1376,7 +1438,7 @@
           // added to the entry manager.
           assertEquals(2, entryManager.printServerPrinters.length);
           const nearbyPrintersElement =
-              page.$$('settings-cups-nearby-printers');
+              page.shadowRoot.querySelector('settings-cups-nearby-printers');
           assertEquals(2, nearbyPrintersElement.nearbyPrinters.length);
         });
   });
@@ -1419,7 +1481,8 @@
       // Verify that we now only have 1 printer in print server printers
       // list.
       assertEquals(1, entryManager.printServerPrinters.length);
-      const nearbyPrintersElement = page.$$('settings-cups-nearby-printers');
+      const nearbyPrintersElement =
+          page.shadowRoot.querySelector('settings-cups-nearby-printers');
       assertEquals(1, nearbyPrintersElement.nearbyPrinters.length);
       // Verify we correctly removed the duplicate printer, 'idA', since
       // it exists in the saved printer list. Expect only 'idB' in
@@ -1439,7 +1502,9 @@
           // Assert that the dialog did not close on errors.
           assertTrue(!!printServerDialog);
           // Assert that the address input field is invalid.
-          assertTrue(printServerDialog.$$('#printServerAddressInput').invalid);
+          assertTrue(printServerDialog.shadowRoot
+                         .querySelector('#printServerAddressInput')
+                         .invalid);
         });
   });
 
diff --git a/chrome/test/data/webui/settings/chromeos/cups_printer_test_utils.js b/chrome/test/data/webui/settings/chromeos/cups_printer_test_utils.js
index c9604b0..e920d553 100644
--- a/chrome/test/data/webui/settings/chromeos/cups_printer_test_utils.js
+++ b/chrome/test/data/webui/settings/chromeos/cups_printer_test_utils.js
@@ -78,7 +78,8 @@
  * @private
  */
 export function getPrinterEntries(printersElement) {
-  const entryList = printersElement.$$('#printerEntryList');
+  const entryList =
+      printersElement.shadowRoot.querySelector('#printerEntryList');
   return entryList.querySelectorAll(
       'settings-cups-printers-entry:not([hidden])');
 }
diff --git a/chrome/test/data/webui/settings/chromeos/os_printing_page_tests.js b/chrome/test/data/webui/settings/chromeos/os_printing_page_tests.js
index 9fc44d2..a84b2a6 100644
--- a/chrome/test/data/webui/settings/chromeos/os_printing_page_tests.js
+++ b/chrome/test/data/webui/settings/chromeos/os_printing_page_tests.js
@@ -44,8 +44,9 @@
 
     flush();
 
-    const deepLinkElement = printingPage.$$('#printManagement')
-                                .shadowRoot.querySelector('cr-icon-button');
+    const deepLinkElement =
+        printingPage.shadowRoot.querySelector('#printManagement')
+            .shadowRoot.querySelector('cr-icon-button');
     await waitAfterNextRender(deepLinkElement);
     assertEquals(
         deepLinkElement, getDeepActiveElement(),
@@ -61,8 +62,9 @@
 
     flush();
 
-    const deepLinkElement = printingPage.$$('#scanningApp')
-                                .shadowRoot.querySelector('cr-icon-button');
+    const deepLinkElement =
+        printingPage.shadowRoot.querySelector('#scanningApp')
+            .shadowRoot.querySelector('cr-icon-button');
     await waitAfterNextRender(deepLinkElement);
     assertEquals(
         deepLinkElement, getDeepActiveElement(),
diff --git a/chrome/test/data/webui/settings/languages_page_metrics_test_browser.ts b/chrome/test/data/webui/settings/languages_page_metrics_test_browser.ts
index f8baef2..59e1c9c 100644
--- a/chrome/test/data/webui/settings/languages_page_metrics_test_browser.ts
+++ b/chrome/test/data/webui/settings/languages_page_metrics_test_browser.ts
@@ -160,6 +160,31 @@
         await languageSettingsMetricsProxy.whenCalled('recordSettingsMetric'));
   });
 
+  // <if expr="is_win">
+  test('records when chrome language is changed', async () => {
+    // Adding language with supportsUI = true in
+    // fake_language_settings_private.ts
+    languageHelper.enableLanguage('sw');
+    // Testing the 'Change Chrome Language' button with 'sw'
+    const languagesSection =
+        languagesSubpage.shadowRoot!.querySelector('#languagesSection');
+    assertTrue(!!languagesSection);
+    const menuButton = languagesSection.querySelector<HTMLElement>(
+        '.list-item cr-icon-button#more-sw');
+    assertTrue(!!menuButton);
+    menuButton.click();
+    flush();
+    const actionMenu = languagesSubpage.$.menu.get();
+    assertTrue(actionMenu.open);
+    const item = actionMenu.querySelector<HTMLElement>('#uiLanguageItem');
+    assertTrue(!!item);
+    item.click();
+    assertEquals(
+        LanguageSettingsActionType.CHANGE_CHROME_LANGUAGE,
+        await languageSettingsMetricsProxy.whenCalled('recordSettingsMetric'));
+  });
+  // </if>
+
   test('records on language list reorder', async () => {
     // Add several languages.
     for (const language of ['en-CA', 'en-US', 'tk', 'no']) {
diff --git a/chrome/updater/BUILD.gn b/chrome/updater/BUILD.gn
index 7afaa765..1769883 100644
--- a/chrome/updater/BUILD.gn
+++ b/chrome/updater/BUILD.gn
@@ -638,6 +638,8 @@
         "app/server/win/com_classes_legacy_unittest.cc",
         "policy/win/group_policy_manager_unittest.cc",
         "test/integration_tests_win.cc",
+        "unittest_util_win.cc",
+        "unittest_util_win.h",
         "util_win_unittest.cc",
         "win/installer_api_unittest.cc",
         "win/manifest_util_unittest.cc",
diff --git a/chrome/updater/app/server/win/com_classes_legacy.cc b/chrome/updater/app/server/win/com_classes_legacy.cc
index 5d11b41..dd3df8ed 100644
--- a/chrome/updater/app/server/win/com_classes_legacy.cc
+++ b/chrome/updater/app/server/win/com_classes_legacy.cc
@@ -700,7 +700,8 @@
 
   if (const base::win::RegKey command_key(
           root,
-          base::StrCat({app_key_name, L"\\", kRegKeyCommands, command_id})
+          base::StrCat(
+              {app_key_name, L"\\", kRegKeyCommands, L"\\", command_id})
               .c_str(),
           Wow6432(KEY_QUERY_VALUE));
       !command_key.Valid()) {
diff --git a/chrome/updater/app/server/win/com_classes_legacy_unittest.cc b/chrome/updater/app/server/win/com_classes_legacy_unittest.cc
index 81f19081..252c824 100644
--- a/chrome/updater/app/server/win/com_classes_legacy_unittest.cc
+++ b/chrome/updater/app/server/win/com_classes_legacy_unittest.cc
@@ -14,19 +14,19 @@
 #include "base/command_line.h"
 #include "base/files/file_path.h"
 #include "base/files/file_util.h"
+#include "base/files/scoped_temp_dir.h"
 #include "base/path_service.h"
 #include "base/strings/strcat.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/synchronization/waitable_event.h"
 #include "base/test/test_timeouts.h"
 #include "base/time/time.h"
-#include "base/win/registry.h"
 #include "base/win/scoped_handle.h"
 #include "base/win/scoped_variant.h"
 #include "chrome/updater/test_scope.h"
+#include "chrome/updater/unittest_util_win.h"
 #include "chrome/updater/win/test/test_executables.h"
 #include "chrome/updater/win/test/test_strings.h"
-#include "chrome/updater/win/win_constants.h"
 #include "chrome/updater/win/win_util.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -35,54 +35,28 @@
 namespace updater {
 namespace {
 
-const wchar_t kAppId1[] = L"{3B1A3CCA-0525-4418-93E6-A0DB3398EC9B}";
+constexpr wchar_t kAppId1[] = L"{3B1A3CCA-0525-4418-93E6-A0DB3398EC9B}";
 
-const wchar_t kCmdExe[] = L"cmd.exe";
-const wchar_t kBadCmdLine[] = L"\"c:\\Program Files\\cmd.exe\"";
-const wchar_t kCmdLineValid[] =
+constexpr wchar_t kBadCmdLine[] = L"\"c:\\Program Files\\cmd.exe\"";
+constexpr wchar_t kCmdLineValid[] =
     L"\"C:\\Program Files\\Windows Media Player\\wmpnscfg.exe\" /Close";
 
-const wchar_t kCmdId1[] = L"command 1";
-const wchar_t kCmdId2[] = L"command 2";
+constexpr wchar_t kCmdId1[] = L"command 1";
+constexpr wchar_t kCmdId2[] = L"command 2";
 
 }  // namespace
 
 class LegacyAppCommandWebImplTest : public testing::Test {
  protected:
   LegacyAppCommandWebImplTest()
-      : test_process_command_line_(base::CommandLine::NO_PROGRAM) {}
+      : cmd_exe_command_line_(base::CommandLine::NO_PROGRAM) {}
   ~LegacyAppCommandWebImplTest() override = default;
 
   void SetUp() override {
-    base::FilePath system_path;
-    ASSERT_TRUE(base::PathService::Get(base::DIR_SYSTEM, &system_path));
-
-    const base::FilePath from_test_process = system_path.Append(kCmdExe);
-    if (GetTestScope() == UpdaterScope::kUser) {
-      test_process_command_line_ = base::CommandLine(from_test_process);
-      return;
-    }
-
-    base::FilePath programfiles_path;
-    ASSERT_TRUE(
-        base::PathService::Get(base::DIR_PROGRAM_FILES, &programfiles_path));
-    ASSERT_TRUE(base::CreateTemporaryDirInDir(
-        programfiles_path, L"com_classes_legacy_unittest", &temp_directory_));
-    base::FilePath test_process_path;
-    test_process_path = temp_directory_.Append(kCmdExe);
-
-    ASSERT_TRUE(base::CopyFile(from_test_process, test_process_path));
-    test_process_command_line_ = base::CommandLine(test_process_path);
+    SetupCmdExe(cmd_exe_command_line_, temp_programfiles_dir_);
   }
 
-  void TearDown() override {
-    base::win::RegKey(UpdaterScopeToHKeyRoot(GetTestScope()), L"",
-                      Wow6432(DELETE))
-        .DeleteKey(GetClientKeyName(kAppId1).c_str());
-
-    if (!temp_directory_.empty())
-      base::DeletePathRecursively(temp_directory_);
-  }
+  void TearDown() override { DeleteAppClientKey(kAppId1); }
 
   template <typename T>
   absl::optional<std::wstring> MakeCommandLine(
@@ -118,7 +92,7 @@
       const std::wstring& command_line_format,
       const std::vector<std::wstring>& parameters) {
     CreateAppClientKey(app_id);
-    CreateCommand(app_id, command_id, command_line_format);
+    CreateAppCommandRegistry(app_id, command_id, command_line_format);
 
     Microsoft::WRL::ComPtr<LegacyAppCommandWebImpl> app_command_web;
     if (HRESULT hr = LegacyAppCommandWebImpl::CreateLegacyAppCommandWebImpl(
@@ -136,7 +110,7 @@
       const std::wstring& command_line_format,
       Microsoft::WRL::ComPtr<LegacyAppCommandWebImpl>& app_command_web) {
     CreateAppClientKey(app_id);
-    CreateCommand(app_id, command_id, command_line_format);
+    CreateAppCommandRegistry(app_id, command_id, command_line_format);
 
     return LegacyAppCommandWebImpl::CreateLegacyAppCommandWebImpl(
         GetTestScope(), app_id, command_id, app_command_web);
@@ -152,43 +126,13 @@
   void NoCmdTest() {
     Microsoft::WRL::ComPtr<LegacyAppCommandWebImpl> app_command_web;
     CreateAppClientKey(kAppId1);
-    CreateCommand(kAppId1, kCmdId1, kCmdLineValid);
+    CreateAppCommandRegistry(kAppId1, kCmdId1, kCmdLineValid);
 
     EXPECT_HRESULT_FAILED(
         LegacyAppCommandWebImpl::CreateLegacyAppCommandWebImpl(
             GetTestScope(), kAppId1, kCmdId2, app_command_web));
   }
 
-  std::wstring GetClientKeyName(const std::wstring& app_id) {
-    return base::StrCat({CLIENTS_KEY, app_id});
-  }
-
-  std::wstring GetCommandKeyName(const std::wstring& app_id,
-                                 const std::wstring& command_id) {
-    return base::StrCat(
-        {CLIENTS_KEY, app_id, L"\\", kRegKeyCommands, command_id});
-  }
-
-  void CreateAppClientKey(const std::wstring& app_id) {
-    base::win::RegKey client_key;
-    EXPECT_EQ(
-        client_key.Create(UpdaterScopeToHKeyRoot(GetTestScope()),
-                          GetClientKeyName(app_id).c_str(), Wow6432(KEY_WRITE)),
-        ERROR_SUCCESS);
-  }
-
-  void CreateCommand(const std::wstring& app_id,
-                     const std::wstring& cmd_id,
-                     const std::wstring& cmd_line) {
-    base::win::RegKey command_key;
-    EXPECT_EQ(command_key.Create(UpdaterScopeToHKeyRoot(GetTestScope()),
-                                 GetCommandKeyName(app_id, cmd_id).c_str(),
-                                 Wow6432(KEY_WRITE)),
-              ERROR_SUCCESS);
-    EXPECT_EQ(command_key.WriteValue(kRegValueCommandLine, cmd_line.c_str()),
-              ERROR_SUCCESS);
-  }
-
   void WaitForUpdateCompletion(
       Microsoft::WRL::ComPtr<LegacyAppCommandWebImpl>& app_command_web,
       const base::TimeDelta& timeout) {
@@ -211,8 +155,8 @@
         .GetCommandLineString();
   }
 
-  base::CommandLine test_process_command_line_;
-  base::FilePath temp_directory_;
+  base::CommandLine cmd_exe_command_line_;
+  base::ScopedTempDir temp_programfiles_dir_;
 };
 
 TEST_F(LegacyAppCommandWebImplTest, InvalidPaths) {
@@ -413,8 +357,8 @@
   Microsoft::WRL::ComPtr<LegacyAppCommandWebImpl> app_command_web;
   ASSERT_HRESULT_SUCCEEDED(CreateAppCommandWeb(
       kAppId1, kCmdId1,
-      base::StrCat({test_process_command_line_.GetCommandLineString(),
-                    L" /c \"exit 7\""}),
+      base::StrCat(
+          {cmd_exe_command_line_.GetCommandLineString(), L" /c \"exit 7\""}),
       app_command_web));
 
   UINT status = 0;
@@ -446,8 +390,8 @@
   Microsoft::WRL::ComPtr<LegacyAppCommandWebImpl> app_command_web;
   ASSERT_HRESULT_SUCCEEDED(CreateAppCommandWeb(
       kAppId1, kCmdId1,
-      base::StrCat({test_process_command_line_.GetCommandLineString(),
-                    L" /c \"exit %1\""}),
+      base::StrCat(
+          {cmd_exe_command_line_.GetCommandLineString(), L" /c \"exit %1\""}),
       app_command_web));
 
   ASSERT_HRESULT_SUCCEEDED(
diff --git a/chrome/updater/persisted_data_unittest.cc b/chrome/updater/persisted_data_unittest.cc
index 0e502c6..355a9577 100644
--- a/chrome/updater/persisted_data_unittest.cc
+++ b/chrome/updater/persisted_data_unittest.cc
@@ -160,7 +160,6 @@
   EXPECT_EQ(metadata_os.wServicePackMinor, os.wServicePackMinor);
   EXPECT_EQ(metadata_os.wSuiteMask, os.wSuiteMask);
   EXPECT_EQ(metadata_os.wProductType, os.wProductType);
-  EXPECT_EQ(metadata_os.wReserved, os.wReserved);
 }
 #endif
 
diff --git a/chrome/updater/unittest_util_win.cc b/chrome/updater/unittest_util_win.cc
new file mode 100644
index 0000000..beb5dea2
--- /dev/null
+++ b/chrome/updater/unittest_util_win.cc
@@ -0,0 +1,86 @@
+// Copyright 2022 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/updater/unittest_util_win.h"
+
+#include <windows.h>
+
+#include <string>
+
+#include "base/base_paths_win.h"
+#include "base/command_line.h"
+#include "base/files/file_util.h"
+#include "base/files/scoped_temp_dir.h"
+#include "base/path_service.h"
+#include "base/strings/strcat.h"
+#include "base/win/registry.h"
+#include "chrome/updater/test_scope.h"
+#include "chrome/updater/win/win_constants.h"
+#include "chrome/updater/win/win_util.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace updater {
+
+std::wstring GetClientKeyName(const std::wstring& app_id) {
+  return base::StrCat({CLIENTS_KEY, app_id});
+}
+
+std::wstring GetAppCommandKeyName(const std::wstring& app_id,
+                                  const std::wstring& command_id) {
+  return base::StrCat(
+      {CLIENTS_KEY, app_id, L"\\", kRegKeyCommands, L"\\", command_id});
+}
+
+void CreateAppClientKey(const std::wstring& app_id) {
+  base::win::RegKey client_key;
+  EXPECT_EQ(
+      client_key.Create(UpdaterScopeToHKeyRoot(GetTestScope()),
+                        GetClientKeyName(app_id).c_str(), Wow6432(KEY_WRITE)),
+      ERROR_SUCCESS);
+}
+
+void DeleteAppClientKey(const std::wstring& app_id) {
+  base::win::RegKey(UpdaterScopeToHKeyRoot(GetTestScope()), L"",
+                    Wow6432(DELETE))
+      .DeleteKey(GetClientKeyName(app_id).c_str());
+}
+
+void CreateAppCommandRegistry(const std::wstring& app_id,
+                              const std::wstring& cmd_id,
+                              const std::wstring& cmd_line) {
+  base::win::RegKey command_key;
+  EXPECT_EQ(command_key.Create(UpdaterScopeToHKeyRoot(GetTestScope()),
+                               GetAppCommandKeyName(app_id, cmd_id).c_str(),
+                               Wow6432(KEY_WRITE)),
+            ERROR_SUCCESS);
+  EXPECT_EQ(command_key.WriteValue(kRegValueCommandLine, cmd_line.c_str()),
+            ERROR_SUCCESS);
+}
+
+void SetupCmdExe(base::CommandLine& cmd_exe_command_line,
+                 base::ScopedTempDir& temp_programfiles_dir) {
+  constexpr wchar_t kCmdExe[] = L"cmd.exe";
+
+  base::FilePath system_path;
+  ASSERT_TRUE(base::PathService::Get(base::DIR_SYSTEM, &system_path));
+
+  const base::FilePath cmd_exe_system_path = system_path.Append(kCmdExe);
+  if (GetTestScope() == UpdaterScope::kUser) {
+    cmd_exe_command_line = base::CommandLine(cmd_exe_system_path);
+    return;
+  }
+
+  base::FilePath programfiles_path;
+  ASSERT_TRUE(
+      base::PathService::Get(base::DIR_PROGRAM_FILES, &programfiles_path));
+  ASSERT_TRUE(
+      temp_programfiles_dir.CreateUniqueTempDirUnderPath(programfiles_path));
+  base::FilePath cmd_exe_path;
+  cmd_exe_path = temp_programfiles_dir.GetPath().Append(kCmdExe);
+
+  ASSERT_TRUE(base::CopyFile(cmd_exe_system_path, cmd_exe_path));
+  cmd_exe_command_line = base::CommandLine(cmd_exe_path);
+}
+
+}  // namespace updater
diff --git a/chrome/updater/unittest_util_win.h b/chrome/updater/unittest_util_win.h
new file mode 100644
index 0000000..1a84dadb
--- /dev/null
+++ b/chrome/updater/unittest_util_win.h
@@ -0,0 +1,49 @@
+// Copyright 2022 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_UPDATER_UNITTEST_UTIL_WIN_H_
+#define CHROME_UPDATER_UNITTEST_UTIL_WIN_H_
+
+#include <string>
+
+#include "base/command_line.h"
+#include "base/files/scoped_temp_dir.h"
+
+namespace updater {
+
+// Returns the registry path `Software\{CompanyName}\Update\Clients\{app_id}`.
+std::wstring GetClientKeyName(const std::wstring& app_id);
+
+// Returns the registry path
+// `Software\{CompanyName}\Update\Clients\{app_id}\Commands\{command_id}`.
+std::wstring GetAppCommandKeyName(const std::wstring& app_id,
+                                  const std::wstring& command_id);
+
+// Creates the key `{HKLM\HKCU}\Software\{CompanyName}\Update\Clients\{app_id}`.
+// `{HKLM\HKCU}` is determined by the current test scope.
+void CreateAppClientKey(const std::wstring& app_id);
+
+// Deletes the key `{HKLM\HKCU}\Software\{CompanyName}\Update\Clients\{app_id}`.
+// `{HKLM\HKCU}` is determined by the current test scope.
+void DeleteAppClientKey(const std::wstring& app_id);
+
+// Creates the key
+// `{HKRoot}\Software\{CompanyName}\Update\Clients\{app_id}\Commands\{cmd_id}`,
+// and adds a `CommandLine` REG_SZ entry with the value `cmd_line`. `{HKRoot}`
+// is determined by the current test scope.
+void CreateAppCommandRegistry(const std::wstring& app_id,
+                              const std::wstring& cmd_id,
+                              const std::wstring& cmd_line);
+
+// Returns the path to "cmd.exe" in `cmd_exe_command_line` based on the current
+// test scope:
+// * "%systemroot%\system32\cmd.exe" for user test scope.
+// * "%programfiles%\`temp_parent_dir`\cmd.exe" for system test scope.
+// `temp_parent_dir` is owned by the caller.
+void SetupCmdExe(base::CommandLine& cmd_exe_command_line,
+                 base::ScopedTempDir& temp_parent_dir);
+
+}  // namespace updater
+
+#endif  // CHROME_UPDATER_UNITTEST_UTIL_WIN_H_
diff --git a/chrome/updater/win/win_util_unittest.cc b/chrome/updater/win/win_util_unittest.cc
index 557cd0ed..aa5044a6 100644
--- a/chrome/updater/win/win_util_unittest.cc
+++ b/chrome/updater/win/win_util_unittest.cc
@@ -157,7 +157,6 @@
   EXPECT_EQ(rtl_os_version->wServicePackMinor, os.wServicePackMinor);
   EXPECT_EQ(rtl_os_version->wSuiteMask, os.wSuiteMask);
   EXPECT_EQ(rtl_os_version->wProductType, os.wProductType);
-  EXPECT_EQ(rtl_os_version->wReserved, os.wReserved);
 }
 
 TEST(WinUtil, CompareOSVersions_SameAsCurrent) {
diff --git a/chromeos/chromeos_strings.grd b/chromeos/chromeos_strings.grd
index bb3e9dd9..d0d312f 100644
--- a/chromeos/chromeos_strings.grd
+++ b/chromeos/chromeos_strings.grd
@@ -3398,6 +3398,9 @@
         Tips on writing feedback
       </message>
       <!-- Share Data Page -->
+      <message name="IDS_FEEDBACK_TOOL_BACK_BUTTON_LABEL" desc="label for the back button to return to a prior page">
+        Back
+      </message>
       <message name="IDS_FEEDBACK_TOOL_DESCRIPTION_HINT" desc="Label for the hint in description textarea">
         Share your feedback or describe your issue. If possible, include steps to reproduce your issue.
       </message>
@@ -3416,6 +3419,9 @@
       <message name="IDS_FEEDBACK_TOOL_SCREENSHOT_LABEL" desc="Label for the screenshot field">
         Screenshot
       </message>
+      <message name="IDS_FEEDBACK_TOOL_USER_CONSENT_LABEL" desc="Label for checkbox indicating whether the user agrees to be contacted back.">
+        We may email you for more information or updates
+      </message>
       <!-- Confirmation Page -->
       <message name="IDS_FEEDBACK_TOOL_PAGE_TITLE_AFTER_SENT" desc="Label showing a thank you message as the title of the confirmation page after a report has been sent.">
         Thanks for your feedback
diff --git a/chromeos/chromeos_strings_grd/IDS_FEEDBACK_TOOL_BACK_BUTTON_LABEL.png.sha1 b/chromeos/chromeos_strings_grd/IDS_FEEDBACK_TOOL_BACK_BUTTON_LABEL.png.sha1
new file mode 100644
index 0000000..14da21f
--- /dev/null
+++ b/chromeos/chromeos_strings_grd/IDS_FEEDBACK_TOOL_BACK_BUTTON_LABEL.png.sha1
@@ -0,0 +1 @@
+21581966d3d1c22e6ca58a00e132a64de15ca3c6
\ No newline at end of file
diff --git a/chromeos/chromeos_strings_grd/IDS_FEEDBACK_TOOL_USER_CONSENT_LABEL.png.sha1 b/chromeos/chromeos_strings_grd/IDS_FEEDBACK_TOOL_USER_CONSENT_LABEL.png.sha1
new file mode 100644
index 0000000..b37089c
--- /dev/null
+++ b/chromeos/chromeos_strings_grd/IDS_FEEDBACK_TOOL_USER_CONSENT_LABEL.png.sha1
@@ -0,0 +1 @@
+f5b1adaf5e8efe949ff318672c6d7c1349b230a7
\ No newline at end of file
diff --git a/chromeos/dbus/dbus_clients_browser.cc b/chromeos/dbus/dbus_clients_browser.cc
index cb65bfc9..3821c6cb 100644
--- a/chromeos/dbus/dbus_clients_browser.cc
+++ b/chromeos/dbus/dbus_clients_browser.cc
@@ -44,8 +44,6 @@
 #include "chromeos/dbus/update_engine/update_engine_client.h"
 #include "chromeos/dbus/virtual_file_provider/fake_virtual_file_provider_client.h"
 #include "chromeos/dbus/virtual_file_provider/virtual_file_provider_client.h"
-#include "chromeos/dbus/vm_plugin_dispatcher/fake_vm_plugin_dispatcher_client.h"
-#include "chromeos/dbus/vm_plugin_dispatcher/vm_plugin_dispatcher_client.h"
 
 namespace chromeos {
 
@@ -102,8 +100,6 @@
 
   virtual_file_provider_client_ =
       CREATE_DBUS_CLIENT(VirtualFileProviderClient, use_real_clients);
-  vm_plugin_dispatcher_client_ =
-      CREATE_DBUS_CLIENT(VmPluginDispatcherClient, use_real_clients);
 }
 
 DBusClientsBrowser::~DBusClientsBrowser() = default;
@@ -129,7 +125,6 @@
   smb_provider_client_->Init(system_bus);
   update_engine_client_->Init(system_bus);
   virtual_file_provider_client_->Init(system_bus);
-  vm_plugin_dispatcher_client_->Init(system_bus);
 }
 
 }  // namespace chromeos
diff --git a/chromeos/dbus/dbus_clients_browser.h b/chromeos/dbus/dbus_clients_browser.h
index 0e1bb471..e4b16b2e 100644
--- a/chromeos/dbus/dbus_clients_browser.h
+++ b/chromeos/dbus/dbus_clients_browser.h
@@ -33,7 +33,6 @@
 class SmbProviderClient;
 class UpdateEngineClient;
 class VirtualFileProviderClient;
-class VmPluginDispatcherClient;
 
 // Owns D-Bus clients.
 // TODO(jamescook): Rename this class. "Browser" refers to the browser process
@@ -74,7 +73,6 @@
   std::unique_ptr<SmbProviderClient> smb_provider_client_;
   std::unique_ptr<UpdateEngineClient> update_engine_client_;
   std::unique_ptr<VirtualFileProviderClient> virtual_file_provider_client_;
-  std::unique_ptr<VmPluginDispatcherClient> vm_plugin_dispatcher_client_;
 };
 
 }  // namespace chromeos
diff --git a/chromeos/dbus/dbus_thread_manager.cc b/chromeos/dbus/dbus_thread_manager.cc
index 8d699bc..2500105 100644
--- a/chromeos/dbus/dbus_thread_manager.cc
+++ b/chromeos/dbus/dbus_thread_manager.cc
@@ -126,11 +126,6 @@
              : nullptr;
 }
 
-VmPluginDispatcherClient* DBusThreadManager::GetVmPluginDispatcherClient() {
-  return clients_browser_ ? clients_browser_->vm_plugin_dispatcher_client_.get()
-                          : nullptr;
-}
-
 #undef RETURN_DBUS_CLIENT
 
 void DBusThreadManager::InitializeClients() {
diff --git a/chromeos/dbus/dbus_thread_manager.h b/chromeos/dbus/dbus_thread_manager.h
index 8e7f3cf..b5f02e7 100644
--- a/chromeos/dbus/dbus_thread_manager.h
+++ b/chromeos/dbus/dbus_thread_manager.h
@@ -34,7 +34,6 @@
 class SmbProviderClient;
 class UpdateEngineClient;
 class VirtualFileProviderClient;
-class VmPluginDispatcherClient;
 
 // THIS CLASS IS BEING DEPRECATED. See README.md for guidelines and
 // https://crbug.com/647367 for details.
@@ -88,7 +87,6 @@
   SmbProviderClient* GetSmbProviderClient();
   UpdateEngineClient* GetUpdateEngineClient();
   VirtualFileProviderClient* GetVirtualFileProviderClient();
-  VmPluginDispatcherClient* GetVmPluginDispatcherClient();
 
  private:
   DBusThreadManager();
diff --git a/chromeos/dbus/vm_plugin_dispatcher/vm_plugin_dispatcher_client.cc b/chromeos/dbus/vm_plugin_dispatcher/vm_plugin_dispatcher_client.cc
index 2bc1ab33..0e7d083ef 100644
--- a/chromeos/dbus/vm_plugin_dispatcher/vm_plugin_dispatcher_client.cc
+++ b/chromeos/dbus/vm_plugin_dispatcher/vm_plugin_dispatcher_client.cc
@@ -8,10 +8,12 @@
 #include <utility>
 
 #include "base/bind.h"
+#include "base/check_op.h"
 #include "base/location.h"
 #include "base/logging.h"
 #include "base/observer_list.h"
 #include "base/threading/thread_task_runner_handle.h"
+#include "chromeos/dbus/vm_plugin_dispatcher/fake_vm_plugin_dispatcher_client.h"
 #include "dbus/bus.h"
 #include "dbus/message.h"
 #include "third_party/cros_system_api/dbus/service_constants.h"
@@ -20,10 +22,15 @@
 namespace dispatcher = vm_tools::plugin_dispatcher;
 
 namespace chromeos {
+namespace {
+
+VmPluginDispatcherClient* g_instance = nullptr;
+
+}  // namespace
 
 class VmPluginDispatcherClientImpl : public VmPluginDispatcherClient {
  public:
-  VmPluginDispatcherClientImpl() {}
+  VmPluginDispatcherClientImpl() = default;
 
   VmPluginDispatcherClientImpl(const VmPluginDispatcherClientImpl&) = delete;
   VmPluginDispatcherClientImpl& operator=(const VmPluginDispatcherClientImpl&) =
@@ -76,7 +83,6 @@
         std::move(callback));
   }
 
- protected:
   void Init(dbus::Bus* bus) override {
     vm_plugin_dispatcher_proxy_ = bus->GetObjectProxy(
         dispatcher::kVmPluginDispatcherServiceName,
@@ -192,12 +198,36 @@
   base::WeakPtrFactory<VmPluginDispatcherClientImpl> weak_ptr_factory_{this};
 };
 
-VmPluginDispatcherClient::VmPluginDispatcherClient() = default;
+VmPluginDispatcherClient::VmPluginDispatcherClient() {
+  CHECK(!g_instance);
+  g_instance = this;
+}
 
-VmPluginDispatcherClient::~VmPluginDispatcherClient() = default;
+VmPluginDispatcherClient::~VmPluginDispatcherClient() {
+  CHECK_EQ(this, g_instance);
+  g_instance = nullptr;
+}
 
-std::unique_ptr<VmPluginDispatcherClient> VmPluginDispatcherClient::Create() {
-  return std::make_unique<VmPluginDispatcherClientImpl>();
+// static
+void VmPluginDispatcherClient::Initialize(dbus::Bus* bus) {
+  CHECK(bus);
+  (new VmPluginDispatcherClientImpl())->Init(bus);
+}
+
+// static
+void VmPluginDispatcherClient::InitializeFake() {
+  new FakeVmPluginDispatcherClient();
+}
+
+// static
+void VmPluginDispatcherClient::Shutdown() {
+  CHECK(g_instance);
+  delete g_instance;
+}
+
+// static
+VmPluginDispatcherClient* VmPluginDispatcherClient::Get() {
+  return g_instance;
 }
 
 }  // namespace chromeos
diff --git a/chromeos/dbus/vm_plugin_dispatcher/vm_plugin_dispatcher_client.h b/chromeos/dbus/vm_plugin_dispatcher/vm_plugin_dispatcher_client.h
index 15b28f11..1b7976b 100644
--- a/chromeos/dbus/vm_plugin_dispatcher/vm_plugin_dispatcher_client.h
+++ b/chromeos/dbus/vm_plugin_dispatcher/vm_plugin_dispatcher_client.h
@@ -5,8 +5,6 @@
 #ifndef CHROMEOS_DBUS_VM_PLUGIN_DISPATCHER_VM_PLUGIN_DISPATCHER_CLIENT_H_
 #define CHROMEOS_DBUS_VM_PLUGIN_DISPATCHER_VM_PLUGIN_DISPATCHER_CLIENT_H_
 
-#include <memory>
-
 #include "base/component_export.h"
 #include "base/observer_list_types.h"
 #include "chromeos/dbus/common/dbus_client.h"
@@ -73,17 +71,26 @@
   virtual void WaitForServiceToBeAvailable(
       dbus::ObjectProxy::WaitForServiceToBeAvailableCallback callback) = 0;
 
-  // Creates an instance of VmPluginDispatcherClient.
-  static std::unique_ptr<VmPluginDispatcherClient> Create();
+  // Creates and initializes the global instance. |bus| must not be null.
+  static void Initialize(dbus::Bus* bus);
+
+  // Creates and initializes a fake global instance.
+  static void InitializeFake();
+
+  // Destroys the global instance if it has been initialized.
+  static void Shutdown();
+
+  // Returns the global instance if initialized. May return null.
+  static VmPluginDispatcherClient* Get();
 
   VmPluginDispatcherClient(const VmPluginDispatcherClient&) = delete;
   VmPluginDispatcherClient& operator=(const VmPluginDispatcherClient&) = delete;
 
-  ~VmPluginDispatcherClient() override;
-
  protected:
-  // Create() should be used instead.
+  // Initialize() should be used instead.
   VmPluginDispatcherClient();
+
+  ~VmPluginDispatcherClient() override;
 };
 
 }  // namespace chromeos
diff --git a/chromeos/network/shill_property_util.cc b/chromeos/network/shill_property_util.cc
index 51e5933..0b970a8f 100644
--- a/chromeos/network/shill_property_util.cc
+++ b/chromeos/network/shill_property_util.cc
@@ -32,7 +32,7 @@
 // Replace non UTF8 characters in |str| with a replacement character.
 std::string ValidateUTF8(const std::string& str) {
   std::string result;
-  for (int32_t index = 0; index < static_cast<int32_t>(str.size()); ++index) {
+  for (size_t index = 0; index < str.size(); ++index) {
     base_icu::UChar32 code_point_out;
     bool is_unicode_char = base::ReadUnicodeCharacter(str.c_str(), str.size(),
                                                       &index, &code_point_out);
diff --git a/components/background_task_scheduler/BUILD.gn b/components/background_task_scheduler/BUILD.gn
index 0d73ea8..2903479 100644
--- a/components/background_task_scheduler/BUILD.gn
+++ b/components/background_task_scheduler/BUILD.gn
@@ -103,6 +103,10 @@
     include_android_sdk = false
     testonly = true
     sources = [
+      "internal/android/java/src/org/chromium/components/background_task_scheduler/internal/BackgroundTaskSchedulerImplWithMockTest.java",
+      "internal/android/java/src/org/chromium/components/background_task_scheduler/internal/BackgroundTaskSchedulerJobServiceTest.java",
+      "internal/android/java/src/org/chromium/components/background_task_scheduler/internal/BundleToPersistableBundleConverterTest.java",
+      "internal/android/java/src/org/chromium/components/background_task_scheduler/internal/MockBackgroundTaskSchedulerDelegate.java",
       "internal/android/junit/src/org/chromium/components/background_task_scheduler/internal/BackgroundTaskGcmTaskServiceTest.java",
       "internal/android/junit/src/org/chromium/components/background_task_scheduler/internal/BackgroundTaskJobServiceTest.java",
       "internal/android/junit/src/org/chromium/components/background_task_scheduler/internal/BackgroundTaskSchedulerAlarmManagerTest.java",
@@ -131,6 +135,7 @@
       "//base:base_java_test_support",
       "//base:base_junit_test_support",
       "//components/background_task_scheduler:public_java",
+      "//content/public/test/android:content_java_test_support",
       "//third_party/android_deps:chromium_play_services_availability_shadows_java",
       "//third_party/android_deps:robolectric_all_java",
       "//third_party/junit",
diff --git a/components/background_task_scheduler/internal/BUILD.gn b/components/background_task_scheduler/internal/BUILD.gn
index 1a63bcb..7c27d4b6 100644
--- a/components/background_task_scheduler/internal/BUILD.gn
+++ b/components/background_task_scheduler/internal/BUILD.gn
@@ -86,30 +86,4 @@
       "android/java/src/org/chromium/components/background_task_scheduler/internal/TaskInfoBridge.java",
     ]
   }
-
-  android_library("background_task_scheduler_javatests") {
-    testonly = true
-
-    sources = [
-      "android/javatests/src/org/chromium/components/background_task_scheduler/internal/BackgroundTaskSchedulerImplWithMockTest.java",
-      "android/javatests/src/org/chromium/components/background_task_scheduler/internal/BackgroundTaskSchedulerJobServiceTest.java",
-      "android/javatests/src/org/chromium/components/background_task_scheduler/internal/BundleToPersistableBundleConverterTest.java",
-      "android/javatests/src/org/chromium/components/background_task_scheduler/internal/MockBackgroundTaskSchedulerDelegate.java",
-    ]
-
-    deps = [
-      ":internal_java",
-      "$google_play_services_package:google_play_services_base_java",
-      "$google_play_services_package:google_play_services_basement_java",
-      "$google_play_services_package:google_play_services_gcm_java",
-      "$google_play_services_package:google_play_services_tasks_java",
-      "//base:base_java_test_support",
-      "//components/background_task_scheduler:background_task_scheduler_task_ids_java",
-      "//components/background_task_scheduler:public_java",
-      "//content/public/test/android:content_java_test_support",
-      "//third_party/android_support_test_runner:runner_java",
-      "//third_party/androidx:androidx_test_runner_java",
-      "//third_party/junit",
-    ]
-  }
 }
diff --git a/components/background_task_scheduler/internal/android/javatests/src/org/chromium/components/background_task_scheduler/internal/BackgroundTaskSchedulerImplWithMockTest.java b/components/background_task_scheduler/internal/android/java/src/org/chromium/components/background_task_scheduler/internal/BackgroundTaskSchedulerImplWithMockTest.java
similarity index 89%
rename from components/background_task_scheduler/internal/android/javatests/src/org/chromium/components/background_task_scheduler/internal/BackgroundTaskSchedulerImplWithMockTest.java
rename to components/background_task_scheduler/internal/android/java/src/org/chromium/components/background_task_scheduler/internal/BackgroundTaskSchedulerImplWithMockTest.java
index 1137963..c7e59d1 100644
--- a/components/background_task_scheduler/internal/android/javatests/src/org/chromium/components/background_task_scheduler/internal/BackgroundTaskSchedulerImplWithMockTest.java
+++ b/components/background_task_scheduler/internal/android/java/src/org/chromium/components/background_task_scheduler/internal/BackgroundTaskSchedulerImplWithMockTest.java
@@ -4,25 +4,27 @@
 
 package org.chromium.components.background_task_scheduler.internal;
 
-import androidx.test.filters.SmallTest;
-
 import org.junit.Assert;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.robolectric.annotation.Config;
 
-import org.chromium.base.test.BaseJUnit4ClassRunner;
+import org.chromium.base.test.BaseRobolectricTestRunner;
 import org.chromium.components.background_task_scheduler.BackgroundTaskScheduler;
 import org.chromium.components.background_task_scheduler.TaskIds;
 import org.chromium.components.background_task_scheduler.TaskInfo;
 import org.chromium.content_public.browser.test.util.TestThreadUtils;
+import org.chromium.gms.shadows.ShadowChromiumPlayServicesAvailability;
 
 import java.util.concurrent.TimeUnit;
 
 /**
  * Tests for {@link BackgroundTaskSchedulerImpl}.
  */
-@RunWith(BaseJUnit4ClassRunner.class)
+@RunWith(BaseRobolectricTestRunner.class)
+@Config(manifest = Config.NONE,
+        shadows = {ShadowGcmNetworkManager.class, ShadowChromiumPlayServicesAvailability.class})
 public class BackgroundTaskSchedulerImplWithMockTest {
     private static final int TEST_MINUTES = 10;
 
@@ -37,7 +39,6 @@
     }
 
     @Test
-    @SmallTest
     public void testOneOffTaskScheduling() {
         TaskInfo.TimingInfo timingInfo =
                 TaskInfo.OneOffInfo.create()
@@ -53,7 +54,6 @@
     }
 
     @Test
-    @SmallTest
     public void testPeriodicTaskScheduling() {
         TaskInfo.TimingInfo timingInfo =
                 TaskInfo.PeriodicInfo.create()
@@ -69,7 +69,6 @@
     }
 
     @Test
-    @SmallTest
     public void testTaskCanceling() {
         TaskInfo.TimingInfo timingInfo =
                 TaskInfo.OneOffInfo.create()
diff --git a/components/background_task_scheduler/internal/android/javatests/src/org/chromium/components/background_task_scheduler/internal/BackgroundTaskSchedulerJobServiceTest.java b/components/background_task_scheduler/internal/android/java/src/org/chromium/components/background_task_scheduler/internal/BackgroundTaskSchedulerJobServiceTest.java
similarity index 88%
rename from components/background_task_scheduler/internal/android/javatests/src/org/chromium/components/background_task_scheduler/internal/BackgroundTaskSchedulerJobServiceTest.java
rename to components/background_task_scheduler/internal/android/java/src/org/chromium/components/background_task_scheduler/internal/BackgroundTaskSchedulerJobServiceTest.java
index 8a4b33e..c799393 100644
--- a/components/background_task_scheduler/internal/android/javatests/src/org/chromium/components/background_task_scheduler/internal/BackgroundTaskSchedulerJobServiceTest.java
+++ b/components/background_task_scheduler/internal/android/java/src/org/chromium/components/background_task_scheduler/internal/BackgroundTaskSchedulerJobServiceTest.java
@@ -8,16 +8,14 @@
 import android.os.Build;
 import android.os.Bundle;
 import android.os.PersistableBundle;
-import android.support.test.InstrumentationRegistry;
-
-import androidx.test.filters.SmallTest;
 
 import org.junit.Assert;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.robolectric.RuntimeEnvironment;
 
-import org.chromium.base.test.BaseJUnit4ClassRunner;
+import org.chromium.base.test.BaseRobolectricTestRunner;
 import org.chromium.base.test.util.MinAndroidSdkLevel;
 import org.chromium.components.background_task_scheduler.TaskIds;
 import org.chromium.components.background_task_scheduler.TaskInfo;
@@ -27,7 +25,7 @@
 /**
  * Tests for {@link BackgroundTaskSchedulerJobService}.
  */
-@RunWith(BaseJUnit4ClassRunner.class)
+@RunWith(BaseRobolectricTestRunner.class)
 @MinAndroidSdkLevel(Build.VERSION_CODES.LOLLIPOP_MR1)
 public class BackgroundTaskSchedulerJobServiceTest {
     private static final long CLOCK_TIME_MS = 1415926535000L;
@@ -45,20 +43,18 @@
     }
 
     @Test
-    @SmallTest
     public void testOneOffTaskWithDeadline() {
         TaskInfo.TimingInfo timingInfo =
                 TaskInfo.OneOffInfo.create().setWindowEndTimeMs(TIME_200_MIN_TO_MS).build();
         TaskInfo oneOffTask = TaskInfo.createTask(TaskIds.TEST, timingInfo).build();
         JobInfo jobInfo = BackgroundTaskSchedulerJobService.createJobInfoFromTaskInfo(
-                InstrumentationRegistry.getTargetContext(), oneOffTask);
+                RuntimeEnvironment.getApplication(), oneOffTask);
         Assert.assertEquals(oneOffTask.getTaskId(), jobInfo.getId());
         Assert.assertFalse(jobInfo.isPeriodic());
         Assert.assertEquals(TIME_200_MIN_TO_MS, jobInfo.getMaxExecutionDelayMillis());
     }
 
     @Test
-    @SmallTest
     public void testOneOffTaskWithDeadlineAndExpiration() {
         TaskInfo.TimingInfo timingInfo = TaskInfo.OneOffInfo.create()
                                                  .setWindowEndTimeMs(TIME_200_MIN_TO_MS)
@@ -66,7 +62,7 @@
                                                  .build();
         TaskInfo oneOffTask = TaskInfo.createTask(TaskIds.TEST, timingInfo).build();
         JobInfo jobInfo = BackgroundTaskSchedulerJobService.createJobInfoFromTaskInfo(
-                InstrumentationRegistry.getTargetContext(), oneOffTask);
+                RuntimeEnvironment.getApplication(), oneOffTask);
         Assert.assertEquals(END_TIME_WITH_DEADLINE_MS, jobInfo.getMaxExecutionDelayMillis());
         Assert.assertEquals(CLOCK_TIME_MS,
                 jobInfo.getExtras().getLong(BackgroundTaskSchedulerGcmNetworkManager
@@ -77,7 +73,6 @@
     }
 
     @Test
-    @SmallTest
     public void testOneOffTaskWithWindow() {
         TaskInfo.TimingInfo timingInfo = TaskInfo.OneOffInfo.create()
                                                  .setWindowStartTimeMs(TIME_100_MIN_TO_MS)
@@ -85,7 +80,7 @@
                                                  .build();
         TaskInfo oneOffTask = TaskInfo.createTask(TaskIds.TEST, timingInfo).build();
         JobInfo jobInfo = BackgroundTaskSchedulerJobService.createJobInfoFromTaskInfo(
-                InstrumentationRegistry.getTargetContext(), oneOffTask);
+                RuntimeEnvironment.getApplication(), oneOffTask);
         Assert.assertEquals(oneOffTask.getTaskId(), jobInfo.getId());
         Assert.assertFalse(jobInfo.isPeriodic());
         Assert.assertEquals(TIME_100_MIN_TO_MS, jobInfo.getMinLatencyMillis());
@@ -93,7 +88,6 @@
     }
 
     @Test
-    @SmallTest
     public void testOneOffTaskWithWindowAndExpiration() {
         TaskInfo.TimingInfo timingInfo = TaskInfo.OneOffInfo.create()
                                                  .setWindowStartTimeMs(TIME_100_MIN_TO_MS)
@@ -102,7 +96,7 @@
                                                  .build();
         TaskInfo oneOffTask = TaskInfo.createTask(TaskIds.TEST, timingInfo).build();
         JobInfo jobInfo = BackgroundTaskSchedulerJobService.createJobInfoFromTaskInfo(
-                InstrumentationRegistry.getTargetContext(), oneOffTask);
+                RuntimeEnvironment.getApplication(), oneOffTask);
         Assert.assertEquals(
                 oneOffTask.getOneOffInfo().getWindowStartTimeMs(), jobInfo.getMinLatencyMillis());
         Assert.assertEquals(END_TIME_WITH_DEADLINE_MS, jobInfo.getMaxExecutionDelayMillis());
@@ -115,20 +109,18 @@
     }
 
     @Test
-    @SmallTest
     public void testPeriodicTaskWithoutFlex() {
         TaskInfo.TimingInfo timingInfo =
                 TaskInfo.PeriodicInfo.create().setIntervalMs(TIME_200_MIN_TO_MS).build();
         TaskInfo periodicTask = TaskInfo.createTask(TaskIds.TEST, timingInfo).build();
         JobInfo jobInfo = BackgroundTaskSchedulerJobService.createJobInfoFromTaskInfo(
-                InstrumentationRegistry.getTargetContext(), periodicTask);
+                RuntimeEnvironment.getApplication(), periodicTask);
         Assert.assertEquals(periodicTask.getTaskId(), jobInfo.getId());
         Assert.assertTrue(jobInfo.isPeriodic());
         Assert.assertEquals(TIME_200_MIN_TO_MS, jobInfo.getIntervalMillis());
     }
 
     @Test
-    @SmallTest
     public void testPeriodicTaskWithFlex() {
         TaskInfo.TimingInfo timingInfo = TaskInfo.PeriodicInfo.create()
                                                  .setIntervalMs(TIME_200_MIN_TO_MS)
@@ -136,7 +128,7 @@
                                                  .build();
         TaskInfo periodicTask = TaskInfo.createTask(TaskIds.TEST, timingInfo).build();
         JobInfo jobInfo = BackgroundTaskSchedulerJobService.createJobInfoFromTaskInfo(
-                InstrumentationRegistry.getTargetContext(), periodicTask);
+                RuntimeEnvironment.getApplication(), periodicTask);
         Assert.assertEquals(TIME_200_MIN_TO_MS, jobInfo.getIntervalMillis());
         if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
             Assert.assertEquals(TIME_50_MIN_TO_MS, jobInfo.getFlexMillis());
@@ -144,7 +136,6 @@
     }
 
     @Test
-    @SmallTest
     public void testTaskInfoWithExtras() {
         Bundle taskExtras = new Bundle();
         taskExtras.putString("foo", "bar");
@@ -155,7 +146,7 @@
         TaskInfo oneOffTask =
                 TaskInfo.createTask(TaskIds.TEST, timingInfo).setExtras(taskExtras).build();
         JobInfo jobInfo = BackgroundTaskSchedulerJobService.createJobInfoFromTaskInfo(
-                InstrumentationRegistry.getTargetContext(), oneOffTask);
+                RuntimeEnvironment.getApplication(), oneOffTask);
         Assert.assertEquals(oneOffTask.getTaskId(), jobInfo.getId());
         PersistableBundle jobExtras = jobInfo.getExtras();
         PersistableBundle persistableBundle = jobExtras.getPersistableBundle(
@@ -167,25 +158,22 @@
     }
 
     @Test
-    @SmallTest
     public void testTaskInfoWithManyConstraints() {
         TaskInfo.TimingInfo timingInfo =
                 TaskInfo.OneOffInfo.create().setWindowEndTimeMs(TIME_200_MIN_TO_MS).build();
         TaskInfo.Builder taskBuilder = TaskInfo.createTask(TaskIds.TEST, timingInfo);
 
         JobInfo jobInfo = BackgroundTaskSchedulerJobService.createJobInfoFromTaskInfo(
-                InstrumentationRegistry.getTargetContext(),
-                taskBuilder.setIsPersisted(true).build());
+                RuntimeEnvironment.getApplication(), taskBuilder.setIsPersisted(true).build());
         Assert.assertTrue(jobInfo.isPersisted());
 
         jobInfo = BackgroundTaskSchedulerJobService.createJobInfoFromTaskInfo(
-                InstrumentationRegistry.getTargetContext(),
+                RuntimeEnvironment.getApplication(),
                 taskBuilder.setRequiredNetworkType(TaskInfo.NetworkType.UNMETERED).build());
         Assert.assertEquals(JobInfo.NETWORK_TYPE_UNMETERED, jobInfo.getNetworkType());
 
         jobInfo = BackgroundTaskSchedulerJobService.createJobInfoFromTaskInfo(
-                InstrumentationRegistry.getTargetContext(),
-                taskBuilder.setRequiresCharging(true).build());
+                RuntimeEnvironment.getApplication(), taskBuilder.setRequiresCharging(true).build());
         Assert.assertTrue(jobInfo.isRequireCharging());
     }
 }
diff --git a/components/background_task_scheduler/internal/android/javatests/src/org/chromium/components/background_task_scheduler/internal/BundleToPersistableBundleConverterTest.java b/components/background_task_scheduler/internal/android/java/src/org/chromium/components/background_task_scheduler/internal/BundleToPersistableBundleConverterTest.java
similarity index 94%
rename from components/background_task_scheduler/internal/android/javatests/src/org/chromium/components/background_task_scheduler/internal/BundleToPersistableBundleConverterTest.java
rename to components/background_task_scheduler/internal/android/java/src/org/chromium/components/background_task_scheduler/internal/BundleToPersistableBundleConverterTest.java
index 2b82b3c..420375c 100644
--- a/components/background_task_scheduler/internal/android/javatests/src/org/chromium/components/background_task_scheduler/internal/BundleToPersistableBundleConverterTest.java
+++ b/components/background_task_scheduler/internal/android/java/src/org/chromium/components/background_task_scheduler/internal/BundleToPersistableBundleConverterTest.java
@@ -8,27 +8,22 @@
 import android.os.Bundle;
 import android.os.PersistableBundle;
 
-import androidx.test.filters.SmallTest;
-
 import org.junit.Assert;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
-import org.chromium.base.test.BaseJUnit4ClassRunner;
+import org.chromium.base.test.BaseRobolectricTestRunner;
 import org.chromium.base.test.util.MinAndroidSdkLevel;
 
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Set;
 
-/**
- * Tests for {@link BundleToPersistableBundleConverter}.
- */
-@RunWith(BaseJUnit4ClassRunner.class)
+/** Tests for {@link BundleToPersistableBundleConverter}. */
+@RunWith(BaseRobolectricTestRunner.class)
 @MinAndroidSdkLevel(Build.VERSION_CODES.LOLLIPOP_MR1)
 public class BundleToPersistableBundleConverterTest {
     @Test
-    @SmallTest
     public void testAllValidConversions() {
         Bundle bundle = new Bundle();
         bundle.putString("s", "bar");
@@ -61,7 +56,6 @@
     }
 
     @Test
-    @SmallTest
     public void testSomeBadConversions() {
         Bundle bundle = new Bundle();
         bundle.putString("s", "this should be there");
@@ -85,7 +79,6 @@
     }
 
     @Test
-    @SmallTest
     public void testNullValue() {
         Bundle bundle = new Bundle();
         bundle.putString("foo", "value1");
diff --git a/components/background_task_scheduler/internal/android/javatests/src/org/chromium/components/background_task_scheduler/internal/MockBackgroundTaskSchedulerDelegate.java b/components/background_task_scheduler/internal/android/java/src/org/chromium/components/background_task_scheduler/internal/MockBackgroundTaskSchedulerDelegate.java
similarity index 100%
rename from components/background_task_scheduler/internal/android/javatests/src/org/chromium/components/background_task_scheduler/internal/MockBackgroundTaskSchedulerDelegate.java
rename to components/background_task_scheduler/internal/android/java/src/org/chromium/components/background_task_scheduler/internal/MockBackgroundTaskSchedulerDelegate.java
diff --git a/components/crash/android/BUILD.gn b/components/crash/android/BUILD.gn
index b7ef049..d20c61a 100644
--- a/components/crash/android/BUILD.gn
+++ b/components/crash/android/BUILD.gn
@@ -39,23 +39,11 @@
   sources += _jni_sources
 }
 
-android_library("javatests") {
-  testonly = true
-  sources = [ "javatests/src/org/chromium/components/crash/PureJavaExceptionReporterTest.java" ]
-  deps = [
-    ":java",
-    "//base:base_java_test_support",
-    "//components/minidump_uploader:minidump_uploader_java_test_support",
-    "//content/public/test/android:content_java_test_support",
-    "//third_party/androidx:androidx_test_runner_java",
-    "//third_party/junit",
-  ]
-}
-
 robolectric_library("junit") {
   testonly = true
   sources = [
     "junit/src/org/chromium/components/crash/LogcatCrashExtractorTest.java",
+    "junit/src/org/chromium/components/crash/PureJavaExceptionReporterTest.java",
     "junit/src/org/chromium/components/crash/anr/AnrCollectorTest.java",
   ]
   deps = [
@@ -64,7 +52,10 @@
     ":java",
     "//base:base_java_test_support",
     "//base:base_junit_test_support",
+    "//components/minidump_uploader:minidump_uploader_java_test_support",
+    "//content/public/test/android:content_java_test_support",
     "//third_party/android_deps:robolectric_all_java",
+    "//third_party/androidx:androidx_test_runner_java",
     "//third_party/junit",
   ]
 }
diff --git a/components/crash/android/javatests/src/org/chromium/components/crash/DEPS b/components/crash/android/junit/src/org/chromium/components/crash/DEPS
similarity index 97%
rename from components/crash/android/javatests/src/org/chromium/components/crash/DEPS
rename to components/crash/android/junit/src/org/chromium/components/crash/DEPS
index 877e2a4d..aa93591 100644
--- a/components/crash/android/javatests/src/org/chromium/components/crash/DEPS
+++ b/components/crash/android/junit/src/org/chromium/components/crash/DEPS
@@ -1,3 +1,3 @@
 include_rules = [
   "+content/public/test/android",
-]
\ No newline at end of file
+]
diff --git a/components/crash/android/javatests/src/org/chromium/components/crash/PureJavaExceptionReporterTest.java b/components/crash/android/junit/src/org/chromium/components/crash/PureJavaExceptionReporterTest.java
similarity index 96%
rename from components/crash/android/javatests/src/org/chromium/components/crash/PureJavaExceptionReporterTest.java
rename to components/crash/android/junit/src/org/chromium/components/crash/PureJavaExceptionReporterTest.java
index 5bff277..f425b2c 100644
--- a/components/crash/android/javatests/src/org/chromium/components/crash/PureJavaExceptionReporterTest.java
+++ b/components/crash/android/junit/src/org/chromium/components/crash/PureJavaExceptionReporterTest.java
@@ -11,7 +11,8 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
-import org.chromium.base.test.BaseJUnit4ClassRunner;
+import org.chromium.base.test.BaseRobolectricTestRunner;
+import org.chromium.base.test.util.Batch;
 import org.chromium.components.minidump_uploader.CrashTestRule;
 import org.chromium.content_public.browser.test.util.TestThreadUtils;
 
@@ -23,7 +24,8 @@
 /**
  * Unittests for {@link PureJavaExceptionReporter}.
  */
-@RunWith(BaseJUnit4ClassRunner.class)
+@RunWith(BaseRobolectricTestRunner.class)
+@Batch(Batch.UNIT_TESTS)
 public class PureJavaExceptionReporterTest {
     @Rule
     public CrashTestRule mTestRule = new CrashTestRule();
diff --git a/components/embedder_support/android/javatests/src/org/chromium/components/embedder_support/delegate/ColorPickerDialogRenderTest.java b/components/embedder_support/android/javatests/src/org/chromium/components/embedder_support/delegate/ColorPickerDialogRenderTest.java
index 40db301..6a5b1fc 100644
--- a/components/embedder_support/android/javatests/src/org/chromium/components/embedder_support/delegate/ColorPickerDialogRenderTest.java
+++ b/components/embedder_support/android/javatests/src/org/chromium/components/embedder_support/delegate/ColorPickerDialogRenderTest.java
@@ -22,6 +22,7 @@
 import org.chromium.base.test.params.ParameterAnnotations.UseRunnerDelegate;
 import org.chromium.base.test.params.ParameterSet;
 import org.chromium.base.test.params.ParameterizedRunner;
+import org.chromium.base.test.util.Batch;
 import org.chromium.base.test.util.Feature;
 import org.chromium.content_public.browser.test.util.TestThreadUtils;
 import org.chromium.ui.R;
@@ -37,6 +38,7 @@
  */
 @RunWith(ParameterizedRunner.class)
 @UseRunnerDelegate(BaseJUnit4RunnerDelegate.class)
+@Batch(Batch.UNIT_TESTS)
 public class ColorPickerDialogRenderTest extends BlankUiTestActivityTestCase {
     @ParameterAnnotations.ClassParameter
     private static List<ParameterSet> sClassParams =
diff --git a/components/live_caption/views/caption_bubble.cc b/components/live_caption/views/caption_bubble.cc
index ac3258fe..d85500a2 100644
--- a/components/live_caption/views/caption_bubble.cc
+++ b/components/live_caption/views/caption_bubble.cc
@@ -90,23 +90,6 @@
 static constexpr int kErrorMessageBetweenChildSpacingDip = 16;
 static constexpr int kNoActivityIntervalSeconds = 5;
 
-// These values are persisted to logs. Entries should not be renumbered and
-// numeric values should never be reused. These should be the same as
-// LiveCaptionSessionEvent in enums.xml.
-enum class SessionEvent {
-  // We began showing captions for an audio stream.
-  kStreamStarted = 0,
-  // The audio stream ended and the caption bubble closes.
-  kStreamEnded = 1,
-  // The close button was clicked, so we stopped listening to an audio stream.
-  kCloseButtonClicked = 2,
-  kMaxValue = kCloseButtonClicked,
-};
-
-void LogSessionEvent(SessionEvent event) {
-  base::UmaHistogramEnumeration("Accessibility.LiveCaption.Session", event);
-}
-
 std::unique_ptr<views::ImageButton> BuildImageButton(
     views::Button::PressedCallback callback,
     const int tooltip_text_id) {
@@ -752,7 +735,7 @@
 
 #if BUILDFLAG(IS_WIN)
   if (error_type ==
-      CaptionBubbleErrorType::MEDIA_FOUNDATION_RENDERER_UNSUPPORTED) {
+      CaptionBubbleErrorType::kMediaFoundationRendererUnsupported) {
     media_foundation_renderer_error_message_->SetVisible(has_error);
     generic_error_message_->SetVisible(false);
   } else {
@@ -1087,7 +1070,7 @@
 void CaptionBubble::MediaFoundationErrorCheckboxPressed() {
 #if BUILDFLAG(IS_WIN)
   error_silenced_callback_.Run(
-      CaptionBubbleErrorType::MEDIA_FOUNDATION_RENDERER_UNSUPPORTED,
+      CaptionBubbleErrorType::kMediaFoundationRendererUnsupported,
       media_foundation_renderer_error_checkbox_->GetChecked());
 #endif
 }
@@ -1095,7 +1078,13 @@
 bool CaptionBubble::HasMediaFoundationError() {
   return (model_ && model_->HasError() &&
           model_->ErrorType() ==
-              CaptionBubbleErrorType::MEDIA_FOUNDATION_RENDERER_UNSUPPORTED);
+              CaptionBubbleErrorType::kMediaFoundationRendererUnsupported);
+}
+
+void CaptionBubble::LogSessionEvent(SessionEvent event) {
+  if (model_ && !model_->HasError()) {
+    base::UmaHistogramEnumeration("Accessibility.LiveCaption.Session2", event);
+  }
 }
 
 bool CaptionBubble::HasActivity() {
diff --git a/components/live_caption/views/caption_bubble.h b/components/live_caption/views/caption_bubble.h
index d60a0d9..e646a4a2 100644
--- a/components/live_caption/views/caption_bubble.h
+++ b/components/live_caption/views/caption_bubble.h
@@ -41,6 +41,19 @@
 class CaptionBubbleFrameView;
 class CaptionBubbleLabel;
 
+// These values are persisted to logs. Entries should not be renumbered and
+// numeric values should never be reused. These should be the same as
+// LiveCaptionSessionEvent in enums.xml.
+enum class SessionEvent {
+  // We began showing captions for an audio stream.
+  kStreamStarted = 0,
+  // The audio stream ended and the caption bubble closes.
+  kStreamEnded = 1,
+  // The close button was clicked, so we stopped listening to an audio stream.
+  kCloseButtonClicked = 2,
+  kMaxValue = kCloseButtonClicked,
+};
+
 ///////////////////////////////////////////////////////////////////////////////
 // Caption Bubble
 //
@@ -160,6 +173,8 @@
   void MediaFoundationErrorCheckboxPressed();
   bool HasMediaFoundationError();
 
+  void LogSessionEvent(SessionEvent event);
+
   // Unowned. Owned by views hierarchy.
   raw_ptr<CaptionBubbleLabel> label_;
   raw_ptr<views::Label> title_;
diff --git a/components/live_caption/views/caption_bubble_model.cc b/components/live_caption/views/caption_bubble_model.cc
index a88886c..e120189 100644
--- a/components/live_caption/views/caption_bubble_model.cc
+++ b/components/live_caption/views/caption_bubble_model.cc
@@ -5,6 +5,7 @@
 #include "components/live_caption/views/caption_bubble_model.h"
 
 #include "base/callback_forward.h"
+#include "base/metrics/histogram_functions.h"
 #include "components/live_caption/caption_bubble_context.h"
 #include "components/live_caption/views/caption_bubble.h"
 
@@ -33,7 +34,7 @@
   if (observer_) {
     observer_->OnTextChanged();
     observer_->OnErrorChanged(
-        CaptionBubbleErrorType::GENERIC, base::RepeatingClosure(),
+        CaptionBubbleErrorType::kGeneric, base::RepeatingClosure(),
         base::BindRepeating(
             [](CaptionBubbleErrorType error_type, bool checked) {}));
   }
@@ -55,7 +56,7 @@
     has_error_ = false;
     if (observer_)
       observer_->OnErrorChanged(
-          CaptionBubbleErrorType::GENERIC, base::RepeatingClosure(),
+          CaptionBubbleErrorType::kGeneric, base::RepeatingClosure(),
           base::BindRepeating(
               [](CaptionBubbleErrorType error_type, bool checked) {}));
   }
@@ -77,9 +78,12 @@
     OnDoNotShowAgainClickedCallback error_silenced_callback) {
   has_error_ = true;
   error_type_ = error_type;
-  if (observer_)
+  if (observer_) {
+    base::UmaHistogramEnumeration(
+        "Accessibility.LiveCaption.CaptionBubbleError", error_type);
     observer_->OnErrorChanged(error_type, std::move(error_clicked_callback),
                               std::move(error_silenced_callback));
+  }
 }
 
 void CaptionBubbleModel::ClearText() {
diff --git a/components/live_caption/views/caption_bubble_model.h b/components/live_caption/views/caption_bubble_model.h
index 06fbc2c7..26e53d5 100644
--- a/components/live_caption/views/caption_bubble_model.h
+++ b/components/live_caption/views/caption_bubble_model.h
@@ -15,7 +15,14 @@
 class CaptionBubble;
 class CaptionBubbleContext;
 
-enum CaptionBubbleErrorType { GENERIC, MEDIA_FOUNDATION_RENDERER_UNSUPPORTED };
+// These values are persisted to logs. Entries should not be renumbered and
+// numeric values should never be reused.
+enum CaptionBubbleErrorType {
+  kGeneric = 0,
+  kMediaFoundationRendererUnsupported = 1,
+  kMaxValue = kMediaFoundationRendererUnsupported
+};
+
 using OnErrorClickedCallback = base::RepeatingCallback<void()>;
 using OnDoNotShowAgainClickedCallback =
     base::RepeatingCallback<void(CaptionBubbleErrorType, bool)>;
@@ -93,7 +100,7 @@
   bool has_error_ = false;
 
   // The most recent error type encountered.
-  CaptionBubbleErrorType error_type_ = CaptionBubbleErrorType::GENERIC;
+  CaptionBubbleErrorType error_type_ = CaptionBubbleErrorType::kGeneric;
 
   // The CaptionBubble observing changes to this model.
   raw_ptr<CaptionBubble> observer_ = nullptr;
diff --git a/components/pdf/renderer/pdf_accessibility_tree.cc b/components/pdf/renderer/pdf_accessibility_tree.cc
index 6faecc06..083b312 100644
--- a/components/pdf/renderer/pdf_accessibility_tree.cc
+++ b/components/pdf/renderer/pdf_accessibility_tree.cc
@@ -250,8 +250,9 @@
     int char_index) {
   std::string chars_utf8;
   for (uint32_t i = 0; i < text_run.len; ++i) {
-    base::WriteUnicodeCharacter(chars[char_index + i].unicode_character,
-                                &chars_utf8);
+    base::WriteUnicodeCharacter(
+        static_cast<base_icu::UChar32>(chars[char_index + i].unicode_character),
+        &chars_utf8);
   }
   return chars_utf8;
 }
diff --git a/components/signin/public/android/BUILD.gn b/components/signin/public/android/BUILD.gn
index 464c819..0983c4c 100644
--- a/components/signin/public/android/BUILD.gn
+++ b/components/signin/public/android/BUILD.gn
@@ -127,7 +127,7 @@
   sources = [ "javatests/res/drawable/test_profile_picture.xml" ]
 }
 
-android_library("javatests") {
+android_library("unit_device_javatests") {
   testonly = true
   deps = [
     ":java",
diff --git a/components/strictmode/android/BUILD.gn b/components/strictmode/android/BUILD.gn
index 5997c7d..403a30c 100644
--- a/components/strictmode/android/BUILD.gn
+++ b/components/strictmode/android/BUILD.gn
@@ -18,7 +18,7 @@
   ]
 }
 
-android_library("javatests") {
+android_library("unit_device_javatests") {
   testonly = true
   sources = [ "javatests/src/org/chromium/components/strictmode/ThreadStrictModeInterceptorTest.java" ]
   deps = [
diff --git a/components/sync/base/model_type.cc b/components/sync/base/model_type.cc
index 85360b4..35d173e 100644
--- a/components/sync/base/model_type.cc
+++ b/components/sync/base/model_type.cc
@@ -162,6 +162,10 @@
     {HISTORY, "HISTORY", "history", "History",
      sync_pb::EntitySpecifics::kHistoryFieldNumber,
      ModelTypeForHistograms::kHistory},
+    {PRINTERS_AUTHORIZATION_SERVERS, "PRINTERS_AUTHORIZATION_SERVER",
+     "printers_authorization_servers", "Printers Authorization Servers",
+     sync_pb::EntitySpecifics::kPrintersAuthorizationServerFieldNumber,
+     ModelTypeForHistograms::kPrintersAuthorizationServers},
     // ---- Proxy types ----
     {PROXY_TABS, "", "", "Tabs", -1, ModelTypeForHistograms::kProxyTabs},
     // ---- Control Types ----
@@ -173,11 +177,11 @@
 static_assert(std::size(kModelTypeInfoMap) == GetNumModelTypes(),
               "kModelTypeInfoMap should have GetNumModelTypes() elements");
 
-static_assert(39 == syncer::GetNumModelTypes(),
+static_assert(40 == syncer::GetNumModelTypes(),
               "When adding a new type, update enum SyncModelTypes in enums.xml "
               "and suffix SyncModelType in histograms.xml.");
 
-static_assert(39 == syncer::GetNumModelTypes(),
+static_assert(40 == syncer::GetNumModelTypes(),
               "When adding a new type, update kAllocatorDumpNameAllowlist in "
               "base/trace_event/memory_infra_background_allowlist.cc.");
 
@@ -259,6 +263,9 @@
     case PRINTERS:
       specifics->mutable_printer();
       break;
+    case PRINTERS_AUTHORIZATION_SERVERS:
+      specifics->mutable_printers_authorization_server();
+      break;
     case READING_LIST:
       specifics->mutable_reading_list();
       break;
@@ -321,7 +328,7 @@
 }
 
 ModelType GetModelTypeFromSpecifics(const sync_pb::EntitySpecifics& specifics) {
-  static_assert(39 == syncer::GetNumModelTypes(),
+  static_assert(40 == syncer::GetNumModelTypes(),
                 "When adding new protocol types, the following type lookup "
                 "logic must be updated.");
   if (specifics.has_bookmark())
@@ -398,6 +405,8 @@
     return WORKSPACE_DESK;
   if (specifics.has_history())
     return HISTORY;
+  if (specifics.has_printers_authorization_server())
+    return PRINTERS_AUTHORIZATION_SERVERS;
 
   // This client version doesn't understand |specifics|.
   DVLOG(1) << "Unknown datatype in sync proto.";
@@ -405,7 +414,7 @@
 }
 
 ModelTypeSet EncryptableUserTypes() {
-  static_assert(39 == syncer::GetNumModelTypes(),
+  static_assert(40 == syncer::GetNumModelTypes(),
                 "If adding an unencryptable type, remove from "
                 "encryptable_user_types below.");
   ModelTypeSet encryptable_user_types = UserTypes();
diff --git a/components/sync/base/model_type.h b/components/sync/base/model_type.h
index 9636c9b..29a038f 100644
--- a/components/sync/base/model_type.h
+++ b/components/sync/base/model_type.h
@@ -136,6 +136,8 @@
   // WEBAUTHN_CREDENTIAL,
   // Synced history. An entity roughly corresponds to a navigation.
   HISTORY,
+  // Trusted Authorization Servers for printers. ChromeOS only.
+  PRINTERS_AUTHORIZATION_SERVERS,
 
   // Proxy types are excluded from the sync protocol, but are still considered
   // real user types. By convention, we prefix them with 'PROXY_' to distinguish
@@ -233,7 +235,8 @@
   kAutofillWalletOffer = 49,
   kWorkspaceDesk = 50,
   kHistory = 51,
-  kMaxValue = kHistory
+  kPrintersAuthorizationServers = 52,
+  kMaxValue = kPrintersAuthorizationServers
 };
 
 // Used to mark the type of EntitySpecifics that has no actual data.
@@ -256,7 +259,7 @@
       ARC_PACKAGE, PRINTERS, READING_LIST, USER_EVENTS, NIGORI, USER_CONSENTS,
       SEND_TAB_TO_SELF, SECURITY_EVENTS, WEB_APPS, WIFI_CONFIGURATIONS,
       OS_PREFERENCES, OS_PRIORITY_PREFERENCES, SHARING_MESSAGE, WORKSPACE_DESK,
-      HISTORY);
+      HISTORY, PRINTERS_AUTHORIZATION_SERVERS);
 }
 
 // These are the normal user-controlled types. This is to distinguish from
diff --git a/components/sync/base/model_type_unittest.cc b/components/sync/base/model_type_unittest.cc
index 93f127d..c929c48f 100644
--- a/components/sync/base/model_type_unittest.cc
+++ b/components/sync/base/model_type_unittest.cc
@@ -47,6 +47,7 @@
   EXPECT_TRUE(IsRealDataType(APPS));
   EXPECT_TRUE(IsRealDataType(ARC_PACKAGE));
   EXPECT_TRUE(IsRealDataType(PRINTERS));
+  EXPECT_TRUE(IsRealDataType(PRINTERS_AUTHORIZATION_SERVERS));
   EXPECT_TRUE(IsRealDataType(READING_LIST));
 }
 
diff --git a/components/sync/base/user_selectable_type.cc b/components/sync/base/user_selectable_type.cc
index 4bf73c7..7b4c7bf 100644
--- a/components/sync/base/user_selectable_type.cc
+++ b/components/sync/base/user_selectable_type.cc
@@ -51,6 +51,7 @@
       if (!chromeos::features::IsSyncSettingsCategorizationEnabled()) {
         // SyncSettingsCategorization makes Printers a separate OS setting.
         model_types.Put(PRINTERS);
+        model_types.Put(PRINTERS_AUTHORIZATION_SERVERS);
 
         // Workspace desk template is an OS-only feature. When
         // SyncSettingsCategorization is disabled, WORKSPACE_DESK should be
@@ -127,10 +128,10 @@
               APPS,
               {APP_LIST, APPS, APP_SETTINGS, ARC_PACKAGE, WEB_APPS}};
     case UserSelectableOsType::kOsPreferences:
-      return {
-          kOsPreferencesTypeName,
-          OS_PREFERENCES,
-          {OS_PREFERENCES, OS_PRIORITY_PREFERENCES, PRINTERS, WORKSPACE_DESK}};
+      return {kOsPreferencesTypeName,
+              OS_PREFERENCES,
+              {OS_PREFERENCES, OS_PRIORITY_PREFERENCES, PRINTERS,
+               PRINTERS_AUTHORIZATION_SERVERS, WORKSPACE_DESK}};
     case UserSelectableOsType::kOsWifiConfigurations:
       return {kOsWifiConfigurationsTypeName,
               WIFI_CONFIGURATIONS,
diff --git a/components/sync/driver/resources/BUILD.gn b/components/sync/driver/resources/BUILD.gn
index c26d83c..9aa9e0f8 100644
--- a/components/sync/driver/resources/BUILD.gn
+++ b/components/sync/driver/resources/BUILD.gn
@@ -69,7 +69,7 @@
     ":chrome_sync",
 
     #":data",
-    ":search",
+    #":search",
     ":sync_index",
     ":sync_log",
 
@@ -101,7 +101,7 @@
 js_library("search") {
   deps = [
     "//ui/webui/resources/js:util.m",
-    "//ui/webui/resources/js/cr/ui:splitter",
+    "//ui/webui/resources/js/cr/ui:list.m",
   ]
 }
 
diff --git a/components/sync/driver/resources/index.html b/components/sync/driver/resources/index.html
index b3d7237..e05b762 100644
--- a/components/sync/driver/resources/index.html
+++ b/components/sync/driver/resources/index.html
@@ -158,7 +158,7 @@
       <div id="sync-node-browser-container">
         <div id="sync-node-tree-container">
         </div>
-        <div id="sync-node-splitter"></div>
+        <cr-splitter id="sync-node-splitter"></cr-splitter>
         <div id="node-details">
           <table>
             <tr>
@@ -221,7 +221,7 @@
 
     <div id="sync-results-container">
       <list id="sync-results-list"></list>
-      <div id="sync-results-splitter"></div>
+      <cr-splitter id="sync-results-splitter"></cr-splitter>
       <div id="sync-result-details-container">
         <pre id="sync-result-details"></pre>
       </div>
diff --git a/components/sync/driver/resources/search.js b/components/sync/driver/resources/search.js
index 47f4127..377c8ad 100644
--- a/components/sync/driver/resources/search.js
+++ b/components/sync/driver/resources/search.js
@@ -2,16 +2,14 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import {decorate} from 'chrome://resources/js/cr/ui.m.js';
+import 'chrome://resources/cr_elements/cr_splitter/cr_splitter.js';
+
 import {ArrayDataModel} from 'chrome://resources/js/cr/ui/array_data_model.m.js';
 import {List} from 'chrome://resources/js/cr/ui/list.m.js';
-import {Splitter} from 'chrome://resources/js/cr/ui/splitter.js';
 import {$, getRequiredElement} from 'chrome://resources/js/util.m.js';
 
 import {decorateQuickQueryControls, decorateSearchControls} from './sync_search.js';
 
-decorate('#sync-results-splitter', Splitter);
-
 decorateQuickQueryControls(
     document.getElementsByClassName('sync-search-quicklink'),
     /** @type {!HTMLButtonElement} */ ($('sync-search-submit')),
diff --git a/components/sync/driver/resources/sync_node_browser.js b/components/sync/driver/resources/sync_node_browser.js
index 1ce98b1b..0f3f862 100644
--- a/components/sync/driver/resources/sync_node_browser.js
+++ b/components/sync/driver/resources/sync_node_browser.js
@@ -2,11 +2,10 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+import 'chrome://resources/cr_elements/cr_splitter/cr_splitter.js';
 import 'chrome://resources/cr_elements/cr_tree/cr_tree.js';
 import 'chrome://resources/cr_elements/cr_tree/cr_tree_item.js';
 
-import {define as crUiDefine} from 'chrome://resources/js/cr/ui.m.js';
-import {Splitter} from 'chrome://resources/js/cr/ui/splitter.js';
 import {$} from 'chrome://resources/js/util.m.js';
 
 import {getAllNodes} from './chrome_sync.js';
@@ -158,20 +157,12 @@
 
 document.addEventListener('DOMContentLoaded', function(e) {
   $('node-browser-refresh-button').addEventListener('click', refresh);
-  const customSplitter = crUiDefine('div');
-
-  customSplitter.prototype = {
-    __proto__: Splitter.prototype,
-
-    handleSplitterDragEnd(e) {
-      Splitter.prototype.handleSplitterDragEnd.apply(this, arguments);
-      const treeElement = $('sync-node-tree-container');
-      const newWidth = parseFloat(treeElement.style.width);
-      treeElement.style.minWidth = Math.max(newWidth, 50) + 'px';
-    }
-  };
-
-  customSplitter.decorate($('sync-node-splitter'));
+  const splitter = document.querySelector('#sync-node-splitter');
+  splitter.addEventListener('resize', () => {
+    const treeElement = document.querySelector('#sync-node-tree-container');
+    const newWidth = parseFloat(treeElement.style.width);
+    treeElement.style.minWidth = Math.max(newWidth, 50) + 'px';
+  });
 
   // Automatically trigger a refresh the first time this tab is selected.
   document.querySelector('cr-tab-box')
diff --git a/components/sync/driver/sync_user_settings_impl.cc b/components/sync/driver/sync_user_settings_impl.cc
index 94c6852..cc309eaa 100644
--- a/components/sync/driver/sync_user_settings_impl.cc
+++ b/components/sync/driver/sync_user_settings_impl.cc
@@ -252,7 +252,7 @@
 #endif
   types.RetainAll(registered_model_types_);
 
-  static_assert(39 == GetNumModelTypes(),
+  static_assert(40 == GetNumModelTypes(),
                 "If adding a new sync data type, update the list below below if"
                 " you want to disable the new data type for local sync.");
   types.PutAll(ControlTypes());
diff --git a/components/sync/driver/sync_user_settings_impl_unittest.cc b/components/sync/driver/sync_user_settings_impl_unittest.cc
index e06e7ac..883c7bf 100644
--- a/components/sync/driver/sync_user_settings_impl_unittest.cc
+++ b/components/sync/driver/sync_user_settings_impl_unittest.cc
@@ -39,9 +39,9 @@
   }
 #else
   // Ignore all Chrome OS types on non-Chrome OS platforms.
-  user_types.RemoveAll({APP_LIST, ARC_PACKAGE, OS_PREFERENCES,
-                        OS_PRIORITY_PREFERENCES, PRINTERS, WIFI_CONFIGURATIONS,
-                        WORKSPACE_DESK});
+  user_types.RemoveAll(
+      {APP_LIST, ARC_PACKAGE, OS_PREFERENCES, OS_PRIORITY_PREFERENCES, PRINTERS,
+       PRINTERS_AUTHORIZATION_SERVERS, WIFI_CONFIGURATIONS, WORKSPACE_DESK});
 #endif
   return user_types;
 }
diff --git a/components/sync/engine/cycle/data_type_tracker.cc b/components/sync/engine/cycle/data_type_tracker.cc
index 31832431..350938b 100644
--- a/components/sync/engine/cycle/data_type_tracker.cc
+++ b/components/sync/engine/cycle/data_type_tracker.cc
@@ -84,6 +84,7 @@
     case APP_LIST:
     case ARC_PACKAGE:
     case PRINTERS:
+    case PRINTERS_AUTHORIZATION_SERVERS:
     case READING_LIST:
     case USER_CONSENTS:
     case SEND_TAB_TO_SELF:
@@ -138,6 +139,7 @@
     case APP_LIST:
     case ARC_PACKAGE:
     case PRINTERS:
+    case PRINTERS_AUTHORIZATION_SERVERS:
     case READING_LIST:
     case USER_CONSENTS:
     case SEND_TAB_TO_SELF:
diff --git a/components/sync/nigori/nigori_state.cc b/components/sync/nigori/nigori_state.cc
index c2ebd2c1..3511e1e 100644
--- a/components/sync/nigori/nigori_state.cc
+++ b/components/sync/nigori/nigori_state.cc
@@ -64,7 +64,7 @@
 void UpdateNigoriSpecificsFromEncryptedTypes(
     ModelTypeSet encrypted_types,
     sync_pb::NigoriSpecifics* specifics) {
-  static_assert(39 == GetNumModelTypes(),
+  static_assert(40 == GetNumModelTypes(),
                 "If adding an encryptable type, update handling below.");
   specifics->set_encrypt_bookmarks(encrypted_types.Has(BOOKMARKS));
   specifics->set_encrypt_preferences(encrypted_types.Has(PREFERENCES));
@@ -86,6 +86,8 @@
   specifics->set_encrypt_app_list(encrypted_types.Has(APP_LIST));
   specifics->set_encrypt_arc_package(encrypted_types.Has(ARC_PACKAGE));
   specifics->set_encrypt_printers(encrypted_types.Has(PRINTERS));
+  specifics->set_encrypt_printers_authorization_servers(
+      encrypted_types.Has(PRINTERS_AUTHORIZATION_SERVERS));
   specifics->set_encrypt_reading_list(encrypted_types.Has(READING_LIST));
   specifics->set_encrypt_send_tab_to_self(
       encrypted_types.Has(SEND_TAB_TO_SELF));
diff --git a/components/sync/protocol/entity_specifics.proto b/components/sync/protocol/entity_specifics.proto
index 6078b07d..dee2616 100644
--- a/components/sync/protocol/entity_specifics.proto
+++ b/components/sync/protocol/entity_specifics.proto
@@ -38,6 +38,7 @@
 import "components/sync/protocol/password_specifics.proto";
 import "components/sync/protocol/preference_specifics.proto";
 import "components/sync/protocol/printer_specifics.proto";
+import "components/sync/protocol/printers_authorization_server_specifics.proto";
 import "components/sync/protocol/priority_preference_specifics.proto";
 import "components/sync/protocol/reading_list_specifics.proto";
 import "components/sync/protocol/search_engine_specifics.proto";
@@ -155,6 +156,7 @@
     // the server and by Play Services. (crbug.com/1223853)
     WebauthnCredentialSpecifics webauthn_credential = 895275;
     HistorySpecifics history = 963985;
+    PrintersAuthorizationServerSpecifics printers_authorization_server = 974304;
   }
   reserved 218175;
   reserved "wifi_credential";
diff --git a/components/sync/protocol/nigori_specifics.proto b/components/sync/protocol/nigori_specifics.proto
index 8d076e4..421d562 100644
--- a/components/sync/protocol/nigori_specifics.proto
+++ b/components/sync/protocol/nigori_specifics.proto
@@ -221,4 +221,8 @@
 
   // Boolean corresponding to whether workspace desk should be encrypted.
   optional bool encrypt_workspace_desk = 51;
+
+  // Boolean corresponding to whether printers authorization server items should
+  // be encrypted.
+  optional bool encrypt_printers_authorization_servers = 52;
 }
diff --git a/components/sync/protocol/printers_authorization_server_specifics.proto b/components/sync/protocol/printers_authorization_server_specifics.proto
new file mode 100644
index 0000000..06275c4
--- /dev/null
+++ b/components/sync/protocol/printers_authorization_server_specifics.proto
@@ -0,0 +1,25 @@
+// Copyright 2022 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.
+//
+// Sync protocol datatype extension for printer data.
+
+// If you change or add any fields in this file, update proto_visitors.h and
+// potentially proto_enum_conversions.{h, cc}.
+
+syntax = "proto2";
+
+option java_multiple_files = true;
+option java_package = "org.chromium.components.sync.protocol";
+
+option optimize_for = LITE_RUNTIME;
+
+package sync_pb;
+
+// Represents trusted Authorization Server for printers.
+message PrintersAuthorizationServerSpecifics {
+  // Universal Resource Identifier for the authorization server on the network.
+  // The scheme must be https. Example: https://address.example:port/path/path.
+  // This field must be unique and is required.
+  optional string uri = 1;
+}
diff --git a/components/sync/protocol/proto_value_conversions.cc b/components/sync/protocol/proto_value_conversions.cc
index 3e840db..65a2a25 100644
--- a/components/sync/protocol/proto_value_conversions.cc
+++ b/components/sync/protocol/proto_value_conversions.cc
@@ -33,6 +33,7 @@
 #include "components/sync/protocol/password_specifics.pb.h"
 #include "components/sync/protocol/preference_specifics.pb.h"
 #include "components/sync/protocol/printer_specifics.pb.h"
+#include "components/sync/protocol/printers_authorization_server_specifics.pb.h"
 #include "components/sync/protocol/priority_preference_specifics.pb.h"
 #include "components/sync/protocol/proto_visitors.h"
 #include "components/sync/protocol/reading_list_specifics.pb.h"
@@ -321,6 +322,7 @@
 IMPLEMENT_PROTO_TO_VALUE(PreferenceSpecifics)
 IMPLEMENT_PROTO_TO_VALUE(PrinterPPDReference)
 IMPLEMENT_PROTO_TO_VALUE(PrinterSpecifics)
+IMPLEMENT_PROTO_TO_VALUE(PrintersAuthorizationServerSpecifics)
 IMPLEMENT_PROTO_TO_VALUE(PriorityPreferenceSpecifics)
 IMPLEMENT_PROTO_TO_VALUE(ReadingListSpecifics)
 IMPLEMENT_PROTO_TO_VALUE(SearchEngineSpecifics)
diff --git a/components/sync/protocol/proto_value_conversions.h b/components/sync/protocol/proto_value_conversions.h
index a800d0c1..e83278a 100644
--- a/components/sync/protocol/proto_value_conversions.h
+++ b/components/sync/protocol/proto_value_conversions.h
@@ -47,6 +47,7 @@
 class PreferenceSpecifics;
 class PrinterPPDReference;
 class PrinterSpecifics;
+class PrintersAuthorizationServerSpecifics;
 class PriorityPreferenceSpecifics;
 class ReadingListSpecifics;
 class SearchEngineSpecifics;
@@ -184,6 +185,11 @@
 std::unique_ptr<base::DictionaryValue> PrinterSpecificsToValue(
     const sync_pb::PrinterSpecifics& printer_specifics);
 
+std::unique_ptr<base::DictionaryValue>
+PrintersAuthorizationServerSpecificsToValue(
+    const sync_pb::PrintersAuthorizationServerSpecifics&
+        printers_authorization_server_specifics);
+
 std::unique_ptr<base::DictionaryValue> PriorityPreferenceSpecificsToValue(
     const sync_pb::PriorityPreferenceSpecifics& proto);
 
diff --git a/components/sync/protocol/proto_value_conversions_unittest.cc b/components/sync/protocol/proto_value_conversions_unittest.cc
index 46bead76f..6ab6c335 100644
--- a/components/sync/protocol/proto_value_conversions_unittest.cc
+++ b/components/sync/protocol/proto_value_conversions_unittest.cc
@@ -57,7 +57,7 @@
 
 DEFINE_SPECIFICS_TO_VALUE_TEST(encrypted)
 
-static_assert(39 == syncer::GetNumModelTypes(),
+static_assert(40 == syncer::GetNumModelTypes(),
               "When adding a new field, add a DEFINE_SPECIFICS_TO_VALUE_TEST "
               "for your field below, and optionally a test for the specific "
               "conversions.");
@@ -84,6 +84,7 @@
 DEFINE_SPECIFICS_TO_VALUE_TEST(password)
 DEFINE_SPECIFICS_TO_VALUE_TEST(preference)
 DEFINE_SPECIFICS_TO_VALUE_TEST(printer)
+DEFINE_SPECIFICS_TO_VALUE_TEST(printers_authorization_server)
 DEFINE_SPECIFICS_TO_VALUE_TEST(priority_preference)
 DEFINE_SPECIFICS_TO_VALUE_TEST(reading_list)
 DEFINE_SPECIFICS_TO_VALUE_TEST(search_engine)
diff --git a/components/sync/protocol/proto_visitors.h b/components/sync/protocol/proto_visitors.h
index 2d8bddd9..23ae6a45 100644
--- a/components/sync/protocol/proto_visitors.h
+++ b/components/sync/protocol/proto_visitors.h
@@ -33,6 +33,7 @@
 #include "components/sync/protocol/persisted_entity_data.pb.h"
 #include "components/sync/protocol/preference_specifics.pb.h"
 #include "components/sync/protocol/printer_specifics.pb.h"
+#include "components/sync/protocol/printers_authorization_server_specifics.pb.h"
 #include "components/sync/protocol/priority_preference_specifics.pb.h"
 #include "components/sync/protocol/proto_enum_conversions.h"
 #include "components/sync/protocol/reading_list_specifics.pb.h"
@@ -462,7 +463,7 @@
 }
 
 VISIT_PROTO_FIELDS(const sync_pb::EntitySpecifics& proto) {
-  static_assert(39 == GetNumModelTypes(),
+  static_assert(40 == GetNumModelTypes(),
                 "When adding a new protocol type, you will likely need to add "
                 "it here as well.");
   VISIT(encrypted);
@@ -488,6 +489,7 @@
   VISIT(password);
   VISIT(preference);
   VISIT(printer);
+  VISIT(printers_authorization_server);
   VISIT(priority_preference);
   VISIT(reading_list);
   VISIT(search_engine);
@@ -827,6 +829,10 @@
   VISIT(make_and_model);
 }
 
+VISIT_PROTO_FIELDS(const sync_pb::PrintersAuthorizationServerSpecifics& proto) {
+  VISIT(uri);
+}
+
 VISIT_PROTO_FIELDS(const sync_pb::PriorityPreferenceSpecifics& proto) {
   VISIT(preference);
 }
diff --git a/components/sync/protocol/protocol_sources.gni b/components/sync/protocol/protocol_sources.gni
index d73f98f..ba612f8 100644
--- a/components/sync/protocol/protocol_sources.gni
+++ b/components/sync/protocol/protocol_sources.gni
@@ -47,6 +47,7 @@
   "persisted_entity_data.proto",
   "preference_specifics.proto",
   "printer_specifics.proto",
+  "printers_authorization_server_specifics.proto",
   "priority_preference_specifics.proto",
   "reading_list_specifics.proto",
   "search_engine_specifics.proto",
diff --git a/components/ukm/debug/BUILD.gn b/components/ukm/debug/BUILD.gn
index ad53401..d464fd8 100644
--- a/components/ukm/debug/BUILD.gn
+++ b/components/ukm/debug/BUILD.gn
@@ -2,7 +2,8 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
-import("//third_party/closure_compiler/compile_js.gni")
+import("//tools/grit/preprocess_if_expr.gni")
+import("//tools/typescript/ts_library.gni")
 
 source_set("util") {
   sources = [
@@ -16,13 +17,16 @@
   ]
 }
 
-js_type_check("closure_compile") {
-  deps = [ ":ukm_internals" ]
+preprocess_if_expr("preprocess") {
+  in_folder = "."
+  out_folder = target_gen_dir
+  in_files = [ "ukm_internals.ts" ]
 }
 
-js_library("ukm_internals") {
-  deps = [
-    "//ui/webui/resources/js:cr",
-    "//ui/webui/resources/js:util",
-  ]
+ts_library("build_ts") {
+  root_dir = target_gen_dir
+  out_dir = "$target_gen_dir/tsc"
+  in_files = [ "ukm_internals.ts" ]
+  deps = [ "//ui/webui/resources:library" ]
+  extra_deps = [ ":preprocess" ]
 }
diff --git a/components/ukm/debug/ukm_internals.js b/components/ukm/debug/ukm_internals.ts
similarity index 66%
rename from components/ukm/debug/ukm_internals.js
rename to components/ukm/debug/ukm_internals.ts
index 991fd1d..6ed0ad67 100644
--- a/components/ukm/debug/ukm_internals.js
+++ b/components/ukm/debug/ukm_internals.ts
@@ -4,80 +4,69 @@
 
 // <if expr="is_ios">
 import 'chrome://resources/js/ios/web_ui.js';
+
 // </if>
 
+import {assert} from 'chrome://resources/js/assert_ts.js';
 import {sendWithPromise} from 'chrome://resources/js/cr.m.js';
 import {$, createElementWithClassName} from 'chrome://resources/js/util.m.js';
 
-/**
- * @typedef {{
- *   name: string,
- *   value: !Array<number>
- * }}
- */
-let Metric;
+type Metric = {
+  name: string,
+  value: [number, number],
+};
 
-/**
- * @typedef {{
- *   name: string,
- *   metrics: !Array<!Metric>
- * }}
- */
-let UkmEntry;
+type UkmEntry = {
+  name: string,
+  metrics: Metric[],
+};
 
-/**
- * @typedef {{
- *   url: string,
- *   id: !Array<number>,
- *   entries: !Array<UkmEntry>,
- * }}
- */
-let UkmDataSource;
+type UkmDataSource = {
+  id: [number, number],
+  entries: UkmEntry[],
+  url?: string,
+};
 
 /**
  * The Ukm data sent from the browser.
- * @typedef {{
- *   state: boolean,
- *   client_id: !Array<number>,
- *   session_id: string,
- *   sources: !Array<!UkmDataSource>,
- *   is_sampling_enabled: boolean,
- * }}
  */
-let UkmData;
+type UkmData = {
+  state: boolean,
+  client_id: number[],
+  session_id: string,
+  sources: UkmDataSource[],
+  is_sampling_enabled: boolean,
+};
 
 /**
  * Stores source id and number of entries shown. If there is a new source id
  * or there are new entries in Ukm recorder, then all the entries for
  * the new source ID will be displayed.
- * @type{Map<string, number>}
  */
-const ClearedSources = new Map();
+const clearedSources: Map<string, number> = new Map();
 
 /**
  * Cached sources to persist beyond the log cut. This will ensure that the data
  * on the page don't disappear if there is a log cut. The caching will
  * start when the page is loaded and when the data is refreshed.
  * Stored data is sourceid -> UkmDataSource with array of distinct entries.
- * @type{Map<string, !UkmDataSource>}
  */
-const CachedSources = new Map();
+const cachedSources: Map<string, UkmDataSource> = new Map();
 
 /**
  * Text for empty url.
- * @type {string}
  */
-const URL_EMPTY = 'missing';
+const URL_EMPTY: string = 'missing';
 
 /**
  * Converts a pair of JS 32 bin number to 64 bit hex string. This is used to
  * pass 64 bit numbers from UKM like client id and 64 bit metrics to
  * the javascript.
- * @param {!Array<number>} num A pair of javascript signed int.
- * @return {string} unsigned int64 as hex number or a decimal number if the
+ * @param num A pair of javascript signed int.
+ * @return unsigned int64 as hex number or a decimal number if the
  *     value is smaller than 32bit.
  */
-function as64Bit(num) {
+function as64Bit(num: [number, number]): string {
   if (num.length !== 2) {
     return '0';
   }
@@ -93,19 +82,19 @@
 /**
  * Sets the display option of all the elements in HtmlCollection to the value
  * passed.
- * @param {!HTMLCollection<!Element>} collection Collection of Elements.
  */
-function setDisplayStyle(collection, display_value) {
-  for (const el of collection) {
-    el.style.display = display_value;
+function setDisplayStyle(
+    elements: NodeListOf<HTMLElement>, displayValue: string) {
+  for (const el of elements) {
+    el.style.display = displayValue;
   }
 }
 
 /**
  * Remove all the child elements.
- * @param {!Element} parent Parent element whose children will get removed.
+ * @param parent Parent element whose children will get removed.
  */
-function removeChildren(parent) {
+function removeChildren(parent: Element) {
   while (parent.firstChild) {
     parent.removeChild(parent.firstChild);
   }
@@ -113,13 +102,14 @@
 
 /**
  * Create card for URL.
- * @param {!Array<!UkmDataSource>} sourcesForUrl Sources that are for same URL.
- * @param {string} url URL or Source id as hex string if the URL is missing.
- * @param {!Element} sourcesDiv Sources div where this card will be added to.
- * @param {!Map<string, ?string>} displayState Map from source id to value
- *     of display property of the entries div.
+ * @param sourcesForUrl Sources that are for same URL.
+ * @param sourcesDiv Sources div where this card will be added to.
+ * @param displayState Map from source id to value of display property of the
+ *     entries div.
  */
-function createUrlCard(sourcesForUrl, url, sourcesDiv, displayState) {
+function createUrlCard(
+    sourcesForUrl: UkmDataSource[], sourcesDiv: Element,
+    displayState: Map<string, string>) {
   const sourceDiv = createElementWithClassName('div', 'url_card');
   sourcesDiv.appendChild(sourceDiv);
   if (!sourcesForUrl || sourcesForUrl.length === 0) {
@@ -127,8 +117,8 @@
   }
   for (const source of sourcesForUrl) {
     // This div allows hiding of the metrics per URL.
-    const sourceContainer = /** @type {!Element} */ (createElementWithClassName(
-        'div', 'source_container'));
+    const sourceContainer = /** @type {!Element} */ (
+        createElementWithClassName('div', 'source_container'));
     sourceDiv.appendChild(sourceContainer);
     createUrlHeader(source.url, source.id, sourceContainer);
     createSourceCard(
@@ -138,22 +128,23 @@
 
 /**
  * Create header containing URL and source ID data.
- * @param {?string} url URL.
- * @param {!Array<number>} id SourceId as hex.
- * @param {!Element} sourceDiv Div under which header will get added.
+ * @param id SourceId as hex.
+ * @param sourceDiv Div under which header will get added.
  */
-function createUrlHeader(url, id, sourceDiv) {
+function createUrlHeader(
+    url: string|undefined, id: [number, number], sourceDiv: Element) {
   const headerElement = createElementWithClassName('div', 'collapsible_header');
   sourceDiv.appendChild(headerElement);
-  const urlElement = createElementWithClassName('span', 'url');
+  const urlElement = createElementWithClassName('span', 'url') as HTMLElement;
   urlElement.innerText = url ? url : URL_EMPTY;
   headerElement.appendChild(urlElement);
-  const idElement = createElementWithClassName('span', 'sourceid');
+  const idElement =
+      createElementWithClassName('span', 'sourceid') as HTMLElement;
   idElement.innerText = as64Bit(id);
   headerElement.appendChild(idElement);
   // Make the click on header toggle entries div.
   headerElement.addEventListener('click', () => {
-    const content = headerElement.nextElementSibling;
+    const content = headerElement.nextElementSibling as HTMLElement;
     if (content.style.display === 'block') {
       content.style.display = 'none';
     } else {
@@ -164,14 +155,15 @@
 
 /**
  * Create a card with UKM Source data.
- * @param {!UkmDataSource} source UKM source data.
- * @param {!Element} sourceDiv Source div where this card will be added to.
- * @param {?string} displayState If display style of this source id is modified
+ * @param source UKM source data.
+ * @param sourceDiv Source div where this card will be added to.
+ * @param displayState If display style of this source id is modified
  *     then the state of the display style.
  */
-function createSourceCard(source, sourceDiv, displayState) {
+function createSourceCard(
+    source: UkmDataSource, sourceDiv: Element, displayState: string|undefined) {
   const metricElement =
-      /** @type {!Element} */ (createElementWithClassName('div', 'entries'));
+      createElementWithClassName('div', 'entries') as HTMLElement;
   sourceDiv.appendChild(metricElement);
   const sortedEntry =
       source.entries.sort((x, y) => x.name.localeCompare(y.name));
@@ -192,10 +184,10 @@
 
 /**
  * Create UKM Entry Table.
- * @param {!UkmEntry} entry A Ukm metrics Entry.
- * @param {!Element} sourceDiv Element whose children will be the entries.
+ * @param entry A Ukm metrics Entry.
+ * @param sourceDiv Element whose children will be the entries.
  */
-function createEntryTable(entry, sourceDiv) {
+function createEntryTable(entry: UkmEntry, sourceDiv: Element) {
   // Add first column to the table.
   const entryTable = createElementWithClassName('table', 'entry_table');
   entryTable.setAttribute('value', entry.name);
@@ -203,7 +195,7 @@
   const firstRow = document.createElement('tr');
   entryTable.appendChild(firstRow);
   const entryName = createElementWithClassName('td', 'entry_name');
-  entryName.setAttribute('rowspan', 0);
+  entryName.setAttribute('rowspan', '0');
   entryName.textContent = entry.name;
   firstRow.appendChild(entryName);
 
@@ -229,11 +221,12 @@
  * urls alphabetically.
  * If the URL field is missing, the source ID will be used as the
  * URL for the purpose of grouping and sorting.
- * @param {!Array<!UkmDataSource>} sources List of UKM data for a source .
- * @return {!Map<string, !Array<!UkmDataSource>>} Mapping in the sorted
- *     order of URL from URL to list of sources for the URL.
+ * @param sources List of UKM data for a source .
+ * @return Mapping in the sorted order of URL from URL to list of sources for
+ *     the URL.
  */
-function urlToSourcesMapping(sources) {
+function urlToSourcesMapping(sources: UkmDataSource[]):
+    Map<string, UkmDataSource[]> {
   const unsorted = new Map();
   for (const source of sources) {
     const key = source.url ? source.url : as64Bit(source.id);
@@ -244,8 +237,8 @@
     }
   }
   // Sort the map by URLs.
-  return new Map(Array.from(unsorted).sort(
-      (s1,s2) => s1[0].localeCompare(s2[0])));
+  return new Map(
+      Array.from(unsorted).sort((s1, s2) => s1[0].localeCompare(s2[0])));
 }
 
 
@@ -258,10 +251,12 @@
   toggleExpand.addEventListener('click', () => {
     if (toggleExpand.textContent === 'Expand') {
       toggleExpand.textContent = 'Collapse';
-      setDisplayStyle(document.getElementsByClassName('entries'), 'block');
+      setDisplayStyle(
+          document.body.querySelectorAll<HTMLElement>('.entries'), 'block');
     } else {
       toggleExpand.textContent = 'Expand';
-      setDisplayStyle(document.getElementsByClassName('entries'), 'none');
+      setDisplayStyle(
+          document.body.querySelectorAll<HTMLElement>('.entries'), 'none');
     }
   });
 }
@@ -277,8 +272,8 @@
     // Note it won't be able to clear if UKM logs got cut during this call.
     sendWithPromise('requestUkmData').then((/** @type {UkmData} */ data) => {
       updateUkmCache(data);
-      for (const s of CachedSources.values()) {
-        ClearedSources.set(as64Bit(s.id), s.entries.length);
+      for (const s of cachedSources.values()) {
+        clearedSources.set(as64Bit(s.id), s.entries.length);
       }
     });
     $('toggle_expand').textContent = 'Expand';
@@ -288,10 +283,12 @@
 
 /**
  * Populate thread ids from the high bit of source id in sources.
- * @param {!Array<!UkmDataSource>} sources Array of UKM source.
+ * @param sources Array of UKM source.
  */
-function populateThreadIds(sources) {
-  const threadIdSelect = $('thread_ids');
+function populateThreadIds(sources: UkmDataSource[]) {
+  const threadIdSelect =
+      document.body.querySelector<HTMLSelectElement>('#thread_ids');
+  assert(threadIdSelect);
   const currentOptions =
       new Set(Array.from(threadIdSelect.options).map(o => o.value));
   // The first 32 bit of the ID is the recorder ID, convert it to a positive
@@ -315,10 +312,10 @@
  * by name to ensure that two entries containing the same metrics and values in
  * different orders have identical string representation to avoid cache
  * duplication.
- * @param {UkmEntry} entry UKM entry to be stringified.
- * @return {string} Normalized string representation of the entry.
+ * @param entry UKM entry to be stringified.
+ * @return Normalized string representation of the entry.
  */
-function normalizeToString(entry) {
+function normalizeToString(entry: UkmEntry): string {
   entry.metrics.sort((x, y) => x.name.localeCompare(y.name));
   return JSON.stringify(entry);
 }
@@ -330,24 +327,25 @@
  * timestamp with entries. So only distinct entries will be recorded in the
  * cache. i.e if two entries have exactly the same set of metrics then one
  * of the entry will not be kept in the cache.
- * @param {UkmData} data New UKM data to add to cache.
+ * @param data New UKM data to add to cache.
  */
-function updateUkmCache(data) {
+function updateUkmCache(data: UkmData) {
   for (const source of data.sources) {
     const key = as64Bit(source.id);
-    if (!CachedSources.has(key)) {
-      const mergedSource = {id: source.id, entries: source.entries};
+    if (!cachedSources.has(key)) {
+      const mergedSource:
+          UkmDataSource = {id: source.id, entries: source.entries};
       if (source.url) {
         mergedSource.url = source.url;
       }
-      CachedSources.set(key, mergedSource);
+      cachedSources.set(key, mergedSource);
     } else {
       // Merge distinct entries from the source.
-      const existingEntries = new Set(CachedSources.get(key).entries.map(
+      const existingEntries = new Set(cachedSources.get(key)!.entries.map(
           cachedEntry => normalizeToString(cachedEntry)));
       for (const sourceEntry of source.entries) {
         if (!existingEntries.has(normalizeToString(sourceEntry))) {
-          CachedSources.get(key).entries.push(sourceEntry);
+          cachedSources.get(key)!.entries.push(sourceEntry);
         }
       }
     }
@@ -361,10 +359,11 @@
 function updateUkmData() {
   sendWithPromise('requestUkmData').then((/** @type {UkmData} */ data) => {
     updateUkmCache(data);
-    if ($('include_cache').checked) {
-      data.sources = [...CachedSources.values()];
+    if (document.body.querySelector<HTMLInputElement>(
+                         '#include_cache')!.checked) {
+      data.sources = [...cachedSources.values()];
     }
-    $('state').innerText = data.state? 'ENABLED' : 'DISABLED';
+    $('state').innerText = data.state ? 'ENABLED' : 'DISABLED';
     $('clientid').innerText = '0x' + data.client_id;
     $('sessionid').innerText = data.session_id;
     $('is_sampling_enabled').innerText = data.is_sampling_enabled;
@@ -384,14 +383,15 @@
     // for example, expanded state.
     const currentDisplayState = new Map();
     for (const el of document.getElementsByClassName('source_container')) {
-      currentDisplayState.set(el.querySelector('.sourceid').textContent,
-                              el.querySelector('.entries').style.display);
+      currentDisplayState.set(
+          el.querySelector<HTMLElement>('.sourceid')!.textContent,
+          el.querySelector<HTMLElement>('.entries')!.style.display);
     }
-    const urlToSources = urlToSourcesMapping(
-        filterSourcesUsingFormOptions(data.sources));
+    const urlToSources =
+        urlToSourcesMapping(filterSourcesUsingFormOptions(data.sources));
     for (const url of urlToSources.keys()) {
-      const sourcesForUrl = urlToSources.get(url);
-      createUrlCard(sourcesForUrl, url, sourcesDiv, currentDisplayState);
+      const sourcesForUrl = urlToSources.get(url)!;
+      createUrlCard(sourcesForUrl, sourcesDiv, currentDisplayState);
     }
     populateThreadIds(data.sources);
   });
@@ -400,23 +400,23 @@
 /**
  * Filter sources that have been recorded previously. If it sees a source id
  * where number of entries has decreased then it will add a warning.
- * @param {!Array<!UkmDataSource>} sources All the sources currently in
- *   UKM recorder.
- * @return {!Array<!UkmDataSource>} Sources which are new or have a new entry
- *   logged for them.
+ * @param sources All the sources currently in UKM recorder.
+ * @return Sources which are new or have a new entry logged for them.
  */
-function filterSourcesUsingFormOptions(sources) {
+function filterSourcesUsingFormOptions(sources: UkmDataSource[]):
+    UkmDataSource[] {
   // Filter sources based on if they have been cleared.
-  const newSources = sources.filter(source => (
-      // Keep sources if it is newly generated since clearing earlier.
-      !ClearedSources.has(as64Bit(source.id)) ||
-      // Keep sources if it has increased entities since clearing earlier.
-      (source.entries.length > ClearedSources.get(as64Bit(source.id)))
-  ));
+  const newSources = sources.filter(
+      source => (
+          // Keep sources if it is newly generated since clearing earlier.
+          !clearedSources.has(as64Bit(source.id)) ||
+          // Keep sources if it has increased entities since clearing earlier.
+          (source.entries.length > clearedSources.get(as64Bit(source.id))!)));
 
   // Applies the filter from Metrics selector.
   const newSourcesWithEntriesCleared = newSources.map(source => {
-    const metricsFilterValue = $('metrics_select').value;
+    const metricsFilterValue =
+        document.body.querySelector<HTMLInputElement>('#metrics_select')!.value;
     if (metricsFilterValue) {
       const metricsRe = new RegExp(metricsFilterValue);
       source.entries = source.entries.filter(e => metricsRe.test(e.name));
@@ -425,10 +425,21 @@
   });
 
   // Filter sources based on the status of check-boxes.
-  const filteredSources = newSourcesWithEntriesCleared.filter(source => (
-      (!$('hide_no_url').checked || source.url) &&
-      (!$('hide_no_metrics').checked || source.entries.length)
-  ));
+  const filteredSources = newSourcesWithEntriesCleared.filter(source => {
+    const noUrlCheckbox =
+        document.body.querySelector<HTMLInputElement>('#hide_no_url');
+    assert(noUrlCheckbox);
+    const noMetricsCheckbox =
+        document.body.querySelector<HTMLInputElement>('#hide_no_metrics');
+    assert(noMetricsCheckbox);
+
+    return (!noUrlCheckbox.checked || source.url) &&
+        (!noMetricsCheckbox.checked || source.entries.length);
+  });
+
+  const threadIds =
+      document.body.querySelector<HTMLSelectElement>('#thread_ids');
+  assert(threadIds);
 
   // Filter sources based on thread id (High bits of UKM Recorder ID).
   const threadsFilteredSource = filteredSources.filter(source => {
@@ -439,8 +450,7 @@
     //     If a UKM is recorded with a custom source id or in renderer, it will
     //     have a unique value for this shared by all metrics that use the
     //     same thread.
-    const selectedOption =
-        $('thread_ids').options[$('thread_ids').selectedIndex];
+    const selectedOption = threadIds.options[threadIds.selectedIndex];
     // Return true if either of the following is true -
     // No option is selected or selected option is "All" or the hexadecimal
     // representation of source id is matching.
@@ -449,8 +459,11 @@
   });
 
   // Filter URLs based on URL selector input.
+  const urlSelect =
+      document.body.querySelector<HTMLInputElement>('#url_select');
+  assert(urlSelect);
   return threadsFilteredSource.filter(source => {
-    const urlFilterValue = $('url_select').value;
+    const urlFilterValue = urlSelect.value;
     if (urlFilterValue) {
       const urlRe = new RegExp(urlFilterValue);
       // Will also match missing URLs by default.
diff --git a/components/update_client/protocol_serializer_json.cc b/components/update_client/protocol_serializer_json.cc
index 4990950..6d698b7 100644
--- a/components/update_client/protocol_serializer_json.cc
+++ b/components/update_client/protocol_serializer_json.cc
@@ -33,8 +33,8 @@
   request_node->SetKey("sessionid", Value(request.session_id));
   request_node->SetKey("requestid", Value(request.request_id));
   request_node->SetKey("@updater", Value(request.updatername));
-  request_node->SetKey("prodversion", Value(request.updaterversion));
-  request_node->SetKey("updaterversion", Value(request.prodversion));
+  request_node->SetKey("prodversion", Value(request.prodversion));
+  request_node->SetKey("updaterversion", Value(request.updaterversion));
   request_node->SetKey("@os", Value(request.operating_system));
   request_node->SetKey("arch", Value(request.arch));
   request_node->SetKey("nacl_arch", Value(request.nacl_arch));
diff --git a/components/url_formatter/android/BUILD.gn b/components/url_formatter/android/BUILD.gn
index 06b27dd..cf39d43 100644
--- a/components/url_formatter/android/BUILD.gn
+++ b/components/url_formatter/android/BUILD.gn
@@ -25,7 +25,7 @@
       [ "java/src/org/chromium/components/url_formatter/UrlFormatter.java" ]
 }
 
-android_library("url_formatter_javatests") {
+android_library("unit_device_javatests") {
   testonly = true
 
   sources = [ "javatests/src/org/chromium/components/url_formatter/UrlFormatterUnitTest.java" ]
diff --git a/components/viz/service/frame_sinks/video_capture/frame_sink_video_capturer_impl.cc b/components/viz/service/frame_sinks/video_capture/frame_sink_video_capturer_impl.cc
index 443b418..c9f2c23 100644
--- a/components/viz/service/frame_sinks/video_capture/frame_sink_video_capturer_impl.cc
+++ b/components/viz/service/frame_sinks/video_capture/frame_sink_video_capturer_impl.cc
@@ -972,7 +972,17 @@
     return;
   }
 
-  if (absl::holds_alternative<RegionCaptureCropId>(target_->sub_target)) {
+  // If the target is in a different renderer than the root renderer (indicated
+  // by having a different frame sink ID), we currently cannot provide
+  // reasonable metadata about the region capture rect. For more context, see
+  // https://crbug.com/1327560.
+  //
+  // TODO(https://crbug.com/1335175): Provide accurate bounds for elements
+  // embedded in different renderers.
+  const bool is_same_frame_sink_as_requested =
+      resolved_target_->GetFrameSinkId() == target_->frame_sink_id;
+  if (absl::holds_alternative<RegionCaptureCropId>(target_->sub_target) &&
+      is_same_frame_sink_as_requested) {
     const float scale_factor = frame_metadata.device_scale_factor;
     metadata.region_capture_rect =
         scale_factor ? ScaleToEnclosingRect(capture_region, 1.0f / scale_factor)
diff --git a/content/BUILD.gn b/content/BUILD.gn
index 7f214c56..5143494 100644
--- a/content/BUILD.gn
+++ b/content/BUILD.gn
@@ -94,6 +94,7 @@
     "content_resources.pak",
   ]
   deps = [
+    "//components/ukm/debug:build_ts",
     "//content/browser/resources/histograms:build_ts",
     "//gpu/ipc/common:vulkan_interface_webui_js",
     "//ui/base/mojom:mojom_js",
diff --git a/content/browser/BUILD.gn b/content/browser/BUILD.gn
index 85461ea..c6a6501 100644
--- a/content/browser/BUILD.gn
+++ b/content/browser/BUILD.gn
@@ -2767,8 +2767,6 @@
     sources += [
       "media/cdm_file_impl.cc",
       "media/cdm_file_impl.h",
-      "media/cdm_storage_impl.cc",
-      "media/cdm_storage_impl.h",
       "media/media_license_database.cc",
       "media/media_license_database.h",
       "media/media_license_manager.cc",
diff --git a/content/browser/attribution_reporting/attribution_internals_ui.h b/content/browser/attribution_reporting/attribution_internals_ui.h
index 2587d7e..e2b8712 100644
--- a/content/browser/attribution_reporting/attribution_internals_ui.h
+++ b/content/browser/attribution_reporting/attribution_internals_ui.h
@@ -7,8 +7,7 @@
 
 #include <memory>
 
-#include "content/browser/attribution_reporting/attribution_internals.mojom.h"
-#include "content/common/content_export.h"
+#include "content/browser/attribution_reporting/attribution_internals.mojom-forward.h"
 #include "content/public/browser/web_ui_controller.h"
 #include "content/public/browser/webui_config.h"
 #include "content/public/common/url_constants.h"
@@ -18,6 +17,8 @@
 
 class AttributionInternalsHandlerImpl;
 class AttributionInternalsUI;
+class RenderFrameHost;
+class WebUI;
 
 // WebUIConfig for chrome://attribution-internals page
 class AttributionInternalsUIConfig
@@ -29,14 +30,13 @@
 };
 
 // WebUI which handles serving the chrome://attribution-internals page.
-class CONTENT_EXPORT AttributionInternalsUI : public WebUIController {
+class AttributionInternalsUI : public WebUIController {
  public:
   explicit AttributionInternalsUI(WebUI* web_ui);
-  AttributionInternalsUI(const AttributionInternalsUI& other) = delete;
-  AttributionInternalsUI& operator=(const AttributionInternalsUI& other) =
-      delete;
-  AttributionInternalsUI(AttributionInternalsUI&& other) = delete;
-  AttributionInternalsUI& operator=(AttributionInternalsUI&& other) = delete;
+  AttributionInternalsUI(const AttributionInternalsUI&) = delete;
+  AttributionInternalsUI& operator=(const AttributionInternalsUI&) = delete;
+  AttributionInternalsUI(AttributionInternalsUI&&) = delete;
+  AttributionInternalsUI& operator=(AttributionInternalsUI&&) = delete;
   ~AttributionInternalsUI() override;
 
   // WebUIController overrides:
diff --git a/content/browser/attribution_reporting/attribution_manager_impl.h b/content/browser/attribution_reporting/attribution_manager_impl.h
index 17434b76..0f59521 100644
--- a/content/browser/attribution_reporting/attribution_manager_impl.h
+++ b/content/browser/attribution_reporting/attribution_manager_impl.h
@@ -10,7 +10,6 @@
 #include <vector>
 
 #include "base/callback_forward.h"
-#include "base/compiler_specific.h"
 #include "base/containers/circular_deque.h"
 #include "base/containers/flat_set.h"
 #include "base/memory/raw_ptr.h"
@@ -76,11 +75,10 @@
       StoragePartitionImpl* storage_partition,
       const base::FilePath& user_data_directory,
       scoped_refptr<storage::SpecialStoragePolicy> special_storage_policy);
-  AttributionManagerImpl(const AttributionManagerImpl& other) = delete;
-  AttributionManagerImpl& operator=(const AttributionManagerImpl& other) =
-      delete;
-  AttributionManagerImpl(AttributionManagerImpl&& other) = delete;
-  AttributionManagerImpl& operator=(AttributionManagerImpl&& other) = delete;
+  AttributionManagerImpl(const AttributionManagerImpl&) = delete;
+  AttributionManagerImpl& operator=(const AttributionManagerImpl&) = delete;
+  AttributionManagerImpl(AttributionManagerImpl&&) = delete;
+  AttributionManagerImpl& operator=(AttributionManagerImpl&&) = delete;
   ~AttributionManagerImpl() override;
 
   // AttributionManager:
diff --git a/content/browser/attribution_reporting/rate_limit_table.h b/content/browser/attribution_reporting/rate_limit_table.h
index 30766ffc..77f8b706 100644
--- a/content/browser/attribution_reporting/rate_limit_table.h
+++ b/content/browser/attribution_reporting/rate_limit_table.h
@@ -12,7 +12,6 @@
 #include "base/sequence_checker.h"
 #include "base/thread_annotations.h"
 #include "base/time/time.h"
-#include "content/browser/attribution_reporting/attribution_storage.h"
 #include "content/browser/attribution_reporting/stored_source.h"
 #include "content/common/content_export.h"
 
@@ -51,10 +50,10 @@
   };
 
   explicit RateLimitTable(const AttributionStorageDelegate* delegate);
-  RateLimitTable(const RateLimitTable& other) = delete;
-  RateLimitTable& operator=(const RateLimitTable& other) = delete;
-  RateLimitTable(RateLimitTable&& other) = delete;
-  RateLimitTable& operator=(RateLimitTable&& other) = delete;
+  RateLimitTable(const RateLimitTable&) = delete;
+  RateLimitTable& operator=(const RateLimitTable&) = delete;
+  RateLimitTable(RateLimitTable&&) = delete;
+  RateLimitTable& operator=(RateLimitTable&&) = delete;
   ~RateLimitTable();
 
   // Creates the table in |db| if it doesn't exist.
diff --git a/content/browser/attribution_reporting/send_result.h b/content/browser/attribution_reporting/send_result.h
index 1dd24e2f..ed8dc359 100644
--- a/content/browser/attribution_reporting/send_result.h
+++ b/content/browser/attribution_reporting/send_result.h
@@ -5,8 +5,6 @@
 #ifndef CONTENT_BROWSER_ATTRIBUTION_REPORTING_SEND_RESULT_H_
 #define CONTENT_BROWSER_ATTRIBUTION_REPORTING_SEND_RESULT_H_
 
-#include "content/common/content_export.h"
-
 namespace content {
 
 // Struct that contains data about sent reports. Some info is displayed in the
@@ -14,7 +12,7 @@
 // TODO(apaseltiner): Consider replacing this struct with a single int that
 // contains either HTTP response code, network error, or custom values for
 // `Status::kDropped` and `Status::kFailedToAssemble`.
-struct CONTENT_EXPORT SendResult {
+struct SendResult {
   enum class Status {
     kSent,
     // The report failed without receiving response headers.
diff --git a/content/browser/buckets/bucket_manager_host.cc b/content/browser/buckets/bucket_manager_host.cc
index 2a06ec9..81998f6 100644
--- a/content/browser/buckets/bucket_manager_host.cc
+++ b/content/browser/buckets/bucket_manager_host.cc
@@ -104,7 +104,7 @@
 }
 
 void BucketManagerHost::Keys(KeysCallback callback) {
-  manager_->quota_manager_proxy()->GetBucketsForStorageKey(
+  manager_->quota_manager_proxy()->GetBucketsForStorageKeyDeleteExpired(
       blink::StorageKey(origin_), blink::mojom::StorageType::kTemporary,
       base::SequencedTaskRunnerHandle::Get(),
       base::BindOnce(&BucketManagerHost::DidGetBuckets,
diff --git a/content/browser/media/cdm_file_impl.cc b/content/browser/media/cdm_file_impl.cc
index 38542a3..feba9c0 100644
--- a/content/browser/media/cdm_file_impl.cc
+++ b/content/browser/media/cdm_file_impl.cc
@@ -4,33 +4,19 @@
 
 #include "content/browser/media/cdm_file_impl.h"
 
-#include <set>
 #include <utility>
 
 #include "base/bind.h"
 #include "base/callback.h"
-#include "base/feature_list.h"
 #include "base/logging.h"
 #include "base/metrics/histogram_functions.h"
-#include "base/notreached.h"
 #include "base/strings/strcat.h"
 #include "base/strings/string_util.h"
 #include "content/browser/media/media_license_storage_host.h"
-#include "content/public/browser/browser_task_traits.h"
-#include "content/public/browser/browser_thread.h"
-#include "content/public/common/content_features.h"
-#include "media/base/bind_to_current_loop.h"
 #include "media/cdm/cdm_type.h"
 #include "media/mojo/mojom/cdm_storage.mojom.h"
 #include "mojo/public/cpp/bindings/pending_associated_receiver.h"
 #include "mojo/public/cpp/bindings/pending_receiver.h"
-#include "net/base/io_buffer.h"
-#include "net/base/net_errors.h"
-#include "storage/browser/file_system/file_stream_reader.h"
-#include "storage/browser/file_system/file_stream_writer.h"
-#include "storage/browser/file_system/file_system_context.h"
-#include "storage/browser/file_system/file_system_operation_context.h"
-#include "storage/browser/file_system/file_system_url.h"
 #include "storage/browser/quota/quota_manager.h"
 #include "storage/common/file_system/file_system_types.h"
 #include "third_party/blink/public/common/storage_key/storage_key.h"
@@ -54,329 +40,8 @@
 const char kWriteTimeUmaName[] = "Media.EME.CdmFileIO.TimeTo.WriteFile";
 const char kDeleteTimeUmaName[] = "Media.EME.CdmFileIO.TimeTo.DeleteFile";
 
-std::string GetTempFileName(const std::string& file_name) {
-  DCHECK(!base::StartsWith(file_name, std::string(1, kTemporaryFilePrefix),
-                           base::CompareCase::SENSITIVE));
-  return kTemporaryFilePrefix + file_name;
-}
-
-// The file system is different for each CDM and each origin. So track files
-// in use based on (origin, CDM type, file name).
-struct FileLockKey {
-  FileLockKey(const url::Origin& origin,
-              const media::CdmType& cdm_type,
-              const std::string& file_name)
-      : origin(origin), cdm_type(cdm_type), file_name(file_name) {}
-  ~FileLockKey() = default;
-
-  // Allow use as a key in std::set.
-  bool operator<(const FileLockKey& other) const {
-    return std::tie(origin, cdm_type, file_name) <
-           std::tie(other.origin, other.cdm_type, other.file_name);
-  }
-
-  url::Origin origin;
-  media::CdmType cdm_type;
-  std::string file_name;
-};
-
-// File map shared by all CdmFileImpl objects to prevent read/write race.
-// A lock must be acquired before opening a file to ensure that the file is not
-// currently in use. The lock must be held until the file is closed.
-class FileLockMap {
- public:
-  FileLockMap() = default;
-
-  FileLockMap(const FileLockMap&) = delete;
-  FileLockMap& operator=(const FileLockMap&) = delete;
-
-  ~FileLockMap() = default;
-
-  // Acquire a lock on the file represented by |key|. Returns true if |key|
-  // is not currently in use, false otherwise.
-  bool AcquireFileLock(const FileLockKey& key) {
-    DVLOG(3) << __func__ << " file: " << key.file_name;
-    DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
-
-    // Add a new entry. If |key| already has an entry, insert() tells so
-    // with the second piece of the returned value and does not modify
-    // the original.
-    return file_lock_map_.insert(key).second;
-  }
-
-  // Release the lock held on the file represented by |key|.
-  void ReleaseFileLock(const FileLockKey& key) {
-    DVLOG(3) << __func__ << " file: " << key.file_name;
-    DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
-
-    auto entry = file_lock_map_.find(key);
-    if (entry == file_lock_map_.end()) {
-      NOTREACHED() << "Unable to release lock on file " << key.file_name;
-      return;
-    }
-
-    file_lock_map_.erase(entry);
-  }
-
- private:
-  // Note that this map is never deleted. As entries are removed when a file
-  // is closed, it should never get too large.
-  std::set<FileLockKey> file_lock_map_;
-
-  THREAD_CHECKER(thread_checker_);
-};
-
-// The FileLockMap is a global lock map shared by all CdmFileImpl instances.
-FileLockMap* GetFileLockMap() {
-  static auto* file_lock_map = new FileLockMap();
-  return file_lock_map;
-}
-
-// File stream operations need an IOBuffer to hold the data. This class stores
-// the data in a std::vector<uint8_t> to match what is used in the
-// mojom::CdmFile API.
-class CdmFileIOBuffer : public net::IOBuffer {
- public:
-  // Create an empty buffer of size |size|.
-  explicit CdmFileIOBuffer(size_t size) : buffer_(size) {
-    data_ = reinterpret_cast<char*>(buffer_.data());
-  }
-
-  // Create a buffer that contains |data|.
-  explicit CdmFileIOBuffer(const std::vector<uint8_t>& data) : buffer_(data) {
-    data_ = reinterpret_cast<char*>(buffer_.data());
-  }
-
-  // Returns ownership of |buffer_| to the caller.
-  std::vector<uint8_t>&& TakeData() { return std::move(buffer_); }
-
- protected:
-  ~CdmFileIOBuffer() override { data_ = nullptr; }
-
- private:
-  std::vector<uint8_t> buffer_;
-};
-
 }  // namespace
 
-// Read a file using FileStreamReader. Implemented as a separate class so that
-// it can be run on the IO thread.
-class CdmFileImpl::FileReader {
- public:
-  // Returns whether the read operation succeeded or not. If |result| = true,
-  // then |data| is the contents of the file.
-  using ReadDoneCB =
-      base::OnceCallback<void(bool result, std::vector<uint8_t> data)>;
-
-  FileReader() = default;
-
-  FileReader(const FileReader&) = delete;
-  FileReader& operator=(const FileReader&) = delete;
-
-  // Reads the contents of |file_url| and calls |callback| with the result
-  // (file contents on success, empty data on error).
-  void Read(scoped_refptr<storage::FileSystemContext> file_system_context,
-            const storage::FileSystemURL& file_url,
-            ReadDoneCB callback) {
-    DVLOG(3) << __func__ << " url: " << file_url.DebugString();
-    DCHECK_CURRENTLY_ON(BrowserThread::IO);
-    DCHECK(!callback_);
-    DCHECK(!file_stream_reader_);
-
-    callback_ = std::move(callback);
-
-    file_stream_reader_ = file_system_context->CreateFileStreamReader(
-        file_url, 0, kMaxFileSizeBytes, base::Time());
-    auto result = file_stream_reader_->GetLength(
-        base::BindOnce(&FileReader::OnGetLength, weak_factory_.GetWeakPtr()));
-    DVLOG(3) << __func__ << " GetLength(): " << result;
-
-    // If GetLength() is running asynchronously, simply return.
-    if (result == net::ERR_IO_PENDING)
-      return;
-
-    // GetLength() was synchronous, so pass the result on.
-    OnGetLength(result);
-  }
-
- private:
-  // Called when the size of the file to be read is known. Allocates a buffer
-  // large enough to hold the contents, then attempts to read the contents into
-  // the buffer. |result| will be the length of the file (if >= 0) or a net::
-  // error on failure (if < 0).
-  void OnGetLength(int64_t result) {
-    DVLOG(3) << __func__ << " result: " << result;
-    DCHECK_CURRENTLY_ON(BrowserThread::IO);
-    DCHECK(callback_);
-    DCHECK(file_stream_reader_);
-
-    // If the file doesn't exist, then pretend it is empty.
-    if (result == net::ERR_FILE_NOT_FOUND) {
-      std::move(callback_).Run(true, {});
-      return;
-    }
-
-    // Any other failure is an error.
-    if (result < 0) {
-      DLOG(WARNING) << __func__
-                    << " Unable to get file length. result = " << result;
-      std::move(callback_).Run(false, {});
-      return;
-    }
-
-    // Files are limited in size, so fail if file too big.
-    if (result > kMaxFileSizeBytes) {
-      DLOG(WARNING) << __func__
-                    << " Too much data to read. #bytes = " << result;
-      std::move(callback_).Run(false, {});
-      return;
-    }
-
-    // Read() sizes (provided and returned) are type int, so cast appropriately.
-    int bytes_to_read = base::checked_cast<int>(result);
-    auto buffer = base::MakeRefCounted<CdmFileIOBuffer>(
-        base::checked_cast<size_t>(bytes_to_read));
-
-    // Read the contents of the file into |buffer|.
-    result = file_stream_reader_->Read(
-        buffer.get(), bytes_to_read,
-        base::BindOnce(&FileReader::OnRead, weak_factory_.GetWeakPtr(), buffer,
-                       bytes_to_read));
-    DVLOG(3) << __func__ << " Read(): " << result;
-
-    // If Read() is running asynchronously, simply return.
-    if (result == net::ERR_IO_PENDING)
-      return;
-
-    // Read() was synchronous, so pass the result on.
-    OnRead(std::move(buffer), bytes_to_read, result);
-  }
-
-  // Called when the file has been read and returns the result to the callback
-  // provided to Read(). |result| will be the number of bytes read (if >= 0) or
-  // a net:: error on failure (if < 0).
-  void OnRead(scoped_refptr<CdmFileIOBuffer> buffer,
-              int bytes_to_read,
-              int result) {
-    DVLOG(3) << __func__ << " Requested " << bytes_to_read << " bytes, got "
-             << result;
-    DCHECK_CURRENTLY_ON(BrowserThread::IO);
-    DCHECK(callback_);
-    DCHECK(file_stream_reader_);
-
-    if (result != bytes_to_read) {
-      // Unable to read the contents of the file completely.
-      DLOG(WARNING) << "Failed to read file. Requested " << bytes_to_read
-                    << " bytes, got " << result;
-      std::move(callback_).Run(false, {});
-      return;
-    }
-
-    // Successful read. Return the bytes read.
-    std::move(callback_).Run(true, std::move(buffer->TakeData()));
-  }
-
-  // Called when the read operation is done.
-  ReadDoneCB callback_;
-
-  // Used to read the stream.
-  std::unique_ptr<storage::FileStreamReader> file_stream_reader_;
-
-  base::WeakPtrFactory<FileReader> weak_factory_{this};
-};
-
-class CdmFileImpl::FileWriter {
- public:
-  // Returns whether the write operation succeeded or not.
-  using WriteDoneCB = base::OnceCallback<void(bool)>;
-
-  FileWriter() {}
-
-  FileWriter(const FileWriter&) = delete;
-  FileWriter& operator=(const FileWriter&) = delete;
-
-  // Writes |buffer| as the contents of |file_url| and calls |callback| with
-  // whether the write succeeded or not.
-  void Write(scoped_refptr<storage::FileSystemContext> file_system_context,
-             const storage::FileSystemURL& file_url,
-             scoped_refptr<net::IOBuffer> buffer,
-             int bytes_to_write,
-             WriteDoneCB callback) {
-    DVLOG(3) << __func__ << " url: " << file_url.DebugString();
-    DCHECK_CURRENTLY_ON(BrowserThread::IO);
-
-    callback_ = std::move(callback);
-
-    // Create a writer on |temp_file_name_|. This temp file will be renamed
-    // after a successful write.
-    file_stream_writer_ =
-        file_system_context->CreateFileStreamWriter(file_url, 0);
-    auto result = file_stream_writer_->Write(
-        buffer.get(), bytes_to_write,
-        base::BindOnce(&FileWriter::OnWrite, weak_factory_.GetWeakPtr(), buffer,
-                       bytes_to_write));
-    DVLOG(3) << __func__ << " Write(): " << result;
-
-    // If Write() is running asynchronously, simply return.
-    if (result == net::ERR_IO_PENDING)
-      return;
-
-    // Write() was synchronous, so pass the result on.
-    OnWrite(std::move(buffer), bytes_to_write, result);
-  }
-
- private:
-  // Called when the file has been written. |result| will be the number of bytes
-  // written (if >= 0) or a net:: error on failure (if < 0).
-  void OnWrite(scoped_refptr<net::IOBuffer> buffer,
-               int bytes_to_write,
-               int result) {
-    DVLOG(3) << __func__ << " Expected to write " << bytes_to_write
-             << " bytes, got " << result;
-    DCHECK_CURRENTLY_ON(BrowserThread::IO);
-
-    if (result != bytes_to_write) {
-      // Unable to write the file.
-      DLOG(WARNING) << "Failed to write file. Sent " << bytes_to_write
-                    << " bytes, wrote " << result;
-      std::move(callback_).Run(false);
-      return;
-    }
-
-    result = file_stream_writer_->Flush(
-        base::BindOnce(&FileWriter::OnFlush, weak_factory_.GetWeakPtr()));
-    DVLOG(3) << __func__ << " Flush(): " << result;
-
-    // If Flush() is running asynchronously, simply return.
-    if (result == net::ERR_IO_PENDING)
-      return;
-
-    // Flush() was synchronous, so pass the result on.
-    OnFlush(result);
-  }
-
-  // Called when the file has been flushed. |result| is the net:: error code.
-  void OnFlush(int result) {
-    DVLOG(3) << __func__ << " Result: " << result;
-    DCHECK_CURRENTLY_ON(BrowserThread::IO);
-
-    // We are done with |file_stream_writer_|.
-    file_stream_writer_.reset();
-
-    DLOG_IF(WARNING, result != net::OK)
-        << "Failed to flush file, result: " << result;
-    std::move(callback_).Run(result == net::OK);
-  }
-
-  // Called when the write operation is done.
-  WriteDoneCB callback_;
-
-  // Used to write the stream.
-  std::unique_ptr<storage::FileStreamWriter> file_stream_writer_;
-
-  base::WeakPtrFactory<FileWriter> weak_factory_{this};
-};
-
 // static
 bool CdmFileImpl::IsValidName(const std::string& name) {
   // File names must only contain letters (A-Za-z), digits(0-9), or "._-",
@@ -410,26 +75,6 @@
   receiver_.Bind(std::move(pending_receiver));
   receiver_.set_disconnect_handler(base::BindOnce(
       &CdmFileImpl::OnReceiverDisconnect, weak_factory_.GetWeakPtr()));
-
-  // The MediaLicenseStorageHost handles file locking.
-  file_locked_ = true;
-}
-
-CdmFileImpl::CdmFileImpl(
-    const std::string& file_name,
-    const url::Origin& origin,
-    const media::CdmType& cdm_type,
-    const std::string& file_system_root_uri,
-    scoped_refptr<storage::FileSystemContext> file_system_context)
-    : file_name_(file_name),
-      temp_file_name_(GetTempFileName(file_name_)),
-      origin_(origin),
-      cdm_type_(cdm_type),
-      file_system_root_uri_(file_system_root_uri),
-      file_system_context_(file_system_context) {
-  DVLOG(3) << __func__ << " " << file_name_;
-  DCHECK(IsValidName(file_name_));
-  DCHECK(!base::FeatureList::IsEnabled(features::kMediaLicenseBackend));
 }
 
 CdmFileImpl::~CdmFileImpl() {
@@ -441,33 +86,12 @@
 
   if (write_callback_)
     std::move(write_callback_).Run(Status::kFailure);
-
-  if (file_locked_)
-    ReleaseFileLock(file_name_);
-}
-
-bool CdmFileImpl::Initialize() {
-  DVLOG(3) << __func__ << " file: " << file_name_;
-  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
-  DCHECK(!file_locked_);
-
-  // Grab the lock on |file_name_|. The lock will be held until this object is
-  // destructed.
-  if (!AcquireFileLock(file_name_)) {
-    DVLOG(2) << "File " << file_name_ << " is already in use.";
-    return false;
-  }
-
-  // We have the lock on |file_name_|. |file_locked_| is set to simplify
-  // validation, and to help destruction not have to check.
-  file_locked_ = true;
-  return true;
 }
 
 void CdmFileImpl::Read(ReadCallback callback) {
   DVLOG(3) << __func__ << " file: " << file_name_;
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
-  DCHECK(file_locked_);
+  DCHECK(host_);
 
   // Only 1 Read() or Write() is allowed at any time.
   if (read_callback_ || write_callback_) {
@@ -479,39 +103,19 @@
   read_callback_ = std::move(callback);
   start_time_ = base::TimeTicks::Now();
 
-  if (host_) {
-    ReadUsingMediaLicenseStorageDelegate();
-    return;
-  }
-
-  // As reading is done on the IO thread, when it's done ReadDone() needs to be
-  // called back on this thread.
-  auto read_done_cb = media::BindToCurrentLoop(
-      base::BindOnce(&CdmFileImpl::ReadDone, weak_factory_.GetWeakPtr()));
-
-  // Create the file reader that runs on the IO thread, and then call Read() on
-  // the IO thread. Use of base::Unretained() is OK as the reader is owned by
-  // |this|, and if |this| is destructed it will destroy the file reader on the
-  // IO thread.
-  file_reader_ = base::SequenceBound<FileReader>(GetIOThreadTaskRunner({}));
-  // TODO(dcheng): Migrate this to use Then()?
-  file_reader_.AsyncCall(&FileReader::Read)
-      .WithArgs(file_system_context_, CreateFileSystemURL(file_name_),
-                std::move(read_done_cb));
+  host_->ReadFile(
+      cdm_type_, file_name_,
+      base::BindOnce(&CdmFileImpl::DidRead, weak_factory_.GetWeakPtr()));
 }
 
-void CdmFileImpl::ReadDone(bool success, std::vector<uint8_t> data) {
+void CdmFileImpl::DidRead(absl::optional<std::vector<uint8_t>> data) {
   DVLOG(3) << __func__ << " file: " << file_name_
-           << ", success: " << (success ? "yes" : "no");
+           << ", success: " << (data.has_value() ? "yes" : "no");
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
-  DCHECK(file_locked_);
-  DCHECK(file_reader_);
   DCHECK(read_callback_);
+  DCHECK(host_);
 
-  // We are done with the reader, so destroy it.
-  file_reader_.Reset();
-
-  if (!success) {
+  if (!data.has_value()) {
     // Unable to read the contents of the file.
     std::move(read_callback_).Run(Status::kFailure, {});
     return;
@@ -519,14 +123,14 @@
 
   // Only report reading time for successful reads.
   ReportFileOperationTimeUMA(kReadTimeUmaName);
-  std::move(read_callback_).Run(Status::kSuccess, std::move(data));
+  std::move(read_callback_).Run(Status::kSuccess, std::move(data.value()));
 }
 
 void CdmFileImpl::Write(const std::vector<uint8_t>& data,
                         WriteCallback callback) {
   DVLOG(3) << __func__ << " file: " << file_name_ << ", size: " << data.size();
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
-  DCHECK(file_locked_);
+  DCHECK(host_);
 
   // Only 1 Read() or Write() is allowed at any time.
   if (read_callback_ || write_callback_) {
@@ -554,244 +158,19 @@
     return;
   }
 
-  if (host_) {
-    WriteUsingMediaLicenseStorageDelegate(data);
-    return;
-  }
-
-  // Copy |data| into a net::IOBuffer.
-  int bytes_to_write = base::checked_cast<int>(data.size());
-  auto buffer = base::MakeRefCounted<CdmFileIOBuffer>(data);
-
-  // FileStreamWriter only works on existing files. |temp_file_name_| should not
-  // exist, so create an empty one if necessary.
-  // We can not use AsyncFileUtil::CreateOrOpen() as it does not work with the
-  // incognito filesystem (http://crbug.com/958294).
-  auto url = CreateFileSystemURL(temp_file_name_);
-  auto* file_util = file_system_context_->GetAsyncFileUtil(
-      storage::kFileSystemTypePluginPrivate);
-  auto operation_context =
-      std::make_unique<storage::FileSystemOperationContext>(
-          file_system_context_.get());
-  operation_context->set_allowed_bytes_growth(storage::QuotaManager::kNoLimit);
-  file_util->EnsureFileExists(
-      std::move(operation_context), url,
-      base::BindOnce(&CdmFileImpl::OnEnsureTempFileExists,
-                     weak_factory_.GetWeakPtr(), std::move(buffer),
-                     bytes_to_write));
-}
-
-void CdmFileImpl::OnEnsureTempFileExists(scoped_refptr<net::IOBuffer> buffer,
-                                         int bytes_to_write,
-                                         base::File::Error result,
-                                         bool created) {
-  DVLOG(3) << __func__ << " file: " << temp_file_name_
-           << ", result: " << base::File::ErrorToString(result);
-  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
-  DCHECK(file_locked_);
-  DCHECK(write_callback_);
-  DCHECK(!file_writer_);
-
-  if (result != base::File::FILE_OK) {
-    // Unable to create the file.
-    DLOG(WARNING) << "Failed to create temporary file, result: "
-                  << base::File::ErrorToString(result);
-    std::move(write_callback_).Run(Status::kFailure);
-    return;
-  }
-
-  // If the temp file has just been created, we know it is empty and can simply
-  // proceed with writing to it. However, if the file exists, truncate it in
-  // case it is longer than the number of bytes we want to write.
-  if (created) {
-    OnTempFileIsEmpty(std::move(buffer), bytes_to_write, result);
-    return;
-  }
-
-  auto url = CreateFileSystemURL(temp_file_name_);
-  auto* file_util = file_system_context_->GetAsyncFileUtil(
-      storage::kFileSystemTypePluginPrivate);
-  auto operation_context =
-      std::make_unique<storage::FileSystemOperationContext>(
-          file_system_context_.get());
-  operation_context->set_allowed_bytes_growth(storage::QuotaManager::kNoLimit);
-  file_util->Truncate(std::move(operation_context), url, 0,
-                      base::BindOnce(&CdmFileImpl::OnTempFileIsEmpty,
-                                     weak_factory_.GetWeakPtr(),
-                                     std::move(buffer), bytes_to_write));
-}
-
-void CdmFileImpl::OnTempFileIsEmpty(scoped_refptr<net::IOBuffer> buffer,
-                                    int bytes_to_write,
-                                    base::File::Error result) {
-  DVLOG(3) << __func__ << " file: " << temp_file_name_
-           << ", result: " << base::File::ErrorToString(result);
-  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
-  DCHECK(file_locked_);
-  DCHECK(write_callback_);
-  DCHECK(!file_writer_);
-
-  if (result != base::File::FILE_OK) {
-    DLOG(WARNING) << "Failed to truncate temporary file, result: "
-                  << base::File::ErrorToString(result);
-    std::move(write_callback_).Run(Status::kFailure);
-    return;
-  }
-
-  // As writing is done on the IO thread, when it's done WriteDone() needs to be
-  // called on this thread.
-  auto write_done_cb = media::BindToCurrentLoop(
-      base::BindOnce(&CdmFileImpl::WriteDone, weak_factory_.GetWeakPtr()));
-
-  // Create the file writer that runs on the IO thread, and then call Write()
-  // on the IO thread to write |buffer| into the temporary file. Use of
-  // base::Unretained() is OK as |file_writer_| is owned by |this|, and if
-  // |this| is destructed it will destroy |file_writer_| on the IO thread.
-  file_writer_ = base::SequenceBound<FileWriter>(GetIOThreadTaskRunner({}));
-  // TODO(dcheng): Migrate this to use Then()?
-  file_writer_.AsyncCall(&FileWriter::Write)
-      .WithArgs(file_system_context_, CreateFileSystemURL(temp_file_name_),
-                std::move(buffer), bytes_to_write, std::move(write_done_cb));
-}
-
-void CdmFileImpl::WriteDone(bool success) {
-  DVLOG(3) << __func__ << " file: " << file_name_
-           << ", success: " << (success ? "yes" : "no");
-  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
-  DCHECK(file_locked_);
-  DCHECK(file_writer_);
-  DCHECK(write_callback_);
-
-  // We are done with |file_writer_|.
-  file_writer_.Reset();
-
-  if (!success) {
-    std::move(write_callback_).Run(Status::kFailure);
-    return;
-  }
-
-  // Now rename |temp_file_name_| to |file_name_|.
-  storage::FileSystemURL src_file_url = CreateFileSystemURL(temp_file_name_);
-  storage::FileSystemURL dest_file_url = CreateFileSystemURL(file_name_);
-  storage::AsyncFileUtil* file_util = file_system_context_->GetAsyncFileUtil(
-      storage::kFileSystemTypePluginPrivate);
-  auto operation_context =
-      std::make_unique<storage::FileSystemOperationContext>(
-          file_system_context_.get());
-  DVLOG(3) << "Renaming " << src_file_url.DebugString() << " to "
-           << dest_file_url.DebugString();
-  file_util->MoveFileLocal(
-      std::move(operation_context), src_file_url, dest_file_url,
-      storage::FileSystemOperation::CopyOrMoveOptionSet(),
-      base::BindOnce(&CdmFileImpl::OnFileRenamed, weak_factory_.GetWeakPtr()));
-}
-
-void CdmFileImpl::OnFileRenamed(base::File::Error move_result) {
-  DVLOG(3) << __func__ << " file: " << file_name_
-           << ", result: " << base::File::ErrorToString(move_result);
-  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
-  DCHECK(file_locked_);
-  DCHECK(!file_writer_);
-  DCHECK(write_callback_);
-
-  // Was the rename successful?
-  if (move_result != base::File::FILE_OK) {
-    DLOG(WARNING) << "Unable to rename file " << temp_file_name_ << " to "
-                  << file_name_
-                  << ", error: " << base::File::ErrorToString(move_result);
-    std::move(write_callback_).Run(Status::kFailure);
-    return;
-  }
-
-  // Only report writing time for successful writes.
-  ReportFileOperationTimeUMA(kWriteTimeUmaName);
-  std::move(write_callback_).Run(Status::kSuccess);
-}
-
-void CdmFileImpl::DeleteFile() {
-  DVLOG(3) << __func__;
-  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
-  DCHECK(file_locked_);
-  DCHECK(!file_writer_);
-  DCHECK(write_callback_);
-
-  if (host_) {
-    DeleteUsingMediaLicenseStorageDelegate();
-    return;
-  }
-
-  storage::FileSystemURL file_url = CreateFileSystemURL(file_name_);
-  storage::AsyncFileUtil* file_util = file_system_context_->GetAsyncFileUtil(
-      storage::kFileSystemTypePluginPrivate);
-  auto operation_context =
-      std::make_unique<storage::FileSystemOperationContext>(
-          file_system_context_.get());
-
-  DVLOG(3) << "Deleting " << file_url.DebugString();
-  file_util->DeleteFile(
-      std::move(operation_context), file_url,
-      base::BindOnce(&CdmFileImpl::OnFileDeleted, weak_factory_.GetWeakPtr()));
-}
-
-void CdmFileImpl::OnFileDeleted(base::File::Error result) {
-  DVLOG(3) << __func__ << " file: " << file_name_
-           << ", result: " << base::File::ErrorToString(result);
-  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
-  DCHECK(file_locked_);
-  DCHECK(!file_writer_);
-  DCHECK(write_callback_);
-
-  if (result != base::File::FILE_OK &&
-      result != base::File::FILE_ERROR_NOT_FOUND) {
-    DLOG(WARNING) << "Unable to delete file " << file_name_
-                  << ", error: " << base::File::ErrorToString(result);
-    std::move(write_callback_).Run(Status::kFailure);
-    return;
-  }
-
-  // Only report time for successful deletes.
-  ReportFileOperationTimeUMA(kDeleteTimeUmaName);
-  std::move(write_callback_).Run(Status::kSuccess);
-}
-
-storage::FileSystemURL CdmFileImpl::CreateFileSystemURL(
-    const std::string& file_name) {
-  const GURL crack_url = GURL(file_system_root_uri_ + file_name);
-  // TODO(https://crbug.com/1231162): determine whether EME/CDM/plugin
-  // private file system will be partitioned and use the appropriate StorageKey
-  const blink::StorageKey crack_storage_key =
-      blink::StorageKey(url::Origin::Create(crack_url));
-  return file_system_context_->CrackURL(crack_url, crack_storage_key);
-}
-
-bool CdmFileImpl::AcquireFileLock(const std::string& file_name) {
-  if (host_) {
-    // The MediaLicenseStorageHost handles file locking and will not call the
-    // `Initialize()` method which acquires file locks.
-    NOTREACHED();
-    return true;
-  }
-
-  FileLockKey file_lock_key(origin_, cdm_type_, file_name);
-  return GetFileLockMap()->AcquireFileLock(file_lock_key);
-}
-
-void CdmFileImpl::ReleaseFileLock(const std::string& file_name) {
-  if (host_) {
-    // The MediaLicenseStorageHost handles file locking.
-    return;
-  }
-
-  FileLockKey file_lock_key(origin_, cdm_type_, file_name);
-  GetFileLockMap()->ReleaseFileLock(file_lock_key);
+  host_->WriteFile(
+      cdm_type_, file_name_, data,
+      base::BindOnce(&CdmFileImpl::DidWrite, weak_factory_.GetWeakPtr()));
 }
 
 void CdmFileImpl::ReportFileOperationTimeUMA(const std::string& uma_name) {
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+  DCHECK(host_);
+
   static const char kIncognito[] = ".Incognito";
   static const char kNormal[] = ".Normal";
 
-  bool is_incognito =
-      host_ ? host_->in_memory() : file_system_context_->is_incognito();
+  bool is_incognito = host_->in_memory();
 
   // This records the time taken to the base histogram as well as splitting it
   // out by incognito or normal mode.
@@ -802,52 +181,9 @@
       time_taken);
 }
 
-void CdmFileImpl::ReadUsingMediaLicenseStorageDelegate() {
+void CdmFileImpl::DidWrite(bool success) {
+  DVLOG(3) << __func__ << " file: " << file_name_;
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
-
-  DCHECK(file_locked_);
-  DCHECK(read_callback_);
-  DCHECK(host_);
-
-  host_->ReadFile(
-      cdm_type_, file_name_,
-      base::BindOnce(&CdmFileImpl::DidReadUsingMediaLicenseStorageDelegate,
-                     weak_factory_.GetWeakPtr()));
-}
-
-void CdmFileImpl::DidReadUsingMediaLicenseStorageDelegate(
-    absl::optional<std::vector<uint8_t>> data) {
-  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
-  DCHECK(file_locked_);
-  DCHECK(read_callback_);
-  DCHECK(host_);
-
-  if (!data.has_value()) {
-    std::move(read_callback_).Run(Status::kFailure, {});
-    return;
-  }
-
-  // Only report reading time for successful reads.
-  ReportFileOperationTimeUMA(kReadTimeUmaName);
-  std::move(read_callback_).Run(Status::kSuccess, std::move(data.value()));
-}
-
-void CdmFileImpl::WriteUsingMediaLicenseStorageDelegate(
-    const std::vector<uint8_t>& data) {
-  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
-  DCHECK(file_locked_);
-  DCHECK(write_callback_);
-  DCHECK(host_);
-
-  host_->WriteFile(
-      cdm_type_, file_name_, data,
-      base::BindOnce(&CdmFileImpl::DidWriteUsingMediaLicenseStorageDelegate,
-                     weak_factory_.GetWeakPtr()));
-}
-
-void CdmFileImpl::DidWriteUsingMediaLicenseStorageDelegate(bool success) {
-  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
-  DCHECK(file_locked_);
   DCHECK(write_callback_);
   DCHECK(host_);
 
@@ -862,21 +198,22 @@
   std::move(write_callback_).Run(Status::kSuccess);
 }
 
-void CdmFileImpl::DeleteUsingMediaLicenseStorageDelegate() {
+void CdmFileImpl::DeleteFile() {
+  DVLOG(3) << __func__;
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
-  DCHECK(file_locked_);
   DCHECK(write_callback_);
   DCHECK(host_);
 
+  DVLOG(3) << "Deleting " << file_name_;
+
   host_->DeleteFile(
       cdm_type_, file_name_,
-      base::BindOnce(&CdmFileImpl::DidDeleteUsingMediaLicenseStorageDelegate,
-                     weak_factory_.GetWeakPtr()));
+      base::BindOnce(&CdmFileImpl::DidDeleteFile, weak_factory_.GetWeakPtr()));
 }
 
-void CdmFileImpl::DidDeleteUsingMediaLicenseStorageDelegate(bool success) {
+void CdmFileImpl::DidDeleteFile(bool success) {
+  DVLOG(3) << __func__ << " file: " << file_name_;
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
-  DCHECK(file_locked_);
   DCHECK(write_callback_);
   DCHECK(host_);
 
diff --git a/content/browser/media/cdm_file_impl.h b/content/browser/media/cdm_file_impl.h
index d79faadd..1bdb852 100644
--- a/content/browser/media/cdm_file_impl.h
+++ b/content/browser/media/cdm_file_impl.h
@@ -18,19 +18,11 @@
 #include "media/cdm/cdm_type.h"
 #include "media/mojo/mojom/cdm_storage.mojom.h"
 #include "mojo/public/cpp/bindings/associated_receiver.h"
-#include "storage/browser/file_system/async_file_util.h"
-#include "url/origin.h"
-
-namespace storage {
-class FileSystemContext;
-class FileSystemURL;
-}  // namespace storage
 
 namespace content {
 class MediaLicenseStorageHost;
 
-// This class implements the media::mojom::CdmFile interface. It uses the same
-// mojo pipe as CdmStorageImpl, to enforce message dispatch order.
+// This class implements the media::mojom::CdmFile interface.
 class CdmFileImpl final : public media::mojom::CdmFile {
  public:
   // Check whether |name| is valid as a usable file name. Returns true if it is,
@@ -46,103 +38,39 @@
       const std::string& file_name,
       mojo::PendingAssociatedReceiver<media::mojom::CdmFile> pending_receiver);
 
-  CdmFileImpl(const std::string& file_name,
-              const url::Origin& origin,
-              const media::CdmType& cdm_type,
-              const std::string& file_system_root_uri,
-              scoped_refptr<storage::FileSystemContext> file_system_context);
-
   CdmFileImpl(const CdmFileImpl&) = delete;
   CdmFileImpl& operator=(const CdmFileImpl&) = delete;
 
   ~CdmFileImpl() final;
 
-  // Called to grab a lock on the file. Returns false if the file is in use by
-  // other CDMs or by the system, true otherwise. Note that |this| should not
-  // be used anymore if Initialize() fails.
-  bool Initialize();
-
   // media::mojom::CdmFile implementation.
   void Read(ReadCallback callback) final;
   void Write(const std::vector<uint8_t>& data, WriteCallback callback) final;
 
  private:
-  class FileReader;
-  class FileWriter;
-
-  // Called when the file is read. If |success| is true, |data| is the contents
-  // of the file read.
-  void ReadDone(bool success, std::vector<uint8_t> data);
-
-  // Called in sequence to write the file. |buffer| is the contents to be
-  // written to the file, |bytes_to_write| is the length. Uses |file_writer_|,
-  // which is cleared when no longer needed. |write_callback_| will always be
-  // called with the result.
-  void OnEnsureTempFileExists(scoped_refptr<net::IOBuffer> buffer,
-                              int bytes_to_write,
-                              base::File::Error result,
-                              bool created);
-  void OnTempFileIsEmpty(scoped_refptr<net::IOBuffer> buffer,
-                         int bytes_to_write,
-                         base::File::Error result);
-  void WriteDone(bool success);
-  void OnFileRenamed(base::File::Error move_result);
+  void DidRead(absl::optional<std::vector<uint8_t>> data);
+  void DidWrite(bool success);
 
   // Deletes |file_name_| asynchronously.
   void DeleteFile();
-  void OnFileDeleted(base::File::Error result);
-
-  // Returns the FileSystemURL for the specified |file_name|.
-  storage::FileSystemURL CreateFileSystemURL(const std::string& file_name);
-
-  // Helper methods to lock and unlock a file.
-  bool AcquireFileLock(const std::string& file_name);
-  void ReleaseFileLock(const std::string& file_name);
+  void DidDeleteFile(bool success);
 
   // Report operation time to UMA.
   void ReportFileOperationTimeUMA(const std::string& uma_name);
 
-  void ReadUsingMediaLicenseStorageDelegate();
-  void DidReadUsingMediaLicenseStorageDelegate(
-      absl::optional<std::vector<uint8_t>> data);
-  void WriteUsingMediaLicenseStorageDelegate(const std::vector<uint8_t>& data);
-  void DidWriteUsingMediaLicenseStorageDelegate(bool success);
-  void DeleteUsingMediaLicenseStorageDelegate();
-  void DidDeleteUsingMediaLicenseStorageDelegate(bool success);
-
   void OnReceiverDisconnect();
 
-  // This receiver is associated with the CdmStorage receiver which creates it.
+  // This receiver is associated with the MediaLicenseStorageHost which creates
+  // it.
   mojo::AssociatedReceiver<media::mojom::CdmFile> receiver_{this};
 
-  // Names of the files this class represents.
   const std::string file_name_;
-  const std::string temp_file_name_;
-
-  // TODO(crbug.com/1231162): Remove some of these fields and update this
-  // comment when we migrate off of the PluginPrivateFileSystem.
-  // Files are stored in the PluginPrivateFileSystem. The following are needed
-  // to access files.
-  const url::Origin origin_;
   const media::CdmType cdm_type_;
-  const std::string file_system_root_uri_;
-  scoped_refptr<storage::FileSystemContext> file_system_context_;
 
-  // Keep track of when the original file |file_name_| is locked.
-  // Initialize() can only be called if false and takes the lock (on success).
-  // Read() and Write() can only be called if true.
-  // Note that having a lock on |file_name| implies that |temp_file_name| is
-  // reserved for use by this object only, and an explicit lock on
-  // |temp_file_name| is not required.
-  bool file_locked_ = false;
-
-  // Used when reading the file. |file_reader_| lives on the IO thread.
+  // Each of these callbacks is only valid while there is an in-progress read
+  // or write operation, respectively.
   ReadCallback read_callback_;
-  base::SequenceBound<FileReader> file_reader_;
-
-  // Used when writing the file. |file_writer_| lives on the IO thread.
   WriteCallback write_callback_;
-  base::SequenceBound<FileWriter> file_writer_;
 
   // Time when the read or write operation starts.
   base::TimeTicks start_time_;
diff --git a/content/browser/media/cdm_storage_impl.cc b/content/browser/media/cdm_storage_impl.cc
deleted file mode 100644
index 8257981..0000000
--- a/content/browser/media/cdm_storage_impl.cc
+++ /dev/null
@@ -1,189 +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 "content/browser/media/cdm_storage_impl.h"
-
-#include <map>
-#include <memory>
-#include <tuple>
-#include <utility>
-
-#include "base/bind.h"
-#include "base/callback.h"
-#include "base/logging.h"
-#include "content/browser/media/cdm_file_impl.h"
-#include "content/public/browser/child_process_security_policy.h"
-#include "content/public/browser/render_frame_host.h"
-#include "content/public/browser/render_process_host.h"
-#include "content/public/browser/storage_partition.h"
-#include "media/cdm/cdm_type.h"
-#include "mojo/public/cpp/bindings/associated_remote.h"
-#include "mojo/public/cpp/bindings/pending_associated_remote.h"
-#include "storage/browser/file_system/file_system_context.h"
-#include "storage/browser/file_system/file_system_operation_context.h"
-#include "storage/browser/file_system/isolated_context.h"
-#include "storage/common/file_system/file_system_types.h"
-#include "storage/common/file_system/file_system_util.h"
-#include "url/origin.h"
-
-// Currently this uses the PluginPrivateFileSystem as the previous CDMs ran
-// as pepper plugins and we need to be able to access any existing files.
-// TODO(crbug.com/1231162): Switch to using a separate file system once CDMs no
-// longer run as pepper plugins.
-
-namespace content {
-
-// static
-void CdmStorageImpl::Create(
-    RenderFrameHost* render_frame_host,
-    const media::CdmType& cdm_type,
-    mojo::PendingReceiver<media::mojom::CdmStorage> receiver) {
-  DVLOG(3) << __func__;
-  DCHECK(!render_frame_host->GetLastCommittedOrigin().opaque())
-      << "Invalid origin specified for CdmStorageImpl::Create";
-
-  // Take a reference to the FileSystemContext.
-  scoped_refptr<storage::FileSystemContext> file_system_context;
-  StoragePartition* storage_partition =
-      render_frame_host->GetProcess()->GetStoragePartition();
-  if (storage_partition)
-    file_system_context = storage_partition->GetFileSystemContext();
-
-  // The created object is bound to (and owned by) |receiver|.
-  new CdmStorageImpl(render_frame_host, cdm_type,
-                     std::move(file_system_context), std::move(receiver));
-}
-
-CdmStorageImpl::CdmStorageImpl(
-    RenderFrameHost* render_frame_host,
-    const media::CdmType& cdm_type,
-    scoped_refptr<storage::FileSystemContext> file_system_context,
-    mojo::PendingReceiver<media::mojom::CdmStorage> receiver)
-    : DocumentService(render_frame_host, std::move(receiver)),
-      cdm_type_(cdm_type),
-      file_system_context_(std::move(file_system_context)),
-      child_process_id_(render_frame_host->GetProcess()->GetID()) {}
-
-CdmStorageImpl::~CdmStorageImpl() {
-  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
-}
-
-void CdmStorageImpl::Open(const std::string& file_name, OpenCallback callback) {
-  DVLOG(3) << __func__ << " file: " << file_name;
-  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
-
-  if (file_name.empty()) {
-    DVLOG(1) << "No file specified.";
-    std::move(callback).Run(Status::kFailure, mojo::NullAssociatedRemote());
-    return;
-  }
-
-  // The file system should only be opened once. If it has been attempted and
-  // failed, we can't create the CdmFile objects.
-  if (file_system_state_ == FileSystemState::kError) {
-    std::move(callback).Run(Status::kFailure, mojo::NullAssociatedRemote());
-    return;
-  }
-
-  // If the file system is already open, create and initialize the CdmFileImpl
-  // object.
-  if (file_system_state_ == FileSystemState::kOpened) {
-    CreateCdmFile(file_name, std::move(callback));
-    return;
-  }
-
-  // Save a file name and callback for when the file system is open. If the
-  // open is already in progress, nothing more to do until the existing
-  // OpenPluginPrivateFileSystem() call completes.
-  pending_open_calls_.emplace(pending_open_calls_.end(), file_name,
-                              std::move(callback));
-  if (file_system_state_ == FileSystemState::kOpening)
-    return;
-
-  DCHECK_EQ(FileSystemState::kUnopened, file_system_state_);
-  file_system_state_ = FileSystemState::kOpening;
-
-  std::string fsid =
-      storage::IsolatedContext::GetInstance()->RegisterFileSystemForVirtualPath(
-          storage::kFileSystemTypePluginPrivate,
-          storage::kPluginPrivateRootName, base::FilePath());
-  if (!storage::ValidateIsolatedFileSystemId(fsid)) {
-    DVLOG(1) << "Invalid file system ID.";
-    OnFileSystemOpened(base::File::FILE_ERROR_NOT_FOUND);
-    return;
-  }
-
-  // Grant full access of isolated file system to child process.
-  ChildProcessSecurityPolicy::GetInstance()->GrantCreateReadWriteFileSystem(
-      child_process_id_, fsid);
-
-  // Keep track of the URI for this instance of the PluginPrivateFileSystem.
-  file_system_root_uri_ = storage::GetIsolatedFileSystemRootURIString(
-      origin().GetURL(), fsid, storage::kPluginPrivateRootName);
-
-  file_system_context_->OpenPluginPrivateFileSystem(
-      origin(), storage::kFileSystemTypePluginPrivate, fsid,
-      cdm_type_.legacy_file_system_id,
-      storage::OPEN_FILE_SYSTEM_CREATE_IF_NONEXISTENT,
-      base::BindOnce(&CdmStorageImpl::OnFileSystemOpened,
-                     weak_factory_.GetWeakPtr()));
-}
-
-void CdmStorageImpl::OnFileSystemOpened(base::File::Error error) {
-  DVLOG(3) << __func__;
-  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
-  DCHECK_EQ(FileSystemState::kOpening, file_system_state_);
-
-  if (error != base::File::FILE_OK) {
-    file_system_state_ = FileSystemState::kError;
-    // All pending calls will fail.
-    for (auto& pending : pending_open_calls_) {
-      std::move(pending.second)
-          .Run(Status::kFailure, mojo::NullAssociatedRemote());
-    }
-    pending_open_calls_.clear();
-    return;
-  }
-
-  // File system successfully opened, so create the CdmFileImpl object for
-  // all pending Open() calls.
-  file_system_state_ = FileSystemState::kOpened;
-  for (auto& pending : pending_open_calls_) {
-    CreateCdmFile(pending.first, std::move(pending.second));
-  }
-  pending_open_calls_.clear();
-}
-
-void CdmStorageImpl::CreateCdmFile(const std::string& file_name,
-                                   OpenCallback callback) {
-  DVLOG(3) << __func__ << " file: " << file_name;
-  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
-  DCHECK_EQ(FileSystemState::kOpened, file_system_state_);
-
-  // File system opened successfully, so create an CdmFileImpl object and
-  // initialize it (which only grabs the lock to prevent any other access to the
-  // file except through this object).
-  if (!CdmFileImpl::IsValidName(file_name)) {
-    std::move(callback).Run(Status::kFailure, mojo::NullAssociatedRemote());
-    return;
-  }
-
-  auto cdm_file_impl = std::make_unique<CdmFileImpl>(
-      file_name, origin(), cdm_type_, file_system_root_uri_,
-      file_system_context_);
-
-  if (!cdm_file_impl->Initialize()) {
-    // Unable to initialize with the file requested.
-    std::move(callback).Run(Status::kInUse, mojo::NullAssociatedRemote());
-    return;
-  }
-
-  // File was opened successfully, so create the binding and return success.
-  mojo::PendingAssociatedRemote<media::mojom::CdmFile> cdm_file;
-  cdm_file_receivers_.Add(std::move(cdm_file_impl),
-                          cdm_file.InitWithNewEndpointAndPassReceiver());
-  std::move(callback).Run(Status::kSuccess, std::move(cdm_file));
-}
-
-}  // namespace content
diff --git a/content/browser/media/cdm_storage_impl.h b/content/browser/media/cdm_storage_impl.h
deleted file mode 100644
index 8975d5c2..0000000
--- a/content/browser/media/cdm_storage_impl.h
+++ /dev/null
@@ -1,114 +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 CONTENT_BROWSER_MEDIA_CDM_STORAGE_IMPL_H_
-#define CONTENT_BROWSER_MEDIA_CDM_STORAGE_IMPL_H_
-
-#include <set>
-#include <string>
-
-#include "base/callback_forward.h"
-#include "base/files/file.h"
-#include "base/memory/ref_counted.h"
-#include "base/memory/weak_ptr.h"
-#include "content/common/content_export.h"
-#include "content/public/browser/document_service.h"
-#include "media/cdm/cdm_type.h"
-#include "media/mojo/mojom/cdm_storage.mojom.h"
-#include "mojo/public/cpp/bindings/pending_receiver.h"
-#include "mojo/public/cpp/bindings/unique_associated_receiver_set.h"
-
-namespace storage {
-class FileSystemContext;
-}
-
-namespace content {
-class CdmFileImpl;
-class RenderFrameHost;
-
-// This class implements the media::mojom::CdmStorage using the
-// PluginPrivateFileSystem for backwards compatibility with CDMs running
-// as a pepper plugin.
-class CONTENT_EXPORT CdmStorageImpl final
-    : public content::DocumentService<media::mojom::CdmStorage> {
- public:
-  // Create a CdmStorageImpl object for |cdm_type| and bind it to |request|.
-  static void Create(RenderFrameHost* render_frame_host,
-                     const media::CdmType& cdm_type,
-                     mojo::PendingReceiver<media::mojom::CdmStorage> receiver);
-
-  CdmStorageImpl(const CdmStorageImpl&) = delete;
-  CdmStorageImpl& operator=(const CdmStorageImpl&) = delete;
-
-  // media::mojom::CdmStorage implementation.
-  void Open(const std::string& file_name, OpenCallback callback) final;
-
- private:
-  // TODO(crbug.com/1231162): Consider reimagining this design once we migrate
-  // off of the PluginPrivateFileSystem.
-  // File system should only be opened once, so keep track if it has already
-  // been opened (or is in the process of opening). State is kError if an error
-  // happens while opening the file system.
-  enum class FileSystemState { kUnopened, kOpening, kOpened, kError };
-
-  CdmStorageImpl(RenderFrameHost* render_frame_host,
-                 const media::CdmType& cdm_type,
-                 scoped_refptr<storage::FileSystemContext> file_system_context,
-                 mojo::PendingReceiver<media::mojom::CdmStorage> receiver);
-  ~CdmStorageImpl() final;
-
-  // Called when the file system is opened.
-  void OnFileSystemOpened(base::File::Error error);
-
-  // After the file system is opened, called to create a CdmFile object.
-  void CreateCdmFile(const std::string& file_name, OpenCallback callback);
-
-  // Called after the CdmFileImpl object has opened the file.
-  void OnCdmFileInitialized(std::unique_ptr<CdmFileImpl> cdm_file_impl,
-                            OpenCallback callback,
-                            bool success);
-
-  // TODO(crbug.com/1231162): Update this comment once we migrate off of the
-  // PluginPrivateFileSystem. CdmType contains a `legacy_file_system_id` used to
-  // access the PluginPrivateFileSystem, which will be removed once all data has
-  // been migrated.
-  // Files are stored in the PluginPrivateFileSystem, so keep track of the CDM
-  // file system ID in order to open the files in the correct context.
-  const media::CdmType cdm_type_;
-  // TODO(crbug.com/1231162): Remove this once we migrate off of the
-  // PluginPrivateFileSystem.
-  scoped_refptr<storage::FileSystemContext> file_system_context_;
-
-  // TODO(crbug.com/1231162): Update this comment and consider removing this
-  // member once we migrate off of the PluginPrivateFileSystem.
-  // The PluginPrivateFileSystem only needs to be opened once.
-  FileSystemState file_system_state_ = FileSystemState::kUnopened;
-
-  // As multiple calls to Open() could happen while the file system is being
-  // opened asynchronously, keep track of the requests so they can be
-  // processed once the file system is open.
-  using PendingOpenData = std::pair<std::string, OpenCallback>;
-  std::vector<PendingOpenData> pending_open_calls_;
-
-  // TODO(crbug.com/1231162): Remove this once we migrate off of the
-  // PluginPrivateFileSystem.
-  // Once the PluginPrivateFileSystem is opened, keep track of the URI that
-  // refers to it.
-  std::string file_system_root_uri_;
-
-  // This is the child process that will actually read and write the file(s)
-  // returned, and it needs permission to access the file(s).
-  const int child_process_id_;
-
-  // Keep track of all media::mojom::CdmFile receivers, as each CdmFileImpl
-  // object keeps a reference to |this|. If |this| goes away unexpectedly,
-  // all remaining CdmFile receivers will be closed.
-  mojo::UniqueAssociatedReceiverSet<media::mojom::CdmFile> cdm_file_receivers_;
-
-  base::WeakPtrFactory<CdmStorageImpl> weak_factory_{this};
-};
-
-}  // namespace content
-
-#endif  // CONTENT_BROWSER_MEDIA_CDM_STORAGE_IMPL_H_
diff --git a/content/browser/media/cdm_storage_impl_unittest.cc b/content/browser/media/cdm_storage_impl_unittest.cc
deleted file mode 100644
index 9fb62b98..0000000
--- a/content/browser/media/cdm_storage_impl_unittest.cc
+++ /dev/null
@@ -1,638 +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 "content/browser/media/cdm_storage_impl.h"
-
-#include <memory>
-#include <vector>
-
-#include "base/bind.h"
-#include "base/callback.h"
-#include "base/files/file.h"
-#include "base/logging.h"
-#include "base/test/bind.h"
-#include "base/test/test_future.h"
-#include "base/test/with_feature_override.h"
-#include "content/browser/media/media_license_manager.h"
-#include "content/browser/storage_partition_impl.h"
-#include "content/public/browser/render_frame_host.h"
-#include "content/public/browser/storage_partition.h"
-#include "content/public/browser/web_contents.h"
-#include "content/public/common/content_features.h"
-#include "content/public/test/navigation_simulator.h"
-#include "content/public/test/test_renderer_host.h"
-#include "media/cdm/cdm_type.h"
-#include "media/mojo/mojom/cdm_storage.mojom.h"
-#include "mojo/public/cpp/bindings/associated_remote.h"
-#include "mojo/public/cpp/bindings/pending_associated_remote.h"
-#include "mojo/public/cpp/bindings/remote.h"
-#include "testing/gtest/include/gtest/gtest.h"
-#include "third_party/blink/public/common/storage_key/storage_key.h"
-#include "third_party/widevine/cdm/buildflags.h"
-#include "url/gurl.h"
-#include "url/origin.h"
-
-#if BUILDFLAG(ENABLE_WIDEVINE)
-#include "third_party/widevine/cdm/widevine_cdm_common.h"
-#endif  // BUILDFLAG(ENABLE_WIDEVINE)
-
-using media::mojom::CdmFile;
-using media::mojom::CdmStorage;
-
-namespace content {
-using CdmFileId = MediaLicenseManager::CdmFileId;
-using CdmFileIdAndContents = MediaLicenseManager::CdmFileIdAndContents;
-
-namespace {
-
-const media::CdmType kTestCdmType{base::Token{1234, 5678}, "test_file_system"};
-const media::CdmType kDifferentCdmType{base::Token{8765, 4321},
-                                       "different_plugin"};
-const media::CdmType kUnrecognizedCdmType{base::Token{1111, 2222}, "imposter"};
-
-const char kTestOrigin[] = "http://www.test.com";
-
-const std::vector<MediaLicenseManager::CdmFileIdAndContents> kDefaultFiles{
-    {{"file1", kTestCdmType}, {'r', 'a', 'n', 'd'}},
-    {{"file2", kTestCdmType}, {'r', 'a', 'n', 'd', 'o'}},
-    {{"file3", kTestCdmType}, {'r', 'a', 'n', 'd', 'o', 'm'}},
-};
-
-// Helper functions to manipulate RenderFrameHosts.
-
-void SimulateNavigation(RenderFrameHost** rfh, const GURL& url) {
-  auto navigation_simulator =
-      NavigationSimulator::CreateRendererInitiated(url, *rfh);
-  navigation_simulator->Commit();
-  *rfh = navigation_simulator->GetFinalRenderFrameHost();
-}
-
-}  // namespace
-
-class CdmStorageTest : public base::test::WithFeatureOverride,
-                       public RenderViewHostTestHarness {
- public:
-  CdmStorageTest()
-      : base::test::WithFeatureOverride(features::kMediaLicenseBackend),
-        RenderViewHostTestHarness(
-            content::BrowserTaskEnvironment::REAL_IO_THREAD) {}
-
- protected:
-  void SetUp() final {
-    RenderViewHostTestHarness::SetUp();
-    rfh_ = web_contents()->GetPrimaryMainFrame();
-    RenderFrameHostTester::For(rfh_)->InitializeRenderFrameIfNeeded();
-    SimulateNavigation(&rfh_, GURL(kTestOrigin));
-
-    if (base::FeatureList::IsEnabled(features::kMediaLicenseBackend)) {
-      // Create the CdmStorageImpl object.
-      auto* media_license_manager =
-          static_cast<StoragePartitionImpl*>(rfh_->GetStoragePartition())
-              ->GetMediaLicenseManager();
-      DCHECK(media_license_manager);
-      media_license_manager->OpenCdmStorage(
-          MediaLicenseManager::BindingContext(
-              blink::StorageKey::CreateFromStringForTesting(kTestOrigin),
-              kTestCdmType),
-          cdm_storage_.BindNewPipeAndPassReceiver());
-    } else {
-      // Create the CdmStorageImpl object. |cdm_storage_| will own the resulting
-      // object.
-      CdmStorageImpl::Create(rfh_, kTestCdmType,
-                             cdm_storage_.BindNewPipeAndPassReceiver());
-    }
-  }
-
-  // Open the file |name|. Returns true if the file returned is valid, false
-  // otherwise. On success |cdm_file| is bound to the CdmFileImpl object.
-  bool Open(const std::string& name,
-            mojo::AssociatedRemote<CdmFile>& cdm_file) {
-    DVLOG(3) << __func__;
-
-    base::test::TestFuture<CdmStorage::Status,
-                           mojo::PendingAssociatedRemote<CdmFile>>
-        future;
-    cdm_storage_->Open(name, future.GetCallback());
-
-    CdmStorage::Status status = future.Get<0>();
-    mojo::PendingAssociatedRemote<CdmFile> actual_file =
-        std::move(std::get<1>(future.Take()));
-    if (!actual_file) {
-      DCHECK_NE(status, CdmStorage::Status::kSuccess);
-      return false;
-    }
-
-    // Open() returns a mojo::PendingAssociatedRemote<CdmFile>, so bind it to
-    // the mojo::AssociatedRemote<CdmFileAssociated> provided.
-    mojo::AssociatedRemote<CdmFile> cdm_file_remote;
-    cdm_file_remote.Bind(std::move(actual_file));
-    cdm_file = std::move(cdm_file_remote);
-
-    return status == CdmStorage::Status::kSuccess;
-  }
-
-  // Reads the contents of the previously opened |cdm_file|. If successful,
-  // true is returned and |data| is updated with the contents of the file.
-  // If unable to read the file, false is returned.
-  bool Read(CdmFile* cdm_file, std::vector<uint8_t>& data) {
-    DVLOG(3) << __func__;
-
-    base::test::TestFuture<CdmFile::Status, std::vector<uint8_t>> future;
-    cdm_file->Read(
-        future.GetCallback<CdmFile::Status, const std::vector<uint8_t>&>());
-
-    CdmFile::Status status = future.Get<0>();
-    data = future.Get<1>();
-    return status == CdmFile::Status::kSuccess;
-  }
-
-  // Writes |data| to the previously opened |cdm_file|, replacing the contents
-  // of the file. Returns true if successful, false otherwise.
-  bool Write(CdmFile* cdm_file, const std::vector<uint8_t>& data) {
-    DVLOG(3) << __func__;
-
-    base::test::TestFuture<CdmFile::Status> future;
-    cdm_file->Write(data, future.GetCallback());
-
-    CdmFile::Status status = future.Get();
-    return status == CdmFile::Status::kSuccess;
-  }
-
-  void WriteFiles(const std::vector<CdmFileIdAndContents>& files) {
-    // Write some data using the old backend.
-    for (const auto& file : files) {
-      mojo::AssociatedRemote<CdmFile> remote;
-      EXPECT_TRUE(Open(file.file.name, remote));
-      ASSERT_TRUE(remote.is_bound());
-      EXPECT_TRUE(Write(remote.get(), file.data));
-    }
-  }
-
-  void ReadFiles(const std::vector<CdmFileIdAndContents>& files) {
-    for (const auto& file : files) {
-      mojo::AssociatedRemote<CdmFile> remote;
-      EXPECT_TRUE(Open(file.file.name, remote));
-      ASSERT_TRUE(remote.is_bound());
-      std::vector<uint8_t> data_read;
-      EXPECT_TRUE(Read(remote.get(), data_read));
-      EXPECT_EQ(file.data, data_read);
-    }
-  }
-
-  void ExpectFilesEmpty(const std::vector<CdmFileIdAndContents>& files) {
-    for (const auto& file : files) {
-      mojo::AssociatedRemote<CdmFile> remote;
-      EXPECT_TRUE(Open(file.file.name, remote));
-      ASSERT_TRUE(remote.is_bound());
-      std::vector<uint8_t> data_read;
-      EXPECT_TRUE(Read(remote.get(), data_read));
-      EXPECT_TRUE(data_read.empty());
-    }
-  }
-
-  void ResetAndBindToOldBackend(const blink::StorageKey& storage_key,
-                                const media::CdmType& cdm_type) {
-    cdm_storage_.reset();
-
-    SimulateNavigation(&rfh_, storage_key.origin().GetURL());
-    CdmStorageImpl::Create(rfh_, cdm_type,
-                           cdm_storage_.BindNewPipeAndPassReceiver());
-  }
-
-  void ResetAndBindToNewBackend(const blink::StorageKey& storage_key,
-                                const media::CdmType& cdm_type) {
-    cdm_storage_.reset();
-
-    SimulateNavigation(&rfh_, storage_key.origin().GetURL());
-    media_license_manager()->OpenCdmStorage(
-        MediaLicenseManager::BindingContext(storage_key, cdm_type),
-        cdm_storage_.BindNewPipeAndPassReceiver());
-  }
-
-  MediaLicenseManager* media_license_manager() const {
-    auto* media_license_manager =
-        static_cast<StoragePartitionImpl*>(rfh_->GetStoragePartition())
-            ->GetMediaLicenseManager();
-    DCHECK(media_license_manager);
-    return media_license_manager;
-  }
-
-  RenderFrameHost* rfh_ = nullptr;
-  mojo::Remote<CdmStorage> cdm_storage_;
-};
-
-// TODO(crbug.com/1231162): Make this a non-parameterized test suite once we no
-// longer have to test against both backends.
-INSTANTIATE_FEATURE_OVERRIDE_TEST_SUITE(CdmStorageTest);
-
-TEST_P(CdmStorageTest, InvalidFileName) {
-  // Anything other than ASCII letter, digits, and -._ will fail. Add a
-  // Unicode character to the name.
-  const char kFileName[] = "openfile\u1234";
-  mojo::AssociatedRemote<CdmFile> cdm_file;
-  EXPECT_FALSE(Open(kFileName, cdm_file));
-  ASSERT_FALSE(cdm_file.is_bound());
-}
-
-TEST_P(CdmStorageTest, InvalidFileNameEmpty) {
-  const char kFileName[] = "";
-  mojo::AssociatedRemote<CdmFile> cdm_file;
-  EXPECT_FALSE(Open(kFileName, cdm_file));
-  ASSERT_FALSE(cdm_file.is_bound());
-}
-
-TEST_P(CdmStorageTest, InvalidFileNameStartWithUnderscore) {
-  const char kFileName[] = "_invalid";
-  mojo::AssociatedRemote<CdmFile> cdm_file;
-  EXPECT_FALSE(Open(kFileName, cdm_file));
-  ASSERT_FALSE(cdm_file.is_bound());
-}
-
-TEST_P(CdmStorageTest, InvalidFileNameTooLong) {
-  // Limit is 256 characters, so try a file name with 257.
-  const std::string kFileName(257, 'a');
-  mojo::AssociatedRemote<CdmFile> cdm_file;
-  EXPECT_FALSE(Open(kFileName, cdm_file));
-  ASSERT_FALSE(cdm_file.is_bound());
-}
-
-TEST_P(CdmStorageTest, OpenFile) {
-  const char kFileName[] = "test_file_name";
-  mojo::AssociatedRemote<CdmFile> cdm_file;
-  EXPECT_TRUE(Open(kFileName, cdm_file));
-  ASSERT_TRUE(cdm_file.is_bound());
-}
-
-TEST_P(CdmStorageTest, OpenFileLocked) {
-  const char kFileName[] = "test_file_name";
-  mojo::AssociatedRemote<CdmFile> cdm_file1;
-  EXPECT_TRUE(Open(kFileName, cdm_file1));
-  ASSERT_TRUE(cdm_file1.is_bound());
-
-  // Second attempt on the same file should fail as the file is locked.
-  mojo::AssociatedRemote<CdmFile> cdm_file2;
-  EXPECT_FALSE(Open(kFileName, cdm_file2));
-  ASSERT_FALSE(cdm_file2.is_bound());
-
-  // Now close the first file and try again. It should be free now.
-  cdm_file1.reset();
-
-  mojo::AssociatedRemote<CdmFile> cdm_file3;
-  EXPECT_TRUE(Open(kFileName, cdm_file3));
-  ASSERT_TRUE(cdm_file3.is_bound());
-}
-
-TEST_P(CdmStorageTest, MultipleFiles) {
-  const char kFileName1[] = "file1";
-  mojo::AssociatedRemote<CdmFile> cdm_file1;
-  EXPECT_TRUE(Open(kFileName1, cdm_file1));
-  ASSERT_TRUE(cdm_file1.is_bound());
-
-  const char kFileName2[] = "file2";
-  mojo::AssociatedRemote<CdmFile> cdm_file2;
-  EXPECT_TRUE(Open(kFileName2, cdm_file2));
-  ASSERT_TRUE(cdm_file2.is_bound());
-
-  const char kFileName3[] = "file3";
-  mojo::AssociatedRemote<CdmFile> cdm_file3;
-  EXPECT_TRUE(Open(kFileName3, cdm_file3));
-  ASSERT_TRUE(cdm_file3.is_bound());
-}
-
-TEST_P(CdmStorageTest, WriteThenReadFile) {
-  const char kFileName[] = "test_file_name";
-  mojo::AssociatedRemote<CdmFile> cdm_file;
-  EXPECT_TRUE(Open(kFileName, cdm_file));
-  ASSERT_TRUE(cdm_file.is_bound());
-
-  // Write several bytes and read them back.
-  std::vector<uint8_t> kTestData = {'r', 'a', 'n', 'd', 'o', 'm'};
-  EXPECT_TRUE(Write(cdm_file.get(), kTestData));
-
-  std::vector<uint8_t> data_read;
-  EXPECT_TRUE(Read(cdm_file.get(), data_read));
-  EXPECT_EQ(kTestData, data_read);
-}
-
-TEST_P(CdmStorageTest, ReadThenWriteEmptyFile) {
-  const char kFileName[] = "empty_file_name";
-  mojo::AssociatedRemote<CdmFile> cdm_file;
-  EXPECT_TRUE(Open(kFileName, cdm_file));
-  ASSERT_TRUE(cdm_file.is_bound());
-
-  // New file should be empty.
-  std::vector<uint8_t> data_read;
-  EXPECT_TRUE(Read(cdm_file.get(), data_read));
-  EXPECT_EQ(0u, data_read.size());
-
-  // Write nothing.
-  EXPECT_TRUE(Write(cdm_file.get(), std::vector<uint8_t>()));
-
-  // Should still be empty.
-  EXPECT_TRUE(Read(cdm_file.get(), data_read));
-  EXPECT_EQ(0u, data_read.size());
-}
-
-TEST_P(CdmStorageTest, ParallelRead) {
-  const char kFileName[] = "duplicate_read_file_name";
-  mojo::AssociatedRemote<CdmFile> cdm_file;
-  EXPECT_TRUE(Open(kFileName, cdm_file));
-  ASSERT_TRUE(cdm_file.is_bound());
-
-  // Attempts to reads the contents of the previously opened |cdm_file| twice.
-  // We don't really care about the data, just that 1 read succeeds and the
-  // other fails.
-  base::test::TestFuture<CdmFile::Status, std::vector<uint8_t>> future1;
-  base::test::TestFuture<CdmFile::Status, std::vector<uint8_t>> future2;
-
-  cdm_file->Read(
-      future1.GetCallback<CdmFile::Status, const std::vector<uint8_t>&>());
-  cdm_file->Read(
-      future2.GetCallback<CdmFile::Status, const std::vector<uint8_t>&>());
-
-  EXPECT_TRUE(future1.Wait());
-  EXPECT_TRUE(future2.Wait());
-
-  CdmFile::Status status1 = future1.Get<0>();
-  CdmFile::Status status2 = future2.Get<0>();
-
-  // One call should succeed, one should fail.
-  EXPECT_TRUE((status1 == CdmFile::Status::kSuccess &&
-               status2 == CdmFile::Status::kFailure) ||
-              (status1 == CdmFile::Status::kFailure &&
-               status2 == CdmFile::Status::kSuccess))
-      << "status 1: " << status1 << ", status2: " << status2;
-}
-
-TEST_P(CdmStorageTest, ParallelWrite) {
-  const char kFileName[] = "duplicate_write_file_name";
-  mojo::AssociatedRemote<CdmFile> cdm_file;
-  EXPECT_TRUE(Open(kFileName, cdm_file));
-  ASSERT_TRUE(cdm_file.is_bound());
-
-  // Attempts to write the contents of the previously opened |cdm_file| twice.
-  // We don't really care about the data, just that 1 write succeeds and the
-  // other fails.
-  base::test::TestFuture<CdmFile::Status> future1;
-  base::test::TestFuture<CdmFile::Status> future2;
-
-  cdm_file->Write({1, 2, 3}, future1.GetCallback());
-  cdm_file->Write({4, 5, 6}, future2.GetCallback());
-
-  EXPECT_TRUE(future1.Wait());
-  EXPECT_TRUE(future2.Wait());
-
-  CdmFile::Status status1 = future1.Get();
-  CdmFile::Status status2 = future2.Get();
-
-  // One call should succeed, one should fail.
-  EXPECT_TRUE((status1 == CdmFile::Status::kSuccess &&
-               status2 == CdmFile::Status::kFailure) ||
-              (status1 == CdmFile::Status::kFailure &&
-               status2 == CdmFile::Status::kSuccess))
-      << "status 1: " << status1 << ", status2: " << status2;
-}
-
-TEST_P(CdmStorageTest, MigrateDataNone) {
-  if (base::FeatureList::IsEnabled(features::kMediaLicenseBackend)) {
-    // No data to migrate if the flag is enabled.
-    return;
-  }
-
-  ResetAndBindToNewBackend(
-      blink::StorageKey::CreateFromStringForTesting(kTestOrigin), kTestCdmType);
-
-  // If there's no data to migrate this should run gracefully and without error.
-  base::RunLoop loop;
-  media_license_manager()->MigrateMediaLicensesForTesting(loop.QuitClosure());
-  loop.Run();
-}
-
-TEST_P(CdmStorageTest, MigrateDataBasic) {
-  if (base::FeatureList::IsEnabled(features::kMediaLicenseBackend)) {
-    // No data to migrate if the flag is enabled.
-    return;
-  }
-
-  // Write some data using the old backend.
-  WriteFiles(kDefaultFiles);
-
-  ResetAndBindToNewBackend(
-      blink::StorageKey::CreateFromStringForTesting(kTestOrigin), kTestCdmType);
-  base::RunLoop loop;
-  media_license_manager()->MigrateMediaLicensesForTesting(loop.QuitClosure());
-  loop.Run();
-
-  // Read data using the new backend.
-  ReadFiles(kDefaultFiles);
-}
-
-TEST_P(CdmStorageTest, MigrateDataAll) {
-  if (base::FeatureList::IsEnabled(features::kMediaLicenseBackend)) {
-    // No data to migrate if the flag is enabled.
-    return;
-  }
-
-  // Write some data using the old backend.
-  WriteFiles(kDefaultFiles);
-
-  // Open a new backend using a different CDM type. The original data should
-  // still have been migrated.
-  ResetAndBindToNewBackend(
-      blink::StorageKey::CreateFromStringForTesting(kTestOrigin),
-      kDifferentCdmType);
-  base::RunLoop loop;
-  media_license_manager()->MigrateMediaLicensesForTesting(loop.QuitClosure());
-  loop.Run();
-
-  // Can't read files from another CDM type.
-  ExpectFilesEmpty(kDefaultFiles);
-
-  // Files from the original CDM type should exist without another migration.
-  ResetAndBindToNewBackend(
-      blink::StorageKey::CreateFromStringForTesting(kTestOrigin), kTestCdmType);
-  ReadFiles(kDefaultFiles);
-}
-
-TEST_P(CdmStorageTest, MigrateDataPluginDataDeleted) {
-  if (base::FeatureList::IsEnabled(features::kMediaLicenseBackend)) {
-    // No data to migrate if the flag is enabled.
-    return;
-  }
-
-  // Write some data using the old backend.
-  WriteFiles(kDefaultFiles);
-
-  ResetAndBindToNewBackend(
-      blink::StorageKey::CreateFromStringForTesting(kTestOrigin), kTestCdmType);
-  base::RunLoop loop;
-  media_license_manager()->MigrateMediaLicensesForTesting(loop.QuitClosure());
-  loop.Run();
-
-  // Read data using the new backend.
-  ReadFiles(kDefaultFiles);
-
-  ResetAndBindToOldBackend(
-      blink::StorageKey::CreateFromStringForTesting(kTestOrigin), kTestCdmType);
-
-  // Data should have been removed from the old backend.
-  ExpectFilesEmpty(kDefaultFiles);
-}
-
-TEST_P(CdmStorageTest, MigrateDataMultipleCdmTypes) {
-  if (base::FeatureList::IsEnabled(features::kMediaLicenseBackend)) {
-    // No data to migrate if the flag is enabled.
-    return;
-  }
-
-  // Write some data using the old backend.
-  WriteFiles(kDefaultFiles);
-
-  // Write data to another CDM type from the same origin.
-  const std::vector<MediaLicenseManager::CdmFileIdAndContents> kDifferentFiles{
-      {{"other0", kDifferentCdmType}, {'e', 'x', 'a', 'm'}},
-      {{"other1", kDifferentCdmType}, {'e', 'x', 'a', 'm', 'p'}},
-      {{"other2", kDifferentCdmType}, {'e', 'x', 'a', 'm', 'p', 'l'}},
-      {{"other3", kDifferentCdmType}, {'e', 'x', 'a', 'm', 'p', 'l', 'e'}},
-  };
-  ResetAndBindToOldBackend(
-      blink::StorageKey::CreateFromStringForTesting(kTestOrigin),
-      kDifferentCdmType);
-  WriteFiles(kDifferentFiles);
-
-  // Ensure files from the first CDM type can be read by the new backend.
-  ResetAndBindToNewBackend(
-      blink::StorageKey::CreateFromStringForTesting(kTestOrigin), kTestCdmType);
-  base::RunLoop loop;
-  media_license_manager()->MigrateMediaLicensesForTesting(loop.QuitClosure());
-  loop.Run();
-
-  ReadFiles(kDefaultFiles);
-
-  // Open storage for the other CDM type. All media licenses should have been
-  // migrated.
-  ResetAndBindToNewBackend(
-      blink::StorageKey::CreateFromStringForTesting(kTestOrigin),
-      kDifferentCdmType);
-
-  ReadFiles(kDifferentFiles);
-}
-
-TEST_P(CdmStorageTest, MigrateDataUnrecognizedCdmType) {
-  if (base::FeatureList::IsEnabled(features::kMediaLicenseBackend)) {
-    // No data to migrate if the flag is enabled.
-    return;
-  }
-
-  // Write some data using the old backend.
-  WriteFiles(kDefaultFiles);
-
-  // Write data an unrecognized CDM type from the same origin.
-  const std::vector<CdmFileIdAndContents> kDifferentFiles{
-      {{"other1", kUnrecognizedCdmType}, {'i', 'g', 'n', 'o'}},
-      {{"other2", kUnrecognizedCdmType}, {'i', 'g', 'n', 'o', 'r'}},
-      {{"other3", kUnrecognizedCdmType}, {'i', 'g', 'n', 'o', 'r', 'e'}},
-      {{"other4", kUnrecognizedCdmType}, {'i', 'g', 'n', 'o', 'r', 'e', 'd'}},
-  };
-  ResetAndBindToOldBackend(
-      blink::StorageKey::CreateFromStringForTesting(kTestOrigin),
-      kUnrecognizedCdmType);
-  WriteFiles(kDifferentFiles);
-
-  // Read data from the original CDM type using the new backend.
-  ResetAndBindToNewBackend(
-      blink::StorageKey::CreateFromStringForTesting(kTestOrigin), kTestCdmType);
-  base::RunLoop loop;
-  media_license_manager()->MigrateMediaLicensesForTesting(loop.QuitClosure());
-  loop.Run();
-  ReadFiles(kDefaultFiles);
-
-  // Open storage for the other CDM type. Media licenses for the unrecognized
-  // CDM type should NOT have been migrated.
-  ResetAndBindToNewBackend(
-      blink::StorageKey::CreateFromStringForTesting(kTestOrigin),
-      kUnrecognizedCdmType);
-  ExpectFilesEmpty(kDifferentFiles);
-
-  // Despite not being migrated, the data should still have been removed from
-  // the old backend.
-  ResetAndBindToOldBackend(
-      blink::StorageKey::CreateFromStringForTesting(kTestOrigin), kTestCdmType);
-  ExpectFilesEmpty(kDefaultFiles);
-}
-
-TEST_P(CdmStorageTest, MigrateDataMultipleOrigins) {
-  if (base::FeatureList::IsEnabled(features::kMediaLicenseBackend)) {
-    // No data to migrate if the flag is enabled.
-    return;
-  }
-
-  // Write some data using the old backend.
-  WriteFiles(kDefaultFiles);
-
-  cdm_storage_.reset();
-
-  const blink::StorageKey kDifferentStorageKey =
-      blink::StorageKey::CreateFromStringForTesting("http://www.example.com");
-  ResetAndBindToOldBackend(kDifferentStorageKey, kTestCdmType);
-
-  const std::vector<CdmFileIdAndContents> kDifferentFiles{
-      {{"dif_file1", kTestCdmType}, {'e', 'x', 'a', 'm'}},
-      {{"dif_file2", kTestCdmType}, {'e', 'x', 'a', 'm', 'p'}},
-      {{"dif_file3", kTestCdmType}, {'e', 'x', 'a', 'm', 'p', 'l'}},
-      {{"dif_file4", kTestCdmType}, {'e', 'x', 'a', 'm', 'p', 'l', 'e'}},
-  };
-  WriteFiles(kDifferentFiles);
-
-  ResetAndBindToNewBackend(
-      blink::StorageKey::CreateFromStringForTesting(kTestOrigin), kTestCdmType);
-  base::RunLoop loop;
-  media_license_manager()->MigrateMediaLicensesForTesting(loop.QuitClosure());
-  loop.Run();
-
-  ReadFiles(kDefaultFiles);
-
-  cdm_storage_.reset();
-
-  // Open storage for the other origin. All media licenses should have been
-  // migrated.
-  ResetAndBindToNewBackend(kDifferentStorageKey, kTestCdmType);
-
-  ReadFiles(kDifferentFiles);
-}
-
-#if BUILDFLAG(ENABLE_WIDEVINE)
-TEST_P(CdmStorageTest, MigrateDataWidevine) {
-  if (base::FeatureList::IsEnabled(features::kMediaLicenseBackend)) {
-    // No data to migrate if the flag is enabled.
-    return;
-  }
-
-  ResetAndBindToOldBackend(
-      blink::StorageKey::CreateFromStringForTesting(kTestOrigin),
-      kWidevineCdmType);
-
-  const std::vector<CdmFileIdAndContents> kWidevineFiles{
-      {{"wide_file1", kWidevineCdmType}, {'e', 'x', 'a', 'm'}},
-      {{"wide_file2", kWidevineCdmType}, {'e', 'x', 'a', 'm', 'p'}},
-      {{"wide_file3", kWidevineCdmType}, {'e', 'x', 'a', 'm', 'p', 'l'}},
-      {{"wide_file4", kWidevineCdmType}, {'e', 'x', 'a', 'm', 'p', 'l', 'e'}},
-  };
-
-  // Write some Widevine data using the old backend.
-  WriteFiles(kWidevineFiles);
-
-  ResetAndBindToNewBackend(
-      blink::StorageKey::CreateFromStringForTesting(kTestOrigin),
-      kWidevineCdmType);
-  base::RunLoop loop;
-  media_license_manager()->MigrateMediaLicensesForTesting(loop.QuitClosure());
-  loop.Run();
-
-  // Read data using the new backend.
-  ReadFiles(kWidevineFiles);
-}
-#endif  // BUILDFLAG(ENABLE_WIDEVINE)
-
-}  // namespace content
diff --git a/content/browser/media/media_interface_proxy.cc b/content/browser/media/media_interface_proxy.cc
index 37ae0e3..fff49eb 100644
--- a/content/browser/media/media_interface_proxy.cc
+++ b/content/browser/media/media_interface_proxy.cc
@@ -49,7 +49,6 @@
 #if BUILDFLAG(ENABLE_LIBRARY_CDMS)
 #include "base/feature_list.h"
 #include "base/metrics/histogram_macros.h"
-#include "content/browser/media/cdm_storage_impl.h"
 #include "content/browser/media/media_license_manager.h"
 #include "media/base/key_system_names.h"
 #include "media/mojo/mojom/cdm_service.mojom.h"
@@ -169,24 +168,17 @@
     if (cdm_type_.id.is_zero())
       return;
 
-    // TODO(crbug.com/1231162): Make more test suites templated to test both
-    // backends.
-    if (base::FeatureList::IsEnabled(features::kMediaLicenseBackend)) {
-      MediaLicenseManager* media_license_manager =
-          static_cast<StoragePartitionImpl*>(
-              render_frame_host_->GetStoragePartition())
-              ->GetMediaLicenseManager();
-      DCHECK(media_license_manager);
+    MediaLicenseManager* media_license_manager =
+        static_cast<StoragePartitionImpl*>(
+            render_frame_host_->GetStoragePartition())
+            ->GetMediaLicenseManager();
+    DCHECK(media_license_manager);
 
-      auto storage_key =
-          static_cast<RenderFrameHostImpl*>(render_frame_host_)->storage_key();
-      media_license_manager->OpenCdmStorage(
-          MediaLicenseManager::BindingContext(storage_key, cdm_type_),
-          std::move(receiver));
-    } else {
-      CdmStorageImpl::Create(render_frame_host_, cdm_type_,
-                             std::move(receiver));
-    }
+    auto storage_key =
+        static_cast<RenderFrameHostImpl*>(render_frame_host_)->storage_key();
+    media_license_manager->OpenCdmStorage(
+        MediaLicenseManager::BindingContext(storage_key, cdm_type_),
+        std::move(receiver));
 #endif
   }
 
diff --git a/content/browser/media/media_license_manager.cc b/content/browser/media/media_license_manager.cc
index 27b37d21..edeabe0 100644
--- a/content/browser/media/media_license_manager.cc
+++ b/content/browser/media/media_license_manager.cc
@@ -8,56 +8,29 @@
 #include <utility>
 #include <vector>
 
-#include "base/barrier_callback.h"
-#include "base/barrier_closure.h"
 #include "base/bind.h"
 #include "base/callback_forward.h"
 #include "base/containers/flat_map.h"
-#include "base/feature_list.h"
 #include "base/files/file_path.h"
 #include "base/files/file_util.h"
 #include "base/memory/scoped_refptr.h"
 #include "base/memory/weak_ptr.h"
-#include "base/notreached.h"
 #include "base/sequence_checker.h"
-#include "base/strings/string_number_conversions.h"
-#include "base/task/bind_post_task.h"
 #include "base/task/task_traits.h"
 #include "base/task/thread_pool.h"
-#include "base/threading/sequence_bound.h"
 #include "base/threading/sequenced_task_runner_handle.h"
 #include "components/services/storage/public/cpp/buckets/bucket_locator.h"
 #include "components/services/storage/public/cpp/buckets/constants.h"
 #include "components/services/storage/public/cpp/constants.h"
 #include "content/browser/media/media_license_database.h"
 #include "content/browser/media/media_license_storage_host.h"
-#include "content/public/browser/browser_task_traits.h"
-#include "content/public/browser/browser_thread.h"
-#include "content/public/common/cdm_info.h"
-#include "content/public/common/content_features.h"
 #include "media/cdm/cdm_type.h"
-#include "media/media_buildflags.h"
-#include "net/base/io_buffer.h"
-#include "net/base/net_errors.h"
 #include "sql/database.h"
-#include "storage/browser/file_system/file_stream_reader.h"
-#include "storage/browser/file_system/file_system_context.h"
-#include "storage/browser/file_system/file_system_url.h"
-#include "storage/browser/file_system/isolated_context.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 #include "third_party/blink/public/common/storage_key/storage_key.h"
 #include "third_party/blink/public/mojom/quota/quota_types.mojom-shared.h"
-#include "third_party/widevine/cdm/buildflags.h"  // nogncheck
 #include "url/origin.h"
 
-#if BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(USE_CHROMEOS_PROTECTED_MEDIA)
-#include "content/public/common/cdm_info.h"
-#endif  // BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(USE_CHROMEOS_PROTECTED_MEDIA)
-
-#if BUILDFLAG(ENABLE_WIDEVINE)
-#include "third_party/widevine/cdm/widevine_cdm_common.h"  // nogncheck
-#endif  // BUILDFLAG(ENABLE_WIDEVINE)
-
 namespace content {
 
 namespace {
@@ -80,189 +53,6 @@
   });
 }
 
-// TODO(crbug.com/1231162): Yes, this code is ugly. It is only in place while we
-// migrate to the new media license backend.
-absl::optional<media::CdmType> GetCdmTypeFromFileSystemId(
-    const std::string& file_system_id) {
-  if (file_system_id == "application_x-ppapi-clearkey-cdm") {
-    // `kClearKeyCdmType` from media/cdm/cdm_paths.h
-    return media::CdmType{
-        base::Token{0x3a2e0fadde4bd1b7ull, 0xcb90df3e240d1694ull},
-        "application_x-ppapi-clearkey-cdm"};
-  }
-#if BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(USE_CHROMEOS_PROTECTED_MEDIA)
-  else if (file_system_id == "application_chromeos-cdm-factory-daemon") {
-    return kChromeOsCdmType;
-  }
-#endif  // BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(USE_CHROMEOS_PROTECTED_MEDIA)
-
-#if BUILDFLAG(ENABLE_WIDEVINE)
-  else if (file_system_id == "application_x-ppapi-widevine-cdm") {
-    return kWidevineCdmType;
-  }
-#if BUILDFLAG(IS_WIN)
-  else if (file_system_id == "") {
-    return kMediaFoundationWidevineCdmType;
-  }
-#endif  // BUILDFLAG(IS_WIN)
-#endif  // BUILDFLAG(ENABLE_WIDEVINE)
-  else if (file_system_id == "test_file_system") {
-    // Used in migration tests in cdm_storage_impl_unittest.cc
-    return media::CdmType{base::Token{1234, 5678}, "test_file_system"};
-  } else if (file_system_id == "different_plugin") {
-    // Used in migration tests in cdm_storage_impl_unittest.cc
-    return media::CdmType{base::Token{8765, 4321}, "different_plugin"};
-  }
-
-  // `file_system_id` doesn't match a known CDM type.
-  return absl::nullopt;
-}
-
-base::flat_map<blink::StorageKey, std::vector<MediaLicenseManager::CdmFileId>>
-GetMediaLicensesOnFileTaskRunner(
-    scoped_refptr<storage::FileSystemContext> context) {
-  DCHECK(context);
-
-  storage::PluginPrivateFileSystemBackend* plugin_private_backend =
-      context->plugin_private_backend();
-
-  auto storage_keys =
-      plugin_private_backend->GetStorageKeysForTypeOnFileTaskRunner(
-          storage::kFileSystemTypePluginPrivate);
-
-  if (storage_keys.empty())
-    return {};
-
-  return base::MakeFlatMap<blink::StorageKey,
-                           std::vector<MediaLicenseManager::CdmFileId>>(
-      storage_keys, /*comp=*/{},
-      [&plugin_private_backend, &context](const auto& storage_key) {
-        std::vector<MediaLicenseManager::CdmFileId> cdm_files_for_storage_key;
-        auto cdm_files = plugin_private_backend
-                             ->GetMediaLicenseFilesForOriginOnFileTaskRunner(
-                                 context.get(), storage_key.origin());
-        for (const auto& cdm_file : cdm_files) {
-          auto maybe_cdm_type =
-              GetCdmTypeFromFileSystemId(cdm_file.legacy_file_system_id);
-          if (!maybe_cdm_type.has_value())
-            continue;
-
-          cdm_files_for_storage_key.emplace_back(cdm_file.name,
-                                                 maybe_cdm_type.value());
-        }
-
-        return std::make_pair(storage_key,
-                              std::move(cdm_files_for_storage_key));
-      });
-}
-
-void DidReadFiles(
-    scoped_refptr<storage::FileSystemContext> context,
-    base::OnceCallback<
-        void(std::vector<MediaLicenseManager::CdmFileIdAndContents>)> callback,
-    const std::vector<MediaLicenseManager::CdmFileIdAndContents>& files) {
-  DCHECK_CURRENTLY_ON(BrowserThread::IO);
-
-  // Don't bother migrating empty files.
-  std::vector<MediaLicenseManager::CdmFileIdAndContents> files_to_migrate;
-  base::ranges::for_each(
-      files,
-      [&files_to_migrate](MediaLicenseManager::CdmFileIdAndContents file) {
-        if (!file.data.empty())
-          files_to_migrate.emplace_back(std::move(file));
-      });
-
-  std::move(callback).Run(std::move(files_to_migrate));
-}
-
-void DidReadFile(
-    std::unique_ptr<storage::FileStreamReader> /*reader*/,
-    scoped_refptr<net::IOBufferWithSize> buffer,
-    MediaLicenseManager::CdmFileId file,
-    base::OnceCallback<void(MediaLicenseManager::CdmFileIdAndContents)>
-        callback,
-    int result) {
-  DCHECK_CURRENTLY_ON(BrowserThread::IO);
-
-  if (result != buffer->size()) {
-    std::move(callback).Run({file, {}});
-    return;
-  }
-
-  std::vector<uint8_t> data(buffer->data(), buffer->data() + buffer->size());
-  std::move(callback).Run({file, std::move(data)});
-}
-
-void DidGetLength(
-    std::unique_ptr<storage::FileStreamReader> reader,
-    MediaLicenseManager::CdmFileId file,
-    base::OnceCallback<void(MediaLicenseManager::CdmFileIdAndContents)>
-        callback,
-    int64_t result) {
-  DCHECK_CURRENTLY_ON(BrowserThread::IO);
-
-  // No need to Read() from `reader` if the file length is 0.
-  if (result <= 0) {
-    std::move(callback).Run({file, {}});
-    return;
-  }
-
-  auto buffer = base::MakeRefCounted<net::IOBufferWithSize>(result);
-  auto* reader_ptr = reader.get();
-  reader_ptr->Read(buffer.get(), buffer->size(),
-                   base::BindOnce(&DidReadFile, std::move(reader), buffer,
-                                  std::move(file), std::move(callback)));
-}
-
-void ReadFiles(
-    scoped_refptr<storage::FileSystemContext> context,
-    std::string file_system_root_uri,
-    std::vector<MediaLicenseManager::CdmFileId> files,
-    base::OnceCallback<void(
-        std::vector<MediaLicenseManager::CdmFileIdAndContents>)> callback) {
-  DCHECK_CURRENTLY_ON(BrowserThread::IO);
-
-  DCHECK(!files.empty());
-
-  // Kick off a number of read file operations and collect the results.
-  // Bind `context` to keep it alive while reading files.
-  auto* context_ptr = context.get();
-  auto barrier =
-      base::BarrierCallback<MediaLicenseManager::CdmFileIdAndContents>(
-          files.size(), base::BindOnce(&DidReadFiles, std::move(context),
-                                       std::move(callback)));
-
-  for (const auto& file : files) {
-    // Adapted from CdmFileImpl::CreateFileSystemURL().
-    const GURL crack_url = GURL(file_system_root_uri + file.name);
-    const blink::StorageKey crack_storage_key =
-        blink::StorageKey(url::Origin::Create(crack_url));
-    auto url = context_ptr->CrackURL(crack_url, crack_storage_key);
-    auto reader = context_ptr->CreateFileStreamReader(
-        url, 0, storage::kMaximumLength, base::Time());
-    if (!reader) {
-      barrier.Run({file, {}});
-      continue;
-    }
-    auto* reader_ptr = reader.get();
-    auto result = reader_ptr->GetLength(base::BindOnce(
-        &DidGetLength, std::move(reader), std::move(file), barrier));
-    // The GetLength() call is expected to run asynchronously.
-    DCHECK_EQ(result, net::ERR_IO_PENDING);
-  }
-}
-
-void WriteFilesOnDbThread(
-    std::vector<MediaLicenseManager::CdmFileIdAndContents> files,
-    const base::FilePath& database_path) {
-  auto db = std::make_unique<MediaLicenseDatabase>(database_path);
-
-  for (auto& file : files) {
-    db->OpenFile(file.file.cdm_type, file.file.name);
-    db->WriteFile(file.file.cdm_type, file.file.name, file.data);
-  }
-}
-
 }  // namespace
 
 MediaLicenseManager::CdmFileId::CdmFileId(const std::string& name,
@@ -280,12 +70,10 @@
 MediaLicenseManager::CdmFileIdAndContents::~CdmFileIdAndContents() = default;
 
 MediaLicenseManager::MediaLicenseManager(
-    scoped_refptr<storage::FileSystemContext> file_system_context,
     bool in_memory,
     scoped_refptr<storage::SpecialStoragePolicy> special_storage_policy,
     scoped_refptr<storage::QuotaManagerProxy> quota_manager_proxy)
-    : file_system_context_(std::move(file_system_context)),
-      db_runner_(CreateDatabaseTaskRunner()),
+    : db_runner_(CreateDatabaseTaskRunner()),
       in_memory_(in_memory),
       special_storage_policy_(std::move(special_storage_policy)),
       quota_manager_proxy_(std::move(quota_manager_proxy)),
@@ -300,203 +88,10 @@
         storage::QuotaClientType::kMediaLicense,
         {blink::mojom::StorageType::kTemporary});
   }
-
-  if (base::FeatureList::IsEnabled(features::kMediaLicenseBackend)) {
-    // Ensure the file system context is kept alive until we're done migrating
-    // media license data from the Plugin Private File System to this backend.
-    MigrateMediaLicenses(
-        base::BindOnce([](scoped_refptr<storage::FileSystemContext>) {},
-                       base::WrapRefCounted(context().get())));
-  }
 }
 
 MediaLicenseManager::~MediaLicenseManager() = default;
 
-void MediaLicenseManager::MigrateMediaLicenses(base::OnceClosure done_closure) {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-
-  DCHECK(plugin_private_data_migration_closure_.is_null());
-  plugin_private_data_migration_closure_ = std::move(done_closure);
-
-  context()->default_file_task_runner()->PostTaskAndReplyWithResult(
-      FROM_HERE, base::BindOnce(&GetMediaLicensesOnFileTaskRunner, context()),
-      base::BindOnce(&MediaLicenseManager::DidGetMediaLicenses,
-                     weak_factory_.GetWeakPtr()));
-}
-
-void MediaLicenseManager::DidGetMediaLicenses(
-    base::flat_map<blink::StorageKey, std::vector<CdmFileId>> files_map) {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  DCHECK(!plugin_private_data_migration_closure_.is_null());
-
-  if (files_map.empty()) {
-    DidMigrateMediaLicenses();
-    return;
-  }
-
-  // Kick off migration process for each storage key.
-  base::RepeatingClosure barrier = base::BarrierClosure(
-      files_map.size(),
-      base::BindPostTask(
-          base::SequencedTaskRunnerHandle::Get(),
-          base::BindOnce(&MediaLicenseManager::DidMigrateMediaLicenses,
-                         weak_factory_.GetWeakPtr())));
-
-  for (auto& storage_key_and_files : files_map) {
-    std::vector<CdmFileId> files = std::move(storage_key_and_files.second);
-    if (files.empty()) {
-      barrier.Run();
-      continue;
-    }
-    const blink::StorageKey& storage_key = storage_key_and_files.first;
-    quota_manager_proxy()->UpdateOrCreateBucket(
-        storage::BucketInitParams::ForDefaultBucket(storage_key),
-        base::SequencedTaskRunnerHandle::Get(),
-        base::BindOnce(&MediaLicenseManager::OpenPluginFileSystemsForStorageKey,
-                       weak_factory_.GetWeakPtr(), storage_key,
-                       std::move(files), barrier));
-  }
-}
-
-void MediaLicenseManager::OpenPluginFileSystemsForStorageKey(
-    const blink::StorageKey& storage_key,
-    std::vector<CdmFileId> files,
-    base::OnceClosure done_migrating_storage_key_closure,
-    storage::QuotaErrorOr<storage::BucketInfo> result) {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  DCHECK(!plugin_private_data_migration_closure_.is_null());
-
-  if (!result.ok()) {
-    std::move(done_migrating_storage_key_closure).Run();
-    return;
-  }
-
-  // Organize files by plugin name.
-  base::flat_map<std::string, std::vector<CdmFileId>> files_by_plugin_name;
-  for (auto& file : files) {
-    files_by_plugin_name[file.cdm_type.legacy_file_system_id].emplace_back(
-        std::move(file));
-  }
-
-  auto barrier = base::BarrierCallback<std::vector<CdmFileIdAndContents>>(
-      files_by_plugin_name.size(),
-      base::BindOnce(&MediaLicenseManager::DidReadFilesForStorageKey,
-                     weak_factory_.GetWeakPtr(), storage_key,
-                     result->ToBucketLocator(),
-                     std::move(done_migrating_storage_key_closure)));
-
-  for (const auto& [plugin_name, files] : files_by_plugin_name) {
-    // Register and open a file system for this plugin type.
-    std::string fsid =
-        storage::IsolatedContext::GetInstance()
-            ->RegisterFileSystemForVirtualPath(
-                storage::kFileSystemTypePluginPrivate,
-                storage::kPluginPrivateRootName, base::FilePath());
-    DCHECK(storage::ValidateIsolatedFileSystemId(fsid));
-
-    std::string file_system_root_uri =
-        storage::GetIsolatedFileSystemRootURIString(
-            storage_key.origin().GetURL(), fsid,
-            storage::kPluginPrivateRootName);
-
-    context()->OpenPluginPrivateFileSystem(
-        storage_key.origin(),
-        storage::FileSystemType::kFileSystemTypePluginPrivate, fsid,
-        plugin_name,
-        storage::OpenFileSystemMode::OPEN_FILE_SYSTEM_FAIL_IF_NONEXISTENT,
-        base::BindOnce(&MediaLicenseManager::DidOpenPluginFileSystem,
-                       weak_factory_.GetWeakPtr(), storage_key,
-                       std::move(files), std::move(file_system_root_uri),
-                       barrier));
-  }
-}
-
-void MediaLicenseManager::DidOpenPluginFileSystem(
-    const blink::StorageKey& storage_key,
-    std::vector<CdmFileId> files,
-    std::string file_system_root_uri,
-    base::OnceCallback<void(std::vector<CdmFileIdAndContents>)> callback,
-    base::File::Error error) {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  DCHECK(!plugin_private_data_migration_closure_.is_null());
-
-  if (error != base::File::FILE_OK) {
-    std::move(callback).Run({});
-    return;
-  }
-
-  auto wrapped_callback = base::BindPostTask(
-      base::SequencedTaskRunnerHandle::Get(), std::move(callback));
-  GetIOThreadTaskRunner({})->PostTask(
-      FROM_HERE,
-      base::BindOnce(&ReadFiles, context(), std::move(file_system_root_uri),
-                     std::move(files), std::move(wrapped_callback)));
-}
-
-void MediaLicenseManager::DidReadFilesForStorageKey(
-    const blink::StorageKey& storage_key,
-    const storage::BucketLocator& bucket_locator,
-    base::OnceClosure done_migrating_storage_key_closure,
-    std::vector<std::vector<CdmFileIdAndContents>> collected_files) {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  DCHECK(!plugin_private_data_migration_closure_.is_null());
-
-  // Flatten collected files into one vector.
-  std::vector<CdmFileIdAndContents> files;
-  for (auto& file_list : collected_files) {
-    for (auto& file : file_list) {
-      // Empty files should have been stripped out.
-      DCHECK(!file.data.empty());
-      files.emplace_back(std::move(file));
-    }
-  }
-
-  if (files.empty()) {
-    std::move(done_migrating_storage_key_closure).Run();
-    return;
-  }
-
-  db_runner()->PostTaskAndReply(
-      FROM_HERE,
-      base::BindOnce(&WriteFilesOnDbThread, std::move(files),
-                     GetDatabasePath(bucket_locator)),
-      std::move(done_migrating_storage_key_closure));
-}
-
-void MediaLicenseManager::DidMigrateMediaLicenses() {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  DCHECK(!plugin_private_data_migration_closure_.is_null());
-
-  // Delete %profile/File System/Plugins since media license data is the only
-  // thing stored in the Plugin Private File System.
-  auto plugin_path = context()->plugin_private_backend()->base_path();
-
-  context()->default_file_task_runner()->PostTaskAndReply(
-      FROM_HERE,
-      base::BindOnce(base::IgnoreResult(&base::DeletePathRecursively),
-                     plugin_path),
-      base::BindOnce(&MediaLicenseManager::DidClearPluginPrivateData,
-                     weak_factory_.GetWeakPtr()));
-}
-
-void MediaLicenseManager::DidClearPluginPrivateData() {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  DCHECK(!plugin_private_data_migration_closure_.is_null());
-
-  std::move(plugin_private_data_migration_closure_).Run();
-
-  // Now that data has been migrated, kick off binding the pending receivers.
-  for (const auto& receivers : pending_receivers_) {
-    const auto& storage_key = receivers.first;
-    // Get the default bucket for `storage_key`.
-    quota_manager_proxy()->UpdateOrCreateBucket(
-        storage::BucketInitParams::ForDefaultBucket(storage_key),
-        base::SequencedTaskRunnerHandle::Get(),
-        base::BindOnce(&MediaLicenseManager::DidGetBucket,
-                       weak_factory_.GetWeakPtr(), storage_key));
-  }
-}
-
 void MediaLicenseManager::OpenCdmStorage(
     const BindingContext& binding_context,
     mojo::PendingReceiver<media::mojom::CdmStorage> receiver) {
@@ -512,12 +107,9 @@
 
   auto& receiver_list = pending_receivers_[storage_key];
   receiver_list.emplace_back(binding_context, std::move(receiver));
-  if (receiver_list.size() > 1 ||
-      !plugin_private_data_migration_closure_.is_null()) {
+  if (receiver_list.size() > 1) {
     // If a pending receiver for this storage key already existed, there is
-    // an in-flight `UpdateOrCreateBucket()` call for this storage key. If we're
-    // in the process of migrating data from the plugin private file system,
-    // pending receivers will be handled in `DidClearPluginPrivateData()`.
+    // an in-flight `UpdateOrCreateBucket()` call for this storage key.
     return;
   }
 
@@ -535,15 +127,7 @@
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 
   auto it = pending_receivers_.find(storage_key);
-  if (it == pending_receivers_.end()) {
-    // No receivers to bind.
-    // TODO(crbug.com/1231162): This case can only be hit
-    // when the migration code is kicked off after `OpenCdmStorage()` has
-    // already been called, since `OpenCdmStorage()` will not call
-    // `UpdateOrCreateBucket()` while there is an in-progress migration. Change
-    // this to a DCHECK once the migration logic is removed.
-    return;
-  }
+  DCHECK(it != pending_receivers_.end());
 
   auto receivers_list = std::move(it->second);
   pending_receivers_.erase(it);
diff --git a/content/browser/media/media_license_manager.h b/content/browser/media/media_license_manager.h
index bde6635..e90e90a 100644
--- a/content/browser/media/media_license_manager.h
+++ b/content/browser/media/media_license_manager.h
@@ -19,7 +19,6 @@
 #include "content/common/content_export.h"
 #include "media/cdm/cdm_type.h"
 #include "media/mojo/mojom/cdm_storage.mojom.h"
-#include "storage/browser/file_system/file_system_context.h"
 #include "storage/browser/quota/quota_manager_proxy.h"
 #include "third_party/blink/public/common/storage_key/storage_key.h"
 
@@ -72,7 +71,6 @@
   };
 
   MediaLicenseManager(
-      scoped_refptr<storage::FileSystemContext> file_system_context,
       bool in_memory,
       scoped_refptr<storage::SpecialStoragePolicy> special_storage_policy,
       scoped_refptr<storage::QuotaManagerProxy> quota_manager_proxy);
@@ -98,10 +96,6 @@
       MediaLicenseStorageHost* host,
       base::PassKey<MediaLicenseStorageHost> pass_key);
 
-  void MigrateMediaLicensesForTesting(base::OnceClosure done_closure) {
-    MigrateMediaLicenses(std::move(done_closure));
-  }
-
   const scoped_refptr<storage::QuotaManagerProxy>& quota_manager_proxy() const {
     DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
     return quota_manager_proxy_;
@@ -117,11 +111,6 @@
     return in_memory_;
   }
 
-  const scoped_refptr<storage::FileSystemContext>& context() {
-    DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-    return file_system_context_;
-  }
-
  private:
   void DidGetBucket(const blink::StorageKey& storage_key,
                     storage::QuotaErrorOr<storage::BucketInfo> result);
@@ -130,37 +119,8 @@
       storage::mojom::QuotaClient::DeleteBucketDataCallback callback,
       bool success);
 
-  // TODO(crbug.com/1231162): The following methods are used to migrate from the
-  // old backend to the new backend. Remove once the migration is complete.
-  void MigrateMediaLicenses(base::OnceClosure done_closure);
-  void DidGetMediaLicenses(
-      base::flat_map<blink::StorageKey, std::vector<CdmFileId>> files);
-  void OpenPluginFileSystemsForStorageKey(
-      const blink::StorageKey& storage_key,
-      std::vector<CdmFileId> files,
-      base::OnceClosure done_migrating_storage_key_closure,
-      storage::QuotaErrorOr<storage::BucketInfo> result);
-  void DidOpenPluginFileSystem(
-      const blink::StorageKey& storage_key,
-      std::vector<CdmFileId> files,
-      std::string file_system_root_uri,
-      base::OnceCallback<void(std::vector<CdmFileIdAndContents>)> callback,
-      base::File::Error result);
-  void DidReadFilesForStorageKey(
-      const blink::StorageKey& storage_key,
-      const storage::BucketLocator& bucket_locator,
-      base::OnceClosure done_migrating_storage_key_closure,
-      std::vector<std::vector<CdmFileIdAndContents>> collected_files);
-  void DidMigrateMediaLicenses();
-  void DidClearPluginPrivateData();
-
   SEQUENCE_CHECKER(sequence_checker_);
 
-  // TODO(crbug.com/1231162): These members are only used to help migrate from
-  // the old backend to the new backend. Remove once the migration is complete.
-  const scoped_refptr<storage::FileSystemContext> file_system_context_;
-  base::OnceClosure plugin_private_data_migration_closure_;
-
   // Task runner which all database operations are routed through.
   const scoped_refptr<base::SequencedTaskRunner> db_runner_;
 
diff --git a/content/browser/media/media_license_manager_unittest.cc b/content/browser/media/media_license_manager_unittest.cc
index 8d58184..b16e1f9 100644
--- a/content/browser/media/media_license_manager_unittest.cc
+++ b/content/browser/media/media_license_manager_unittest.cc
@@ -11,9 +11,7 @@
 #include "base/files/file_util.h"
 #include "base/files/scoped_temp_dir.h"
 #include "base/memory/scoped_refptr.h"
-#include "base/strings/strcat.h"
 #include "base/strings/string_piece_forward.h"
-#include "base/test/scoped_feature_list.h"
 #include "base/test/task_environment.h"
 #include "base/test/test_future.h"
 #include "base/token.h"
@@ -21,7 +19,6 @@
 #include "components/services/storage/public/cpp/constants.h"
 #include "content/browser/media/media_license_quota_client.h"
 #include "content/public/browser/storage_partition.h"
-#include "content/public/common/content_features.h"
 #include "media/cdm/cdm_type.h"
 #include "media/mojo/mojom/cdm_storage.mojom-forward.h"
 #include "mojo/public/cpp/bindings/associated_remote.h"
@@ -44,22 +41,20 @@
 
 class MediaLicenseManagerTest : public testing::Test {
  public:
-  MediaLicenseManagerTest() : feature_list_(features::kMediaLicenseBackend) {}
+  MediaLicenseManagerTest() : in_memory_(false) {}
+  explicit MediaLicenseManagerTest(bool in_memory) : in_memory_(in_memory) {}
 
   void SetUp() override {
     ASSERT_TRUE(profile_path_.CreateUniqueTempDir());
-    ASSERT_TRUE(file_system_context_path_.CreateUniqueTempDir());
     quota_manager_ = base::MakeRefCounted<storage::MockQuotaManager>(
-        /*is_incognito=*/false, profile_path_.GetPath(),
+        in_memory_, in_memory_ ? base::FilePath() : profile_path_.GetPath(),
         base::ThreadTaskRunnerHandle::Get().get(),
         /*special storage policy=*/nullptr);
     quota_manager_proxy_ = base::MakeRefCounted<storage::MockQuotaManagerProxy>(
         static_cast<storage::MockQuotaManager*>(quota_manager_.get()),
         base::ThreadTaskRunnerHandle::Get());
-    file_system_context_ = storage::CreateFileSystemContextForTesting(
-        /*quota_manager_proxy=*/nullptr, file_system_context_path_.GetPath());
     manager_ = std::make_unique<MediaLicenseManager>(
-        file_system_context_, file_system_context_->is_incognito(),
+        in_memory_,
         /*special storage policy=*/nullptr, quota_manager_proxy_);
   }
 
@@ -129,16 +124,14 @@
   }
 
  protected:
+  const bool in_memory_;
+
   scoped_refptr<storage::MockQuotaManager> quota_manager_;
   scoped_refptr<storage::QuotaManagerProxy> quota_manager_proxy_;
-  scoped_refptr<storage::FileSystemContext> file_system_context_;
-
-  base::test::ScopedFeatureList feature_list_;
 
   // This must be above MediaLicenseManager, to ensure that no file is accessed
   // when the temporary directory is deleted.
   base::ScopedTempDir profile_path_;
-  base::ScopedTempDir file_system_context_path_;
   base::test::TaskEnvironment task_environment_;
 
   std::unique_ptr<MediaLicenseManager> manager_;
@@ -184,6 +177,9 @@
   // not.
   EXPECT_FALSE(base::PathExists(database_file));
   EXPECT_TRUE(base::DirectoryExists(database_file.DirName()));
+
+  // Confirm that the file is now empty.
+  ExpectFileContents(cdm_file, "");
 }
 
 TEST_F(MediaLicenseManagerTest, DeleteBucketDataClosedStorage) {
@@ -273,6 +269,9 @@
   EXPECT_FALSE(base::PathExists(database_file));
   EXPECT_TRUE(base::DirectoryExists(database_file.DirName()));
 
+  // Confirm that the file is now empty.
+  ExpectFileContents(cdm_file, "");
+
   // Write some more data. This should succeed.
   Write(cdm_file, kTestData);
 
@@ -306,27 +305,9 @@
 }
 
 class MediaLicenseManagerIncognitoTest : public MediaLicenseManagerTest {
-  void SetUp() override {
-    // Still create this dir so the teardown will confirm it remains empty (on
-    // Windows, at least).
-    ASSERT_TRUE(profile_path_.CreateUniqueTempDir());
-    ASSERT_TRUE(file_system_context_path_.CreateUniqueTempDir());
-
-    quota_manager_ = base::MakeRefCounted<storage::MockQuotaManager>(
-        /*is_incognito=*/true, /*profile_path=*/base::FilePath(),
-        base::ThreadTaskRunnerHandle::Get().get(),
-        /*special storage policy=*/nullptr);
-    quota_manager_proxy_ = base::MakeRefCounted<storage::MockQuotaManagerProxy>(
-        static_cast<storage::MockQuotaManager*>(quota_manager_.get()),
-        base::ThreadTaskRunnerHandle::Get());
-    file_system_context_ = storage::CreateIncognitoFileSystemContextForTesting(
-        base::ThreadTaskRunnerHandle::Get(),
-        base::ThreadTaskRunnerHandle::Get(), /*quota_manager_proxy=*/nullptr,
-        file_system_context_path_.GetPath());
-    manager_ = std::make_unique<MediaLicenseManager>(
-        file_system_context_, file_system_context_->is_incognito(),
-        /*special storage policy=*/nullptr, quota_manager_proxy_);
-  }
+ public:
+  MediaLicenseManagerIncognitoTest()
+      : MediaLicenseManagerTest(/*in_memory=*/true) {}
 };
 
 TEST_F(MediaLicenseManagerIncognitoTest, DeleteBucketData) {
diff --git a/content/browser/media/media_license_storage_host_unittest.cc b/content/browser/media/media_license_storage_host_unittest.cc
new file mode 100644
index 0000000..0653512
--- /dev/null
+++ b/content/browser/media/media_license_storage_host_unittest.cc
@@ -0,0 +1,345 @@
+// 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 <memory>
+#include <vector>
+
+#include "base/bind.h"
+#include "base/callback.h"
+#include "base/files/file.h"
+#include "base/logging.h"
+#include "base/test/bind.h"
+#include "base/test/test_future.h"
+#include "content/browser/media/media_license_manager.h"
+#include "content/browser/storage_partition_impl.h"
+#include "content/public/browser/render_frame_host.h"
+#include "content/public/browser/storage_partition.h"
+#include "content/public/browser/web_contents.h"
+#include "content/public/test/navigation_simulator.h"
+#include "content/public/test/test_renderer_host.h"
+#include "media/cdm/cdm_type.h"
+#include "media/mojo/mojom/cdm_storage.mojom.h"
+#include "mojo/public/cpp/bindings/associated_remote.h"
+#include "mojo/public/cpp/bindings/pending_associated_remote.h"
+#include "mojo/public/cpp/bindings/remote.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/public/common/storage_key/storage_key.h"
+#include "url/gurl.h"
+
+using media::mojom::CdmFile;
+using media::mojom::CdmStorage;
+
+namespace content {
+using CdmFileId = MediaLicenseManager::CdmFileId;
+using CdmFileIdAndContents = MediaLicenseManager::CdmFileIdAndContents;
+
+namespace {
+
+const media::CdmType kTestCdmType{base::Token{1234, 5678}, "test_file_system"};
+
+const char kTestOrigin[] = "http://www.test.com";
+
+// Helper functions to manipulate RenderFrameHosts.
+
+void SimulateNavigation(RenderFrameHost** rfh, const GURL& url) {
+  auto navigation_simulator =
+      NavigationSimulator::CreateRendererInitiated(url, *rfh);
+  navigation_simulator->Commit();
+  *rfh = navigation_simulator->GetFinalRenderFrameHost();
+}
+
+}  // namespace
+
+class CdmStorageTest : public RenderViewHostTestHarness {
+ public:
+  CdmStorageTest()
+      : RenderViewHostTestHarness(
+            content::BrowserTaskEnvironment::REAL_IO_THREAD) {}
+
+ protected:
+  void SetUp() final {
+    RenderViewHostTestHarness::SetUp();
+    rfh_ = web_contents()->GetPrimaryMainFrame();
+    RenderFrameHostTester::For(rfh_)->InitializeRenderFrameIfNeeded();
+    SimulateNavigation(&rfh_, GURL(kTestOrigin));
+
+    auto* media_license_manager =
+        static_cast<StoragePartitionImpl*>(rfh_->GetStoragePartition())
+            ->GetMediaLicenseManager();
+    DCHECK(media_license_manager);
+    media_license_manager->OpenCdmStorage(
+        MediaLicenseManager::BindingContext(
+            blink::StorageKey::CreateFromStringForTesting(kTestOrigin),
+            kTestCdmType),
+        cdm_storage_.BindNewPipeAndPassReceiver());
+  }
+
+  // Open the file |name|. Returns true if the file returned is valid, false
+  // otherwise. On success |cdm_file| is bound to the CdmFileImpl object.
+  bool Open(const std::string& name,
+            mojo::AssociatedRemote<CdmFile>& cdm_file) {
+    DVLOG(3) << __func__;
+
+    base::test::TestFuture<CdmStorage::Status,
+                           mojo::PendingAssociatedRemote<CdmFile>>
+        future;
+    cdm_storage_->Open(name, future.GetCallback());
+
+    CdmStorage::Status status = future.Get<0>();
+    mojo::PendingAssociatedRemote<CdmFile> actual_file =
+        std::move(std::get<1>(future.Take()));
+    if (!actual_file) {
+      DCHECK_NE(status, CdmStorage::Status::kSuccess);
+      return false;
+    }
+
+    // Open() returns a mojo::PendingAssociatedRemote<CdmFile>, so bind it to
+    // the mojo::AssociatedRemote<CdmFileAssociated> provided.
+    mojo::AssociatedRemote<CdmFile> cdm_file_remote;
+    cdm_file_remote.Bind(std::move(actual_file));
+    cdm_file = std::move(cdm_file_remote);
+
+    return status == CdmStorage::Status::kSuccess;
+  }
+
+  // Reads the contents of the previously opened |cdm_file|. If successful,
+  // true is returned and |data| is updated with the contents of the file.
+  // If unable to read the file, false is returned.
+  bool Read(CdmFile* cdm_file, std::vector<uint8_t>& data) {
+    DVLOG(3) << __func__;
+
+    base::test::TestFuture<CdmFile::Status, std::vector<uint8_t>> future;
+    cdm_file->Read(
+        future.GetCallback<CdmFile::Status, const std::vector<uint8_t>&>());
+
+    CdmFile::Status status = future.Get<0>();
+    data = future.Get<1>();
+    return status == CdmFile::Status::kSuccess;
+  }
+
+  // Writes |data| to the previously opened |cdm_file|, replacing the contents
+  // of the file. Returns true if successful, false otherwise.
+  bool Write(CdmFile* cdm_file, const std::vector<uint8_t>& data) {
+    DVLOG(3) << __func__;
+
+    base::test::TestFuture<CdmFile::Status> future;
+    cdm_file->Write(data, future.GetCallback());
+
+    CdmFile::Status status = future.Get();
+    return status == CdmFile::Status::kSuccess;
+  }
+
+  void WriteFiles(const std::vector<CdmFileIdAndContents>& files) {
+    // Write some data using the old backend.
+    for (const auto& file : files) {
+      mojo::AssociatedRemote<CdmFile> remote;
+      EXPECT_TRUE(Open(file.file.name, remote));
+      ASSERT_TRUE(remote.is_bound());
+      EXPECT_TRUE(Write(remote.get(), file.data));
+    }
+  }
+
+  void ReadFiles(const std::vector<CdmFileIdAndContents>& files) {
+    for (const auto& file : files) {
+      mojo::AssociatedRemote<CdmFile> remote;
+      EXPECT_TRUE(Open(file.file.name, remote));
+      ASSERT_TRUE(remote.is_bound());
+      std::vector<uint8_t> data_read;
+      EXPECT_TRUE(Read(remote.get(), data_read));
+      EXPECT_EQ(file.data, data_read);
+    }
+  }
+
+  void ExpectFilesEmpty(const std::vector<CdmFileIdAndContents>& files) {
+    for (const auto& file : files) {
+      mojo::AssociatedRemote<CdmFile> remote;
+      EXPECT_TRUE(Open(file.file.name, remote));
+      ASSERT_TRUE(remote.is_bound());
+      std::vector<uint8_t> data_read;
+      EXPECT_TRUE(Read(remote.get(), data_read));
+      EXPECT_TRUE(data_read.empty());
+    }
+  }
+
+  MediaLicenseManager* media_license_manager() const {
+    auto* media_license_manager =
+        static_cast<StoragePartitionImpl*>(rfh_->GetStoragePartition())
+            ->GetMediaLicenseManager();
+    DCHECK(media_license_manager);
+    return media_license_manager;
+  }
+
+  RenderFrameHost* rfh_ = nullptr;
+  mojo::Remote<CdmStorage> cdm_storage_;
+};
+
+TEST_F(CdmStorageTest, InvalidFileName) {
+  // Anything other than ASCII letter, digits, and -._ will fail. Add a
+  // Unicode character to the name.
+  const char kFileName[] = "openfile\u1234";
+  mojo::AssociatedRemote<CdmFile> cdm_file;
+  EXPECT_FALSE(Open(kFileName, cdm_file));
+  ASSERT_FALSE(cdm_file.is_bound());
+}
+
+TEST_F(CdmStorageTest, InvalidFileNameEmpty) {
+  const char kFileName[] = "";
+  mojo::AssociatedRemote<CdmFile> cdm_file;
+  EXPECT_FALSE(Open(kFileName, cdm_file));
+  ASSERT_FALSE(cdm_file.is_bound());
+}
+
+TEST_F(CdmStorageTest, InvalidFileNameStartWithUnderscore) {
+  const char kFileName[] = "_invalid";
+  mojo::AssociatedRemote<CdmFile> cdm_file;
+  EXPECT_FALSE(Open(kFileName, cdm_file));
+  ASSERT_FALSE(cdm_file.is_bound());
+}
+
+TEST_F(CdmStorageTest, InvalidFileNameTooLong) {
+  // Limit is 256 characters, so try a file name with 257.
+  const std::string kFileName(257, 'a');
+  mojo::AssociatedRemote<CdmFile> cdm_file;
+  EXPECT_FALSE(Open(kFileName, cdm_file));
+  ASSERT_FALSE(cdm_file.is_bound());
+}
+
+TEST_F(CdmStorageTest, OpenFile) {
+  const char kFileName[] = "test_file_name";
+  mojo::AssociatedRemote<CdmFile> cdm_file;
+  EXPECT_TRUE(Open(kFileName, cdm_file));
+  ASSERT_TRUE(cdm_file.is_bound());
+}
+
+TEST_F(CdmStorageTest, OpenFileLocked) {
+  const char kFileName[] = "test_file_name";
+  mojo::AssociatedRemote<CdmFile> cdm_file1;
+  EXPECT_TRUE(Open(kFileName, cdm_file1));
+  ASSERT_TRUE(cdm_file1.is_bound());
+
+  // Second attempt on the same file should fail as the file is locked.
+  mojo::AssociatedRemote<CdmFile> cdm_file2;
+  EXPECT_FALSE(Open(kFileName, cdm_file2));
+  ASSERT_FALSE(cdm_file2.is_bound());
+
+  // Now close the first file and try again. It should be free now.
+  cdm_file1.reset();
+
+  mojo::AssociatedRemote<CdmFile> cdm_file3;
+  EXPECT_TRUE(Open(kFileName, cdm_file3));
+  ASSERT_TRUE(cdm_file3.is_bound());
+}
+
+TEST_F(CdmStorageTest, MultipleFiles) {
+  const char kFileName1[] = "file1";
+  mojo::AssociatedRemote<CdmFile> cdm_file1;
+  EXPECT_TRUE(Open(kFileName1, cdm_file1));
+  ASSERT_TRUE(cdm_file1.is_bound());
+
+  const char kFileName2[] = "file2";
+  mojo::AssociatedRemote<CdmFile> cdm_file2;
+  EXPECT_TRUE(Open(kFileName2, cdm_file2));
+  ASSERT_TRUE(cdm_file2.is_bound());
+
+  const char kFileName3[] = "file3";
+  mojo::AssociatedRemote<CdmFile> cdm_file3;
+  EXPECT_TRUE(Open(kFileName3, cdm_file3));
+  ASSERT_TRUE(cdm_file3.is_bound());
+}
+
+TEST_F(CdmStorageTest, WriteThenReadFile) {
+  const char kFileName[] = "test_file_name";
+  mojo::AssociatedRemote<CdmFile> cdm_file;
+  EXPECT_TRUE(Open(kFileName, cdm_file));
+  ASSERT_TRUE(cdm_file.is_bound());
+
+  // Write several bytes and read them back.
+  std::vector<uint8_t> kTestData = {'r', 'a', 'n', 'd', 'o', 'm'};
+  EXPECT_TRUE(Write(cdm_file.get(), kTestData));
+
+  std::vector<uint8_t> data_read;
+  EXPECT_TRUE(Read(cdm_file.get(), data_read));
+  EXPECT_EQ(kTestData, data_read);
+}
+
+TEST_F(CdmStorageTest, ReadThenWriteEmptyFile) {
+  const char kFileName[] = "empty_file_name";
+  mojo::AssociatedRemote<CdmFile> cdm_file;
+  EXPECT_TRUE(Open(kFileName, cdm_file));
+  ASSERT_TRUE(cdm_file.is_bound());
+
+  // New file should be empty.
+  std::vector<uint8_t> data_read;
+  EXPECT_TRUE(Read(cdm_file.get(), data_read));
+  EXPECT_EQ(0u, data_read.size());
+
+  // Write nothing.
+  EXPECT_TRUE(Write(cdm_file.get(), std::vector<uint8_t>()));
+
+  // Should still be empty.
+  EXPECT_TRUE(Read(cdm_file.get(), data_read));
+  EXPECT_EQ(0u, data_read.size());
+}
+
+TEST_F(CdmStorageTest, ParallelRead) {
+  const char kFileName[] = "duplicate_read_file_name";
+  mojo::AssociatedRemote<CdmFile> cdm_file;
+  EXPECT_TRUE(Open(kFileName, cdm_file));
+  ASSERT_TRUE(cdm_file.is_bound());
+
+  // Attempts to reads the contents of the previously opened |cdm_file| twice.
+  // We don't really care about the data, just that 1 read succeeds and the
+  // other fails.
+  base::test::TestFuture<CdmFile::Status, std::vector<uint8_t>> future1;
+  base::test::TestFuture<CdmFile::Status, std::vector<uint8_t>> future2;
+
+  cdm_file->Read(
+      future1.GetCallback<CdmFile::Status, const std::vector<uint8_t>&>());
+  cdm_file->Read(
+      future2.GetCallback<CdmFile::Status, const std::vector<uint8_t>&>());
+
+  EXPECT_TRUE(future1.Wait());
+  EXPECT_TRUE(future2.Wait());
+
+  CdmFile::Status status1 = future1.Get<0>();
+  CdmFile::Status status2 = future2.Get<0>();
+
+  // One call should succeed, one should fail.
+  EXPECT_TRUE((status1 == CdmFile::Status::kSuccess &&
+               status2 == CdmFile::Status::kFailure) ||
+              (status1 == CdmFile::Status::kFailure &&
+               status2 == CdmFile::Status::kSuccess))
+      << "status 1: " << status1 << ", status2: " << status2;
+}
+
+TEST_F(CdmStorageTest, ParallelWrite) {
+  const char kFileName[] = "duplicate_write_file_name";
+  mojo::AssociatedRemote<CdmFile> cdm_file;
+  EXPECT_TRUE(Open(kFileName, cdm_file));
+  ASSERT_TRUE(cdm_file.is_bound());
+
+  // Attempts to write the contents of the previously opened |cdm_file| twice.
+  // We don't really care about the data, just that 1 write succeeds and the
+  // other fails.
+  base::test::TestFuture<CdmFile::Status> future1;
+  base::test::TestFuture<CdmFile::Status> future2;
+
+  cdm_file->Write({1, 2, 3}, future1.GetCallback());
+  cdm_file->Write({4, 5, 6}, future2.GetCallback());
+
+  EXPECT_TRUE(future1.Wait());
+  EXPECT_TRUE(future2.Wait());
+
+  CdmFile::Status status1 = future1.Get();
+  CdmFile::Status status2 = future2.Get();
+
+  // One call should succeed, one should fail.
+  EXPECT_TRUE((status1 == CdmFile::Status::kSuccess &&
+               status2 == CdmFile::Status::kFailure) ||
+              (status1 == CdmFile::Status::kFailure &&
+               status2 == CdmFile::Status::kSuccess))
+      << "status 1: " << status1 << ", status2: " << status2;
+}
+
+}  // 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 cf7d850..6f0950e8 100644
--- a/content/browser/renderer_host/render_process_host_impl.cc
+++ b/content/browser/renderer_host/render_process_host_impl.cc
@@ -3239,6 +3239,7 @@
     switches::kEnableWebGLDeveloperExtensions,
     switches::kEnableWebGLDraftExtensions,
     switches::kEnableWebGLImageChromium,
+    switches::kEnableWebGPUDeveloperFeatures,
     switches::kFileUrlPathAlias,
     switches::kForceDeviceScaleFactor,
     switches::kForceDisplayColorProfile,
diff --git a/content/browser/storage_partition_impl.cc b/content/browser/storage_partition_impl.cc
index a34ad787..287f44b 100644
--- a/content/browser/storage_partition_impl.cc
+++ b/content/browser/storage_partition_impl.cc
@@ -1361,8 +1361,8 @@
 
 #if BUILDFLAG(ENABLE_LIBRARY_CDMS)
   media_license_manager_ = std::make_unique<MediaLicenseManager>(
-      filesystem_context_, is_in_memory(),
-      browser_context_->GetSpecialStoragePolicy(), quota_manager_proxy);
+      is_in_memory(), browser_context_->GetSpecialStoragePolicy(),
+      quota_manager_proxy);
 #endif  // BUILDFLAG(ENABLE_LIBRARY_CDMS)
 
   if (base::FeatureList::IsEnabled(blink::features::kSharedStorageAPI)) {
diff --git a/content/browser/webauth/client_data_json.cc b/content/browser/webauth/client_data_json.cc
index 9c3e329b..c65d369b 100644
--- a/content/browser/webauth/client_data_json.cc
+++ b/content/browser/webauth/client_data_json.cc
@@ -30,12 +30,11 @@
   ret.push_back('"');
 
   const char* const in_bytes = in.data();
-  // ICU uses |int32_t| for lengths.
-  const int32_t length = base::checked_cast<int32_t>(in.size());
-  int32_t offset = 0;
+  const size_t length = in.size();
+  size_t offset = 0;
 
   while (offset < length) {
-    const int32_t prior_offset = offset;
+    const size_t prior_offset = offset;
     // Input strings must be valid UTF-8.
     base_icu::UChar32 codepoint;
     CHECK(base::ReadUnicodeCharacter(in_bytes, length, &offset, &codepoint));
diff --git a/content/child/dwrite_font_proxy/font_fallback_win.cc b/content/child/dwrite_font_proxy/font_fallback_win.cc
index 4435749..de6d056 100644
--- a/content/child/dwrite_font_proxy/font_fallback_win.cc
+++ b/content/child/dwrite_font_proxy/font_fallback_win.cc
@@ -97,10 +97,13 @@
 
   locale = locale ? locale : L"";
 
+  size_t mapped_length_size_t = *mapped_length;
   if (GetCachedFont(text_chunk, base_family_name, locale, base_weight,
-                    base_style, base_stretch, mapped_font, mapped_length)) {
+                    base_style, base_stretch, mapped_font,
+                    &mapped_length_size_t)) {
     DCHECK(*mapped_font);
-    DCHECK_GT(*mapped_length, 0u);
+    DCHECK_GT(mapped_length_size_t, 0u);
+    *mapped_length = base::checked_cast<UINT32>(mapped_length_size_t);
     LogFallbackResult(SUCCESS_CACHE);
     return S_OK;
   }
@@ -172,7 +175,7 @@
                                  DWRITE_FONT_STYLE base_style,
                                  DWRITE_FONT_STRETCH base_stretch,
                                  IDWriteFont** font,
-                                 uint32_t* mapped_length) {
+                                 size_t* mapped_length) {
   base::AutoLock guard(lock_);
   std::map<std::wstring, std::list<mswr::ComPtr<IDWriteFontFamily>>>::iterator
       it = fallback_family_cache_.find(MakeCacheKey(base_family_name, locale));
@@ -197,9 +200,9 @@
     // different from |mapped_length| because ReadUnicodeCharacter can advance
     // |character_index| even if the character cannot be mapped (invalid
     // surrogate pair or font does not contain a matching glyph).
-    int32_t character_index = 0;
-    uint32_t length = 0;  // How much of the text can actually be mapped.
-    while (static_cast<uint32_t>(character_index) < text.length()) {
+    size_t character_index = 0;
+    size_t length = 0;  // How much of the text can actually be mapped.
+    while (character_index < text.length()) {
       BOOL exists = false;
       base_icu::UChar32 character = 0;
       if (!base::ReadUnicodeCharacter(text.c_str(), text.length(),
diff --git a/content/child/dwrite_font_proxy/font_fallback_win.h b/content/child/dwrite_font_proxy/font_fallback_win.h
index f3778a13..6bc0230 100644
--- a/content/child/dwrite_font_proxy/font_fallback_win.h
+++ b/content/child/dwrite_font_proxy/font_fallback_win.h
@@ -65,7 +65,7 @@
                      DWRITE_FONT_STYLE base_style,
                      DWRITE_FONT_STRETCH base_stretch,
                      IDWriteFont** mapped_font,
-                     uint32_t* mapped_length) LOCKS_EXCLUDED(lock_);
+                     size_t* mapped_length) LOCKS_EXCLUDED(lock_);
 
   void AddCachedFamily(Microsoft::WRL::ComPtr<IDWriteFontFamily> family,
                        const wchar_t* base_family_name,
diff --git a/content/child/runtime_features.cc b/content/child/runtime_features.cc
index d8046d3..0b4c6c1 100644
--- a/content/child/runtime_features.cc
+++ b/content/child/runtime_features.cc
@@ -476,6 +476,8 @@
       {wrf::EnableWebGLDraftExtensions, switches::kEnableWebGLDraftExtensions,
        true},
       {wrf::EnableWebGPU, switches::kEnableUnsafeWebGPU, true},
+      {wrf::EnableWebGPUDeveloperFeatures,
+       switches::kEnableWebGPUDeveloperFeatures, true},
       {wrf::EnableDirectSockets, switches::kIsolatedAppOrigins, true},
   };
   for (const auto& mapping : switchToFeatureMapping) {
diff --git a/content/content_resources.grd b/content/content_resources.grd
index 1a9f01e..21d50c1 100644
--- a/content/content_resources.grd
+++ b/content/content_resources.grd
@@ -41,7 +41,7 @@
     </if>
       <include name="IDR_UI_WINDOW_OPEN_DISPOSITION_MOJO_WEBUI_JS" file="${root_gen_dir}/mojom-webui/ui/base/mojom/window_open_disposition.mojom-webui.js" resource_path="mojo/ui/base/mojom/window_open_disposition.mojom-webui.js" use_base_dir="false" type="BINDATA" />
       <include name="IDR_UKM_INTERNALS_HTML" file="../components/ukm/debug/ukm_internals.html" type="BINDATA" />
-      <include name="IDR_UKM_INTERNALS_JS" file="../components/ukm/debug/ukm_internals.js" preprocess="true" type="BINDATA" />
+      <include name="IDR_UKM_INTERNALS_JS" file="${root_gen_dir}/components/ukm/debug/tsc/ukm_internals.js" use_base_dir="false" resource_path="ukm_internals.js" type="BINDATA" />
       <include name="IDR_UKM_INTERNALS_CSS" file="../components/ukm/debug/ukm_internals.css" type="BINDATA" />
       <if expr="chromeos_ash">
         <include name="IDR_UNGUESSABLE_TOKEN_MOJO_HTML" file="${root_gen_dir}/mojo/public/mojom/base/unguessable_token.mojom.html" resource_path="mojo/mojo/public/mojom/base/unguessable_token.mojom.html" use_base_dir="false" type="BINDATA" />
diff --git a/content/public/common/content_features.cc b/content/public/common/content_features.cc
index 155aba4e..fa5914f 100644
--- a/content/public/common/content_features.cc
+++ b/content/public/common/content_features.cc
@@ -553,11 +553,6 @@
 #endif
 };
 
-// Use a custom backend to store media licenses in lieu of the
-// Plugin Private File System.
-const base::Feature kMediaLicenseBackend{"MediaLicenseBackend",
-                                         base::FEATURE_ENABLED_BY_DEFAULT};
-
 // Allow cross-context transfer of MediaStreamTracks.
 const base::Feature kMediaStreamTrackTransfer{
     "MediaStreamTrackTransfer", base::FEATURE_DISABLED_BY_DEFAULT};
diff --git a/content/public/common/content_features.h b/content/public/common/content_features.h
index 9663e85..163f6c0 100644
--- a/content/public/common/content_features.h
+++ b/content/public/common/content_features.h
@@ -145,7 +145,6 @@
 };
 CONTENT_EXPORT extern const base::FeatureParam<MBIMode> kMBIModeParam;
 CONTENT_EXPORT extern const base::Feature kMediaDevicesSystemMonitorCache;
-CONTENT_EXPORT extern const base::Feature kMediaLicenseBackend;
 CONTENT_EXPORT extern const base::Feature kMediaStreamTrackTransfer;
 CONTENT_EXPORT extern const base::Feature kMojoDedicatedThread;
 CONTENT_EXPORT extern const base::Feature kMojoVideoCapture;
diff --git a/content/test/BUILD.gn b/content/test/BUILD.gn
index 78e1f80..86d4ad4 100644
--- a/content/test/BUILD.gn
+++ b/content/test/BUILD.gn
@@ -2791,9 +2791,9 @@
 
   if (enable_library_cdms) {
     sources += [
-      "../browser/media/cdm_storage_impl_unittest.cc",
       "../browser/media/key_system_support_impl_unittest.cc",
       "../browser/media/media_license_manager_unittest.cc",
+      "../browser/media/media_license_storage_host_unittest.cc",
     ]
   }
 
diff --git a/docs/ozone_overview.md b/docs/ozone_overview.md
index 378dfdd..169e57bb 100644
--- a/docs/ozone_overview.md
+++ b/docs/ozone_overview.md
@@ -354,6 +354,12 @@
   Note: traditional TTYs are not the ideal browsing experience.<br/>
   ![Picture of a workstation using Ozone/caca to display the Google home page in a text terminal](./images/ozone_caca.jpg)
 
+### drm
+Ash-chrome client implementation.
+
+### flatland / scenic
+For fuchsia.
+
 ## Communication
 
 There is a public mailing list:
diff --git a/extensions/browser/updater/extension_downloader.cc b/extensions/browser/updater/extension_downloader.cc
index 542137d..2a2569b 100644
--- a/extensions/browser/updater/extension_downloader.cc
+++ b/extensions/browser/updater/extension_downloader.cc
@@ -187,24 +187,23 @@
 
 UpdateDetails::~UpdateDetails() = default;
 
-ExtensionDownloader::ExtensionFetch::ExtensionFetch()
-    : credentials(CREDENTIALS_NONE) {}
-
 ExtensionDownloader::ExtensionFetch::ExtensionFetch(
-    const std::string& id,
+    ExtensionDownloaderTask task,
     const GURL& url,
     const std::string& package_hash,
     const std::string& version,
     const std::set<int>& request_ids,
     const DownloadFetchPriority fetch_priority)
-    : id(id),
+    : id(task.id),
       url(url),
       package_hash(package_hash),
       version(version),
       request_ids(request_ids),
       fetch_priority(fetch_priority),
       credentials(CREDENTIALS_NONE),
-      oauth2_attempt_count(0) {}
+      oauth2_attempt_count(0) {
+  associated_tasks.emplace_back(std::move(task));
+}
 
 ExtensionDownloader::ExtensionFetch::~ExtensionFetch() = default;
 
@@ -654,15 +653,16 @@
                            /*version not fetched*/ base::Version(),
                            /*manifest_fetch_failed*/ true);
     if (cached_crx_path) {
+      const ExtensionId id = task.id;
       delegate_->OnExtensionDownloadStageChanged(
-          task.id, ExtensionDownloaderDelegate::Stage::FINISHED);
+          id, ExtensionDownloaderDelegate::Stage::FINISHED);
       auto extension_fetch_data(std::make_unique<ExtensionFetch>(
-          task.id, fetch_data->base_url(), /*hash not fetched*/ "",
+          std::move(task), fetch_data->base_url(), /*hash not fetched*/ "",
           /*version not fetched*/ "", fetch_data->request_ids(),
           fetch_data->fetch_priority()));
       NotifyDelegateDownloadFinished(std::move(extension_fetch_data), true,
                                      cached_crx_path.value(), false);
-      extensions_fetched_from_cache.insert(task.id);
+      extensions_fetched_from_cache.insert(id);
     } else {
       tasks_left.emplace_back(std::move(task));
     }
@@ -809,7 +809,7 @@
   // which returns tasks back via its output arguments.
   DetermineUpdates(fetch_data->TakeAssociatedTasks(), *results, &to_update,
                    &failures);
-  for (const auto& update : to_update) {
+  for (auto& update : to_update) {
     const std::string& extension_id = update.first.id;
 
     GURL crx_url = update.second->crx_url;
@@ -820,7 +820,7 @@
     }
     FetchUpdatedExtension(
         std::make_unique<ExtensionFetch>(
-            extension_id, crx_url, update.second->package_hash,
+            std::move(update.first), crx_url, update.second->package_hash,
             update.second->version, fetch_data->request_ids(),
             fetch_data->fetch_priority()),
         update.second->info);
@@ -1090,6 +1090,10 @@
     if (iter->id == fetch_data->id || iter->url == fetch_data->url) {
       delegate_->OnExtensionDownloadStageChanged(
           fetch_data->id, ExtensionDownloaderDelegate::Stage::QUEUED_FOR_CRX);
+      iter->associated_tasks.insert(
+          iter->associated_tasks.end(),
+          std::make_move_iterator(fetch_data->associated_tasks.begin()),
+          std::make_move_iterator(fetch_data->associated_tasks.end()));
       iter->request_ids.insert(fetch_data->request_ids.begin(),
                                fetch_data->request_ids.end());
       return;  // already scheduled
@@ -1100,6 +1104,10 @@
       extensions_queue_.active_request()->url == fetch_data->url) {
     delegate_->OnExtensionDownloadStageChanged(
         fetch_data->id, ExtensionDownloaderDelegate::Stage::DOWNLOADING_CRX);
+    extensions_queue_.active_request()->associated_tasks.insert(
+        extensions_queue_.active_request()->associated_tasks.end(),
+        std::make_move_iterator(fetch_data->associated_tasks.begin()),
+        std::make_move_iterator(fetch_data->associated_tasks.end()));
     extensions_queue_.active_request()->request_ids.insert(
         fetch_data->request_ids.begin(), fetch_data->request_ids.end());
     return;
diff --git a/extensions/browser/updater/extension_downloader.h b/extensions/browser/updater/extension_downloader.h
index b77c260..10c9973 100644
--- a/extensions/browser/updater/extension_downloader.h
+++ b/extensions/browser/updater/extension_downloader.h
@@ -176,8 +176,7 @@
   // We need to keep track of some information associated with a url
   // when doing a fetch.
   struct ExtensionFetch {
-    ExtensionFetch();
-    ExtensionFetch(const std::string& id,
+    ExtensionFetch(ExtensionDownloaderTask task,
                    const GURL& url,
                    const std::string& package_hash,
                    const std::string& version,
@@ -189,8 +188,11 @@
     GURL url;
     std::string package_hash;
     base::Version version;
+    // TODO(b:235968596): Remove `request_ids` from this struct, as we have all
+    // data needed in the associated tasks.
     std::set<int> request_ids;
     DownloadFetchPriority fetch_priority;
+    std::vector<ExtensionDownloaderTask> associated_tasks;
 
     enum CredentialsMode {
       CREDENTIALS_NONE = 0,
diff --git a/fuchsia/base/BUILD.gn b/fuchsia/base/BUILD.gn
index fd9d342..c43860b1 100644
--- a/fuchsia/base/BUILD.gn
+++ b/fuchsia/base/BUILD.gn
@@ -13,6 +13,7 @@
     ":cr_fuchsia_base_unittests__exec",
     "./test/*",
     "//chromecast/internal/*",
+    "//fuchsia_web/common/test/*",
     "//fuchsia_web/runners/*",
     "//fuchsia_web/webengine/*",
   ]
@@ -31,16 +32,6 @@
   ]
 }
 
-static_library("run_all_integration_tests") {
-  testonly = true
-  visibility = [
-    "//fuchsia_web/runners/*",
-    "//fuchsia_web/webengine/*",
-  ]
-  sources = [ "run_all_integration_tests.cc" ]
-  deps = [ "//base/test:test_support" ]
-}
-
 # Unit-tests for all //fuchsia/base utilities.
 test("cr_fuchsia_base_unittests") {
   sources = [ "agent_impl_unittest.cc" ]
@@ -50,7 +41,7 @@
     "//base:testfidl",
     "//base/test:run_all_unittests",
     "//base/test:test_support",
-    "//fuchsia/base/test:test_support",
+    "//fuchsia_web/common/test:test_support",
     "//testing/gmock",
     "//testing/gtest",
     "//third_party/fuchsia-sdk/sdk/pkg/sys_cpp",
diff --git a/fuchsia/base/agent_impl_unittest.cc b/fuchsia/base/agent_impl_unittest.cc
index 8577678..a2419cf 100644
--- a/fuchsia/base/agent_impl_unittest.cc
+++ b/fuchsia/base/agent_impl_unittest.cc
@@ -11,7 +11,7 @@
 #include "base/test/task_environment.h"
 #include "base/test/test_future.h"
 #include "base/testfidl/cpp/fidl.h"
-#include "fuchsia/base/test/fit_adapter.h"
+#include "fuchsia_web/common/test/fit_adapter.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace cr_fuchsia {
diff --git a/fuchsia/base/test/BUILD.gn b/fuchsia_web/common/test/BUILD.gn
similarity index 65%
rename from fuchsia/base/test/BUILD.gn
rename to fuchsia_web/common/test/BUILD.gn
index b988c2b9..468d7b8 100644
--- a/fuchsia/base/test/BUILD.gn
+++ b/fuchsia_web/common/test/BUILD.gn
@@ -4,17 +4,18 @@
 
 assert(is_fuchsia)
 
+# Only allow use by WebEngine-related Fuchsia targets.
+visibility = [ "//fuchsia_web/*" ]
+
 source_set("test_support") {
   testonly = true
+
+  # TODO(crbug.com/1081525): Remove once directory is deleted.
+  visibility += [ "//fuchsia/base/*" ]
   sources = [
-    "context_provider_test_connector.cc",
-    "context_provider_test_connector.h",
-    "fake_component_context.cc",
-    "fake_component_context.h",
     "fit_adapter.h",
     "frame_test_util.cc",
     "frame_test_util.h",
-    "scoped_connection_checker.h",
     "test_devtools_list_fetcher.cc",
     "test_devtools_list_fetcher.h",
     "test_navigation_listener.cc",
@@ -34,3 +35,14 @@
     "//url",
   ]
 }
+
+static_library("run_all_integration_tests") {
+  testonly = true
+  visibility = []
+  visibility += [
+    "//fuchsia_web/runners/*",
+    "//fuchsia_web/webengine/*",
+  ]
+  sources = [ "run_all_integration_tests.cc" ]
+  deps = [ "//base/test:test_support" ]
+}
diff --git a/fuchsia/base/test/DEPS b/fuchsia_web/common/test/DEPS
similarity index 100%
rename from fuchsia/base/test/DEPS
rename to fuchsia_web/common/test/DEPS
diff --git a/fuchsia/base/test/fit_adapter.h b/fuchsia_web/common/test/fit_adapter.h
similarity index 84%
rename from fuchsia/base/test/fit_adapter.h
rename to fuchsia_web/common/test/fit_adapter.h
index f2e453f..3675f19 100644
--- a/fuchsia/base/test/fit_adapter.h
+++ b/fuchsia_web/common/test/fit_adapter.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef FUCHSIA_BASE_TEST_FIT_ADAPTER_H_
-#define FUCHSIA_BASE_TEST_FIT_ADAPTER_H_
+#ifndef FUCHSIA_WEB_COMMON_TEST_FIT_ADAPTER_H_
+#define FUCHSIA_WEB_COMMON_TEST_FIT_ADAPTER_H_
 
 #include <lib/fit/function.h>
 
@@ -24,4 +24,4 @@
 
 }  // namespace cr_fuchsia
 
-#endif  // FUCHSIA_BASE_TEST_FIT_ADAPTER_H_
+#endif  // FUCHSIA_WEB_COMMON_TEST_FIT_ADAPTER_H_
diff --git a/fuchsia/base/test/frame_test_util.cc b/fuchsia_web/common/test/frame_test_util.cc
similarity index 93%
rename from fuchsia/base/test/frame_test_util.cc
rename to fuchsia_web/common/test/frame_test_util.cc
index 2e934b6..b592f81 100644
--- a/fuchsia/base/test/frame_test_util.cc
+++ b/fuchsia_web/common/test/frame_test_util.cc
@@ -2,15 +2,15 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "fuchsia/base/test/frame_test_util.h"
+#include "fuchsia_web/common/test/frame_test_util.h"
 
 #include "base/fuchsia/mem_buffer_util.h"
 #include "base/json/json_reader.h"
 #include "base/run_loop.h"
 #include "base/strings/string_piece.h"
 #include "base/test/test_future.h"
-#include "fuchsia/base/test/fit_adapter.h"
-#include "fuchsia/base/test/test_navigation_listener.h"
+#include "fuchsia_web/common/test/fit_adapter.h"
+#include "fuchsia_web/common/test/test_navigation_listener.h"
 
 namespace cr_fuchsia {
 
diff --git a/fuchsia/base/test/frame_test_util.h b/fuchsia_web/common/test/frame_test_util.h
similarity index 91%
rename from fuchsia/base/test/frame_test_util.h
rename to fuchsia_web/common/test/frame_test_util.h
index 6f6074a5..e270025 100644
--- a/fuchsia/base/test/frame_test_util.h
+++ b/fuchsia_web/common/test/frame_test_util.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef FUCHSIA_BASE_TEST_FRAME_TEST_UTIL_H_
-#define FUCHSIA_BASE_TEST_FRAME_TEST_UTIL_H_
+#ifndef FUCHSIA_WEB_COMMON_TEST_FRAME_TEST_UTIL_H_
+#define FUCHSIA_WEB_COMMON_TEST_FRAME_TEST_UTIL_H_
 
 #include <fuchsia/mem/cpp/fidl.h>
 #include <fuchsia/web/cpp/fidl.h>
@@ -44,4 +44,4 @@
 
 }  // namespace cr_fuchsia
 
-#endif  // FUCHSIA_BASE_TEST_FRAME_TEST_UTIL_H_
+#endif  // FUCHSIA_WEB_COMMON_TEST_FRAME_TEST_UTIL_H_
diff --git a/fuchsia/base/run_all_integration_tests.cc b/fuchsia_web/common/test/run_all_integration_tests.cc
similarity index 100%
rename from fuchsia/base/run_all_integration_tests.cc
rename to fuchsia_web/common/test/run_all_integration_tests.cc
diff --git a/fuchsia/base/test/test_devtools_list_fetcher.cc b/fuchsia_web/common/test/test_devtools_list_fetcher.cc
similarity index 95%
rename from fuchsia/base/test/test_devtools_list_fetcher.cc
rename to fuchsia_web/common/test/test_devtools_list_fetcher.cc
index 03c1a7a..edaf97d 100644
--- a/fuchsia/base/test/test_devtools_list_fetcher.cc
+++ b/fuchsia_web/common/test/test_devtools_list_fetcher.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "fuchsia/base/test/test_devtools_list_fetcher.h"
+#include "fuchsia_web/common/test/test_devtools_list_fetcher.h"
 
 #include "base/callback.h"
 #include "base/json/json_reader.h"
diff --git a/fuchsia/base/test/test_devtools_list_fetcher.h b/fuchsia_web/common/test/test_devtools_list_fetcher.h
similarity index 69%
rename from fuchsia/base/test/test_devtools_list_fetcher.h
rename to fuchsia_web/common/test/test_devtools_list_fetcher.h
index 8bde0b9a..d908e36 100644
--- a/fuchsia/base/test/test_devtools_list_fetcher.h
+++ b/fuchsia_web/common/test/test_devtools_list_fetcher.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef FUCHSIA_BASE_TEST_TEST_DEVTOOLS_LIST_FETCHER_H_
-#define FUCHSIA_BASE_TEST_TEST_DEVTOOLS_LIST_FETCHER_H_
+#ifndef FUCHSIA_WEB_COMMON_TEST_TEST_DEVTOOLS_LIST_FETCHER_H_
+#define FUCHSIA_WEB_COMMON_TEST_TEST_DEVTOOLS_LIST_FETCHER_H_
 
 #include "base/values.h"
 
@@ -15,4 +15,4 @@
 
 }  // namespace cr_fuchsia
 
-#endif  // FUCHSIA_BASE_TEST_TEST_DEVTOOLS_LIST_FETCHER_H_
+#endif  // FUCHSIA_WEB_COMMON_TEST_TEST_DEVTOOLS_LIST_FETCHER_H_
diff --git a/fuchsia/base/test/test_navigation_listener.cc b/fuchsia_web/common/test/test_navigation_listener.cc
similarity index 98%
rename from fuchsia/base/test/test_navigation_listener.cc
rename to fuchsia_web/common/test/test_navigation_listener.cc
index b68c5b87..0ffcae9 100644
--- a/fuchsia/base/test/test_navigation_listener.cc
+++ b/fuchsia_web/common/test/test_navigation_listener.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "fuchsia/base/test/test_navigation_listener.h"
+#include "fuchsia_web/common/test/test_navigation_listener.h"
 
 #include <string>
 #include <utility>
diff --git a/fuchsia/base/test/test_navigation_listener.h b/fuchsia_web/common/test/test_navigation_listener.h
similarity index 95%
rename from fuchsia/base/test/test_navigation_listener.h
rename to fuchsia_web/common/test/test_navigation_listener.h
index beeb4ab7..bd41aae 100644
--- a/fuchsia/base/test/test_navigation_listener.h
+++ b/fuchsia_web/common/test/test_navigation_listener.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef FUCHSIA_BASE_TEST_TEST_NAVIGATION_LISTENER_H_
-#define FUCHSIA_BASE_TEST_TEST_NAVIGATION_LISTENER_H_
+#ifndef FUCHSIA_WEB_COMMON_TEST_TEST_NAVIGATION_LISTENER_H_
+#define FUCHSIA_WEB_COMMON_TEST_TEST_NAVIGATION_LISTENER_H_
 
 #include <fuchsia/web/cpp/fidl.h>
 #include <string>
@@ -103,4 +103,4 @@
 
 }  // namespace cr_fuchsia
 
-#endif  // FUCHSIA_BASE_TEST_TEST_NAVIGATION_LISTENER_H_
+#endif  // FUCHSIA_WEB_COMMON_TEST_TEST_NAVIGATION_LISTENER_H_
diff --git a/fuchsia/base/test/url_request_rewrite_test_util.cc b/fuchsia_web/common/test/url_request_rewrite_test_util.cc
similarity index 97%
rename from fuchsia/base/test/url_request_rewrite_test_util.cc
rename to fuchsia_web/common/test/url_request_rewrite_test_util.cc
index 45da5a55..be7ace2 100644
--- a/fuchsia/base/test/url_request_rewrite_test_util.cc
+++ b/fuchsia_web/common/test/url_request_rewrite_test_util.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "fuchsia/base/test/url_request_rewrite_test_util.h"
+#include "fuchsia_web/common/test/url_request_rewrite_test_util.h"
 
 #include "base/strings/string_piece.h"
 #include "fuchsia_web/common/string_util.h"
diff --git a/fuchsia/base/test/url_request_rewrite_test_util.h b/fuchsia_web/common/test/url_request_rewrite_test_util.h
similarity index 84%
rename from fuchsia/base/test/url_request_rewrite_test_util.h
rename to fuchsia_web/common/test/url_request_rewrite_test_util.h
index 8308c3c5..99b9d5f 100644
--- a/fuchsia/base/test/url_request_rewrite_test_util.h
+++ b/fuchsia_web/common/test/url_request_rewrite_test_util.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef FUCHSIA_BASE_TEST_URL_REQUEST_REWRITE_TEST_UTIL_H_
-#define FUCHSIA_BASE_TEST_URL_REQUEST_REWRITE_TEST_UTIL_H_
+#ifndef FUCHSIA_WEB_COMMON_TEST_URL_REQUEST_REWRITE_TEST_UTIL_H_
+#define FUCHSIA_WEB_COMMON_TEST_URL_REQUEST_REWRITE_TEST_UTIL_H_
 
 #include <fuchsia/web/cpp/fidl.h>
 
@@ -35,4 +35,4 @@
 
 }  // namespace cr_fuchsia
 
-#endif  // FUCHSIA_BASE_TEST_URL_REQUEST_REWRITE_TEST_UTIL_H_
+#endif  // FUCHSIA_WEB_COMMON_TEST_URL_REQUEST_REWRITE_TEST_UTIL_H_
diff --git a/fuchsia_web/runners/BUILD.gn b/fuchsia_web/runners/BUILD.gn
index 9c2a423..9496f4c 100644
--- a/fuchsia_web/runners/BUILD.gn
+++ b/fuchsia_web/runners/BUILD.gn
@@ -212,7 +212,7 @@
     "//base",
     "//base/test:run_all_unittests",
     "//base/test:test_support",
-    "//fuchsia/base/test:test_support",
+    "//fuchsia_web/common/test:test_support",
     "//net:test_support",
     "//testing/gmock",
     "//testing/gtest",
@@ -222,15 +222,19 @@
 }
 
 test("cast_runner_integration_tests") {
-  sources = [ "cast/cast_runner_integration_test.cc" ]
+  sources = [
+    "cast/cast_runner_integration_test.cc",
+    "cast/fake_component_context.cc",
+    "cast/fake_component_context.h",
+  ]
   data = [ "cast/testdata" ]
   deps = [
     ":cast_runner_core",
     ":cast_runner_test_core",
     "//base/test:test_support",
     "//components/cast/message_port",
-    "//fuchsia/base:run_all_integration_tests",
-    "//fuchsia/base/test:test_support",
+    "//fuchsia_web/common/test:run_all_integration_tests",
+    "//fuchsia_web/common/test:test_support",
     "//media/fuchsia/audio:test_support",
     "//net:test_support",
     "//testing/gtest",
@@ -274,7 +278,7 @@
     "//components/cast/message_port:test_message_port_receiver",
     "//content/public/browser",
     "//content/test:test_support",
-    "//fuchsia/base/test:test_support",
+    "//fuchsia_web/common/test:test_support",
     "//fuchsia_web/webengine:browsertest_core",
     "//testing/gmock",
     "//testing/gtest",
@@ -328,7 +332,7 @@
     "//base/test:test_support",
     "//components/cast/message_port",
     "//fuchsia/base:modular",
-    "//fuchsia/base:run_all_integration_tests",
+    "//fuchsia_web/common/test:run_all_integration_tests",
     "//net:test_support",
     "//testing/gtest",
     "//third_party/fuchsia-sdk/sdk/fidl/fuchsia.sys",
diff --git a/fuchsia_web/runners/cast/api_bindings_client_browsertest.cc b/fuchsia_web/runners/cast/api_bindings_client_browsertest.cc
index d9a3cc1..197477fa 100644
--- a/fuchsia_web/runners/cast/api_bindings_client_browsertest.cc
+++ b/fuchsia_web/runners/cast/api_bindings_client_browsertest.cc
@@ -13,9 +13,9 @@
 #include "base/test/test_future.h"
 #include "components/cast/message_port/fuchsia/message_port_fuchsia.h"
 #include "content/public/test/browser_test.h"
-#include "fuchsia/base/test/fit_adapter.h"
-#include "fuchsia/base/test/frame_test_util.h"
-#include "fuchsia/base/test/test_navigation_listener.h"
+#include "fuchsia_web/common/test/fit_adapter.h"
+#include "fuchsia_web/common/test/frame_test_util.h"
+#include "fuchsia_web/common/test/test_navigation_listener.h"
 #include "fuchsia_web/runners/cast/api_bindings_client.h"
 #include "fuchsia_web/runners/cast/create_web_message.h"
 #include "fuchsia_web/runners/cast/fake_api_bindings.h"
diff --git a/fuchsia_web/runners/cast/application_controller_impl_unittest.cc b/fuchsia_web/runners/cast/application_controller_impl_unittest.cc
index 06280026..f7272a7 100644
--- a/fuchsia_web/runners/cast/application_controller_impl_unittest.cc
+++ b/fuchsia_web/runners/cast/application_controller_impl_unittest.cc
@@ -11,7 +11,7 @@
 #include "base/logging.h"
 #include "base/test/task_environment.h"
 #include "base/test/test_future.h"
-#include "fuchsia/base/test/fit_adapter.h"
+#include "fuchsia_web/common/test/fit_adapter.h"
 #include "fuchsia_web/runners/cast/application_controller_impl.h"
 #include "fuchsia_web/runners/cast/fidl/fidl/chromium/cast/cpp/fidl.h"
 #include "testing/gmock/include/gmock/gmock.h"
diff --git a/fuchsia_web/runners/cast/cast_runner_integration_test.cc b/fuchsia_web/runners/cast/cast_runner_integration_test.cc
index bfcf102..9aa8072 100644
--- a/fuchsia_web/runners/cast/cast_runner_integration_test.cc
+++ b/fuchsia_web/runners/cast/cast_runner_integration_test.cc
@@ -42,17 +42,16 @@
 #include "base/threading/sequenced_task_runner_handle.h"
 #include "build/build_config.h"
 #include "fuchsia/base/agent_impl.h"
-#include "fuchsia/base/test/context_provider_test_connector.h"
-#include "fuchsia/base/test/fake_component_context.h"
-#include "fuchsia/base/test/fit_adapter.h"
-#include "fuchsia/base/test/frame_test_util.h"
-#include "fuchsia/base/test/test_devtools_list_fetcher.h"
-#include "fuchsia/base/test/url_request_rewrite_test_util.h"
 #include "fuchsia_web/common/string_util.h"
+#include "fuchsia_web/common/test/fit_adapter.h"
+#include "fuchsia_web/common/test/frame_test_util.h"
+#include "fuchsia_web/common/test/test_devtools_list_fetcher.h"
+#include "fuchsia_web/common/test/url_request_rewrite_test_util.h"
 #include "fuchsia_web/runners/cast/cast_runner.h"
 #include "fuchsia_web/runners/cast/cast_runner_switches.h"
 #include "fuchsia_web/runners/cast/fake_api_bindings.h"
 #include "fuchsia_web/runners/cast/fake_application_config_manager.h"
+#include "fuchsia_web/runners/cast/fake_component_context.h"
 #include "fuchsia_web/runners/cast/fidl/fidl/chromium/cast/cpp/fidl.h"
 #include "media/fuchsia/audio/fake_audio_device_enumerator.h"
 #include "net/test/embedded_test_server/default_handlers.h"
diff --git a/fuchsia/base/test/fake_component_context.cc b/fuchsia_web/runners/cast/fake_component_context.cc
similarity index 96%
rename from fuchsia/base/test/fake_component_context.cc
rename to fuchsia_web/runners/cast/fake_component_context.cc
index e3889df..bc7197a 100644
--- a/fuchsia/base/test/fake_component_context.cc
+++ b/fuchsia_web/runners/cast/fake_component_context.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "fuchsia/base/test/fake_component_context.h"
+#include "fuchsia_web/runners/cast/fake_component_context.h"
 
 #include <memory>
 #include <string>
diff --git a/fuchsia/base/test/fake_component_context.h b/fuchsia_web/runners/cast/fake_component_context.h
similarity index 91%
rename from fuchsia/base/test/fake_component_context.h
rename to fuchsia_web/runners/cast/fake_component_context.h
index 1c4fd8a..7ec95f0 100644
--- a/fuchsia/base/test/fake_component_context.h
+++ b/fuchsia_web/runners/cast/fake_component_context.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef FUCHSIA_BASE_TEST_FAKE_COMPONENT_CONTEXT_H_
-#define FUCHSIA_BASE_TEST_FAKE_COMPONENT_CONTEXT_H_
+#ifndef FUCHSIA_WEB_RUNNERS_CAST_FAKE_COMPONENT_CONTEXT_H_
+#define FUCHSIA_WEB_RUNNERS_CAST_FAKE_COMPONENT_CONTEXT_H_
 
 #include <fuchsia/modular/cpp/fidl_test_base.h>
 
@@ -57,4 +57,4 @@
 };
 
 }  // namespace cr_fuchsia
-#endif  // FUCHSIA_BASE_TEST_FAKE_COMPONENT_CONTEXT_H_
+#endif  // FUCHSIA_WEB_RUNNERS_CAST_FAKE_COMPONENT_CONTEXT_H_
diff --git a/fuchsia_web/runners/cast/named_message_port_connector_fuchsia_browsertest.cc b/fuchsia_web/runners/cast/named_message_port_connector_fuchsia_browsertest.cc
index 9d487d6..36d9a91 100644
--- a/fuchsia_web/runners/cast/named_message_port_connector_fuchsia_browsertest.cc
+++ b/fuchsia_web/runners/cast/named_message_port_connector_fuchsia_browsertest.cc
@@ -10,9 +10,9 @@
 #include "components/cast/message_port/fuchsia/message_port_fuchsia.h"
 #include "components/cast/message_port/test_message_port_receiver.h"
 #include "content/public/test/browser_test.h"
-#include "fuchsia/base/test/fit_adapter.h"
-#include "fuchsia/base/test/frame_test_util.h"
-#include "fuchsia/base/test/test_navigation_listener.h"
+#include "fuchsia_web/common/test/fit_adapter.h"
+#include "fuchsia_web/common/test/frame_test_util.h"
+#include "fuchsia_web/common/test/test_navigation_listener.h"
 #include "fuchsia_web/runners/cast/create_web_message.h"
 #include "fuchsia_web/runners/cast/named_message_port_connector_fuchsia.h"
 #include "fuchsia_web/webengine/test/frame_for_test.h"
diff --git a/fuchsia_web/webengine/BUILD.gn b/fuchsia_web/webengine/BUILD.gn
index 4b966260..6833a163 100644
--- a/fuchsia_web/webengine/BUILD.gn
+++ b/fuchsia_web/webengine/BUILD.gn
@@ -418,6 +418,7 @@
     "test/frame_for_test.h",
     "test/scenic_test_helper.cc",
     "test/scenic_test_helper.h",
+    "test/scoped_connection_checker.h",
     "test/test_data.cc",
     "test/test_data.h",
     "test/web_engine_browser_test.cc",
@@ -430,7 +431,7 @@
     ":web_engine_core",
     "//content/public/browser",
     "//content/test:test_support",
-    "//fuchsia/base/test:test_support",
+    "//fuchsia_web/common/test:test_support",
     "//net:test_support",
     "//testing/gtest",
     "//third_party/fuchsia-sdk/sdk/fidl/fuchsia.web",
@@ -494,8 +495,8 @@
     "//components/version_info",
     "//content/public/browser",
     "//content/test:test_support",
-    "//fuchsia/base/test:test_support",
     "//fuchsia_web/common",
+    "//fuchsia_web/common/test:test_support",
     "//net:test_support",
     "//testing/gmock",
     "//testing/gtest",
@@ -567,7 +568,7 @@
     "//components/url_rewrite/common",
     "//components/url_rewrite/mojom",
     "//content/test:test_support",
-    "//fuchsia/base/test:test_support",
+    "//fuchsia_web/common/test:test_support",
     "//mojo/core/embedder",
     "//services/media_session/public/mojom",
     "//services/network:network_service",
@@ -596,6 +597,8 @@
 
 test("web_engine_integration_tests") {
   sources = [
+    "test/context_provider_test_connector.cc",
+    "test/context_provider_test_connector.h",
     "test_debug_listener.cc",
     "test_debug_listener.h",
     "web_engine_debug_integration_test.cc",
@@ -613,8 +616,8 @@
     "//base",
     "//base:test_log_listener_safe",
     "//components/version_info",
-    "//fuchsia/base:run_all_integration_tests",
-    "//fuchsia/base/test:test_support",
+    "//fuchsia_web/common/test:run_all_integration_tests",
+    "//fuchsia_web/common/test:test_support",
     "//fuchsia_web/webinstance_host",
     "//media",
     "//media/fuchsia/audio:test_support",
diff --git a/fuchsia_web/webengine/browser/accessibility_bridge_browsertest.cc b/fuchsia_web/webengine/browser/accessibility_bridge_browsertest.cc
index 1eba95da..844bcce 100644
--- a/fuchsia_web/webengine/browser/accessibility_bridge_browsertest.cc
+++ b/fuchsia_web/webengine/browser/accessibility_bridge_browsertest.cc
@@ -11,8 +11,8 @@
 #include "base/strings/stringprintf.h"
 #include "base/test/bind.h"
 #include "content/public/test/browser_test.h"
-#include "fuchsia/base/test/frame_test_util.h"
-#include "fuchsia/base/test/test_navigation_listener.h"
+#include "fuchsia_web/common/test/frame_test_util.h"
+#include "fuchsia_web/common/test/test_navigation_listener.h"
 #include "fuchsia_web/webengine/browser/accessibility_bridge.h"
 #include "fuchsia_web/webengine/browser/context_impl.h"
 #include "fuchsia_web/webengine/browser/fake_semantics_manager.h"
diff --git a/fuchsia_web/webengine/browser/accessibility_browsertest.cc b/fuchsia_web/webengine/browser/accessibility_browsertest.cc
index 284d7d93..a531472 100644
--- a/fuchsia_web/webengine/browser/accessibility_browsertest.cc
+++ b/fuchsia_web/webengine/browser/accessibility_browsertest.cc
@@ -13,8 +13,8 @@
 #include "base/strings/stringprintf.h"
 #include "base/test/bind.h"
 #include "content/public/test/browser_test.h"
-#include "fuchsia/base/test/frame_test_util.h"
-#include "fuchsia/base/test/test_navigation_listener.h"
+#include "fuchsia_web/common/test/frame_test_util.h"
+#include "fuchsia_web/common/test/test_navigation_listener.h"
 #include "fuchsia_web/webengine/browser/accessibility_bridge.h"
 #include "fuchsia_web/webengine/browser/context_impl.h"
 #include "fuchsia_web/webengine/browser/fake_semantics_manager.h"
diff --git a/fuchsia_web/webengine/browser/autoplay_browsertest.cc b/fuchsia_web/webengine/browser/autoplay_browsertest.cc
index 3144b3b..07d6d84e 100644
--- a/fuchsia_web/webengine/browser/autoplay_browsertest.cc
+++ b/fuchsia_web/webengine/browser/autoplay_browsertest.cc
@@ -6,8 +6,8 @@
 
 #include "base/files/file_path.h"
 #include "content/public/test/browser_test.h"
-#include "fuchsia/base/test/frame_test_util.h"
-#include "fuchsia/base/test/test_navigation_listener.h"
+#include "fuchsia_web/common/test/frame_test_util.h"
+#include "fuchsia_web/common/test/test_navigation_listener.h"
 #include "fuchsia_web/webengine/browser/context_impl.h"
 #include "fuchsia_web/webengine/browser/frame_impl.h"
 #include "fuchsia_web/webengine/test/frame_for_test.h"
diff --git a/fuchsia_web/webengine/browser/cast_streaming_browsertest.cc b/fuchsia_web/webengine/browser/cast_streaming_browsertest.cc
index e825413..318faf8d 100644
--- a/fuchsia_web/webengine/browser/cast_streaming_browsertest.cc
+++ b/fuchsia_web/webengine/browser/cast_streaming_browsertest.cc
@@ -12,9 +12,9 @@
 #include "components/cast/message_port/platform_message_port.h"
 #include "components/cast_streaming/browser/test/cast_streaming_test_sender.h"
 #include "content/public/test/browser_test.h"
-#include "fuchsia/base/test/fit_adapter.h"
-#include "fuchsia/base/test/frame_test_util.h"
-#include "fuchsia/base/test/test_navigation_listener.h"
+#include "fuchsia_web/common/test/fit_adapter.h"
+#include "fuchsia_web/common/test/frame_test_util.h"
+#include "fuchsia_web/common/test/test_navigation_listener.h"
 #include "fuchsia_web/webengine/browser/context_impl.h"
 #include "fuchsia_web/webengine/browser/frame_impl.h"
 #include "fuchsia_web/webengine/test/frame_for_test.h"
diff --git a/fuchsia_web/webengine/browser/client_hints_browsertest.cc b/fuchsia_web/webengine/browser/client_hints_browsertest.cc
index f5f611b..40b801f 100644
--- a/fuchsia_web/webengine/browser/client_hints_browsertest.cc
+++ b/fuchsia_web/webengine/browser/client_hints_browsertest.cc
@@ -10,8 +10,8 @@
 #include "content/public/browser/web_contents.h"
 #include "content/public/test/browser_test.h"
 #include "content/public/test/browser_test_utils.h"
-#include "fuchsia/base/test/frame_test_util.h"
-#include "fuchsia/base/test/test_navigation_listener.h"
+#include "fuchsia_web/common/test/frame_test_util.h"
+#include "fuchsia_web/common/test/test_navigation_listener.h"
 #include "fuchsia_web/webengine/browser/context_impl.h"
 #include "fuchsia_web/webengine/browser/frame_impl.h"
 #include "fuchsia_web/webengine/browser/frame_impl_browser_test_base.h"
diff --git a/fuchsia_web/webengine/browser/content_directory_browsertest.cc b/fuchsia_web/webengine/browser/content_directory_browsertest.cc
index 85a2a97..62559b3 100644
--- a/fuchsia_web/webengine/browser/content_directory_browsertest.cc
+++ b/fuchsia_web/webengine/browser/content_directory_browsertest.cc
@@ -19,8 +19,8 @@
 #include "base/threading/thread_restrictions.h"
 #include "content/public/test/browser_test.h"
 #include "content/public/test/content_test_suite_base.h"
-#include "fuchsia/base/test/frame_test_util.h"
-#include "fuchsia/base/test/test_navigation_listener.h"
+#include "fuchsia_web/common/test/frame_test_util.h"
+#include "fuchsia_web/common/test/test_navigation_listener.h"
 #include "fuchsia_web/webengine/browser/content_directory_loader_factory.h"
 #include "fuchsia_web/webengine/switches.h"
 #include "fuchsia_web/webengine/test/frame_for_test.h"
diff --git a/fuchsia_web/webengine/browser/context_impl_browsertest.cc b/fuchsia_web/webengine/browser/context_impl_browsertest.cc
index bbea024e..c9189a3 100644
--- a/fuchsia_web/webengine/browser/context_impl_browsertest.cc
+++ b/fuchsia_web/webengine/browser/context_impl_browsertest.cc
@@ -5,8 +5,8 @@
 #include "base/command_line.h"
 #include "base/no_destructor.h"
 #include "content/public/test/browser_test.h"
-#include "fuchsia/base/test/frame_test_util.h"
-#include "fuchsia/base/test/test_navigation_listener.h"
+#include "fuchsia_web/common/test/frame_test_util.h"
+#include "fuchsia_web/common/test/test_navigation_listener.h"
 #include "fuchsia_web/webengine/switches.h"
 #include "fuchsia_web/webengine/test/frame_for_test.h"
 #include "fuchsia_web/webengine/test/web_engine_browser_test.h"
diff --git a/fuchsia_web/webengine/browser/cookie_manager_impl_unittest.cc b/fuchsia_web/webengine/browser/cookie_manager_impl_unittest.cc
index 481ff1a8..26ed8f26 100644
--- a/fuchsia_web/webengine/browser/cookie_manager_impl_unittest.cc
+++ b/fuchsia_web/webengine/browser/cookie_manager_impl_unittest.cc
@@ -13,7 +13,7 @@
 #include "base/test/bind.h"
 #include "base/test/task_environment.h"
 #include "base/test/test_future.h"
-#include "fuchsia/base/test/fit_adapter.h"
+#include "fuchsia_web/common/test/fit_adapter.h"
 #include "fuchsia_web/webengine/browser/cookie_manager_impl.h"
 #include "mojo/public/cpp/bindings/remote.h"
 #include "net/cookies/cookie_access_result.h"
diff --git a/fuchsia_web/webengine/browser/explicit_sites_filter_browsertest.cc b/fuchsia_web/webengine/browser/explicit_sites_filter_browsertest.cc
index 0f7453d3..8ef18f0 100644
--- a/fuchsia_web/webengine/browser/explicit_sites_filter_browsertest.cc
+++ b/fuchsia_web/webengine/browser/explicit_sites_filter_browsertest.cc
@@ -12,8 +12,8 @@
 #include "components/safe_search_api/stub_url_checker.h"
 #include "components/safe_search_api/url_checker.h"
 #include "content/public/test/browser_test.h"
-#include "fuchsia/base/test/frame_test_util.h"
-#include "fuchsia/base/test/test_navigation_listener.h"
+#include "fuchsia_web/common/test/frame_test_util.h"
+#include "fuchsia_web/common/test/test_navigation_listener.h"
 #include "fuchsia_web/webengine/browser/context_impl.h"
 #include "fuchsia_web/webengine/browser/frame_impl.h"
 #include "fuchsia_web/webengine/browser/frame_impl_browser_test_base.h"
diff --git a/fuchsia_web/webengine/browser/favicon_browsertest.cc b/fuchsia_web/webengine/browser/favicon_browsertest.cc
index aa5f692..4735b6f 100644
--- a/fuchsia_web/webengine/browser/favicon_browsertest.cc
+++ b/fuchsia_web/webengine/browser/favicon_browsertest.cc
@@ -4,8 +4,8 @@
 
 #include "base/fuchsia/mem_buffer_util.h"
 #include "content/public/test/browser_test.h"
-#include "fuchsia/base/test/frame_test_util.h"
-#include "fuchsia/base/test/test_navigation_listener.h"
+#include "fuchsia_web/common/test/frame_test_util.h"
+#include "fuchsia_web/common/test/test_navigation_listener.h"
 #include "fuchsia_web/webengine/test/frame_for_test.h"
 #include "fuchsia_web/webengine/test/test_data.h"
 #include "fuchsia_web/webengine/test/web_engine_browser_test.h"
diff --git a/fuchsia_web/webengine/browser/frame_host_impl_browsertest.cc b/fuchsia_web/webengine/browser/frame_host_impl_browsertest.cc
index 1c9ff395..666c140 100644
--- a/fuchsia_web/webengine/browser/frame_host_impl_browsertest.cc
+++ b/fuchsia_web/webengine/browser/frame_host_impl_browsertest.cc
@@ -5,7 +5,7 @@
 #include <lib/sys/cpp/service_directory.h>
 
 #include "content/public/test/browser_test.h"
-#include "fuchsia/base/test/test_navigation_listener.h"
+#include "fuchsia_web/common/test/test_navigation_listener.h"
 #include "fuchsia_web/webengine/browser/context_impl.h"
 #include "fuchsia_web/webengine/test/web_engine_browser_test.h"
 #include "testing/gtest/include/gtest/gtest.h"
diff --git a/fuchsia_web/webengine/browser/frame_impl_browsertest.cc b/fuchsia_web/webengine/browser/frame_impl_browsertest.cc
index f043bdc..ab9c026 100644
--- a/fuchsia_web/webengine/browser/frame_impl_browsertest.cc
+++ b/fuchsia_web/webengine/browser/frame_impl_browsertest.cc
@@ -11,10 +11,10 @@
 #include "content/public/browser/navigation_handle.h"
 #include "content/public/test/browser_test.h"
 #include "content/public/test/test_utils.h"
-#include "fuchsia/base/test/fit_adapter.h"
-#include "fuchsia/base/test/frame_test_util.h"
-#include "fuchsia/base/test/test_navigation_listener.h"
 #include "fuchsia_web/common/string_util.h"
+#include "fuchsia_web/common/test/fit_adapter.h"
+#include "fuchsia_web/common/test/frame_test_util.h"
+#include "fuchsia_web/common/test/test_navigation_listener.h"
 #include "fuchsia_web/webengine/browser/context_impl.h"
 #include "fuchsia_web/webengine/browser/fake_semantics_manager.h"
 #include "fuchsia_web/webengine/browser/frame_impl.h"
diff --git a/fuchsia_web/webengine/browser/headless_browsertest.cc b/fuchsia_web/webengine/browser/headless_browsertest.cc
index f5c13fb..9142f1d 100644
--- a/fuchsia_web/webengine/browser/headless_browsertest.cc
+++ b/fuchsia_web/webengine/browser/headless_browsertest.cc
@@ -5,8 +5,8 @@
 #include "base/auto_reset.h"
 #include "base/test/scoped_command_line.h"
 #include "content/public/test/browser_test.h"
-#include "fuchsia/base/test/frame_test_util.h"
-#include "fuchsia/base/test/test_navigation_listener.h"
+#include "fuchsia_web/common/test/frame_test_util.h"
+#include "fuchsia_web/common/test/test_navigation_listener.h"
 #include "fuchsia_web/webengine/switches.h"
 #include "fuchsia_web/webengine/test/frame_for_test.h"
 #include "fuchsia_web/webengine/test/test_data.h"
diff --git a/fuchsia_web/webengine/browser/input_browsertest.cc b/fuchsia_web/webengine/browser/input_browsertest.cc
index ce93835..d449c4d 100644
--- a/fuchsia_web/webengine/browser/input_browsertest.cc
+++ b/fuchsia_web/webengine/browser/input_browsertest.cc
@@ -11,13 +11,13 @@
 #include "base/fuchsia/test_component_context_for_process.h"
 #include "base/test/scoped_feature_list.h"
 #include "content/public/test/browser_test.h"
-#include "fuchsia/base/test/frame_test_util.h"
-#include "fuchsia/base/test/scoped_connection_checker.h"
-#include "fuchsia/base/test/test_navigation_listener.h"
+#include "fuchsia_web/common/test/frame_test_util.h"
+#include "fuchsia_web/common/test/test_navigation_listener.h"
 #include "fuchsia_web/webengine/browser/context_impl.h"
 #include "fuchsia_web/webengine/features.h"
 #include "fuchsia_web/webengine/test/frame_for_test.h"
 #include "fuchsia_web/webengine/test/scenic_test_helper.h"
+#include "fuchsia_web/webengine/test/scoped_connection_checker.h"
 #include "fuchsia_web/webengine/test/test_data.h"
 #include "fuchsia_web/webengine/test/web_engine_browser_test.h"
 #include "testing/gtest/include/gtest/gtest.h"
diff --git a/fuchsia_web/webengine/browser/javascript_browsertest.cc b/fuchsia_web/webengine/browser/javascript_browsertest.cc
index f58fd9a..fb45d3b 100644
--- a/fuchsia_web/webengine/browser/javascript_browsertest.cc
+++ b/fuchsia_web/webengine/browser/javascript_browsertest.cc
@@ -4,8 +4,8 @@
 
 #include "base/fuchsia/mem_buffer_util.h"
 #include "content/public/test/browser_test.h"
-#include "fuchsia/base/test/frame_test_util.h"
-#include "fuchsia/base/test/test_navigation_listener.h"
+#include "fuchsia_web/common/test/frame_test_util.h"
+#include "fuchsia_web/common/test/test_navigation_listener.h"
 #include "fuchsia_web/webengine/browser/frame_impl_browser_test_base.h"
 #include "fuchsia_web/webengine/test/frame_for_test.h"
 
diff --git a/fuchsia_web/webengine/browser/media_browsertest.cc b/fuchsia_web/webengine/browser/media_browsertest.cc
index b9e045d..09d587a0 100644
--- a/fuchsia_web/webengine/browser/media_browsertest.cc
+++ b/fuchsia_web/webengine/browser/media_browsertest.cc
@@ -11,8 +11,8 @@
 #include "base/fuchsia/test_component_context_for_process.h"
 #include "base/test/scoped_feature_list.h"
 #include "content/public/test/browser_test.h"
-#include "fuchsia/base/test/frame_test_util.h"
-#include "fuchsia/base/test/test_navigation_listener.h"
+#include "fuchsia_web/common/test/frame_test_util.h"
+#include "fuchsia_web/common/test/test_navigation_listener.h"
 #include "fuchsia_web/webengine/features.h"
 #include "fuchsia_web/webengine/test/frame_for_test.h"
 #include "fuchsia_web/webengine/test/test_data.h"
diff --git a/fuchsia_web/webengine/browser/navigation_policy_browsertest.cc b/fuchsia_web/webengine/browser/navigation_policy_browsertest.cc
index 397c43b1..4b4b462 100644
--- a/fuchsia_web/webengine/browser/navigation_policy_browsertest.cc
+++ b/fuchsia_web/webengine/browser/navigation_policy_browsertest.cc
@@ -5,8 +5,8 @@
 #include <fuchsia/web/cpp/fidl.h>
 
 #include "content/public/test/browser_test.h"
-#include "fuchsia/base/test/frame_test_util.h"
-#include "fuchsia/base/test/test_navigation_listener.h"
+#include "fuchsia_web/common/test/frame_test_util.h"
+#include "fuchsia_web/common/test/test_navigation_listener.h"
 #include "fuchsia_web/webengine/browser/context_impl.h"
 #include "fuchsia_web/webengine/browser/fake_navigation_policy_provider.h"
 #include "fuchsia_web/webengine/browser/frame_impl.h"
diff --git a/fuchsia_web/webengine/browser/permissions_browsertest.cc b/fuchsia_web/webengine/browser/permissions_browsertest.cc
index 940d5e8..d22b096b 100644
--- a/fuchsia_web/webengine/browser/permissions_browsertest.cc
+++ b/fuchsia_web/webengine/browser/permissions_browsertest.cc
@@ -7,8 +7,8 @@
 #include "base/strings/stringprintf.h"
 #include "base/threading/scoped_blocking_call.h"
 #include "content/public/test/browser_test.h"
-#include "fuchsia/base/test/frame_test_util.h"
-#include "fuchsia/base/test/test_navigation_listener.h"
+#include "fuchsia_web/common/test/frame_test_util.h"
+#include "fuchsia_web/common/test/test_navigation_listener.h"
 #include "fuchsia_web/webengine/browser/frame_impl_browser_test_base.h"
 #include "fuchsia_web/webengine/test/frame_for_test.h"
 #include "fuchsia_web/webengine/test/test_data.h"
diff --git a/fuchsia_web/webengine/browser/popup_browsertest.cc b/fuchsia_web/webengine/browser/popup_browsertest.cc
index 3c65687..0f294c52 100644
--- a/fuchsia_web/webengine/browser/popup_browsertest.cc
+++ b/fuchsia_web/webengine/browser/popup_browsertest.cc
@@ -3,8 +3,8 @@
 // found in the LICENSE file.
 
 #include "content/public/test/browser_test.h"
-#include "fuchsia/base/test/frame_test_util.h"
-#include "fuchsia/base/test/test_navigation_listener.h"
+#include "fuchsia_web/common/test/frame_test_util.h"
+#include "fuchsia_web/common/test/test_navigation_listener.h"
 #include "fuchsia_web/webengine/browser/frame_impl_browser_test_base.h"
 #include "fuchsia_web/webengine/test/frame_for_test.h"
 
diff --git a/fuchsia_web/webengine/browser/post_message_browsertest.cc b/fuchsia_web/webengine/browser/post_message_browsertest.cc
index 066fa42..c98f07e2 100644
--- a/fuchsia_web/webengine/browser/post_message_browsertest.cc
+++ b/fuchsia_web/webengine/browser/post_message_browsertest.cc
@@ -5,9 +5,9 @@
 #include "base/fuchsia/mem_buffer_util.h"
 #include "base/test/test_future.h"
 #include "content/public/test/browser_test.h"
-#include "fuchsia/base/test/fit_adapter.h"
-#include "fuchsia/base/test/frame_test_util.h"
-#include "fuchsia/base/test/test_navigation_listener.h"
+#include "fuchsia_web/common/test/fit_adapter.h"
+#include "fuchsia_web/common/test/frame_test_util.h"
+#include "fuchsia_web/common/test/test_navigation_listener.h"
 #include "fuchsia_web/webengine/browser/frame_impl_browser_test_base.h"
 #include "fuchsia_web/webengine/test/frame_for_test.h"
 
diff --git a/fuchsia_web/webengine/browser/request_monitoring_browsertest.cc b/fuchsia_web/webengine/browser/request_monitoring_browsertest.cc
index 9f3f1c0..074f918 100644
--- a/fuchsia_web/webengine/browser/request_monitoring_browsertest.cc
+++ b/fuchsia_web/webengine/browser/request_monitoring_browsertest.cc
@@ -3,10 +3,10 @@
 // found in the LICENSE file.
 
 #include "content/public/test/browser_test.h"
-#include "fuchsia/base/test/frame_test_util.h"
-#include "fuchsia/base/test/test_navigation_listener.h"
-#include "fuchsia/base/test/url_request_rewrite_test_util.h"
 #include "fuchsia_web/common/string_util.h"
+#include "fuchsia_web/common/test/frame_test_util.h"
+#include "fuchsia_web/common/test/test_navigation_listener.h"
+#include "fuchsia_web/common/test/url_request_rewrite_test_util.h"
 #include "fuchsia_web/webengine/browser/frame_impl_browser_test_base.h"
 #include "fuchsia_web/webengine/switches.h"
 #include "fuchsia_web/webengine/test/frame_for_test.h"
diff --git a/fuchsia_web/webengine/browser/theme_manager_browsertest.cc b/fuchsia_web/webengine/browser/theme_manager_browsertest.cc
index 09fbbb28..f65c15e3 100644
--- a/fuchsia_web/webengine/browser/theme_manager_browsertest.cc
+++ b/fuchsia_web/webengine/browser/theme_manager_browsertest.cc
@@ -11,8 +11,8 @@
 #include "base/strings/stringprintf.h"
 #include "content/public/test/browser_test.h"
 #include "content/public/test/browser_test_utils.h"
-#include "fuchsia/base/test/frame_test_util.h"
-#include "fuchsia/base/test/test_navigation_listener.h"
+#include "fuchsia_web/common/test/frame_test_util.h"
+#include "fuchsia_web/common/test/test_navigation_listener.h"
 #include "fuchsia_web/webengine/browser/context_impl.h"
 #include "fuchsia_web/webengine/browser/frame_impl.h"
 #include "fuchsia_web/webengine/test/frame_for_test.h"
diff --git a/fuchsia_web/webengine/browser/url_request_invalidation_browsertest.cc b/fuchsia_web/webengine/browser/url_request_invalidation_browsertest.cc
index 43f65491..daa0eb8 100644
--- a/fuchsia_web/webengine/browser/url_request_invalidation_browsertest.cc
+++ b/fuchsia_web/webengine/browser/url_request_invalidation_browsertest.cc
@@ -5,9 +5,9 @@
 #include <fuchsia/web/cpp/fidl.h>
 
 #include "content/public/test/browser_test.h"
-#include "fuchsia/base/test/frame_test_util.h"
-#include "fuchsia/base/test/test_navigation_listener.h"
-#include "fuchsia/base/test/url_request_rewrite_test_util.h"
+#include "fuchsia_web/common/test/frame_test_util.h"
+#include "fuchsia_web/common/test/test_navigation_listener.h"
+#include "fuchsia_web/common/test/url_request_rewrite_test_util.h"
 #include "fuchsia_web/webengine/test/frame_for_test.h"
 #include "fuchsia_web/webengine/test/web_engine_browser_test.h"
 #include "testing/gtest/include/gtest/gtest.h"
diff --git a/fuchsia_web/webengine/browser/url_request_rewrite_type_converters_unittest.cc b/fuchsia_web/webengine/browser/url_request_rewrite_type_converters_unittest.cc
index b4b294b..22ade085 100644
--- a/fuchsia_web/webengine/browser/url_request_rewrite_type_converters_unittest.cc
+++ b/fuchsia_web/webengine/browser/url_request_rewrite_type_converters_unittest.cc
@@ -8,7 +8,7 @@
 
 #include "base/run_loop.h"
 #include "base/test/bind.h"
-#include "fuchsia/base/test/url_request_rewrite_test_util.h"
+#include "fuchsia_web/common/test/url_request_rewrite_test_util.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace {
diff --git a/fuchsia_web/webengine/browser/virtual_keyboard_browsertest.cc b/fuchsia_web/webengine/browser/virtual_keyboard_browsertest.cc
index dc3fb23..7b97759 100644
--- a/fuchsia_web/webengine/browser/virtual_keyboard_browsertest.cc
+++ b/fuchsia_web/webengine/browser/virtual_keyboard_browsertest.cc
@@ -15,15 +15,15 @@
 #include "base/strings/stringprintf.h"
 #include "base/test/scoped_feature_list.h"
 #include "content/public/test/browser_test.h"
-#include "fuchsia/base/test/frame_test_util.h"
-#include "fuchsia/base/test/scoped_connection_checker.h"
-#include "fuchsia/base/test/test_navigation_listener.h"
+#include "fuchsia_web/common/test/frame_test_util.h"
+#include "fuchsia_web/common/test/test_navigation_listener.h"
 #include "fuchsia_web/webengine/browser/context_impl.h"
 #include "fuchsia_web/webengine/browser/frame_impl.h"
 #include "fuchsia_web/webengine/browser/mock_virtual_keyboard.h"
 #include "fuchsia_web/webengine/features.h"
 #include "fuchsia_web/webengine/test/frame_for_test.h"
 #include "fuchsia_web/webengine/test/scenic_test_helper.h"
+#include "fuchsia_web/webengine/test/scoped_connection_checker.h"
 #include "fuchsia_web/webengine/test/test_data.h"
 #include "fuchsia_web/webengine/test/web_engine_browser_test.h"
 #include "testing/gtest/include/gtest/gtest.h"
diff --git a/fuchsia/base/test/context_provider_test_connector.cc b/fuchsia_web/webengine/test/context_provider_test_connector.cc
similarity index 97%
rename from fuchsia/base/test/context_provider_test_connector.cc
rename to fuchsia_web/webengine/test/context_provider_test_connector.cc
index 1372ac38..9b49a60 100644
--- a/fuchsia/base/test/context_provider_test_connector.cc
+++ b/fuchsia_web/webengine/test/context_provider_test_connector.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "fuchsia/base/test/context_provider_test_connector.h"
+#include "fuchsia_web/webengine/test/context_provider_test_connector.h"
 
 #include <unistd.h>
 
diff --git a/fuchsia/base/test/context_provider_test_connector.h b/fuchsia_web/webengine/test/context_provider_test_connector.h
similarity index 86%
rename from fuchsia/base/test/context_provider_test_connector.h
rename to fuchsia_web/webengine/test/context_provider_test_connector.h
index a3e59f1..f956a91 100644
--- a/fuchsia/base/test/context_provider_test_connector.h
+++ b/fuchsia_web/webengine/test/context_provider_test_connector.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef FUCHSIA_BASE_TEST_CONTEXT_PROVIDER_TEST_CONNECTOR_H_
-#define FUCHSIA_BASE_TEST_CONTEXT_PROVIDER_TEST_CONNECTOR_H_
+#ifndef FUCHSIA_WEB_WEBENGINE_TEST_CONTEXT_PROVIDER_TEST_CONNECTOR_H_
+#define FUCHSIA_WEB_WEBENGINE_TEST_CONTEXT_PROVIDER_TEST_CONNECTOR_H_
 
 #include <fuchsia/sys/cpp/fidl.h>
 #include <fuchsia/web/cpp/fidl.h>
@@ -34,4 +34,4 @@
         base::CommandLine(base::CommandLine::NO_PROGRAM));
 }  // namespace cr_fuchsia
 
-#endif  // FUCHSIA_BASE_TEST_CONTEXT_PROVIDER_TEST_CONNECTOR_H_
+#endif  // FUCHSIA_WEB_WEBENGINE_TEST_CONTEXT_PROVIDER_TEST_CONNECTOR_H_
diff --git a/fuchsia_web/webengine/test/frame_for_test.cc b/fuchsia_web/webengine/test/frame_for_test.cc
index 9fd1a7a..e21ed99 100644
--- a/fuchsia_web/webengine/test/frame_for_test.cc
+++ b/fuchsia_web/webengine/test/frame_for_test.cc
@@ -4,7 +4,7 @@
 
 #include "fuchsia_web/webengine/test/frame_for_test.h"
 
-#include "fuchsia/base/test/test_navigation_listener.h"
+#include "fuchsia_web/common/test/test_navigation_listener.h"
 
 namespace cr_fuchsia {
 
diff --git a/fuchsia_web/webengine/test/scenic_test_helper.cc b/fuchsia_web/webengine/test/scenic_test_helper.cc
index 8f90d36..60a6031 100644
--- a/fuchsia_web/webengine/test/scenic_test_helper.cc
+++ b/fuchsia_web/webengine/test/scenic_test_helper.cc
@@ -10,7 +10,7 @@
 #include "base/fuchsia/fuchsia_logging.h"
 #include "base/run_loop.h"
 #include "content/public/browser/render_widget_host_view.h"
-#include "fuchsia/base/test/frame_test_util.h"
+#include "fuchsia_web/common/test/frame_test_util.h"
 #include "fuchsia_web/webengine/browser/context_impl.h"
 #include "fuchsia_web/webengine/browser/frame_window_tree_host.h"
 #include "fuchsia_web/webengine/test/test_data.h"
diff --git a/fuchsia/base/test/scoped_connection_checker.h b/fuchsia_web/webengine/test/scoped_connection_checker.h
similarity index 90%
rename from fuchsia/base/test/scoped_connection_checker.h
rename to fuchsia_web/webengine/test/scoped_connection_checker.h
index cebc816..f69db0d 100644
--- a/fuchsia/base/test/scoped_connection_checker.h
+++ b/fuchsia_web/webengine/test/scoped_connection_checker.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef FUCHSIA_BASE_TEST_SCOPED_CONNECTION_CHECKER_H_
-#define FUCHSIA_BASE_TEST_SCOPED_CONNECTION_CHECKER_H_
+#ifndef FUCHSIA_WEB_WEBENGINE_TEST_SCOPED_CONNECTION_CHECKER_H_
+#define FUCHSIA_WEB_WEBENGINE_TEST_SCOPED_CONNECTION_CHECKER_H_
 
 #include <lib/fdio/directory.h>
 #include <lib/vfs/cpp/service.h>
@@ -55,4 +55,4 @@
 template <typename Service>
 using EnsureConnectedChecker = ScopedConnectionCheckerBase<Service, true>;
 
-#endif  // FUCHSIA_BASE_TEST_SCOPED_CONNECTION_CHECKER_H_
+#endif  // FUCHSIA_WEB_WEBENGINE_TEST_SCOPED_CONNECTION_CHECKER_H_
diff --git a/fuchsia_web/webengine/web_engine_debug_integration_test.cc b/fuchsia_web/webengine/web_engine_debug_integration_test.cc
index 48f0c373..ef7eeeb 100644
--- a/fuchsia_web/webengine/web_engine_debug_integration_test.cc
+++ b/fuchsia_web/webengine/web_engine_debug_integration_test.cc
@@ -13,11 +13,11 @@
 #include "base/fuchsia/file_utils.h"
 #include "base/test/task_environment.h"
 #include "base/test/test_future.h"
-#include "fuchsia/base/test/context_provider_test_connector.h"
-#include "fuchsia/base/test/fit_adapter.h"
-#include "fuchsia/base/test/frame_test_util.h"
-#include "fuchsia/base/test/test_devtools_list_fetcher.h"
-#include "fuchsia/base/test/test_navigation_listener.h"
+#include "fuchsia_web/common/test/fit_adapter.h"
+#include "fuchsia_web/common/test/frame_test_util.h"
+#include "fuchsia_web/common/test/test_devtools_list_fetcher.h"
+#include "fuchsia_web/common/test/test_navigation_listener.h"
+#include "fuchsia_web/webengine/test/context_provider_test_connector.h"
 #include "fuchsia_web/webengine/test_debug_listener.h"
 #include "net/test/embedded_test_server/embedded_test_server.h"
 #include "testing/gtest/include/gtest/gtest.h"
diff --git a/fuchsia_web/webengine/web_engine_integration_logging_test.cc b/fuchsia_web/webengine/web_engine_integration_logging_test.cc
index 5700760..de76d741 100644
--- a/fuchsia_web/webengine/web_engine_integration_logging_test.cc
+++ b/fuchsia_web/webengine/web_engine_integration_logging_test.cc
@@ -13,8 +13,8 @@
 #include "base/strings/string_piece.h"
 #include "base/strings/stringprintf.h"
 #include "base/test/bind.h"
-#include "fuchsia/base/test/context_provider_test_connector.h"
-#include "fuchsia/base/test/frame_test_util.h"
+#include "fuchsia_web/common/test/frame_test_util.h"
+#include "fuchsia_web/webengine/test/context_provider_test_connector.h"
 #include "fuchsia_web/webengine/web_engine_integration_test_base.h"
 
 namespace {
diff --git a/fuchsia_web/webengine/web_engine_integration_test.cc b/fuchsia_web/webengine/web_engine_integration_test.cc
index da76f72..fd6ee50 100644
--- a/fuchsia_web/webengine/web_engine_integration_test.cc
+++ b/fuchsia_web/webengine/web_engine_integration_test.cc
@@ -17,9 +17,9 @@
 #include "base/test/test_future.h"
 #include "build/build_config.h"
 #include "components/version_info/version_info.h"
-#include "fuchsia/base/test/fit_adapter.h"
-#include "fuchsia/base/test/frame_test_util.h"
-#include "fuchsia/base/test/test_devtools_list_fetcher.h"
+#include "fuchsia_web/common/test/fit_adapter.h"
+#include "fuchsia_web/common/test/frame_test_util.h"
+#include "fuchsia_web/common/test/test_devtools_list_fetcher.h"
 #include "fuchsia_web/webengine/web_engine_integration_test_base.h"
 #include "media/base/media_switches.h"
 #include "media/fuchsia/audio/fake_audio_consumer.h"
diff --git a/fuchsia_web/webengine/web_engine_integration_test_base.cc b/fuchsia_web/webengine/web_engine_integration_test_base.cc
index 2e28417f..d4d053b8 100644
--- a/fuchsia_web/webengine/web_engine_integration_test_base.cc
+++ b/fuchsia_web/webengine/web_engine_integration_test_base.cc
@@ -13,8 +13,8 @@
 #include "base/fuchsia/process_context.h"
 #include "base/path_service.h"
 #include "base/strings/string_piece.h"
-#include "fuchsia/base/test/context_provider_test_connector.h"
-#include "fuchsia/base/test/frame_test_util.h"
+#include "fuchsia_web/common/test/frame_test_util.h"
+#include "fuchsia_web/webengine/test/context_provider_test_connector.h"
 #include "net/test/embedded_test_server/default_handlers.h"
 
 namespace {
diff --git a/fuchsia_web/webengine/web_engine_integration_test_base.h b/fuchsia_web/webengine/web_engine_integration_test_base.h
index 6647631d..1e7a4ae 100644
--- a/fuchsia_web/webengine/web_engine_integration_test_base.h
+++ b/fuchsia_web/webengine/web_engine_integration_test_base.h
@@ -16,7 +16,7 @@
 #include "base/fuchsia/filtered_service_directory.h"
 #include "base/strings/string_piece_forward.h"
 #include "base/test/task_environment.h"
-#include "fuchsia/base/test/test_navigation_listener.h"
+#include "fuchsia_web/common/test/test_navigation_listener.h"
 #include "net/test/embedded_test_server/embedded_test_server.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
diff --git a/fuchsia_web/webengine/web_instance_host_integration_test.cc b/fuchsia_web/webengine/web_instance_host_integration_test.cc
index 17a7464..fd4284e 100644
--- a/fuchsia_web/webengine/web_instance_host_integration_test.cc
+++ b/fuchsia_web/webengine/web_instance_host_integration_test.cc
@@ -14,10 +14,10 @@
 #include "base/strings/string_piece_forward.h"
 #include "base/test/task_environment.h"
 #include "base/test/test_future.h"
-#include "fuchsia/base/test/fit_adapter.h"
-#include "fuchsia/base/test/frame_test_util.h"
-#include "fuchsia/base/test/test_devtools_list_fetcher.h"
-#include "fuchsia/base/test/test_navigation_listener.h"
+#include "fuchsia_web/common/test/fit_adapter.h"
+#include "fuchsia_web/common/test/frame_test_util.h"
+#include "fuchsia_web/common/test/test_devtools_list_fetcher.h"
+#include "fuchsia_web/common/test/test_navigation_listener.h"
 #include "fuchsia_web/webengine/test/frame_for_test.h"
 #include "fuchsia_web/webinstance_host/web_instance_host.h"
 #include "net/test/embedded_test_server/default_handlers.h"
diff --git a/gpu/config/gpu_switches.cc b/gpu/config/gpu_switches.cc
index 17d91d4..0324a7c 100644
--- a/gpu/config/gpu_switches.cc
+++ b/gpu/config/gpu_switches.cc
@@ -43,6 +43,11 @@
 
 const char kEnableUnsafeWebGPU[] = "enable-unsafe-webgpu";
 
+// Enables WebGPU developer features which are not generally exposed to the web
+// platform.
+const char kEnableWebGPUDeveloperFeatures[] =
+    "enable-webgpu-developer-features";
+
 // Enable validation layers in Dawn backends.
 const char kEnableDawnBackendValidation[] = "enable-dawn-backend-validation";
 
diff --git a/gpu/config/gpu_switches.h b/gpu/config/gpu_switches.h
index f4d43cd..0dc1a04a 100644
--- a/gpu/config/gpu_switches.h
+++ b/gpu/config/gpu_switches.h
@@ -19,6 +19,7 @@
 GPU_EXPORT extern const char kShaderDiskCacheSizeKB[];
 GPU_EXPORT extern const char kDisableGpuProcessForDX12InfoCollection[];
 GPU_EXPORT extern const char kEnableUnsafeWebGPU[];
+GPU_EXPORT extern const char kEnableWebGPUDeveloperFeatures[];
 GPU_EXPORT extern const char kEnableDawnBackendValidation[];
 GPU_EXPORT extern const char kUseWebGPUAdapter[];
 GPU_EXPORT extern const char kEnableDawnFeatures[];
diff --git a/infra/config/generated/luci/commit-queue.cfg b/infra/config/generated/luci/commit-queue.cfg
index 7d07b3cd..41e7486d 100644
--- a/infra/config/generated/luci/commit-queue.cfg
+++ b/infra/config/generated/luci/commit-queue.cfg
@@ -1947,6 +1947,7 @@
       }
       builders {
         name: "chromium/try/win_chromium_compile_dbg_ng"
+        cancel_stale: NO
         location_regexp: ".*"
         location_regexp_exclude: ".+/[+]/docs/.+"
         location_regexp_exclude: ".+/[+]/infra/config/.+"
diff --git a/infra/config/subprojects/chromium/try/tryserver.chromium.win.star b/infra/config/subprojects/chromium/try/tryserver.chromium.win.star
index af47eca..7ff5d7a3 100644
--- a/infra/config/subprojects/chromium/try/tryserver.chromium.win.star
+++ b/infra/config/subprojects/chromium/try/tryserver.chromium.win.star
@@ -82,7 +82,11 @@
     ),
     goma_jobs = goma.jobs.J150,
     main_list_view = "try",
-    tryjob = try_.job(),
+    tryjob = try_.job(
+        # TODO(crbug.com/1335555) Remove once cancelling doesn't wipe
+        # out builder cache
+        cancel_stale = False,
+    ),
     builderless = False,
     cores = 16,
     ssd = True,
diff --git a/ios/chrome/app/resources/BUILD.gn b/ios/chrome/app/resources/BUILD.gn
index 53d876c..458b3ce 100644
--- a/ios/chrome/app/resources/BUILD.gn
+++ b/ios/chrome/app/resources/BUILD.gn
@@ -29,6 +29,8 @@
     "grit/ios_resources.h",
     "ios_resources.pak",
   ]
+
+  deps = [ "//components/ukm/debug:build_ts" ]
 }
 
 group("packed_resources") {
diff --git a/ios/chrome/app/resources/ios_resources.grd b/ios/chrome/app/resources/ios_resources.grd
index 3800f8ad..91624f44 100644
--- a/ios/chrome/app/resources/ios_resources.grd
+++ b/ios/chrome/app/resources/ios_resources.grd
@@ -13,7 +13,7 @@
       <include name="IDR_IOS_OMAHA_HTML" file="omaha/omaha.html" flattenhtml="true" allowexternalscript="true" type="BINDATA" />
       <include name="IDR_IOS_OMAHA_JS" file="omaha/omaha.js" type="BINDATA" />
       <include name="IDR_IOS_UKM_INTERNALS_HTML" file="../../../../components/ukm/debug/ukm_internals.html" flattenhtml="true" allowexternalscript="true" type="BINDATA" />
-      <include name="IDR_IOS_UKM_INTERNALS_JS" file="../../../../components/ukm/debug/ukm_internals.js" type="BINDATA" />
+      <include name="IDR_IOS_UKM_INTERNALS_JS" file="${root_gen_dir}/components/ukm/debug/tsc/ukm_internals.js" use_base_dir="false" type="BINDATA" />
       <include name="IDR_IOS_TRANSLATE_INTERNALS_CSS" file="../../../../components/translate/translate_internals/translate_internals.css" type="BINDATA" />
       <include name="IDR_IOS_TRANSLATE_INTERNALS_HTML" file="../../../../components/translate/translate_internals/translate_internals.html" type="BINDATA" />
       <include name="IDR_IOS_TRANSLATE_INTERNALS_JS" file="../../../../components/translate/translate_internals/translate_internals.js" preprocess="true" type="BINDATA" />
diff --git a/ios/chrome/browser/sync/sync_service_factory_unittest.cc b/ios/chrome/browser/sync/sync_service_factory_unittest.cc
index 644a9ec..55dd50c 100644
--- a/ios/chrome/browser/sync/sync_service_factory_unittest.cc
+++ b/ios/chrome/browser/sync/sync_service_factory_unittest.cc
@@ -53,7 +53,7 @@
  protected:
   // Returns the collection of default datatypes.
   syncer::ModelTypeSet DefaultDatatypes() {
-    static_assert(39 == syncer::GetNumModelTypes(),
+    static_assert(40 == syncer::GetNumModelTypes(),
                   "When adding a new type, you probably want to add it here as "
                   "well (assuming it is already enabled).");
 
diff --git a/ios/chrome/browser/ui/default_promo/default_browser_promo_non_modal_scheduler.mm b/ios/chrome/browser/ui/default_promo/default_browser_promo_non_modal_scheduler.mm
index eba2d79..d6a6fbd3 100644
--- a/ios/chrome/browser/ui/default_promo/default_browser_promo_non_modal_scheduler.mm
+++ b/ios/chrome/browser/ui/default_promo/default_browser_promo_non_modal_scheduler.mm
@@ -37,7 +37,7 @@
 const int64_t kShowPromoPostShareWaitTime = 1;
 
 // Number of times to show the promo to a user.
-const int kPromoShownTimesLimit = 2;
+const int kPromoShownTimesLimit = 3;
 
 // Timeout before the promo is dismissed.
 const double kPromoTimeout = 45;
diff --git a/ios/google_internal/frameworks/chrome_internal_dynamic_framework.ios.zip.sha1 b/ios/google_internal/frameworks/chrome_internal_dynamic_framework.ios.zip.sha1
index 1ce55b2..a97a23a 100644
--- a/ios/google_internal/frameworks/chrome_internal_dynamic_framework.ios.zip.sha1
+++ b/ios/google_internal/frameworks/chrome_internal_dynamic_framework.ios.zip.sha1
@@ -1 +1 @@
-7776f31abf78d11b7350d5b31744300cfba5a2ab
\ No newline at end of file
+29d83827f1ce62ffa1d2b1338facee4e37e4326f
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_internal_dynamic_framework.iossimulator.zip.sha1 b/ios/google_internal/frameworks/chrome_internal_dynamic_framework.iossimulator.zip.sha1
index 7bdb288d..a2f8bf6 100644
--- a/ios/google_internal/frameworks/chrome_internal_dynamic_framework.iossimulator.zip.sha1
+++ b/ios/google_internal/frameworks/chrome_internal_dynamic_framework.iossimulator.zip.sha1
@@ -1 +1 @@
-98188e2515d1e375167883bd88154e4e6437703a
\ No newline at end of file
+d6d80d986cd86dc79a8d9eca8e8ed164170898d5
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.ios.zip.sha1 b/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.ios.zip.sha1
index 9570e9b4..1110df9 100644
--- a/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.ios.zip.sha1
+++ b/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.ios.zip.sha1
@@ -1 +1 @@
-443a1b06914c5b658a657fca986dc598aea86e87
\ No newline at end of file
+3feb59b8cf1dfa10c8d41b76fae45f4bca0c4c64
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.iossimulator.zip.sha1 b/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.iossimulator.zip.sha1
index 9816eed..dd069644 100644
--- a/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.iossimulator.zip.sha1
+++ b/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.iossimulator.zip.sha1
@@ -1 +1 @@
-3f86dac1459635954bd1e4d2f20af844e66e6dcc
\ No newline at end of file
+26c43d93baa3b6a949bae43556aa87765dc235b7
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.ios.zip.sha1 b/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.ios.zip.sha1
index e1c924a..17e5def 100644
--- a/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.ios.zip.sha1
+++ b/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.ios.zip.sha1
@@ -1 +1 @@
-6ecf51009361a4779fe8bc09f0ff11e6acfa8ecc
\ No newline at end of file
+253a1275e663c8bb1eb4e2c83e909782556fbdad
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.iossimulator.zip.sha1 b/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.iossimulator.zip.sha1
index eaf9926..81f5f3b 100644
--- a/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.iossimulator.zip.sha1
+++ b/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.iossimulator.zip.sha1
@@ -1 +1 @@
-df9d5e658b048ae09fb1e9794b8c9982763d1399
\ No newline at end of file
+cbd07fe889552cf5d33d0e0926802561b325a13f
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/remoting_internal_dynamic_framework.ios.zip.sha1 b/ios/google_internal/frameworks/remoting_internal_dynamic_framework.ios.zip.sha1
index e7424e3..bc4ba87 100644
--- a/ios/google_internal/frameworks/remoting_internal_dynamic_framework.ios.zip.sha1
+++ b/ios/google_internal/frameworks/remoting_internal_dynamic_framework.ios.zip.sha1
@@ -1 +1 @@
-e6eb9295cf0298158b80aaa2b35419cdf9f2805e
\ No newline at end of file
+310b21e037a9da1bf152f9dc481a017f42ce71ea
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/remoting_internal_dynamic_framework.iossimulator.zip.sha1 b/ios/google_internal/frameworks/remoting_internal_dynamic_framework.iossimulator.zip.sha1
index 18b241d..251b980 100644
--- a/ios/google_internal/frameworks/remoting_internal_dynamic_framework.iossimulator.zip.sha1
+++ b/ios/google_internal/frameworks/remoting_internal_dynamic_framework.iossimulator.zip.sha1
@@ -1 +1 @@
-2ae51309119eb83d7eec7a086dc4fc5eda8588f4
\ No newline at end of file
+2f0ad7b0a73049a41ead37659ae01f374a148817
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.ios.zip.sha1 b/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.ios.zip.sha1
index d13d47f..ae88bba 100644
--- a/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.ios.zip.sha1
+++ b/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.ios.zip.sha1
@@ -1 +1 @@
-05c64536bde680a3cec23c08416479c38e99216b
\ No newline at end of file
+b34f34a98411d2d6bcf38dd9c65407b44c764159
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.iossimulator.zip.sha1 b/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.iossimulator.zip.sha1
index 3fe28fb..6ff9541 100644
--- a/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.iossimulator.zip.sha1
+++ b/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.iossimulator.zip.sha1
@@ -1 +1 @@
-92ebcb591c43b55551d76bc7d88631c8798d7f25
\ No newline at end of file
+48f4b69a2b73fca5745be58a1a311414ee6829cc
\ No newline at end of file
diff --git a/ios/web_view/internal/app/application_context.h b/ios/web_view/internal/app/application_context.h
index ad319e0..379a33f 100644
--- a/ios/web_view/internal/app/application_context.h
+++ b/ios/web_view/internal/app/application_context.h
@@ -85,6 +85,9 @@
   // Gets the SafeBrowsingService.
   SafeBrowsingService* GetSafeBrowsingService();
 
+  // Shuts down SafeBrowsingService if it was created.
+  void ShutdownSafeBrowsingServiceIfNecessary();
+
  private:
   friend class base::NoDestructor<ApplicationContext>;
 
diff --git a/ios/web_view/internal/app/application_context.mm b/ios/web_view/internal/app/application_context.mm
index 6cea5c1628..58120b3 100644
--- a/ios/web_view/internal/app/application_context.mm
+++ b/ios/web_view/internal/app/application_context.mm
@@ -232,4 +232,11 @@
   return safe_browsing_service_.get();
 }
 
+void ApplicationContext::ShutdownSafeBrowsingServiceIfNecessary() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  if (safe_browsing_service_) {
+    safe_browsing_service_->ShutDown();
+  }
+}
+
 }  // namespace ios_web_view
diff --git a/ios/web_view/internal/web_view_web_main_parts.mm b/ios/web_view/internal/web_view_web_main_parts.mm
index 534632d7..6a61190 100644
--- a/ios/web_view/internal/web_view_web_main_parts.mm
+++ b/ios/web_view/internal/web_view_web_main_parts.mm
@@ -99,6 +99,8 @@
 }
 
 void WebViewWebMainParts::PostMainMessageLoopRun() {
+  ApplicationContext::GetInstance()->ShutdownSafeBrowsingServiceIfNecessary();
+
   // CWVWebViewConfiguration must destroy its WebViewBrowserStates before the
   // threads are stopped by ApplicationContext.
   [CWVWebViewConfiguration shutDown];
diff --git a/media/base/video_frame_metadata.h b/media/base/video_frame_metadata.h
index 26ea562..de5b134 100644
--- a/media/base/video_frame_metadata.h
+++ b/media/base/video_frame_metadata.h
@@ -63,6 +63,10 @@
   // If cropping was applied due to Region Capture to produce this frame,
   // then this reflects where the frame's contents originate from in the
   // original uncropped frame.
+  //
+  // NOTE: May also be nullopt if region capture is enabled but the capture rect
+  // is in a different coordinate space. For more info, see
+  // https://crbug.com/1327560.
   absl::optional<gfx::Rect> region_capture_rect;
 
   // Whenever cropTo() is called, Blink increments the crop_version and records
diff --git a/native_client_sdk/PRESUBMIT.py b/native_client_sdk/PRESUBMIT.py
index 8788955..4c7cea13 100644
--- a/native_client_sdk/PRESUBMIT.py
+++ b/native_client_sdk/PRESUBMIT.py
@@ -30,7 +30,8 @@
   canned = input_api.canned_checks
   output.extend(canned.RunPylint(input_api, output_api,
                                  files_to_skip=files_to_skip,
-                                 disabled_warnings=disabled_warnings))
+                                 disabled_warnings=disabled_warnings,
+                                 version='1.5'))
   return output
 
 
diff --git a/net/cert/internal/trust_store_win.cc b/net/cert/internal/trust_store_win.cc
index c2d5ac0..b1649620 100644
--- a/net/cert/internal/trust_store_win.cc
+++ b/net/cert/internal/trust_store_win.cc
@@ -154,6 +154,18 @@
                                    CERT_SYSTEM_STORE_CURRENT_USER_GROUP_POLICY,
                                    L"Disallowed");
 
+  // Auto-sync all of the cert stores to get updates to the cert store.
+  // Auto-syncing on all_certs_store seems to work to resync the nested stores,
+  // although the docs at
+  // https://docs.microsoft.com/en-us/windows/win32/api/wincrypt/nf-wincrypt-certcontrolstore
+  // are somewhat unclear. If and when root store changes are linked to clearing
+  // various caches, this should be replaced with CERT_STORE_CTRL_NOTIFY_CHANGE
+  // and CERT_STORE_CTRL_RESYNC.
+  if (!CertControlStore(all_certs_store.get(), 0, CERT_STORE_CTRL_AUTO_RESYNC,
+                        0)) {
+    PLOG(ERROR) << "Error enabling CERT_STORE_CTRL_AUTO_RESYNC";
+  }
+
   return base::WrapUnique(new TrustStoreWin(
       std::move(root_cert_store), std::move(intermediate_cert_store),
       std::move(disallowed_cert_store), std::move(all_certs_store)));
diff --git a/net/der/parse_values.cc b/net/der/parse_values.cc
index aac0b25..caab98e 100644
--- a/net/der/parse_values.cc
+++ b/net/der/parse_values.cc
@@ -450,7 +450,7 @@
     memcpy(in_32bit.data(), in.UnsafeData(), in.Length());
   for (const uint32_t c : in_32bit) {
     // UniversalString is UCS-4 in big-endian order.
-    uint32_t codepoint = base::NetToHost32(c);
+    auto codepoint = static_cast<base_icu::UChar32>(base::NetToHost32(c));
     if (!CBU_IS_UNICODE_CHAR(codepoint))
       return false;
 
@@ -469,7 +469,7 @@
     memcpy(in_16bit.data(), in.UnsafeData(), in.Length());
   for (const uint16_t c : in_16bit) {
     // BMPString is UCS-2 in big-endian order.
-    uint32_t codepoint = base::NetToHost16(c);
+    base_icu::UChar32 codepoint = base::NetToHost16(c);
 
     // BMPString only supports codepoints in the Basic Multilingual Plane;
     // surrogates are not allowed. CBU_IS_UNICODE_CHAR excludes the surrogate
diff --git a/remoting/host/input_injector_x11.cc b/remoting/host/input_injector_x11.cc
index a895fb9..b10a860 100644
--- a/remoting/host/input_injector_x11.cc
+++ b/remoting/host/input_injector_x11.cc
@@ -352,7 +352,7 @@
   pressed_keys_.clear();
 
   const std::string text = event.text();
-  for (int32_t index = 0; index < static_cast<int32_t>(text.size()); ++index) {
+  for (size_t index = 0; index < text.size(); ++index) {
     base_icu::UChar32 code_point;
     if (!base::ReadUnicodeCharacter(text.c_str(), text.size(), &index,
                                     &code_point)) {
diff --git a/remoting/protocol/webrtc_video_encoder_wrapper.cc b/remoting/protocol/webrtc_video_encoder_wrapper.cc
index 6fed61c..c50432b5 100644
--- a/remoting/protocol/webrtc_video_encoder_wrapper.cc
+++ b/remoting/protocol/webrtc_video_encoder_wrapper.cc
@@ -302,14 +302,8 @@
 
   encode_pending_ = true;
 
-  // Just in case the encoder runs the callback on an arbitrary thread,
-  // BindPostTask() is used here to trampoline onto the correct thread.
-  // This object is bound via a WeakPtr which must only be dereferenced
-  // on the current thread.
-  auto encode_callback = base::BindPostTask(
-      base::SequencedTaskRunnerHandle::Get(),
-      base::BindOnce(&WebrtcVideoEncoderWrapper::OnFrameEncoded,
-                     weak_factory_.GetWeakPtr()));
+  auto encode_callback = base::BindOnce(
+      &WebrtcVideoEncoderWrapper::OnFrameEncoded, weak_factory_.GetWeakPtr());
   encoder_->Encode(std::move(desktop_frame), frame_params,
                    std::move(encode_callback));
   return WEBRTC_VIDEO_CODEC_OK;
diff --git a/remoting/protocol/webrtc_video_encoder_wrapper_unittest.cc b/remoting/protocol/webrtc_video_encoder_wrapper_unittest.cc
index cd1cd4d..8f269e8 100644
--- a/remoting/protocol/webrtc_video_encoder_wrapper_unittest.cc
+++ b/remoting/protocol/webrtc_video_encoder_wrapper_unittest.cc
@@ -165,9 +165,8 @@
 class WebrtcVideoEncoderWrapperTest : public testing::Test {
  public:
   void SetUp() override {
-    mock_video_encoder_ = std::make_unique<NiceMock<MockVideoEncoder>>();
-
     // Configure the mock encoder's default behavior to mimic a real encoder.
+    mock_video_encoder_ = std::make_unique<NiceMock<MockVideoEncoder>>();
     ON_CALL(*mock_video_encoder_, Encode)
         .WillByDefault([](std::unique_ptr<webrtc::DesktopFrame> frame,
                           const WebrtcVideoEncoder::FrameParams& param,
@@ -177,6 +176,27 @@
           std::move(done).Run(WebrtcVideoEncoder::EncodeResult::SUCCEEDED,
                               std::move(encoded_frame));
         });
+
+    // Configure this mock encoder's behavior to mimic a real async encoder.
+    mock_video_encoder_async_ = std::make_unique<NiceMock<MockVideoEncoder>>();
+    ON_CALL(*mock_video_encoder_async_, Encode)
+        .WillByDefault([this](std::unique_ptr<webrtc::DesktopFrame> frame,
+                              const WebrtcVideoEncoder::FrameParams& param,
+                              WebrtcVideoEncoder::EncodeCallback done) {
+          auto encoded_frame =
+              std::make_unique<WebrtcVideoEncoder::EncodedFrame>();
+          encoded_frame->size = frame->size();
+          encoded_frame->data.assign(
+              frame->size().width() * frame->size().height(), 'a');
+          encoded_frame->key_frame = param.key_frame;
+          encoded_frame->quantizer = param.vpx_min_quantizer;
+          encoded_frame->codec = kVideoCodecVP9;
+          task_environment_.GetMainThreadTaskRunner()->PostTask(
+              FROM_HERE,
+              base::BindOnce(std::move(done),
+                             WebrtcVideoEncoder::EncodeResult::SUCCEEDED,
+                             std::move(encoded_frame)));
+        });
   }
 
   std::unique_ptr<WebrtcVideoEncoderWrapper> InitEncoder(SdpVideoFormat sdp,
@@ -205,6 +225,7 @@
   NiceMock<MockVideoChannelStateObserver> observer_;
   MockEncodedImageCallback callback_;
   std::unique_ptr<NiceMock<MockVideoEncoder>> mock_video_encoder_;
+  std::unique_ptr<NiceMock<MockVideoEncoder>> mock_video_encoder_async_;
 };
 
 TEST_F(WebrtcVideoEncoderWrapperTest, ReturnsVP8EncodedFrames) {
@@ -257,7 +278,7 @@
   PostQuitAndRun();
 }
 
-TEST_F(WebrtcVideoEncoderWrapperTest, FrameDroppedIfEncoderBusy) {
+TEST_F(WebrtcVideoEncoderWrapperTest, FrameDroppedIfAsyncEncoderBusy) {
   EXPECT_CALL(callback_, OnEncodedImage(_, Field(&CodecSpecificInfo::codecType,
                                                  kVideoCodecVP9)))
       .WillOnce(Return(kResultOk));
@@ -265,6 +286,7 @@
   auto frame1 = MakeVideoFrame();
   auto frame2 = MakeVideoFrame();
   auto encoder = InitEncoder(GetVp9Format(), GetVp9Codec());
+  encoder->SetEncoderForTest(std::move(mock_video_encoder_async_));
   std::vector<VideoFrameType> frame_types{VideoFrameType::kVideoFrameKey};
   encoder->Encode(frame1, &frame_types);
   encoder->Encode(frame2, &frame_types);
@@ -278,13 +300,21 @@
     InSequence s;
 
     // Encode frame1.
-    EXPECT_CALL(*mock_video_encoder_, Encode);
+    EXPECT_CALL(*mock_video_encoder_async_, Encode);
+    EXPECT_CALL(
+        callback_,
+        OnEncodedImage(_, Field(&CodecSpecificInfo::codecType, kVideoCodecVP9)))
+        .WillOnce(Return(kResultOk));
 
     // Encode frame3. Its update-region should be the rectangle-union of frame2
     // and frame3.
     auto combined_rect = DesktopRect::MakeLTRB(100, 200, 310, 410);
-    EXPECT_CALL(*mock_video_encoder_,
+    EXPECT_CALL(*mock_video_encoder_async_,
                 Encode(Pointee(MatchesUpdateRect(combined_rect)), _, _));
+    EXPECT_CALL(
+        callback_,
+        OnEncodedImage(_, Field(&CodecSpecificInfo::codecType, kVideoCodecVP9)))
+        .WillOnce(Return(kResultOk));
   }
 
   auto frame1 = MakeVideoFrame();
@@ -296,7 +326,7 @@
       .offset_x = 300, .offset_y = 400, .width = 10, .height = 10});
 
   auto encoder = InitEncoder(GetVp9Format(), GetVp9Codec());
-  encoder->SetEncoderForTest(std::move(mock_video_encoder_));
+  encoder->SetEncoderForTest(std::move(mock_video_encoder_async_));
   std::vector<VideoFrameType> frame_types{VideoFrameType::kVideoFrameKey};
 
   // frame2 should be dropped since the encoder is busy.
@@ -365,7 +395,8 @@
   PostQuitAndRun();
 }
 
-TEST_F(WebrtcVideoEncoderWrapperTest, KeyFrameRequestRememberedIfEncoderBusy) {
+TEST_F(WebrtcVideoEncoderWrapperTest,
+       KeyFrameRequestRememberedIfAsyncEncoderBusy) {
   // Three frames are used for this test:
   // Frame 1 kicks off the encoder.
   // Frame 2 is a key-frame request, which is dropped because frame 1 is still
@@ -376,10 +407,10 @@
   //
   // The end-result is that the encoder should see two key-frame requests (for
   // frames 1 and 3).
-  // Note that |mock_video_encoder_| does not produce any video frames. This
-  // means that no encoded frames are returned to WebRTC and so OnEncodedImage()
-  // is not EXPECT_CALL()ed by this test.
-  EXPECT_CALL(*mock_video_encoder_, Encode(_, IsKeyFrame(), _)).Times(2);
+  EXPECT_CALL(*mock_video_encoder_async_, Encode(_, IsKeyFrame(), _)).Times(2);
+  EXPECT_CALL(callback_, OnEncodedImage(_, _))
+      .Times(2)
+      .WillRepeatedly(Return(kResultOk));
 
   auto frame1 = MakeVideoFrame();
   auto frame2 = MakeVideoFrame();
@@ -388,7 +419,7 @@
   std::vector<VideoFrameType> frame_types2{VideoFrameType::kVideoFrameKey};
   std::vector<VideoFrameType> frame_types3{VideoFrameType::kVideoFrameDelta};
   auto encoder = InitEncoder(GetVp9Format(), GetVp9Codec());
-  encoder->SetEncoderForTest(std::move(mock_video_encoder_));
+  encoder->SetEncoderForTest(std::move(mock_video_encoder_async_));
 
   encoder->Encode(frame1, &frame_types1);
   encoder->Encode(frame2, &frame_types2);
diff --git a/services/network/cookie_settings.cc b/services/network/cookie_settings.cc
index 37c88296..26348b5e 100644
--- a/services/network/cookie_settings.cc
+++ b/services/network/cookie_settings.cc
@@ -187,6 +187,51 @@
   }
 }
 
+bool CookieSettings::BlockDueToThirdPartyCookieBlockingSetting(
+    bool is_third_party_request,
+    const GURL& url,
+    const GURL& first_party_url,
+    ContentSetting cookie_setting) const {
+  if (block_third_party_cookies_ && is_third_party_request &&
+      !base::Contains(third_party_cookies_allowed_schemes_,
+                      first_party_url.scheme())) {
+    // See if a Storage Access permission grant can unblock.
+    if (const ContentSettingPatternSource* match =
+            FindMatchingSetting(url, first_party_url, storage_access_grants_);
+        match && match->GetContentSetting() == CONTENT_SETTING_ALLOW) {
+      FireStorageAccessHistogram(net::cookie_util::StorageAccessResult::
+                                     ACCESS_ALLOWED_STORAGE_ACCESS_GRANT);
+      return false;
+    }
+
+    FireStorageAccessHistogram(
+        net::cookie_util::StorageAccessResult::ACCESS_BLOCKED);
+    return true;
+  }
+
+  // Cookies aren't blocked solely due to the third-party-cookie blocking
+  // setting, but they still may be blocked due to a global default. So we
+  // have to check what the setting is here.
+  FireStorageAccessHistogram(
+      cookie_setting == CONTENT_SETTING_BLOCK
+          ? net::cookie_util::StorageAccessResult::ACCESS_BLOCKED
+          : net::cookie_util::StorageAccessResult::ACCESS_ALLOWED);
+
+  return false;
+}
+
+CookieSettings::ThirdPartyBlockingOutcome
+CookieSettings::GetThirdPartyBlockingScope(const GURL& first_party_url) const {
+  // If cookies are allowed for the first-party URL then we allow
+  // partitioned cross-site cookies.
+  if (const ContentSettingPatternSource* match = FindMatchingSetting(
+          first_party_url, first_party_url, content_settings_);
+      !match || match->GetContentSetting() == CONTENT_SETTING_ALLOW) {
+    return ThirdPartyBlockingOutcome::kPartitionedStateAllowed;
+  }
+  return ThirdPartyBlockingOutcome::kAllStateDisallowed;
+}
+
 CookieSettings::CookieSettingWithMetadata
 CookieSettings::GetCookieSettingWithMetadata(
     const GURL& url,
@@ -204,66 +249,27 @@
   ContentSetting cookie_setting = CONTENT_SETTING_ALLOW;
   ThirdPartyBlockingOutcome third_party_blocking_outcome =
       ThirdPartyBlockingOutcome::kIrrelevant;
-  if (block_third_party_cookies_ && is_third_party_request &&
-      !base::Contains(third_party_cookies_allowed_schemes_,
-                      first_party_url.scheme())) {
-    // We'll set `cookie_setting` to `CONTENT_SETTING_BLOCK`
-    // below iff there's no Storage Access permission grant
-    // that can allow access.
-    third_party_blocking_outcome =
-        ThirdPartyBlockingOutcome::kAllStateDisallowed;
-  }
 
+  bool found_explicit_setting = false;
   if (const ContentSettingPatternSource* match =
           FindMatchingSetting(url, first_party_url, content_settings_);
       match) {
     cookie_setting = match->GetContentSetting();
-    if (cookie_setting == CONTENT_SETTING_BLOCK || IsExplicitSetting(*match)) {
-      third_party_blocking_outcome = ThirdPartyBlockingOutcome::kIrrelevant;
-    }
+    found_explicit_setting = IsExplicitSetting(*match);
   }
 
-  switch (third_party_blocking_outcome) {
-    case ThirdPartyBlockingOutcome::kAllStateDisallowed:
-      if (const ContentSettingPatternSource* match =
-              FindMatchingSetting(url, first_party_url, storage_access_grants_);
-          match && match->GetContentSetting() == CONTENT_SETTING_ALLOW) {
-        third_party_blocking_outcome = ThirdPartyBlockingOutcome::kIrrelevant;
-        FireStorageAccessHistogram(net::cookie_util::StorageAccessResult::
-                                       ACCESS_ALLOWED_STORAGE_ACCESS_GRANT);
-      } else {
-        cookie_setting = CONTENT_SETTING_BLOCK;
-        FireStorageAccessHistogram(
-            net::cookie_util::StorageAccessResult::ACCESS_BLOCKED);
-      }
-      break;
-    case ThirdPartyBlockingOutcome::kIrrelevant:
-      // Cookies aren't blocked solely due to the third-party-cookie blocking
-      // setting, but they still may be blocked due to a global default. So we
-      // have to check what the setting is here.
-      FireStorageAccessHistogram(
-          cookie_setting == CONTENT_SETTING_BLOCK
-              ? net::cookie_util::StorageAccessResult::ACCESS_BLOCKED
-              : net::cookie_util::StorageAccessResult::ACCESS_ALLOWED);
-      break;
-    case ThirdPartyBlockingOutcome::kPartitionedStateAllowed:
-      NOTREACHED();
-      break;
-  }
-
-  if (third_party_blocking_outcome ==
-      ThirdPartyBlockingOutcome::kAllStateDisallowed) {
-    // If the third-party cookie blocking setting is enabled and will block
-    // access to unpartitioned cookies, we check if the user has any content
-    // settings for the first-party URL as the primary pattern. If cookies are
-    // allowed for the first-party URL then we allow partitioned cross-site
-    // cookies.
-    if (const ContentSettingPatternSource* match = FindMatchingSetting(
-            first_party_url, first_party_url, content_settings_);
-        !match || match->GetContentSetting() == CONTENT_SETTING_ALLOW) {
+  if (cookie_setting != CONTENT_SETTING_BLOCK && !found_explicit_setting) {
+    if (BlockDueToThirdPartyCookieBlockingSetting(
+            is_third_party_request, url, first_party_url, cookie_setting)) {
+      cookie_setting = CONTENT_SETTING_BLOCK;
       third_party_blocking_outcome =
-          ThirdPartyBlockingOutcome::kPartitionedStateAllowed;
+          GetThirdPartyBlockingScope(first_party_url);
     }
+  } else {
+    FireStorageAccessHistogram(
+        cookie_setting == CONTENT_SETTING_BLOCK
+            ? net::cookie_util::StorageAccessResult::ACCESS_BLOCKED
+            : net::cookie_util::StorageAccessResult::ACCESS_ALLOWED);
   }
 
   return {cookie_setting, third_party_blocking_outcome};
diff --git a/services/network/cookie_settings.h b/services/network/cookie_settings.h
index d23edcf..cefd5dc 100644
--- a/services/network/cookie_settings.h
+++ b/services/network/cookie_settings.h
@@ -177,6 +177,23 @@
     ThirdPartyBlockingOutcome third_party_blocking_outcome;
   };
 
+  // Determines whether cookie access should be blocked due to the
+  // third-party-cookie-blocking setting and any relevant Storage Access
+  // permission grants. Does not distinguish between access to unpartitioned
+  // state and access to partitioned state.
+  bool BlockDueToThirdPartyCookieBlockingSetting(
+      bool is_third_party_request,
+      const GURL& url,
+      const GURL& first_party_url,
+      ContentSetting cookie_setting) const;
+
+  // Determines the scope of third-party-cookie-blocking, i.e. whether it
+  // applies to all cookies or just unpartitioned cookies. Assumes that
+  // BlockDueToThirdpartyCookieBlockingSetting has been called and returned
+  // true.
+  ThirdPartyBlockingOutcome GetThirdPartyBlockingScope(
+      const GURL& first_party_url) const;
+
   // Returns the cookie setting for the given request, along with metadata
   // associated with the lookup. Namely, whether the setting is due to
   // third-party cookie blocking settings or not.
diff --git a/storage/browser/quota/quota_manager_impl.cc b/storage/browser/quota/quota_manager_impl.cc
index 3bc3344..f8e3798 100644
--- a/storage/browser/quota/quota_manager_impl.cc
+++ b/storage/browser/quota/quota_manager_impl.cc
@@ -1256,7 +1256,8 @@
 void QuotaManagerImpl::GetBucketsForStorageKey(
     const StorageKey& storage_key,
     blink::mojom::StorageType type,
-    base::OnceCallback<void(QuotaErrorOr<std::set<BucketInfo>>)> callback) {
+    base::OnceCallback<void(QuotaErrorOr<std::set<BucketInfo>>)> callback,
+    bool delete_expired) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   DCHECK(callback);
   EnsureDatabaseOpened();
@@ -1265,6 +1266,16 @@
     std::move(callback).Run(QuotaError::kDatabaseError);
     return;
   }
+
+  base::OnceCallback<void(QuotaErrorOr<std::set<BucketInfo>>)> reply;
+  if (delete_expired) {
+    reply = base::BindOnce(&QuotaManagerImpl::DidGetBucketsCheckExpiration,
+                           weak_factory_.GetWeakPtr(), std::move(callback));
+  } else {
+    reply = base::BindOnce(&QuotaManagerImpl::DidGetBuckets,
+                           weak_factory_.GetWeakPtr(), std::move(callback));
+  }
+
   PostTaskAndReplyWithResultForDBThread(
       base::BindOnce(
           [](const StorageKey& storage_key, StorageType type,
@@ -1273,8 +1284,7 @@
             return database->GetBucketsForStorageKey(storage_key, type);
           },
           storage_key, type),
-      base::BindOnce(&QuotaManagerImpl::DidGetBuckets,
-                     weak_factory_.GetWeakPtr(), std::move(callback)));
+      std::move(reply));
 }
 
 void QuotaManagerImpl::GetUsageInfo(GetUsageInfoCallback callback) {
@@ -2956,6 +2966,48 @@
   std::move(callback).Run(std::move(result));
 }
 
+void QuotaManagerImpl::DidGetBucketsCheckExpiration(
+    base::OnceCallback<void(QuotaErrorOr<std::set<BucketInfo>>)> callback,
+    QuotaErrorOr<std::set<BucketInfo>> result) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  DCHECK(callback);
+
+  DidDatabaseWork(result.ok() || result.error() != QuotaError::kDatabaseError);
+
+  if (!result.ok()) {
+    std::move(callback).Run(std::move(result));
+    return;
+  }
+
+  std::set<BucketInfo> kept_buckets;
+  std::set<BucketInfo> buckets_to_delete;
+  for (const BucketInfo& bucket : result.value()) {
+    if (!bucket.expiration.is_null() &&
+        bucket.expiration <= QuotaDatabase::GetNow()) {
+      buckets_to_delete.insert(bucket);
+    } else {
+      kept_buckets.insert(bucket);
+    }
+  }
+
+  if (buckets_to_delete.empty()) {
+    std::move(callback).Run(kept_buckets);
+    return;
+  }
+
+  base::RepeatingClosure barrier =
+      base::BarrierClosure(buckets_to_delete.size(),
+                           base::BindOnce(std::move(callback), kept_buckets));
+  for (const BucketInfo& bucket : buckets_to_delete) {
+    StatusCallback on_delete_done =
+        base::BindOnce([](base::RepeatingClosure barrier,
+                          blink::mojom::QuotaStatusCode) { barrier.Run(); },
+                       barrier);
+    DeleteBucketDataInternal(bucket.ToBucketLocator(), AllQuotaClientTypes(),
+                             std::move(on_delete_done));
+  }
+}
+
 void QuotaManagerImpl::DidGetModifiedBetween(
     GetBucketsCallback callback,
     StorageType type,
diff --git a/storage/browser/quota/quota_manager_impl.h b/storage/browser/quota/quota_manager_impl.h
index 0df957b0..19d3bf5 100644
--- a/storage/browser/quota/quota_manager_impl.h
+++ b/storage/browser/quota/quota_manager_impl.h
@@ -245,11 +245,13 @@
       base::OnceCallback<void(QuotaErrorOr<std::set<BucketInfo>>)> callback);
 
   // Retrieves all buckets for `storage_key` and `type` that are in the buckets
-  // table. Used for retrieving storage key usage data in the UsageTracker.
+  // table. When `delete_expired` is true, the expired buckets will be filtered
+  // out of the reply and also deleted from disk.
   void GetBucketsForStorageKey(
       const blink::StorageKey& storage_key,
       blink::mojom::StorageType type,
-      base::OnceCallback<void(QuotaErrorOr<std::set<BucketInfo>>)> callback);
+      base::OnceCallback<void(QuotaErrorOr<std::set<BucketInfo>>)> callback,
+      bool delete_expired = false);
 
   // Called by clients or webapps. Returns usage per host.
   void GetUsageInfo(GetUsageInfoCallback callback);
@@ -715,6 +717,9 @@
   void DidGetBuckets(
       base::OnceCallback<void(QuotaErrorOr<std::set<BucketInfo>>)> callback,
       QuotaErrorOr<std::set<BucketInfo>> result);
+  void DidGetBucketsCheckExpiration(
+      base::OnceCallback<void(QuotaErrorOr<std::set<BucketInfo>>)> callback,
+      QuotaErrorOr<std::set<BucketInfo>> result);
   void DidGetModifiedBetween(GetBucketsCallback callback,
                              blink::mojom::StorageType type,
                              QuotaErrorOr<std::set<BucketLocator>> result);
diff --git a/storage/browser/quota/quota_manager_proxy.cc b/storage/browser/quota/quota_manager_proxy.cc
index b2168a4..099eb2f 100644
--- a/storage/browser/quota/quota_manager_proxy.cc
+++ b/storage/browser/quota/quota_manager_proxy.cc
@@ -252,7 +252,7 @@
                                  std::move(respond));
 }
 
-void QuotaManagerProxy::GetBucketsForStorageKey(
+void QuotaManagerProxy::GetBucketsForStorageKeyDeleteExpired(
     const blink::StorageKey& storage_key,
     blink::mojom::StorageType type,
     scoped_refptr<base::SequencedTaskRunner> callback_task_runner,
@@ -263,8 +263,8 @@
   if (!quota_manager_impl_task_runner_->RunsTasksInCurrentSequence()) {
     quota_manager_impl_task_runner_->PostTask(
         FROM_HERE,
-        base::BindOnce(&QuotaManagerProxy::GetBucketsForStorageKey, this,
-                       storage_key, type, std::move(callback_task_runner),
+        base::BindOnce(&QuotaManagerProxy::GetBucketsForStorageKeyDeleteExpired,
+                       this, storage_key, type, std::move(callback_task_runner),
                        std::move(callback)));
     return;
   }
@@ -278,8 +278,8 @@
     return;
   }
 
-  quota_manager_impl_->GetBucketsForStorageKey(storage_key, type,
-                                               std::move(respond));
+  quota_manager_impl_->GetBucketsForStorageKey(
+      storage_key, type, std::move(respond), /*delete_expired=*/true);
 }
 
 void QuotaManagerProxy::GetBucketById(
diff --git a/storage/browser/quota/quota_manager_proxy.h b/storage/browser/quota/quota_manager_proxy.h
index 6bb225dc..add2907 100644
--- a/storage/browser/quota/quota_manager_proxy.h
+++ b/storage/browser/quota/quota_manager_proxy.h
@@ -146,8 +146,9 @@
       base::OnceCallback<void(QuotaErrorOr<BucketInfo>)> callback);
 
   // Retrieves all buckets for `storage_key` and `type` that are in the buckets
-  // table.
-  virtual void GetBucketsForStorageKey(
+  // table. Expired buckets will be filtered out of the reply and also deleted
+  // from disk.
+  virtual void GetBucketsForStorageKeyDeleteExpired(
       const blink::StorageKey& storage_key,
       blink::mojom::StorageType type,
       scoped_refptr<base::SequencedTaskRunner> callback_task_runner,
diff --git a/storage/browser/quota/quota_manager_unittest.cc b/storage/browser/quota/quota_manager_unittest.cc
index 28ca894..f9579cf 100644
--- a/storage/browser/quota/quota_manager_unittest.cc
+++ b/storage/browser/quota/quota_manager_unittest.cc
@@ -275,10 +275,11 @@
 
   QuotaErrorOr<std::set<BucketInfo>> GetBucketsForStorageKey(
       const StorageKey& storage_key,
-      blink::mojom::StorageType storage_type) {
+      blink::mojom::StorageType storage_type,
+      bool delete_expired = false) {
     base::test::TestFuture<QuotaErrorOr<std::set<BucketInfo>>> future;
-    quota_manager_impl_->GetBucketsForStorageKey(storage_key, storage_type,
-                                                 future.GetCallback());
+    quota_manager_impl_->GetBucketsForStorageKey(
+        storage_key, storage_type, future.GetCallback(), delete_expired);
     return future.Take();
   }
 
@@ -1050,6 +1051,42 @@
   EXPECT_THAT(buckets, testing::Contains(bucket_c));
 }
 
+TEST_F(QuotaManagerImplTest, GetBucketsForStorageKey_Expiration) {
+  StorageKey storage_key = ToStorageKey("http://a.com/");
+
+  auto clock = std::make_unique<base::SimpleTestClock>();
+  QuotaDatabase::SetClockForTesting(clock.get());
+  clock->SetNow(base::Time::Now());
+
+  BucketInitParams params(storage_key, "bucket_1");
+  auto bucket = UpdateOrCreateBucket(params);
+  EXPECT_TRUE(bucket.ok());
+  BucketInfo bucket_1 = bucket.value();
+
+  params.name = "bucket_2";
+  params.expiration = clock->Now() + base::Days(1);
+  bucket = UpdateOrCreateBucket(params);
+  EXPECT_TRUE(bucket.ok());
+  BucketInfo bucket_2 = bucket.value();
+
+  params.name = "bucket_3";
+  bucket = UpdateOrCreateBucket(params);
+  EXPECT_TRUE(bucket.ok());
+  BucketInfo bucket_3 = bucket.value();
+
+  clock->Advance(base::Days(2));
+
+  QuotaErrorOr<std::set<BucketInfo>> result =
+      GetBucketsForStorageKey(storage_key, kTemp, /*delete_expired=*/true);
+  EXPECT_TRUE(result.ok());
+
+  std::set<BucketInfo> buckets = result.value();
+  ASSERT_EQ(1U, buckets.size());
+  EXPECT_EQ(*buckets.begin(), bucket_1);
+
+  QuotaDatabase::SetClockForTesting(nullptr);
+}
+
 TEST_F(QuotaManagerImplTest, GetUsageAndQuota_Simple) {
   static const ClientBucketData kData[] = {
       {"http://foo.com/", "logs", kTemp, 10},
diff --git a/testing/buildbot/chrome.json b/testing/buildbot/chrome.json
index d897b8e..db681e0d 100644
--- a/testing/buildbot/chrome.json
+++ b/testing/buildbot/chrome.json
@@ -1569,6 +1569,33 @@
   "chromeos-octopus-chrome": {
     "additional_compile_targets": [
       "chromiumos_preflight"
+    ],
+    "gtest_tests": [
+      {
+        "args": [
+          "--test-launcher-filter-file=../../testing/buildbot/filters/chromeos.base_unittests.filter"
+        ],
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "device_type": "octopus",
+              "os": "ChromeOS",
+              "pool": "chrome.tests"
+            }
+          ],
+          "service_account": "chrome-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "base_unittests",
+        "test_id_prefix": "ninja://base:base_unittests/",
+        "trigger_script": {
+          "script": "//testing/trigger_scripts/chromeos_device_trigger.py"
+        }
+      }
     ]
   },
   "chromeos-reven-chrome": {
diff --git a/testing/buildbot/chromium.android.json b/testing/buildbot/chromium.android.json
index 5e98dd5..d4836adf 100644
--- a/testing/buildbot/chromium.android.json
+++ b/testing/buildbot/chromium.android.json
@@ -14796,7 +14796,8 @@
               "name": "shard #${SHARD_INDEX} logcats"
             }
           ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
+          "shards": 2
         },
         "test": "webview_trichrome_cts_tests",
         "test_id_prefix": "ninja://android_webview/test:webview_trichrome_cts_tests/",
@@ -19684,7 +19685,8 @@
               "name": "shard #${SHARD_INDEX} logcats"
             }
           ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
+          "shards": 2
         },
         "test": "webview_trichrome_64_cts_tests",
         "test_id_prefix": "ninja://android_webview/test:webview_trichrome_64_cts_tests/",
@@ -50492,7 +50494,8 @@
               "name": "shard #${SHARD_INDEX} logcats"
             }
           ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
+          "shards": 2
         },
         "test": "webview_trichrome_cts_tests",
         "test_id_prefix": "ninja://android_webview/test:webview_trichrome_cts_tests/",
diff --git a/testing/buildbot/test_suite_exceptions.pyl b/testing/buildbot/test_suite_exceptions.pyl
index 6a1b756..badeb40 100644
--- a/testing/buildbot/test_suite_exceptions.pyl
+++ b/testing/buildbot/test_suite_exceptions.pyl
@@ -494,6 +494,15 @@
           'hard_timeout': 1200,
         },
       },
+      'win11-blink-rel': {
+        'swarming': {
+          'hard_timeout': 1200,
+        },
+        'args': [
+          '--target',
+          'Release_x64',
+        ],
+      },
       'win11-blink-rel-dummy': {
         'swarming': {
           'hard_timeout': 1200,
diff --git a/testing/buildbot/tryserver.blink.json b/testing/buildbot/tryserver.blink.json
index c7ce8e8..8ca4d81 100644
--- a/testing/buildbot/tryserver.blink.json
+++ b/testing/buildbot/tryserver.blink.json
@@ -143,5 +143,48 @@
         "test_id_prefix": "ninja://:blink_web_tests/"
       }
     ]
+  },
+  "win11-blink-rel": {
+    "isolated_scripts": [
+      {
+        "args": [
+          "--num-retries=3",
+          "--fuzzy-diff",
+          "--git-revision=${got_revision}",
+          "--target",
+          "Release_x64"
+        ],
+        "check_flakiness_for_new_tests": false,
+        "isolate_name": "blink_web_tests",
+        "merge": {
+          "args": [
+            "--verbose"
+          ],
+          "script": "//third_party/blink/tools/merge_web_test_results.py"
+        },
+        "name": "blink_web_tests",
+        "precommit_args": [
+          "--gerrit-issue=${patch_issue}",
+          "--gerrit-patchset=${patch_set}",
+          "--buildbucket-id=${buildbucket_build_id}"
+        ],
+        "resultdb": {
+          "enable": true
+        },
+        "results_handler": "layout tests",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Windows-11-22000"
+            }
+          ],
+          "hard_timeout": 1200,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
+          "shards": 12
+        },
+        "test_id_prefix": "ninja://:blink_web_tests/"
+      }
+    ]
   }
 }
diff --git a/testing/buildbot/variants.pyl b/testing/buildbot/variants.pyl
index 8bc5236..422ec84 100644
--- a/testing/buildbot/variants.pyl
+++ b/testing/buildbot/variants.pyl
@@ -463,6 +463,7 @@
   'WEBVIEW_TRICHROME_FULL_CTS_TESTS': {
     'identifier': 'full_mode',
     'swarming': {
+      'shards': 2,
       'cipd_packages': [
         {
           'cipd_package': 'chromium/android_webview/tools/cts_archive',
diff --git a/testing/buildbot/waterfalls.pyl b/testing/buildbot/waterfalls.pyl
index 9b56e1a..491a4049 100644
--- a/testing/buildbot/waterfalls.pyl
+++ b/testing/buildbot/waterfalls.pyl
@@ -153,11 +153,9 @@
         'additional_compile_targets': [
           'chromiumos_preflight',
         ],
-        # TODO(crbug.com/1293222): Restore after octopus capacity has been
-        # returned.
-        #'test_suites': {
-        #  'gtest_tests': 'chromeos_device_only_gtests',
-        #},
+        'test_suites': {
+          'gtest_tests': 'chromeos_device_only_gtests',
+        },
         'swarming': {
           'dimension_sets': [
             {
@@ -6397,6 +6395,17 @@
           'isolated_scripts': 'chromium_webkit_isolated_scripts',
         },
       },
+      'win11-blink-rel': {
+        'mixins': [
+            'win11',
+        ],
+        'swarming': {
+          'hard_timeout': 900,
+        },
+        'test_suites': {
+          'isolated_scripts': 'chromium_webkit_isolated_scripts',
+        },
+      },
     },
   },
   {
diff --git a/testing/variations/fieldtrial_testing_config.json b/testing/variations/fieldtrial_testing_config.json
index de80293f..f039a8f 100644
--- a/testing/variations/fieldtrial_testing_config.json
+++ b/testing/variations/fieldtrial_testing_config.json
@@ -4146,6 +4146,28 @@
             ]
         }
     ],
+    "HTMLParamElementUrlSupport": [
+        {
+            "platforms": [
+                "android",
+                "android_webview",
+                "android_weblayer",
+                "chromeos",
+                "chromeos_lacros",
+                "linux",
+                "mac",
+                "windows"
+            ],
+            "experiments": [
+                {
+                    "name": "Disabled_20220615",
+                    "disable_features": [
+                        "HTMLParamElementUrlSupport"
+                    ]
+                }
+            ]
+        }
+    ],
     "HTTP2": [
         {
             "platforms": [
@@ -5198,6 +5220,26 @@
             ]
         }
     ],
+    "LocalFrameRootPrePostFCPMetrics": [
+        {
+            "platforms": [
+                "android",
+                "chromeos",
+                "chromeos_lacros",
+                "linux",
+                "mac",
+                "windows"
+            ],
+            "experiments": [
+                {
+                    "name": "Enabled",
+                    "enable_features": [
+                        "LocalFrameRootPrePostFCPMetrics"
+                    ]
+                }
+            ]
+        }
+    ],
     "LocationBarModelOptimizations": [
         {
             "platforms": [
@@ -8986,6 +9028,21 @@
             ]
         }
     ],
+    "Vp9kSVCHWDecoding": [
+        {
+            "platforms": [
+                "chromeos"
+            ],
+            "experiments": [
+                {
+                    "name": "Enabled",
+                    "enable_features": [
+                        "Vp9kSVCHWDecoding"
+                    ]
+                }
+            ]
+        }
+    ],
     "Vulkan": [
         {
             "platforms": [
diff --git a/third_party/androidx/build.gradle.template b/third_party/androidx/build.gradle.template
index cfa496e..4db4d85 100644
--- a/third_party/androidx/build.gradle.template
+++ b/third_party/androidx/build.gradle.template
@@ -88,6 +88,7 @@
     compile 'androidx.legacy:legacy-support-v4:1.0.0'
 
     // testCompile targets have test_only = true.
+    androidTestCompile 'androidx.fragment:fragment-testing:{{androidx_dependency_version}}'
     androidTestCompile 'androidx.test:core:1.4.0-rc01'
     androidTestCompile 'androidx.test:monitor:1.4.0-rc01'
     androidTestCompile 'androidx.test:rules:1.2.0'
diff --git a/third_party/androidx/fetch_all_androidx.py b/third_party/androidx/fetch_all_androidx.py
index 09145619..b4390aa 100755
--- a/third_party/androidx/fetch_all_androidx.py
+++ b/third_party/androidx/fetch_all_androidx.py
@@ -32,7 +32,9 @@
     os.path.join(_ANDROIDX_PATH, '..', 'android_deps', 'fetch_all.py'))
 
 # URL to BUILD_INFO in latest androidx snapshot.
-_ANDROIDX_LATEST_SNAPSHOT_BUILD_INFO_URL = 'https://androidx.dev/snapshots/latest/artifacts/BUILD_INFO'
+#_ANDROIDX_LATEST_SNAPSHOT_BUILD_INFO_URL = 'https://androidx.dev/snapshots/latest/artifacts/BUILD_INFO'
+# Force latest to be last known good snapshot 8722497. See https://crbug.com/1336778
+_ANDROIDX_LATEST_SNAPSHOT_BUILD_INFO_URL = 'https://androidx.dev/snapshots/builds/8722497/artifacts/BUILD_INFO'
 
 # Snapshot repository URL with {{version}} placeholder.
 _SNAPSHOT_REPOSITORY_URL = 'https://androidx.dev/snapshots/builds/{{version}}/artifacts/repository'
diff --git a/third_party/blink/PRESUBMIT.py b/third_party/blink/PRESUBMIT.py
index dff8fd97..6d23f9ce 100644
--- a/third_party/blink/PRESUBMIT.py
+++ b/third_party/blink/PRESUBMIT.py
@@ -142,8 +142,7 @@
     for f in input_api.AffectedFiles():
         file_path = f.LocalPath()
         # Filter out changes in web_tests/.
-        if ('web_tests' + input_api.os_path.sep in file_path
-                and 'TestExpectations' not in file_path):
+        if 'web_tests' + input_api.os_path.sep in file_path:
             continue
         if '/PRESUBMIT' in file_path:
             continue
diff --git a/third_party/blink/common/features.cc b/third_party/blink/common/features.cc
index 4436d33..1f951b669 100644
--- a/third_party/blink/common/features.cc
+++ b/third_party/blink/common/features.cc
@@ -65,7 +65,7 @@
 // Used as a binding for controlling the runtime enabled blink feature
 // "FixedElementsDontOverscroll". This is needed for experimentation.
 const base::Feature kFixedElementsDontOverscroll{
-    "FixedElementsDontOverscroll", base::FEATURE_ENABLED_BY_DEFAULT};
+    "FixedElementsDontOverscroll", base::FEATURE_DISABLED_BY_DEFAULT};
 
 const base::Feature kGMSCoreEmoji{"GMSCoreEmoji",
                                   base::FEATURE_ENABLED_BY_DEFAULT};
@@ -1532,6 +1532,9 @@
 const base::Feature kEarlyExitOnNoopClassOrStyleChange{
     "EarlyExitOnNoopClassOrStyleChange", base::FEATURE_DISABLED_BY_DEFAULT};
 
+const base::Feature kLocalFrameRootPrePostFCPMetrics{
+    "LocalFrameRootPrePostFCPMetrics", base::FEATURE_DISABLED_BY_DEFAULT};
+
 const base::Feature kDisableArrayBufferSizeLimitsForTesting{
     "DisableArrayBufferSizeLimitsForTesting",
     base::FEATURE_DISABLED_BY_DEFAULT};
diff --git a/third_party/blink/public/common/features.h b/third_party/blink/public/common/features.h
index 6880d88..d2525a1 100644
--- a/third_party/blink/public/common/features.h
+++ b/third_party/blink/public/common/features.h
@@ -778,6 +778,10 @@
 BLINK_COMMON_EXPORT extern const base::Feature
     kEarlyExitOnNoopClassOrStyleChange;
 
+// Enables correct PreFCP and PostFCP metrics for non-main local frame roots.
+// See https://crbug.com/1330675.
+BLINK_COMMON_EXPORT extern const base::Feature kLocalFrameRootPrePostFCPMetrics;
+
 // TODO(https://crbug.com/1201109): temporary flag to disable new ArrayBuffer
 // size limits, so that tests can be written against code receiving these
 // buffers. Remove when the bindings code instituting these limits is removed.
diff --git a/third_party/blink/public/platform/web_runtime_features.h b/third_party/blink/public/platform/web_runtime_features.h
index da95562d..5c39eae 100644
--- a/third_party/blink/public/platform/web_runtime_features.h
+++ b/third_party/blink/public/platform/web_runtime_features.h
@@ -177,6 +177,7 @@
   BLINK_PLATFORM_EXPORT static void EnableWebGLDraftExtensions(bool);
   BLINK_PLATFORM_EXPORT static void EnableWebGLImageChromium(bool);
   BLINK_PLATFORM_EXPORT static void EnableWebGPU(bool);
+  BLINK_PLATFORM_EXPORT static void EnableWebGPUDeveloperFeatures(bool);
   BLINK_PLATFORM_EXPORT static void EnableWebNfc(bool);
   BLINK_PLATFORM_EXPORT static void EnableWebOTPAssertionFeaturePolicy(bool);
   BLINK_PLATFORM_EXPORT static void EnableWebShare(bool);
diff --git a/third_party/blink/renderer/bindings/bindings.gni b/third_party/blink/renderer/bindings/bindings.gni
index 79a1419..712e987 100644
--- a/third_party/blink/renderer/bindings/bindings.gni
+++ b/third_party/blink/renderer/bindings/bindings.gni
@@ -24,6 +24,7 @@
                     "core/v8/custom/v8_custom_xpath_ns_resolver.cc",
                     "core/v8/custom/v8_custom_xpath_ns_resolver.h",
                     "core/v8/custom/v8_dev_tools_host_custom.cc",
+                    "core/v8/custom/v8_element_custom.cc",
                     "core/v8/custom/v8_html_all_collection_custom.cc",
                     "core/v8/custom/v8_html_plugin_element_custom.cc",
                     "core/v8/custom/v8_promise_rejection_event_custom.cc",
diff --git a/third_party/blink/renderer/bindings/core/v8/custom/v8_element_custom.cc b/third_party/blink/renderer/bindings/core/v8/custom/v8_element_custom.cc
new file mode 100644
index 0000000..39154bdb
--- /dev/null
+++ b/third_party/blink/renderer/bindings/core/v8/custom/v8_element_custom.cc
@@ -0,0 +1,68 @@
+// Copyright 2022 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/bindings/core/v8/v8_element.h"
+
+#include "base/metrics/histogram_macros.h"
+#include "base/timer/elapsed_timer.h"
+#include "third_party/blink/renderer/bindings/core/v8/generated_code_helper.h"
+#include "third_party/blink/renderer/bindings/core/v8/idl_types.h"
+#include "third_party/blink/renderer/bindings/core/v8/native_value_traits_impl.h"
+#include "third_party/blink/renderer/core/dom/element.h"
+#include "third_party/blink/renderer/core/execution_context/execution_context.h"
+#include "third_party/blink/renderer/core/html/custom/ce_reactions_scope.h"
+#include "third_party/blink/renderer/platform/bindings/exception_state.h"
+#include "v8/include/v8.h"
+
+namespace blink {
+
+// TODO(https://crbug.com/1335986): Custom setter is needed to collect metrics,
+// and can be removed once metrics are captured.
+// static
+void V8Element::InnerHTMLAttributeSetterCustom(
+    v8::Local<v8::Value> html_value,
+    const v8::FunctionCallbackInfo<v8::Value>& info) {
+  v8::Isolate* isolate = info.GetIsolate();
+  const ExceptionState::ContextType exception_state_context_type =
+      ExceptionContext::Context::kAttributeSet;
+  const char* const class_like_name = "Element";
+  const char* const property_name = "innerHTML";
+  ExceptionState exception_state(isolate, exception_state_context_type,
+                                 class_like_name, property_name);
+
+  // [CEReactions]
+  CEReactionsScope ce_reactions_scope;
+
+  v8::Local<v8::Object> v8_receiver = info.This();
+  Element* blink_receiver = V8Element::ToWrappableUnsafe(v8_receiver);
+  ExecutionContext* execution_context_of_document_tree =
+      bindings::ExecutionContextFromV8Wrappable(blink_receiver);
+  const bool is_high_resolution_timer = base::TimeTicks::IsHighResolution();
+  base::ElapsedTimer timer;
+  auto&& html = NativeValueTraits<
+      IDLStringStringContextTrustedHTMLTreatNullAsEmptyString>::
+      NativeValue(isolate, html_value, exception_state,
+                  execution_context_of_document_tree);
+  if (is_high_resolution_timer) {
+    UMA_HISTOGRAM_CUSTOM_MICROSECONDS_TIMES(
+        "Blink.SetInnerHtml.ConversionTime", timer.Elapsed(),
+        base::Microseconds(1), base::Seconds(1), 100);
+  }
+  if (UNLIKELY(exception_state.HadException())) {
+    return;
+  }
+  timer = base::ElapsedTimer();
+  blink_receiver->setInnerHTML(html, exception_state);
+  if (is_high_resolution_timer) {
+    UMA_HISTOGRAM_CUSTOM_MICROSECONDS_TIMES(
+        "Blink.SetInnerHtml.ExecutionTime", timer.Elapsed(),
+        base::Microseconds(1), base::Seconds(10), 100);
+    UMA_HISTOGRAM_COUNTS_1M("Blink.SetInnerHtml.StringLength", html.length());
+  }
+  if (UNLIKELY(exception_state.HadException())) {
+    return;
+  }
+}
+
+}  // namespace blink
diff --git a/third_party/blink/renderer/core/css/css_custom_font_data.h b/third_party/blink/renderer/core/css/css_custom_font_data.h
index 56ea0ae..37d259a 100644
--- a/third_party/blink/renderer/core/css/css_custom_font_data.h
+++ b/third_party/blink/renderer/core/css/css_custom_font_data.h
@@ -71,15 +71,9 @@
 
   // TODO(Oilpan): consider moving (Custom)FontFace hierarchy to the heap,
   // thereby making this reference a Member<>.
-#if defined(USE_PARALLEL_TEXT_SHAPING)
-  CrossThreadWeakPersistent<CSSFontFaceSource> font_face_source_;
-  std::atomic<FallbackVisibility> fallback_visibility_;
-  mutable std::atomic<bool> is_loading_;
-#else
   WeakPersistent<CSSFontFaceSource> font_face_source_;
   FallbackVisibility fallback_visibility_;
   mutable bool is_loading_;
-#endif
 };
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/css/css_font_face.cc b/third_party/blink/renderer/core/css/css_font_face.cc
index 8df72fd2d..91a3bdb 100644
--- a/third_party/blink/renderer/core/css/css_font_face.cc
+++ b/third_party/blink/renderer/core/css/css_font_face.cc
@@ -36,93 +36,63 @@
 #include "third_party/blink/renderer/core/workers/worker_global_scope.h"
 #include "third_party/blink/renderer/platform/fonts/font_description.h"
 #include "third_party/blink/renderer/platform/fonts/simple_font_data.h"
-#include "third_party/blink/renderer/platform/heap/thread_state.h"
 #include "third_party/blink/renderer/platform/instrumentation/use_counter.h"
-#include "third_party/blink/renderer/platform/scheduler/public/post_cross_thread_task.h"
-#include "third_party/blink/renderer/platform/scheduler/public/thread.h"
-#include "third_party/blink/renderer/platform/wtf/cross_thread_copier.h"
-#include "third_party/blink/renderer/platform/wtf/cross_thread_functional.h"
 
 namespace blink {
 
-CSSFontFace::CSSFontFace(FontFace* font_face, Vector<UnicodeRange>& ranges)
-    : ranges_(base::AdoptRef(new UnicodeRangeSet(ranges))),
-      font_face_(font_face)
-#if defined(USE_PARALLEL_TEXT_SHAPING)
-      ,
-      task_runner_(font_face->GetExecutionContext()->GetTaskRunner(
-          TaskType::kFontLoading))
-#endif
-{
-  DCHECK(font_face_);
-}
-
 void CSSFontFace::AddSource(CSSFontFaceSource* source) {
-  AutoLockForParallelTextShaping guard(sources_lock_);
   sources_.push_back(source);
 }
 
 void CSSFontFace::AddSegmentedFontFace(
     CSSSegmentedFontFace* segmented_font_face) {
-  DCHECK(IsContextThread());
   DCHECK(!segmented_font_faces_.Contains(segmented_font_face));
   segmented_font_faces_.insert(segmented_font_face);
 }
 
 void CSSFontFace::RemoveSegmentedFontFace(
     CSSSegmentedFontFace* segmented_font_face) {
-  DCHECK(IsContextThread());
   DCHECK(segmented_font_faces_.Contains(segmented_font_face));
   segmented_font_faces_.erase(segmented_font_face);
 }
 
 void CSSFontFace::DidBeginLoad() {
-  DCHECK(IsContextThread());
   if (LoadStatus() == FontFace::kUnloaded)
     SetLoadStatus(FontFace::kLoading);
 }
 
 bool CSSFontFace::FontLoaded(CSSFontFaceSource* source) {
-  DCHECK(IsContextThread());
-  if (source != FrontSource())
+  if (!IsValid() || source != sources_.front())
     return false;
 
   if (LoadStatus() == FontFace::kLoading) {
     if (source->IsValid()) {
       SetLoadStatus(FontFace::kLoaded);
     } else if (source->IsInFailurePeriod()) {
-      {
-        AutoLockForParallelTextShaping guard(sources_lock_);
-        sources_.clear();
-      }
+      sources_.clear();
       SetLoadStatus(FontFace::kError);
     } else {
-      {
-        AutoLockForParallelTextShaping guard(sources_lock_);
-        if (!sources_.IsEmpty() && source == sources_.front())
-          sources_.pop_front();
-      }
+      sources_.pop_front();
       Load();
     }
   }
 
-  for (const auto& segmented_font_face : segmented_font_faces_)
+  for (CSSSegmentedFontFace* segmented_font_face : segmented_font_faces_)
     segmented_font_face->FontFaceInvalidated();
   return true;
 }
 
 void CSSFontFace::SetDisplay(FontDisplay value) {
-  for (auto& source : GetSources()) {
+  for (auto& source : sources_) {
     source->SetDisplay(value);
   }
 }
 
 size_t CSSFontFace::ApproximateBlankCharacterCount() const {
-  auto* const source = FrontSource();
-  if (!source || !source->IsInBlockPeriod())
+  if (sources_.IsEmpty() || !sources_.front()->IsInBlockPeriod())
     return 0;
   size_t approximate_character_count_ = 0;
-  for (const auto& segmented_font_face : segmented_font_faces_) {
+  for (CSSSegmentedFontFace* segmented_font_face : segmented_font_faces_) {
     approximate_character_count_ +=
         segmented_font_face->ApproximateCharacterCount();
   }
@@ -130,18 +100,16 @@
 }
 
 bool CSSFontFace::FallbackVisibilityChanged(RemoteFontFaceSource* source) {
-  if (source != FrontSource())
+  if (!IsValid() || source != sources_.front())
     return false;
-  for (const auto& segmented_font_face : segmented_font_faces_)
+  for (CSSSegmentedFontFace* segmented_font_face : segmented_font_faces_)
     segmented_font_face->FontFaceInvalidated();
   return true;
 }
 
 scoped_refptr<SimpleFontData> CSSFontFace::GetFontData(
     const FontDescription& font_description) {
-  AutoLockForParallelTextShaping guard(sources_lock_);
-
-  if (sources_.IsEmpty())
+  if (!IsValid())
     return nullptr;
 
   // Apply the 'size-adjust' descriptor before font selection.
@@ -174,107 +142,63 @@
       }
       // The active source may already be loading or loaded. Adjust our
       // FontFace status accordingly.
-      UpdateLoadStatusForActiveSource(source);
+      if (LoadStatus() == FontFace::kUnloaded &&
+          (source->IsLoading() || source->IsLoaded()))
+        SetLoadStatus(FontFace::kLoading);
+      if (LoadStatus() == FontFace::kLoading && source->IsLoaded())
+        SetLoadStatus(FontFace::kLoaded);
       return result;
     }
     sources_.pop_front();
   }
 
   // We ran out of source. Set the FontFace status to "error" and return.
-  UpdateLoadStatusForNoSource();
-  return nullptr;
-}
-
-void CSSFontFace::UpdateLoadStatusForActiveSource(CSSFontFaceSource* source) {
-#if defined(USE_PARALLEL_TEXT_SHAPING)
-  if (auto task_runner = GetCrossThreadTaskRunner()) {
-    PostCrossThreadTask(
-        *task_runner, FROM_HERE,
-        CrossThreadBindOnce(&CSSFontFace::UpdateLoadStatusForActiveSource,
-                            WrapCrossThreadPersistent(this),
-                            WrapCrossThreadPersistent(source)));
-    return;
-  }
-  if (!font_face_->GetExecutionContext())
-    return;
-  DCHECK(IsContextThread());
-#endif
-  if (LoadStatus() == FontFace::kUnloaded &&
-      (source->IsLoading() || source->IsLoaded()))
-    SetLoadStatus(FontFace::kLoading);
-  if (LoadStatus() == FontFace::kLoading && source->IsLoaded())
-    SetLoadStatus(FontFace::kLoaded);
-}
-
-void CSSFontFace::UpdateLoadStatusForNoSource() {
-#if defined(USE_PARALLEL_TEXT_SHAPING)
-  if (auto task_runner = GetCrossThreadTaskRunner()) {
-    PostCrossThreadTask(
-        *task_runner, FROM_HERE,
-        CrossThreadBindOnce(&CSSFontFace::UpdateLoadStatusForNoSource,
-                            WrapCrossThreadPersistent(this)));
-    return;
-  }
-  if (!font_face_->GetExecutionContext())
-    return;
-  DCHECK(IsContextThread());
-#endif
   if (LoadStatus() == FontFace::kUnloaded)
     SetLoadStatus(FontFace::kLoading);
   if (LoadStatus() == FontFace::kLoading)
     SetLoadStatus(FontFace::kError);
+  return nullptr;
 }
 
 bool CSSFontFace::MaybeLoadFont(const FontDescription& font_description,
-                                const StringView& text) {
-  DCHECK(IsContextThread());
+                                const String& text) {
   // This is a fast path of loading web font in style phase. For speed, this
   // only checks if the first character of the text is included in the font's
   // unicode range. If this font is needed by subsequent characters, load is
   // kicked off in layout phase.
-  const UChar32 character = text.length() ? text.CodepointAt(0) : 0;
-  if (!ranges_->Contains(character))
-    return false;
-  if (LoadStatus() != FontFace::kUnloaded)
+  UChar32 character = text.CharacterStartingAt(0);
+  if (ranges_->Contains(character)) {
+    if (LoadStatus() == FontFace::kUnloaded)
+      Load(font_description);
     return true;
-  LoadInternal(font_description);
-  return true;
+  }
+  return false;
 }
 
 bool CSSFontFace::MaybeLoadFont(const FontDescription& font_description,
                                 const FontDataForRangeSet& range_set) {
-  if (ranges_ != range_set.Ranges())
-    return false;
-  if (LoadStatus() != FontFace::kUnloaded)
+  if (ranges_ == range_set.Ranges()) {
+    if (LoadStatus() == FontFace::kUnloaded) {
+      Load(font_description);
+    }
     return true;
-  LoadInternal(font_description);
-  return true;
+  }
+  return false;
 }
 
 void CSSFontFace::Load() {
-  DCHECK(IsContextThread());
   FontDescription font_description;
   FontFamily font_family;
   font_family.SetFamily(font_face_->family(), FontFamily::Type::kFamilyName);
   font_description.SetFamily(font_family);
-  LoadInternal(font_description);
+  Load(font_description);
 }
 
-void CSSFontFace::LoadInternal(const FontDescription& font_description) {
-#if defined(USE_PARALLEL_TEXT_SHAPING)
-  if (auto task_runner = GetCrossThreadTaskRunner()) {
-    PostCrossThreadTask(
-        *task_runner, FROM_HERE,
-        CrossThreadBindOnce(&CSSFontFace::LoadInternal,
-                            WrapCrossThreadPersistent(this), font_description));
-    return;
-  }
-#endif
+void CSSFontFace::Load(const FontDescription& font_description) {
   if (LoadStatus() == FontFace::kUnloaded)
     SetLoadStatus(FontFace::kLoading);
   DCHECK_EQ(LoadStatus(), FontFace::kLoading);
 
-  AutoLockForParallelTextShaping guard(sources_lock_);
   while (!sources_.IsEmpty()) {
     Member<CSSFontFaceSource>& source = sources_.front();
     if (source->IsValid()) {
@@ -297,7 +221,6 @@
 }
 
 void CSSFontFace::SetLoadStatus(FontFace::LoadStatusType new_status) {
-  DCHECK(IsContextThread());
   DCHECK(font_face_);
   if (new_status == FontFace::kError)
     font_face_->SetError();
@@ -324,7 +247,7 @@
   if (LoadStatus() == FontFace::kLoaded)
     return false;
   bool changed = false;
-  for (CSSFontFaceSource* source : GetSources()) {
+  for (CSSFontFaceSource* source : sources_) {
     if (source->UpdatePeriod())
       changed = true;
   }
@@ -332,36 +255,9 @@
 }
 
 void CSSFontFace::Trace(Visitor* visitor) const {
-  {
-    AutoLockForParallelTextShaping guard(sources_lock_);
-    visitor->Trace(sources_);
-  }
+  visitor->Trace(segmented_font_faces_);
+  visitor->Trace(sources_);
   visitor->Trace(font_face_);
 }
 
-bool CSSFontFace::IsContextThread() const {
-#if defined(USE_PARALLEL_TEXT_SHAPING)
-  return font_face_->GetExecutionContext()->IsContextThread();
-#else
-  return true;
-#endif
-}
-
-#if defined(USE_PARALLEL_TEXT_SHAPING)
-scoped_refptr<base::SequencedTaskRunner> CSSFontFace::GetCrossThreadTaskRunner()
-    const {
-  auto* const context = font_face_->GetExecutionContext();
-  if (!context || context->IsContextThread())
-    return nullptr;
-  return task_runner_;
-}
-#endif
-
-HeapVector<Member<CSSFontFaceSource>> CSSFontFace::GetSources() const {
-  AutoLockForParallelTextShaping guard(sources_lock_);
-  HeapVector<Member<CSSFontFaceSource>> sources;
-  CopyToVector(sources_, sources);
-  return sources;
-}
-
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/css/css_font_face.h b/third_party/blink/renderer/core/css/css_font_face.h
index cf9bd7d1..97924a85 100644
--- a/third_party/blink/renderer/core/css/css_font_face.h
+++ b/third_party/blink/renderer/core/css/css_font_face.h
@@ -32,7 +32,6 @@
 #include "third_party/blink/renderer/core/css/css_segmented_font_face.h"
 #include "third_party/blink/renderer/core/css/font_face.h"
 #include "third_party/blink/renderer/core/css/font_face_source.h"
-#include "third_party/blink/renderer/platform/fonts/lock_for_parallel_text_shaping.h"
 #include "third_party/blink/renderer/platform/fonts/segmented_font_data.h"
 #include "third_party/blink/renderer/platform/fonts/unicode_range_set.h"
 #include "third_party/blink/renderer/platform/heap/collection_support/heap_deque.h"
@@ -49,17 +48,16 @@
 
 class CORE_EXPORT CSSFontFace final : public GarbageCollected<CSSFontFace> {
  public:
-  CSSFontFace(FontFace* font_face, Vector<UnicodeRange>& ranges);
+  CSSFontFace(FontFace* font_face, Vector<UnicodeRange>& ranges)
+      : ranges_(base::AdoptRef(new UnicodeRangeSet(ranges))),
+        font_face_(font_face) {
+    DCHECK(font_face_);
+  }
   CSSFontFace(const CSSFontFace&) = delete;
   CSSFontFace& operator=(const CSSFontFace&) = delete;
 
   // Front source is the first successfully loaded source.
-  const CSSFontFaceSource* FrontSource() const LOCKS_EXCLUDED(sources_lock_) {
-    AutoLockForParallelTextShaping guard(sources_lock_);
-    return sources_.IsEmpty() ? nullptr : sources_.front();
-  }
-  CSSFontFaceSource* FrontSource() LOCKS_EXCLUDED(sources_lock_) {
-    AutoLockForParallelTextShaping guard(sources_lock_);
+  const CSSFontFaceSource* FrontSource() const {
     return sources_.IsEmpty() ? nullptr : sources_.front();
   }
   FontFace* GetFontFace() const { return font_face_; }
@@ -69,57 +67,42 @@
   void AddSegmentedFontFace(CSSSegmentedFontFace*);
   void RemoveSegmentedFontFace(CSSSegmentedFontFace*);
 
-  bool IsValid() const { return FrontSource(); }
+  bool IsValid() const { return !sources_.IsEmpty(); }
   size_t ApproximateBlankCharacterCount() const;
 
-  void AddSource(CSSFontFaceSource*) LOCKS_EXCLUDED(sources_lock_);
+  void AddSource(CSSFontFaceSource*);
   void SetDisplay(FontDisplay);
 
   void DidBeginLoad();
   bool FontLoaded(CSSFontFaceSource*);
   bool FallbackVisibilityChanged(RemoteFontFaceSource*);
 
-  scoped_refptr<SimpleFontData> GetFontData(const FontDescription&)
-      LOCKS_EXCLUDED(sources_lock_);
+  scoped_refptr<SimpleFontData> GetFontData(const FontDescription&);
 
   FontFace::LoadStatusType LoadStatus() const {
     return font_face_->LoadStatus();
   }
-  bool MaybeLoadFont(const FontDescription&, const StringView&);
+  bool MaybeLoadFont(const FontDescription&, const String&);
   bool MaybeLoadFont(const FontDescription&, const FontDataForRangeSet&);
   void Load();
+  void Load(const FontDescription&);
 
   // Recalculate the font loading timeline period for the font face.
   // https://drafts.csswg.org/css-fonts-4/#font-display-timeline
   // Returns true if the display period is changed.
   bool UpdatePeriod();
 
-  bool HadBlankText() {
-    if (auto* source = FrontSource())
-      return source->HadBlankText();
-    return false;
-  }
+  bool HadBlankText() { return IsValid() && sources_.front()->HadBlankText(); }
 
   void Trace(Visitor*) const;
 
  private:
-  HeapVector<Member<CSSFontFaceSource>> GetSources() const
-      LOCKS_EXCLUDED(sources_lock_);
-  bool IsContextThread() const;
-  void LoadInternal(const FontDescription&) LOCKS_EXCLUDED(sources_lock_);
   void SetLoadStatus(FontFace::LoadStatusType);
-  void UpdateLoadStatusForActiveSource(CSSFontFaceSource*);
-  void UpdateLoadStatusForNoSource();
 
   scoped_refptr<UnicodeRangeSet> ranges_;
-  HashSet<scoped_refptr<CSSSegmentedFontFace>> segmented_font_faces_;
-  mutable LockForParallelTextShaping sources_lock_;
-  HeapDeque<Member<CSSFontFaceSource>> sources_ GUARDED_BY(sources_lock_);
-  const Member<FontFace> font_face_;
-#if defined(USE_PARALLEL_TEXT_SHAPING)
-  scoped_refptr<base::SequencedTaskRunner> GetCrossThreadTaskRunner() const;
-  scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
-#endif
+  HeapHashSet<Member<CSSSegmentedFontFace>> segmented_font_faces_;
+  HeapDeque<Member<CSSFontFaceSource>> sources_;
+  Member<FontFace> font_face_;
 };
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/css/css_font_selector.cc b/third_party/blink/renderer/core/css/css_font_selector.cc
index fb149ec..c4cb3ff3f 100644
--- a/third_party/blink/renderer/core/css/css_font_selector.cc
+++ b/third_party/blink/renderer/core/css/css_font_selector.cc
@@ -40,11 +40,7 @@
 namespace blink {
 
 CSSFontSelector::CSSFontSelector(const TreeScope& tree_scope)
-    : CSSFontSelectorBase(
-          tree_scope.GetDocument().GetExecutionContext()->GetTaskRunner(
-              TaskType::kInternalDefault)),
-      tree_scope_(&tree_scope) {
-  DCHECK(tree_scope.GetDocument().GetExecutionContext()->IsContextThread());
+    : tree_scope_(&tree_scope) {
   DCHECK(tree_scope.GetDocument().GetFrame());
   generic_font_family_settings_ = tree_scope.GetDocument()
                                       .GetFrame()
@@ -61,8 +57,7 @@
 CSSFontSelector::~CSSFontSelector() = default;
 
 UseCounter* CSSFontSelector::GetUseCounter() const {
-  auto* const context = GetExecutionContext();
-  return context && context->IsContextThread() ? context : nullptr;
+  return GetExecutionContext();
 }
 
 void CSSFontSelector::RegisterForInvalidationCallbacks(
@@ -129,7 +124,8 @@
   }
 
   if (!font_family.FamilyIsGeneric()) {
-    if (auto face = font_face_cache_->Get(request_description, family_name)) {
+    if (CSSSegmentedFontFace* face =
+            font_face_cache_->Get(request_description, family_name)) {
       ReportWebFontFamily(family_name);
       return face->GetFontData(request_description);
     }
@@ -152,7 +148,7 @@
       FontCache::Get().GetFontData(request_description, settings_family_name);
 
   ReportFontLookupByUniqueOrFamilyName(settings_family_name,
-                                       request_description, font_data);
+                                       request_description, font_data.get());
 
   return font_data;
 }
@@ -169,10 +165,6 @@
   return GetDocument().GetFontMatchingMetrics();
 }
 
-bool CSSFontSelector::IsAlive() const {
-  return tree_scope_;
-}
-
 void CSSFontSelector::Trace(Visitor* visitor) const {
   visitor->Trace(tree_scope_);
   visitor->Trace(clients_);
diff --git a/third_party/blink/renderer/core/css/css_font_selector.h b/third_party/blink/renderer/core/css/css_font_selector.h
index c568a0e..74b7ee2 100644
--- a/third_party/blink/renderer/core/css/css_font_selector.h
+++ b/third_party/blink/renderer/core/css/css_font_selector.h
@@ -82,7 +82,6 @@
   void DispatchInvalidationCallbacks(FontInvalidationReason);
 
   // `CSSFontSelectorBase` overrides
-  bool IsAlive() const override;
   FontMatchingMetrics* GetFontMatchingMetrics() const override;
   UseCounter* GetUseCounter() const override;
 
diff --git a/third_party/blink/renderer/core/css/css_font_selector_base.cc b/third_party/blink/renderer/core/css/css_font_selector_base.cc
index 2fa1fe9..aeec9b2 100644
--- a/third_party/blink/renderer/core/css/css_font_selector_base.cc
+++ b/third_party/blink/renderer/core/css/css_font_selector_base.cc
@@ -16,58 +16,17 @@
 
 namespace blink {
 
-CSSFontSelectorBase::CSSFontSelectorBase(
-    scoped_refptr<base::SingleThreadTaskRunner> task_runner)
-#if defined(USE_PARALLEL_TEXT_SHAPING)
-    : task_runner_(task_runner)
-#endif
-{
-  DCHECK(IsContextThread());
-}
-
 void CSSFontSelectorBase::CountUse(WebFeature feature) const {
-#if defined(USE_PARALLEL_TEXT_SHAPING)
-  if (!IsAlive())
-    return;
-  if (IsContextThread())
-    return UseCounter::Count(GetUseCounter(), feature);
-  PostCrossThreadTask(
-      *task_runner_, FROM_HERE,
-      CrossThreadBindOnce(&CSSFontSelectorBase::CountUse,
-                          WrapCrossThreadPersistent(this), feature));
-#endif
+  return UseCounter::Count(GetUseCounter(), feature);
 }
 
 AtomicString CSSFontSelectorBase::FamilyNameFromSettings(
     const FontDescription& font_description,
     const FontFamily& generic_family_name) {
-#if defined(USE_PARALLEL_TEXT_SHAPING)
-  if (!IsContextThread()) {
-    if (IsWebkitBodyFamily(font_description)) {
-      PostCrossThreadTask(
-          *task_runner_, FROM_HERE,
-          CrossThreadBindOnce(
-              &CSSFontSelectorBase::CountUse, WrapCrossThreadPersistent(this),
-              WebFeature::kFontSelectorCSSFontFamilyWebKitPrefixBody));
-    }
-    return FontSelector::FamilyNameFromSettings(generic_font_family_settings_,
-                                                font_description,
-                                                generic_family_name, nullptr);
-  }
-#endif
   return FontSelector::FamilyNameFromSettings(
       generic_font_family_settings_, font_description, generic_family_name,
       GetUseCounter());
 }
-
-bool CSSFontSelectorBase::IsContextThread() const {
-#if defined(USE_PARALLEL_TEXT_SHAPING)
-  return task_runner_->RunsTasksInCurrentSequence();
-#else
-  return true;
-#endif
-}
-
 bool CSSFontSelectorBase::IsPlatformFamilyMatchAvailable(
     const FontDescription& font_description,
     const FontFamily& passed_family) {
@@ -81,19 +40,6 @@
 void CSSFontSelectorBase::ReportEmojiSegmentGlyphCoverage(
     unsigned num_clusters,
     unsigned num_broken_clusters) {
-#if defined(USE_PARALLEL_TEXT_SHAPING)
-  if (!IsAlive())
-    return;
-  if (!IsContextThread()) {
-    PostCrossThreadTask(
-        *task_runner_, FROM_HERE,
-        CrossThreadBindOnce(
-            &CSSFontSelectorBase::ReportEmojiSegmentGlyphCoverage,
-            WrapCrossThreadPersistent(this), num_clusters,
-            num_broken_clusters));
-    return;
-  }
-#endif
   GetFontMatchingMetrics()->ReportEmojiSegmentGlyphCoverage(
       num_clusters, num_broken_clusters);
 }
@@ -103,19 +49,6 @@
     UScriptCode script,
     FontDescription::GenericFamilyType generic_family_type,
     const AtomicString& resulting_font_name) {
-#if defined(USE_PARALLEL_TEXT_SHAPING)
-  if (!IsAlive())
-    return;
-  if (!IsContextThread()) {
-    PostCrossThreadTask(
-        *task_runner_, FROM_HERE,
-        CrossThreadBindOnce(
-            &CSSFontSelectorBase::ReportFontFamilyLookupByGenericFamily,
-            WrapCrossThreadPersistent(this), generic_font_family_name, script,
-            generic_family_type, resulting_font_name));
-    return;
-  }
-#endif
   GetFontMatchingMetrics()->ReportFontFamilyLookupByGenericFamily(
       generic_font_family_name, script, generic_family_type,
       resulting_font_name);
@@ -123,153 +56,56 @@
 
 void CSSFontSelectorBase::ReportSuccessfulFontFamilyMatch(
     const AtomicString& font_family_name) {
-#if defined(USE_PARALLEL_TEXT_SHAPING)
-  if (!IsAlive())
-    return;
-  if (!IsContextThread()) {
-    PostCrossThreadTask(
-        *task_runner_, FROM_HERE,
-        CrossThreadBindOnce(&CSSFontSelectorBase::ReportFailedFontFamilyMatch,
-                            WrapCrossThreadPersistent(this), font_family_name));
-    return;
-  }
-#endif
   GetFontMatchingMetrics()->ReportSuccessfulFontFamilyMatch(font_family_name);
 }
 
 void CSSFontSelectorBase::ReportFailedFontFamilyMatch(
     const AtomicString& font_family_name) {
-#if defined(USE_PARALLEL_TEXT_SHAPING)
-  if (!IsAlive())
-    return;
-  if (!IsContextThread()) {
-    PostCrossThreadTask(
-        *task_runner_, FROM_HERE,
-        CrossThreadBindOnce(&CSSFontSelectorBase::ReportFailedFontFamilyMatch,
-                            WrapCrossThreadPersistent(this), font_family_name));
-    return;
-  }
-#endif
   GetFontMatchingMetrics()->ReportFailedFontFamilyMatch(font_family_name);
 }
 
 void CSSFontSelectorBase::ReportSuccessfulLocalFontMatch(
     const AtomicString& font_name) {
-#if defined(USE_PARALLEL_TEXT_SHAPING)
-  if (!IsAlive())
-    return;
-  if (!IsContextThread()) {
-    PostCrossThreadTask(
-        *task_runner_, FROM_HERE,
-        CrossThreadBindOnce(
-            &CSSFontSelectorBase::ReportSuccessfulLocalFontMatch,
-            WrapCrossThreadPersistent(this), font_name));
-    return;
-  }
-#endif
   GetFontMatchingMetrics()->ReportSuccessfulLocalFontMatch(font_name);
 }
 
 void CSSFontSelectorBase::ReportFailedLocalFontMatch(
     const AtomicString& font_name) {
-#if defined(USE_PARALLEL_TEXT_SHAPING)
-  if (!IsAlive())
-    return;
-  if (!IsContextThread()) {
-    PostCrossThreadTask(
-        *task_runner_, FROM_HERE,
-        CrossThreadBindOnce(&CSSFontSelectorBase::ReportFailedLocalFontMatch,
-                            WrapCrossThreadPersistent(this), font_name));
-    return;
-  }
-#endif
   GetFontMatchingMetrics()->ReportFailedLocalFontMatch(font_name);
 }
 
 void CSSFontSelectorBase::ReportFontLookupByUniqueOrFamilyName(
     const AtomicString& name,
     const FontDescription& font_description,
-    scoped_refptr<SimpleFontData> resulting_font_data) {
-#if defined(USE_PARALLEL_TEXT_SHAPING)
-  if (!IsAlive())
-    return;
-  if (!IsContextThread()) {
-    PostCrossThreadTask(
-        *task_runner_, FROM_HERE,
-        CrossThreadBindOnce(
-            &CSSFontSelectorBase::ReportFontLookupByUniqueOrFamilyName,
-            WrapCrossThreadPersistent(this), name, font_description,
-            resulting_font_data));
-    return;
-  }
-#endif
+    SimpleFontData* resulting_font_data) {
   GetFontMatchingMetrics()->ReportFontLookupByUniqueOrFamilyName(
-      name, font_description, resulting_font_data.get());
+      name, font_description, resulting_font_data);
 }
 
 void CSSFontSelectorBase::ReportFontLookupByUniqueNameOnly(
     const AtomicString& name,
     const FontDescription& font_description,
-    scoped_refptr<SimpleFontData> resulting_font_data,
+    SimpleFontData* resulting_font_data,
     bool is_loading_fallback) {
-#if defined(USE_PARALLEL_TEXT_SHAPING)
-  if (!IsAlive())
-    return;
-  if (!IsContextThread()) {
-    PostCrossThreadTask(
-        *task_runner_, FROM_HERE,
-        CrossThreadBindOnce(
-            &CSSFontSelectorBase::ReportFontLookupByUniqueNameOnly,
-            WrapCrossThreadPersistent(this), name, font_description,
-            resulting_font_data, is_loading_fallback));
-    return;
-  }
-#endif
   GetFontMatchingMetrics()->ReportFontLookupByUniqueNameOnly(
-      name, font_description, resulting_font_data.get(), is_loading_fallback);
+      name, font_description, resulting_font_data, is_loading_fallback);
 }
 
 void CSSFontSelectorBase::ReportFontLookupByFallbackCharacter(
     UChar32 fallback_character,
     FontFallbackPriority fallback_priority,
     const FontDescription& font_description,
-    scoped_refptr<SimpleFontData> resulting_font_data) {
-#if defined(USE_PARALLEL_TEXT_SHAPING)
-  if (!IsAlive())
-    return;
-  if (!IsContextThread()) {
-    PostCrossThreadTask(
-        *task_runner_, FROM_HERE,
-        CrossThreadBindOnce(
-            &CSSFontSelectorBase::ReportFontLookupByFallbackCharacter,
-            WrapCrossThreadPersistent(this), fallback_character,
-            fallback_priority, font_description, resulting_font_data));
-    return;
-  }
-#endif
+    SimpleFontData* resulting_font_data) {
   GetFontMatchingMetrics()->ReportFontLookupByFallbackCharacter(
       fallback_character, fallback_priority, font_description,
-      resulting_font_data.get());
+      resulting_font_data);
 }
 
 void CSSFontSelectorBase::ReportLastResortFallbackFontLookup(
     const FontDescription& font_description,
-    scoped_refptr<SimpleFontData> resulting_font_data) {
-#if defined(USE_PARALLEL_TEXT_SHAPING)
-  if (!IsAlive())
-    return;
-  if (!IsContextThread()) {
-    PostCrossThreadTask(
-        *task_runner_, FROM_HERE,
-        CrossThreadBindOnce(
-            &CSSFontSelectorBase::ReportLastResortFallbackFontLookup,
-            WrapCrossThreadPersistent(this), font_description,
-            resulting_font_data));
-    return;
-  }
-#endif
+    SimpleFontData* resulting_font_data) {
   GetFontMatchingMetrics()->ReportLastResortFallbackFontLookup(
-      font_description, resulting_font_data.get());
+      font_description, resulting_font_data);
 }
 
 void CSSFontSelectorBase::ReportNotDefGlyph() const {
@@ -278,33 +114,11 @@
 
 void CSSFontSelectorBase::ReportSystemFontFamily(
     const AtomicString& font_family_name) {
-#if defined(USE_PARALLEL_TEXT_SHAPING)
-  if (!IsAlive())
-    return;
-  if (!IsContextThread()) {
-    PostCrossThreadTask(
-        *task_runner_, FROM_HERE,
-        CrossThreadBindOnce(&CSSFontSelectorBase::ReportSystemFontFamily,
-                            WrapCrossThreadPersistent(this), font_family_name));
-    return;
-  }
-#endif
   GetFontMatchingMetrics()->ReportSystemFontFamily(font_family_name);
 }
 
 void CSSFontSelectorBase::ReportWebFontFamily(
     const AtomicString& font_family_name) {
-#if defined(USE_PARALLEL_TEXT_SHAPING)
-  if (!IsAlive())
-    return;
-  if (!IsContextThread()) {
-    PostCrossThreadTask(
-        *task_runner_, FROM_HERE,
-        CrossThreadBindOnce(&CSSFontSelectorBase::ReportWebFontFamily,
-                            WrapCrossThreadPersistent(this), font_family_name));
-    return;
-  }
-#endif
   GetFontMatchingMetrics()->ReportWebFontFamily(font_family_name);
 }
 
@@ -313,7 +127,7 @@
     const FontFamily& family,
     const String& text) {
   if (family.FamilyIsGeneric()) {
-    if (family.IsPrewarmed() || UNLIKELY(family.FamilyName().IsEmpty()))
+    if (family.IsPrewarmed())
       return;
     family.SetIsPrewarmed();
     // |FamilyNameFromSettings| has a visible impact on the load performance.
@@ -321,13 +135,9 @@
     // only when the |Font| is shared across elements, and therefore it can't
     // help when e.g., the font size is different, check once more if this
     // generic family is already prewarmed.
-    {
-      AutoLockForParallelTextShaping guard(prewarmed_generic_families_lock_);
-      const auto result =
-          prewarmed_generic_families_.insert(family.FamilyName());
-      if (!result.is_new_entry)
-        return;
-    }
+    const auto result = prewarmed_generic_families_.insert(family.FamilyName());
+    if (!result.is_new_entry)
+      return;
     const AtomicString& family_name =
         FamilyNameFromSettings(font_description, family);
     if (!family_name.IsEmpty())
@@ -335,13 +145,13 @@
     return;
   }
 
-  if (auto face =
+  if (CSSSegmentedFontFace* face =
           font_face_cache_->Get(font_description, family.FamilyName())) {
     face->WillUseFontData(font_description, text);
     return;
   }
 
-  if (family.IsPrewarmed() || UNLIKELY(family.FamilyName().IsEmpty()))
+  if (family.IsPrewarmed())
     return;
   family.SetIsPrewarmed();
   FontCache::PrewarmFamily(family.FamilyName());
@@ -350,7 +160,8 @@
 void CSSFontSelectorBase::WillUseRange(const FontDescription& font_description,
                                        const AtomicString& family,
                                        const FontDataForRangeSet& range_set) {
-  if (auto face = font_face_cache_->Get(font_description, family))
+  if (CSSSegmentedFontFace* face =
+          font_face_cache_->Get(font_description, family))
     face->WillUseRange(font_description, range_set);
 }
 
diff --git a/third_party/blink/renderer/core/css/css_font_selector_base.h b/third_party/blink/renderer/core/css/css_font_selector_base.h
index 5c4047d0..00eea28 100644
--- a/third_party/blink/renderer/core/css/css_font_selector_base.h
+++ b/third_party/blink/renderer/core/css/css_font_selector_base.h
@@ -9,7 +9,6 @@
 #include "third_party/blink/renderer/core/css/font_face_cache.h"
 #include "third_party/blink/renderer/platform/fonts/font_selector.h"
 #include "third_party/blink/renderer/platform/fonts/generic_font_family_settings.h"
-#include "third_party/blink/renderer/platform/fonts/lock_for_parallel_text_shaping.h"
 #include "third_party/blink/renderer/platform/wtf/forward.h"
 
 namespace blink {
@@ -47,23 +46,23 @@
   void ReportFontLookupByUniqueOrFamilyName(
       const AtomicString& name,
       const FontDescription& font_description,
-      scoped_refptr<SimpleFontData> resulting_font_data) override;
+      SimpleFontData* resulting_font_data) override;
 
   void ReportFontLookupByUniqueNameOnly(
       const AtomicString& name,
       const FontDescription& font_description,
-      scoped_refptr<SimpleFontData> resulting_font_data,
+      SimpleFontData* resulting_font_data,
       bool is_loading_fallback = false) override;
 
   void ReportFontLookupByFallbackCharacter(
       UChar32 fallback_character,
       FontFallbackPriority fallback_priority,
       const FontDescription& font_description,
-      scoped_refptr<SimpleFontData> resulting_font_data) override;
+      SimpleFontData* resulting_font_data) override;
 
   void ReportLastResortFallbackFontLookup(
       const FontDescription& font_description,
-      scoped_refptr<SimpleFontData> resulting_font_data) override;
+      SimpleFontData* resulting_font_data) override;
 
   void ReportFontFamilyLookupByGenericFamily(
       const AtomicString& generic_font_family_name,
@@ -76,18 +75,9 @@
   void ReportEmojiSegmentGlyphCoverage(unsigned num_clusters,
                                        unsigned num_broken_clusters) override;
 
-  bool IsContextThread() const override;
-
   void Trace(Visitor*) const override;
 
  protected:
-  explicit CSSFontSelectorBase(
-      scoped_refptr<base::SingleThreadTaskRunner> task_runner);
-
-  // TODO(crbug.com/383860): We should get rid of `IsAlive()` once lifetime
-  // issue of `CSSFontSelector` is solved. It will be alive after `TreeScope`
-  // is dead.
-  virtual bool IsAlive() const { return true; }
   virtual FontMatchingMetrics* GetFontMatchingMetrics() const = 0;
   virtual UseCounter* GetUseCounter() const = 0;
 
@@ -99,12 +89,7 @@
 
   Member<FontFaceCache> font_face_cache_;
   GenericFontFamilySettings generic_font_family_settings_;
-  LockForParallelTextShaping prewarmed_generic_families_lock_;
-  HashSet<AtomicString> prewarmed_generic_families_
-      GUARDED_BY(prewarmed_generic_families_lock_);
-#if defined(USE_PARALLEL_TEXT_SHAPING)
-  scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
-#endif
+  HashSet<AtomicString> prewarmed_generic_families_;
 };
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/css/css_segmented_font_face.cc b/third_party/blink/renderer/core/css/css_segmented_font_face.cc
index b859dbe..eec016f9 100644
--- a/third_party/blink/renderer/core/css/css_segmented_font_face.cc
+++ b/third_party/blink/renderer/core/css/css_segmented_font_face.cc
@@ -37,7 +37,6 @@
 #include "third_party/blink/renderer/platform/fonts/font_face_creation_params.h"
 #include "third_party/blink/renderer/platform/fonts/segmented_font_data.h"
 #include "third_party/blink/renderer/platform/fonts/simple_font_data.h"
-#include "third_party/blink/renderer/platform/heap/thread_state.h"
 
 // See comment below in CSSSegmentedFontFace::GetFontData - the cache from
 // CSSSegmentedFontFace (which represents a group of @font-face declarations
@@ -52,23 +51,21 @@
 namespace blink {
 
 // static
-scoped_refptr<CSSSegmentedFontFace> CSSSegmentedFontFace::Create(
+CSSSegmentedFontFace* CSSSegmentedFontFace::Create(
     FontSelectionCapabilities capabilities) {
-  return base::AdoptRef(new CSSSegmentedFontFace(capabilities));
+  return MakeGarbageCollected<CSSSegmentedFontFace>(capabilities);
 }
 
 CSSSegmentedFontFace::CSSSegmentedFontFace(
     FontSelectionCapabilities font_selection_capabilities)
     : font_selection_capabilities_(font_selection_capabilities),
       font_data_table_(kFontDataTableMaxSize),
-      approximate_character_count_(0) {
-  DCHECK(ThreadState::Current()->GetIsolate());
-}
+      font_faces_(MakeGarbageCollected<FontFaceList>()),
+      approximate_character_count_(0) {}
 
 CSSSegmentedFontFace::~CSSSegmentedFontFace() = default;
 
 void CSSSegmentedFontFace::PruneTable() {
-  lock_.AssertAcquired();
   // Make sure the glyph page tree prunes out all uses of this custom font.
   if (!font_data_table_.size())
     return;
@@ -77,9 +74,8 @@
 }
 
 bool CSSSegmentedFontFace::IsValid() const {
-  lock_.AssertAcquired();
   // Valid if at least one font face is valid.
-  return font_faces_.ForEachUntilTrue(
+  return font_faces_->ForEachUntilTrue(
       WTF::BindRepeating([](Member<FontFace> font_face) -> bool {
         if (font_face->CssFontFace()->IsValid())
           return true;
@@ -88,21 +84,18 @@
 }
 
 void CSSSegmentedFontFace::FontFaceInvalidated() {
-  AutoLockForParallelTextShaping guard(lock_);
   PruneTable();
 }
 
 void CSSSegmentedFontFace::AddFontFace(FontFace* font_face,
                                        bool css_connected) {
-  AutoLockForParallelTextShaping guard(lock_);
   PruneTable();
   font_face->CssFontFace()->AddSegmentedFontFace(this);
-  font_faces_.Insert(font_face, css_connected);
+  font_faces_->Insert(font_face, css_connected);
 }
 
 void CSSSegmentedFontFace::RemoveFontFace(FontFace* font_face) {
-  AutoLockForParallelTextShaping guard(lock_);
-  if (!font_faces_.Erase(font_face))
+  if (!font_faces_->Erase(font_face))
     return;
 
   PruneTable();
@@ -111,7 +104,6 @@
 
 scoped_refptr<FontData> CSSSegmentedFontFace::GetFontData(
     const FontDescription& font_description) {
-  AutoLockForParallelTextShaping guard(lock_);
   if (!IsValid())
     return nullptr;
 
@@ -152,7 +144,7 @@
       font_selection_request.slope >= ItalicSlopeValue() &&
       font_description.SyntheticItalicAllowed());
 
-  font_faces_.ForEachReverse(WTF::BindRepeating(
+  font_faces_->ForEachReverse(WTF::BindRepeating(
       [](const FontDescription& requested_font_description,
          scoped_refptr<SegmentedFontData> created_font_data,
          Member<FontFace> font_face) {
@@ -189,12 +181,10 @@
 
 void CSSSegmentedFontFace::WillUseFontData(
     const FontDescription& font_description,
-    const StringView& text) {
-  // This function is called from main thread or worker thread.
-  AutoLockForParallelTextShaping guard(lock_);
+    const String& text) {
   approximate_character_count_ += text.length();
-  font_faces_.ForEachReverseUntilTrue(WTF::BindRepeating(
-      [](const FontDescription& font_description, const StringView& text,
+  font_faces_->ForEachReverseUntilTrue(WTF::BindRepeating(
+      [](const FontDescription& font_description, const String& text,
          Member<FontFace> font_face) -> bool {
         if (font_face->LoadStatus() != FontFace::kUnloaded)
           return true;
@@ -208,11 +198,10 @@
 void CSSSegmentedFontFace::WillUseRange(
     const blink::FontDescription& font_description,
     const blink::FontDataForRangeSet& range_set) {
-  AutoLockForParallelTextShaping guard(lock_);
   // Iterating backwards since later defined unicode-range faces override
   // previously defined ones, according to the CSS3 fonts module.
   // https://drafts.csswg.org/css-fonts/#composite-fonts
-  font_faces_.ForEachReverseUntilTrue(WTF::BindRepeating(
+  font_faces_->ForEachReverseUntilTrue(WTF::BindRepeating(
       [](const blink::FontDescription& font_description,
          const blink::FontDataForRangeSet& range_set,
          Member<FontFace> font_face) -> bool {
@@ -225,8 +214,7 @@
 }
 
 bool CSSSegmentedFontFace::CheckFont(const String& text) const {
-  AutoLockForParallelTextShaping guard(lock_);
-  return font_faces_.ForEachUntilFalse(WTF::BindRepeating(
+  return font_faces_->ForEachUntilFalse(WTF::BindRepeating(
       [](const String& text, Member<FontFace> font_face) -> bool {
         if (font_face->LoadStatus() != FontFace::kLoaded &&
             font_face->CssFontFace()->Ranges()->IntersectsWith(text))
@@ -238,10 +226,9 @@
 
 void CSSSegmentedFontFace::Match(const String& text,
                                  HeapVector<Member<FontFace>>* faces) const {
-  AutoLockForParallelTextShaping guard(lock_);
   // WTF::BindRepeating requires WrapPersistent around |faces|, which is fine,
   // because the wrap's lifetime is contained to this function.
-  font_faces_.ForEach(WTF::BindRepeating(
+  font_faces_->ForEach(WTF::BindRepeating(
       [](const String& text, HeapVector<Member<FontFace>>* faces,
          Member<FontFace> font_face) {
         if (font_face->CssFontFace()->Ranges()->IntersectsWith(text))
@@ -251,7 +238,6 @@
 }
 
 void CSSSegmentedFontFace::Trace(Visitor* visitor) const {
-  AutoLockForParallelTextShaping guard(lock_);
   visitor->Trace(font_faces_);
 }
 
diff --git a/third_party/blink/renderer/core/css/css_segmented_font_face.h b/third_party/blink/renderer/core/css/css_segmented_font_face.h
index 019ef6f..f74aad11 100644
--- a/third_party/blink/renderer/core/css/css_segmented_font_face.h
+++ b/third_party/blink/renderer/core/css/css_segmented_font_face.h
@@ -27,10 +27,8 @@
 #define THIRD_PARTY_BLINK_RENDERER_CORE_CSS_CSS_SEGMENTED_FONT_FACE_H_
 
 #include "base/callback.h"
-#include "third_party/blink/renderer/core/core_export.h"
 #include "third_party/blink/renderer/platform/fonts/font_cache_key.h"
 #include "third_party/blink/renderer/platform/fonts/font_selection_types.h"
-#include "third_party/blink/renderer/platform/fonts/lock_for_parallel_text_shaping.h"
 #include "third_party/blink/renderer/platform/fonts/segmented_font_data.h"
 #include "third_party/blink/renderer/platform/heap/collection_support/heap_linked_hash_set.h"
 #include "third_party/blink/renderer/platform/heap/collection_support/heap_vector.h"
@@ -38,7 +36,6 @@
 #include "third_party/blink/renderer/platform/wtf/forward.h"
 #include "third_party/blink/renderer/platform/wtf/lru_cache.h"
 #include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
-#include "third_party/blink/renderer/platform/wtf/thread_safe_ref_counted.h"
 #include "third_party/blink/renderer/platform/wtf/vector.h"
 
 namespace blink {
@@ -54,18 +51,8 @@
 // Note however, |Insert| has to be instructed which sub-list to insert it to.
 // Iterating over the combined set, behaves as if all non-CSS-connected
 // FontFaces were stored after the CSS-connected ones.
-class FontFaceList final {
-  DISALLOW_NEW();
-
-  // TODO(yosin): Once Oilpan allowed mixed thread heap, we should make
-  // |FontFaceList| as |HeapLinkedHashSet<T>|.
-  class FontFaceListPart : public LinkedHashSet<Member<FontFace>> {
-   public:
-    void Trace(Visitor* visitor) const {
-      for (auto& entry : *this)
-        visitor->Trace(*entry);
-    }
-  };
+class FontFaceList : public GarbageCollected<FontFaceList> {
+  using FontFaceListPart = HeapLinkedHashSet<Member<FontFace>>;
 
  public:
   bool IsEmpty() const;
@@ -99,13 +86,12 @@
   FontFaceListPart non_css_connected_face_;
 };
 
-// TODO(yosin): Once Oilpan allowed mixed thread heap, we should make
-// |CSSSegmentedFontFace| as |GarbageCollected<T>|.
-class CORE_EXPORT CSSSegmentedFontFace final
-    : public RefCountedWillBeThreadSafeForParallelTextShaping<
-          CSSSegmentedFontFace> {
+class CSSSegmentedFontFace final
+    : public GarbageCollected<CSSSegmentedFontFace> {
  public:
-  static scoped_refptr<CSSSegmentedFontFace> Create(FontSelectionCapabilities);
+  static CSSSegmentedFontFace* Create(FontSelectionCapabilities);
+
+  explicit CSSSegmentedFontFace(FontSelectionCapabilities);
   ~CSSSegmentedFontFace();
 
   FontSelectionCapabilities GetFontSelectionCapabilities() const {
@@ -114,25 +100,18 @@
 
   // Called when status of a FontFace has changed (e.g. loaded or timed out)
   // so cached FontData must be discarded.
-  void FontFaceInvalidated() LOCKS_EXCLUDED(lock_);
+  void FontFaceInvalidated();
 
-  void AddFontFace(FontFace*, bool css_connected) LOCKS_EXCLUDED(lock_);
-  void RemoveFontFace(FontFace*) LOCKS_EXCLUDED(lock_);
-  bool IsEmpty() const LOCKS_EXCLUDED(lock_) {
-    AutoLockForParallelTextShaping guard(lock_);
-    return font_faces_.IsEmpty();
-  }
+  void AddFontFace(FontFace*, bool css_connected);
+  void RemoveFontFace(FontFace*);
+  bool IsEmpty() const { return font_faces_->IsEmpty(); }
 
-  scoped_refptr<FontData> GetFontData(const FontDescription&)
-      LOCKS_EXCLUDED(lock_);
+  scoped_refptr<FontData> GetFontData(const FontDescription&);
 
-  bool CheckFont(const String&) const LOCKS_EXCLUDED(lock_);
-  void Match(const String&, HeapVector<Member<FontFace>>*) const
-      LOCKS_EXCLUDED(lock_);
-  void WillUseFontData(const FontDescription&, const StringView& text)
-      LOCKS_EXCLUDED(lock_);
-  void WillUseRange(const FontDescription&, const blink::FontDataForRangeSet&)
-      LOCKS_EXCLUDED(lock_);
+  bool CheckFont(const String&) const;
+  void Match(const String&, HeapVector<Member<FontFace>>*) const;
+  void WillUseFontData(const FontDescription&, const String& text);
+  void WillUseRange(const FontDescription&, const blink::FontDataForRangeSet&);
   size_t ApproximateCharacterCount() const {
     return approximate_character_count_;
   }
@@ -140,19 +119,16 @@
   void Trace(Visitor*) const;
 
  private:
-  explicit CSSSegmentedFontFace(FontSelectionCapabilities);
-
-  void PruneTable() EXCLUSIVE_LOCKS_REQUIRED(lock_);
-  bool IsValid() const EXCLUSIVE_LOCKS_REQUIRED(lock_);
+  void PruneTable();
+  bool IsValid() const;
 
   FontSelectionCapabilities font_selection_capabilities_;
 
-  mutable LockForParallelTextShaping lock_;
-  WTF::LruCache<FontCacheKey, scoped_refptr<SegmentedFontData>> font_data_table_
-      GUARDED_BY(lock_);
+  WTF::LruCache<FontCacheKey, scoped_refptr<SegmentedFontData>>
+      font_data_table_;
 
   // All non-CSS-connected FontFaces are stored after the CSS-connected ones.
-  FontFaceList font_faces_ GUARDED_BY(lock_);
+  Member<FontFaceList> font_faces_;
 
   // Approximate number of characters styled with this CSSSegmentedFontFace.
   // LayoutText::StyleDidChange() increments this on the first
diff --git a/third_party/blink/renderer/core/css/font_face.cc b/third_party/blink/renderer/core/css/font_face.cc
index a8c4763..b4a0cc4b 100644
--- a/third_party/blink/renderer/core/css/font_face.cc
+++ b/third_party/blink/renderer/core/css/font_face.cc
@@ -872,8 +872,7 @@
         RemoteFontFaceSource* source =
             MakeGarbageCollected<RemoteFontFaceSource>(
                 css_font_face_, font_selector,
-                CSSValueToFontDisplay(display_.Get()),
-                GetExecutionContext()->GetTaskRunner(TaskType::kFontLoading));
+                CSSValueToFontDisplay(display_.Get()));
         item.Fetch(context, source);
         css_font_face_->AddSource(source);
       }
diff --git a/third_party/blink/renderer/core/css/font_face_cache.cc b/third_party/blink/renderer/core/css/font_face_cache.cc
index 435fd72..068c9b0 100644
--- a/third_party/blink/renderer/core/css/font_face_cache.cc
+++ b/third_party/blink/renderer/core/css/font_face_cache.cc
@@ -52,9 +52,9 @@
                                                         bool css_connected) {
   const auto result = map_.insert(font_face->family(), nullptr);
   if (result.is_new_entry)
-    result.stored_value->value = base::MakeRefCounted<CapabilitiesSet>();
+    result.stored_value->value = MakeGarbageCollected<CapabilitiesSet>();
 
-  scoped_refptr<CapabilitiesSet> family_faces = result.stored_value->value;
+  CapabilitiesSet* family_faces = result.stored_value->value;
   family_faces->AddFontFace(font_face, css_connected);
 }
 
@@ -73,7 +73,6 @@
 
 void FontFaceCache::FontSelectionQueryCache::Remove(
     const AtomicString& family) {
-  AutoLockForParallelTextShaping guard(lock_);
   map_.erase(family);
 }
 
@@ -104,7 +103,7 @@
   if (it == map_.end())
     return false;
 
-  scoped_refptr<CapabilitiesSet> family_segmented_faces = it->value;
+  CapabilitiesSet* family_segmented_faces = it->value;
   if (family_segmented_faces->RemoveFontFace(font_face))
     map_.erase(it);
   return true;
@@ -127,7 +126,7 @@
   if (it == map_.end())
     return false;
 
-  scoped_refptr<CSSSegmentedFontFace> segmented_font_face = it->value;
+  CSSSegmentedFontFace* segmented_font_face = it->value;
   segmented_font_face->RemoveFontFace(font_face);
   if (!segmented_font_face->IsEmpty())
     return false;
@@ -156,7 +155,6 @@
 }
 
 void FontFaceCache::FontSelectionQueryCache::Clear() {
-  AutoLockForParallelTextShaping guard(lock_);
   map_.clear();
 }
 
@@ -167,21 +165,21 @@
   version_ = g_version.GetNext();
 }
 
-scoped_refptr<FontFaceCache::CapabilitiesSet>
-FontFaceCache::SegmentedFacesByFamily::Find(const AtomicString& family) const {
+FontFaceCache::CapabilitiesSet* FontFaceCache::SegmentedFacesByFamily::Find(
+    const AtomicString& family) const {
   const auto it = map_.find(family);
   if (it == map_.end() || it->value->IsEmpty())
     return nullptr;
   return it->value;
 }
 
-scoped_refptr<CSSSegmentedFontFace> FontFaceCache::Get(
+CSSSegmentedFontFace* FontFaceCache::Get(
     const FontDescription& font_description,
     const AtomicString& family) {
   if (family.IsEmpty())
     return nullptr;
 
-  scoped_refptr<CapabilitiesSet> family_faces = segmented_faces_.Find(family);
+  CapabilitiesSet* family_faces = segmented_faces_.Find(family);
   if (!family_faces)
     return nullptr;
 
@@ -189,22 +187,19 @@
       font_description.GetFontSelectionRequest(), family, family_faces);
 }
 
-scoped_refptr<CSSSegmentedFontFace>
-FontFaceCache::FontSelectionQueryCache::GetOrCreate(
+CSSSegmentedFontFace* FontFaceCache::FontSelectionQueryCache::GetOrCreate(
     const FontSelectionRequest& request,
     const AtomicString& family,
-    scoped_refptr<CapabilitiesSet> family_faces) {
-  AutoLockForParallelTextShaping guard(lock_);
+    CapabilitiesSet* family_faces) {
   const auto result = map_.insert(family, nullptr);
   if (result.is_new_entry) {
     result.stored_value->value =
-        base::MakeRefCounted<FontSelectionQueryResult>();
+        MakeGarbageCollected<FontSelectionQueryResult>();
   }
   return result.stored_value->value->GetOrCreate(request, *family_faces);
 }
 
-scoped_refptr<CSSSegmentedFontFace>
-FontFaceCache::FontSelectionQueryResult::GetOrCreate(
+CSSSegmentedFontFace* FontFaceCache::FontSelectionQueryResult::GetOrCreate(
     const FontSelectionRequest& request,
     const CapabilitiesSet& family_faces) {
   const auto face_entry = map_.insert(request, nullptr);
@@ -227,7 +222,7 @@
                                                   all_faces_boundaries);
   for (const auto& item : family_faces) {
     const FontSelectionCapabilities& candidate_key = item.key;
-    scoped_refptr<CSSSegmentedFontFace> candidate_value = item.value;
+    CSSSegmentedFontFace* candidate_value = item.value;
     if (!face_entry.stored_value->value ||
         font_selection_algorithm.IsBetterMatchForRequest(
             candidate_key,
@@ -257,24 +252,19 @@
 }
 
 void FontFaceCache::CapabilitiesSet::Trace(Visitor* visitor) const {
-  for (auto& entry : map_)
-    visitor->Trace(*entry.value);
+  visitor->Trace(map_);
 }
 
 void FontFaceCache::FontSelectionQueryCache::Trace(Visitor* visitor) const {
-  AutoLockForParallelTextShaping guard(lock_);
-  for (auto& entry : map_)
-    visitor->Trace(*entry.value);
+  visitor->Trace(map_);
 }
 
 void FontFaceCache::FontSelectionQueryResult::Trace(Visitor* visitor) const {
-  for (auto& entry : map_)
-    visitor->Trace(*entry.value);
+  visitor->Trace(map_);
 }
 
 void FontFaceCache::SegmentedFacesByFamily::Trace(Visitor* visitor) const {
-  for (auto& entry : map_)
-    visitor->Trace(*entry.value);
+  visitor->Trace(map_);
 }
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/css/font_face_cache.h b/third_party/blink/renderer/core/css/font_face_cache.h
index f7f381a..927a5da 100644
--- a/third_party/blink/renderer/core/css/font_face_cache.h
+++ b/third_party/blink/renderer/core/css/font_face_cache.h
@@ -32,13 +32,11 @@
 #include "third_party/blink/renderer/core/css/font_face.h"
 #include "third_party/blink/renderer/core/css/style_rule.h"
 #include "third_party/blink/renderer/platform/fonts/font_selection_types.h"
-#include "third_party/blink/renderer/platform/fonts/lock_for_parallel_text_shaping.h"
 #include "third_party/blink/renderer/platform/heap/collection_support/heap_hash_map.h"
 #include "third_party/blink/renderer/platform/heap/garbage_collected.h"
 #include "third_party/blink/renderer/platform/wtf/forward.h"
 #include "third_party/blink/renderer/platform/wtf/linked_hash_set.h"
 #include "third_party/blink/renderer/platform/wtf/text/string_hash.h"
-#include "third_party/blink/renderer/platform/wtf/thread_safe_ref_counted.h"
 
 namespace blink {
 
@@ -60,8 +58,7 @@
 
   // FIXME: It's sort of weird that add/remove uses StyleRuleFontFace* as key,
   // but this function uses FontDescription/family pair.
-  scoped_refptr<CSSSegmentedFontFace> Get(const FontDescription&,
-                                          const AtomicString& family);
+  CSSSegmentedFontFace* Get(const FontDescription&, const AtomicString& family);
 
   const HeapLinkedHashSet<Member<FontFace>>& CssConnectedFontFaces() const {
     return css_connected_font_faces_;
@@ -83,11 +80,9 @@
   // A second lookup table caches the previously received FontSelectionRequest
   // queries, which is: HeapHashMap <String, HeapHashMap<FontSelectionRequest,
   // CSSSegmentedFontFace>>
-  class CapabilitiesSet final
-      : public RefCountedWillBeThreadSafeForParallelTextShaping<
-            CapabilitiesSet> {
+  class CapabilitiesSet final : public GarbageCollected<CapabilitiesSet> {
     using Map =
-        HashMap<FontSelectionCapabilities, scoped_refptr<CSSSegmentedFontFace>>;
+        HeapHashMap<FontSelectionCapabilities, Member<CSSSegmentedFontFace>>;
 
    public:
     Map::const_iterator begin() const { return map_.begin(); }
@@ -97,7 +92,7 @@
     void AddFontFace(FontFace* font_face, bool css_connected);
     bool IsEmpty() const { return map_.IsEmpty(); }
 
-    // Returns true if associated `CSSSegmentedFontFace` is empty.
+    // Returns true if associated |CSSSegmentedFontFace| is empty.
     bool RemoveFontFace(FontFace* font_face);
 
     void Trace(Visitor*) const;
@@ -106,19 +101,18 @@
     Map map_;
   };
 
-  // The map from `FontSelectionRequestKey` to `CSSSegmentedFontFace`.
+  // The map from |FontSelectionRequestKey| to |CSSSegmentedFontFace|.
   class FontSelectionQueryResult final
-      : public RefCountedWillBeThreadSafeForParallelTextShaping<
-            FontSelectionQueryResult> {
-    using Map = HashMap<FontSelectionRequestKey,
-                        scoped_refptr<CSSSegmentedFontFace>,
-                        FontSelectionRequestKeyHash,
-                        WTF::SimpleClassHashTraits<FontSelectionRequestKey>>;
+      : public GarbageCollected<FontSelectionQueryResult> {
+    using Map =
+        HeapHashMap<FontSelectionRequestKey,
+                    Member<CSSSegmentedFontFace>,
+                    FontSelectionRequestKeyHash,
+                    WTF::SimpleClassHashTraits<FontSelectionRequestKey>>;
 
    public:
-    scoped_refptr<CSSSegmentedFontFace> GetOrCreate(
-        const FontSelectionRequest& request,
-        const CapabilitiesSet& family_faces);
+    CSSSegmentedFontFace* GetOrCreate(const FontSelectionRequest& request,
+                                      const CapabilitiesSet& family_faces);
 
     void Trace(Visitor*) const;
 
@@ -126,38 +120,36 @@
     Map map_;
   };
 
-  // The map from font family name to `FontSelectionQueryResult`.
+  // The map from font family name to |FontSelectionQueryResult|.
   class FontSelectionQueryCache final {
-    using Map = HashMap<String,
-                        scoped_refptr<FontSelectionQueryResult>,
-                        CaseFoldingHash>;
+    DISALLOW_NEW();
+
+    using Map =
+        HeapHashMap<String, Member<FontSelectionQueryResult>, CaseFoldingHash>;
 
    public:
-    void Clear() LOCKS_EXCLUDED(lock_);
-    scoped_refptr<CSSSegmentedFontFace> GetOrCreate(
-        const FontSelectionRequest& request,
-        const AtomicString& family,
-        scoped_refptr<CapabilitiesSet> family_faces) LOCKS_EXCLUDED(lock_);
-    void Remove(const AtomicString& family) LOCKS_EXCLUDED(lock_);
+    void Clear();
+    CSSSegmentedFontFace* GetOrCreate(const FontSelectionRequest& request,
+                                      const AtomicString& family,
+                                      CapabilitiesSet* family_faces);
+    void Remove(const AtomicString& family);
 
-    void Trace(Visitor*) const LOCKS_EXCLUDED(lock_);
+    void Trace(Visitor*) const;
 
    private:
-    mutable LockForParallelTextShaping lock_;
-    Map map_ GUARDED_BY(lock_);
+    Map map_;
   };
 
-  // The map from font family name to `CapabilitiesSet`.
+  // The map from font family name to |CapabilitiesSet|.
   class SegmentedFacesByFamily final {
-    using Map =
-        HashMap<String, scoped_refptr<CapabilitiesSet>, CaseFoldingHash>;
+    DISALLOW_NEW();
 
    public:
     void AddFontFace(FontFace* font_face, bool css_connected);
     void Clear() { map_.clear(); }
-    scoped_refptr<CapabilitiesSet> Find(const AtomicString& family) const;
+    CapabilitiesSet* Find(const AtomicString& family) const;
     bool IsEmpty() const { return map_.IsEmpty(); }
-    // Returns true if `font_face` is removed from `map_`.
+    // Returns true if |font_face| is removed from |map_|.
     bool RemoveFontFace(FontFace* font_face);
 
     size_t GetNumSegmentedFacesForTesting() const;
@@ -165,6 +157,8 @@
     void Trace(Visitor*) const;
 
    private:
+    using Map = HeapHashMap<String, Member<CapabilitiesSet>, CaseFoldingHash>;
+
     Map map_;
   };
 
diff --git a/third_party/blink/renderer/core/css/font_face_cache_test.cc b/third_party/blink/renderer/core/css/font_face_cache_test.cc
index fec64cae..35b59b13 100644
--- a/third_party/blink/renderer/core/css/font_face_cache_test.cc
+++ b/third_party/blink/renderer/core/css/font_face_cache_test.cc
@@ -146,7 +146,7 @@
 
   const FontDescription& description_condensed = FontDescriptionForRequest(
       CondensedWidthValue(), NormalSlopeValue(), NormalWeightValue());
-  scoped_refptr<CSSSegmentedFontFace> result =
+  CSSSegmentedFontFace* result =
       cache_->Get(description_condensed, kFontNameForTesting);
   ASSERT_TRUE(result);
 
@@ -177,7 +177,7 @@
 
   const FontDescription& description_bold = FontDescriptionForRequest(
       NormalWidthValue(), NormalSlopeValue(), BoldWeightValue());
-  scoped_refptr<CSSSegmentedFontFace> result =
+  CSSSegmentedFontFace* result =
       cache_->Get(description_bold, kFontNameForTesting);
   ASSERT_TRUE(result);
   FontSelectionCapabilities result_capabilities =
@@ -272,7 +272,7 @@
           }
         }
         for (FontDescription& test_description : test_descriptions) {
-          scoped_refptr<CSSSegmentedFontFace> result =
+          CSSSegmentedFontFace* result =
               cache_->Get(test_description, kFontNameForTesting);
           ASSERT_TRUE(result);
           FontSelectionCapabilities result_capabilities =
@@ -320,7 +320,7 @@
 
   const FontDescription& description_bold = FontDescriptionForRequest(
       NormalWidthValue(), NormalSlopeValue(), BoldWeightValue());
-  scoped_refptr<CSSSegmentedFontFace> result =
+  CSSSegmentedFontFace* result =
       cache_->Get(description_bold, kFontNameForTesting);
   ASSERT_TRUE(result);
   FontSelectionCapabilities result_capabilities =
@@ -372,7 +372,7 @@
 
   const FontDescription& description_expanded = FontDescriptionForRequest(
       NormalWidthValue(), NormalSlopeValue(), test_weight);
-  scoped_refptr<CSSSegmentedFontFace> result =
+  CSSSegmentedFontFace* result =
       cache_->Get(description_expanded, kFontNameForTesting);
   ASSERT_TRUE(result);
   ASSERT_EQ(result->GetFontSelectionCapabilities().weight.minimum,
@@ -429,7 +429,7 @@
 
   const FontDescription& description_expanded = FontDescriptionForRequest(
       FontSelectionValue(105), NormalSlopeValue(), NormalWeightValue());
-  scoped_refptr<CSSSegmentedFontFace> result =
+  CSSSegmentedFontFace* result =
       cache_->Get(description_expanded, kFontNameForTesting);
   ASSERT_TRUE(result);
   FontSelectionCapabilities result_capabilities =
@@ -482,7 +482,7 @@
 
   const FontDescription& description_italic = FontDescriptionForRequest(
       NormalWidthValue(), ItalicSlopeValue(), NormalWeightValue());
-  scoped_refptr<CSSSegmentedFontFace> result =
+  CSSSegmentedFontFace* result =
       cache_->Get(description_italic, kFontNameForTesting);
   ASSERT_TRUE(result);
   FontSelectionCapabilities result_capabilities =
diff --git a/third_party/blink/renderer/core/css/font_face_set.cc b/third_party/blink/renderer/core/css/font_face_set.cc
index 49d5433..f4e144d 100644
--- a/third_party/blink/renderer/core/css/font_face_set.cc
+++ b/third_party/blink/renderer/core/css/font_face_set.cc
@@ -184,7 +184,7 @@
        f = f->Next()) {
     if (f->FamilyIsGeneric())
       continue;
-    auto segmented_font_face =
+    CSSSegmentedFontFace* segmented_font_face =
         font_face_cache->Get(font.GetFontDescription(), f->FamilyName());
     if (segmented_font_face)
       segmented_font_face->Match(text, faces);
@@ -220,7 +220,7 @@
        f = f->Next()) {
     if (f->FamilyIsGeneric())
       continue;
-    auto face =
+    CSSSegmentedFontFace* face =
         font_face_cache->Get(font.GetFontDescription(), f->FamilyName());
     if (face) {
       if (!face->CheckFont(text))
diff --git a/third_party/blink/renderer/core/css/offscreen_font_selector.cc b/third_party/blink/renderer/core/css/offscreen_font_selector.cc
index 7b7d462..fa1b592f 100644
--- a/third_party/blink/renderer/core/css/offscreen_font_selector.cc
+++ b/third_party/blink/renderer/core/css/offscreen_font_selector.cc
@@ -12,8 +12,7 @@
 namespace blink {
 
 OffscreenFontSelector::OffscreenFontSelector(WorkerGlobalScope* worker)
-    : CSSFontSelectorBase(worker->GetTaskRunner(TaskType::kInternalDefault)),
-      worker_(worker) {
+    : worker_(worker) {
   DCHECK(worker);
   font_face_cache_ = MakeGarbageCollected<FontFaceCache>();
   FontCache::Get().AddClient(this);
@@ -44,7 +43,8 @@
     const FontDescription& font_description,
     const FontFamily& font_family) {
   const auto& family_name = font_family.FamilyName();
-  if (auto face = font_face_cache_->Get(font_description, family_name)) {
+  if (CSSSegmentedFontFace* face =
+          font_face_cache_->Get(font_description, family_name)) {
     ReportWebFontFamily(family_name);
     return face->GetFontData(font_description);
   }
diff --git a/third_party/blink/renderer/core/css/remote_font_face_source.cc b/third_party/blink/renderer/core/css/remote_font_face_source.cc
index cf944c61..11d1ef1 100644
--- a/third_party/blink/renderer/core/css/remote_font_face_source.cc
+++ b/third_party/blink/renderer/core/css/remote_font_face_source.cc
@@ -28,10 +28,6 @@
 #include "third_party/blink/renderer/platform/loader/fetch/resource_fetcher.h"
 #include "third_party/blink/renderer/platform/loader/fetch/resource_load_priority.h"
 #include "third_party/blink/renderer/platform/network/network_state_notifier.h"
-#include "third_party/blink/renderer/platform/scheduler/public/post_cross_thread_task.h"
-#include "third_party/blink/renderer/platform/scheduler/public/thread.h"
-#include "third_party/blink/renderer/platform/wtf/cross_thread_copier.h"
-#include "third_party/blink/renderer/platform/wtf/cross_thread_functional.h"
 
 namespace blink {
 
@@ -137,16 +133,11 @@
   return kSwapPeriod;
 }
 
-RemoteFontFaceSource::RemoteFontFaceSource(
-    CSSFontFace* css_font_face,
-    FontSelector* font_selector,
-    FontDisplay display,
-    scoped_refptr<base::SingleThreadTaskRunner> task_runner)
+RemoteFontFaceSource::RemoteFontFaceSource(CSSFontFace* css_font_face,
+                                           FontSelector* font_selector,
+                                           FontDisplay display)
     : face_(css_font_face),
       font_selector_(font_selector),
-#if defined(USE_PARALLEL_TEXT_SHAPING)
-      task_runner_(task_runner),
-#endif
       // No need to report the violation here since the font is not loaded yet
       display_(
           GetFontDisplayWithDocumentPolicyCheck(display,
@@ -185,7 +176,6 @@
   ExecutionContext* execution_context = font_selector_->GetExecutionContext();
   if (!execution_context)
     return;
-  DCHECK(execution_context->IsContextThread());
   // Prevent promise rejection while shutting down the document.
   // See crbug.com/960290
   auto* window = DynamicTo<LocalDOMWindow>(execution_context);
@@ -382,29 +372,8 @@
 }
 
 void RemoteFontFaceSource::BeginLoadIfNeeded() {
-  if (IsLoaded())
+  if (IsLoaded() || !font_selector_->GetExecutionContext())
     return;
-  ExecutionContext* const execution_context =
-      font_selector_->GetExecutionContext();
-  if (!execution_context)
-    return;
-#if defined(USE_PARALLEL_TEXT_SHAPING)
-  if (!execution_context->IsContextThread()) {
-    // Following tests reache here.
-    //  * fast/css3-text/css3-text-decoration/text-decoration-skip-ink-links.html
-    //  * fast/css3-text/css3-word-break/word-break-break-all-in-span.html
-    //  * virtual/text-antialias/justify-vertical.html
-    //  * virtual/text-antialias/line-break-8bit-after-16bit.html
-    // Note: |ExecutionContext::GetTaskRunner()| works only for context
-    // thread, so we ask main thread to handle |BeginLoadIfNeeded()|.
-    PostCrossThreadTask(
-        *task_runner_, FROM_HERE,
-        CrossThreadBindOnce(&RemoteFontFaceSource::BeginLoadIfNeeded,
-                            WrapCrossThreadPersistent(this)));
-    return;
-  }
-#endif
-
   DCHECK(GetResource());
 
   SetDisplay(face_->GetFontFace()->GetFontDisplay());
@@ -412,19 +381,20 @@
   auto* font = To<FontResource>(GetResource());
   if (font->StillNeedsLoad()) {
     if (font->IsLowPriorityLoadingAllowedForRemoteFont()) {
-      execution_context->AddConsoleMessage(MakeGarbageCollected<ConsoleMessage>(
-          mojom::blink::ConsoleMessageSource::kIntervention,
-          mojom::blink::ConsoleMessageLevel::kInfo,
-          "Slow network is detected. See "
-          "https://www.chromestatus.com/feature/5636954674692096 for more "
-          "details. Fallback font will be used while loading: " +
-              font->Url().ElidedString()));
+      font_selector_->GetExecutionContext()->AddConsoleMessage(
+          MakeGarbageCollected<ConsoleMessage>(
+              mojom::ConsoleMessageSource::kIntervention,
+              mojom::ConsoleMessageLevel::kInfo,
+              "Slow network is detected. See "
+              "https://www.chromestatus.com/feature/5636954674692096 for more "
+              "details. Fallback font will be used while loading: " +
+                  font->Url().ElidedString()));
 
       // Set the loading priority to VeryLow only when all other clients agreed
       // that this font is not required for painting the text.
       font->DidChangePriority(ResourceLoadPriority::kVeryLow, 0);
     }
-    if (execution_context->Fetcher()->StartLoad(font))
+    if (font_selector_->GetExecutionContext()->Fetcher()->StartLoad(font))
       histograms_.LoadStarted();
   }
 
@@ -432,7 +402,9 @@
   // Note that <link rel=preload> may have initiated loading without kicking
   // off the timers.
   font->StartLoadLimitTimersIfNecessary(
-      execution_context->GetTaskRunner(TaskType::kInternalLoading).get());
+      font_selector_->GetExecutionContext()
+          ->GetTaskRunner(TaskType::kInternalLoading)
+          .get());
 
   face_->DidBeginLoad();
 }
diff --git a/third_party/blink/renderer/core/css/remote_font_face_source.h b/third_party/blink/renderer/core/css/remote_font_face_source.h
index 6fe503d..7744db3 100644
--- a/third_party/blink/renderer/core/css/remote_font_face_source.h
+++ b/third_party/blink/renderer/core/css/remote_font_face_source.h
@@ -23,10 +23,7 @@
  public:
   enum Phase { kNoLimitExceeded, kShortLimitExceeded, kLongLimitExceeded };
 
-  RemoteFontFaceSource(CSSFontFace*,
-                       FontSelector*,
-                       FontDisplay,
-                       scoped_refptr<base::SingleThreadTaskRunner>);
+  RemoteFontFaceSource(CSSFontFace*, FontSelector*, FontDisplay);
   ~RemoteFontFaceSource() override;
 
   bool IsLoading() const override;
@@ -151,11 +148,6 @@
   Member<CSSFontFace> face_;
   Member<FontSelector> font_selector_;
 
-#if defined(USE_PARALLEL_TEXT_SHAPING)
-  // Post `BeginLoadIfNeeded()` unless context thread.
-  scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
-#endif
-
   // |nullptr| if font is not loaded or failed to decode.
   scoped_refptr<FontCustomPlatformData> custom_font_data_;
   // |nullptr| if font is not loaded or failed to decode.
diff --git a/third_party/blink/renderer/core/css/style_engine_test.cc b/third_party/blink/renderer/core/css/style_engine_test.cc
index 7e24486..3c569934 100644
--- a/third_party/blink/renderer/core/css/style_engine_test.cc
+++ b/third_party/blink/renderer/core/css/style_engine_test.cc
@@ -343,10 +343,10 @@
   // There's only one font and it's bold and normal.
   EXPECT_EQ(1u, GetStyleEngine().GetFontSelector()->GetFontFaceCache()
                 ->GetNumSegmentedFacesForTesting());
-  scoped_refptr<CSSSegmentedFontFace> font_face =
-      GetStyleEngine().GetFontSelector()->GetFontFaceCache()->Get(
-          t4->GetComputedStyle()->GetFontDescription(),
-          AtomicString("Cool Font"));
+  CSSSegmentedFontFace* font_face =
+      GetStyleEngine().GetFontSelector()->GetFontFaceCache()
+      ->Get(t4->GetComputedStyle()->GetFontDescription(),
+            AtomicString("Cool Font"));
   EXPECT_TRUE(font_face);
   FontSelectionCapabilities capabilities =
       font_face->GetFontSelectionCapabilities();
diff --git a/third_party/blink/renderer/core/dom/element.idl b/third_party/blink/renderer/core/dom/element.idl
index 07ac60d..56d58d2 100644
--- a/third_party/blink/renderer/core/dom/element.idl
+++ b/third_party/blink/renderer/core/dom/element.idl
@@ -101,7 +101,9 @@
     // TODO(mkwst): Write a spec for the `TrustedHTML` variants.
     // TODO(lyf): Change the type to `[TreatNullAs=xxx] HTMLString` after
     // https://crbug.com/1058762 has been fixed.
-    [CEReactions, RuntimeCallStatsCounter=ElementInnerHTML, RaisesException=Setter] attribute [TreatNullAs=EmptyString, StringContext=TrustedHTML] DOMString innerHTML;
+    // TODO(https://crbug.com/1335986): Custom setter is needed to collect
+    // metrics, and can be removed once metrics are captured.
+    [Custom=Setter, CEReactions, RuntimeCallStatsCounter=ElementInnerHTML, RaisesException=Setter] attribute [TreatNullAs=EmptyString, StringContext=TrustedHTML] DOMString innerHTML;
     [CEReactions, RaisesException=Setter] attribute [TreatNullAs=EmptyString, StringContext=TrustedHTML] DOMString outerHTML;
     [CEReactions, RaisesException] void insertAdjacentHTML(DOMString position, HTMLString text);
 
diff --git a/third_party/blink/renderer/core/frame/local_frame_ukm_aggregator.cc b/third_party/blink/renderer/core/frame/local_frame_ukm_aggregator.cc
index 24cb711..23b64a1 100644
--- a/third_party/blink/renderer/core/frame/local_frame_ukm_aggregator.cc
+++ b/third_party/blink/renderer/core/frame/local_frame_ukm_aggregator.cc
@@ -237,15 +237,8 @@
   clock_ = clock;
 }
 
-void LocalFrameUkmAggregator::DidReachFirstContentfulPaint(
-    bool are_painting_main_frame) {
-  DCHECK(fcp_state_ != kHavePassedFCP);
-
-  if (!are_painting_main_frame) {
-    DCHECK(AllMetricsAreZero());
-    return;
-  }
-
+void LocalFrameUkmAggregator::DidReachFirstContentfulPaint() {
+  DCHECK_NE(fcp_state_, kHavePassedFCP);
   fcp_state_ = kThisFrameReachedFCP;
 }
 
@@ -626,20 +619,6 @@
     record.reset();
 }
 
-bool LocalFrameUkmAggregator::AllMetricsAreZero() {
-  if (primary_metric_.interval_count != 0)
-    return false;
-  for (auto& record : absolute_metric_records_) {
-    if (record.interval_count != 0) {
-      return false;
-    }
-    if (record.main_frame_count != 0) {
-      return false;
-    }
-  }
-  return true;
-}
-
 void LocalFrameUkmAggregator::ChooseNextFrameForTest() {
   next_frame_sample_control_for_test_ = kMustChooseNextFrame;
 }
@@ -648,4 +627,8 @@
   next_frame_sample_control_for_test_ = kMustNotChooseNextFrame;
 }
 
+bool LocalFrameUkmAggregator::IsBeforeFCPForTesting() const {
+  return fcp_state_ == kBeforeFCPSignal;
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/frame/local_frame_ukm_aggregator.h b/third_party/blink/renderer/core/frame/local_frame_ukm_aggregator.h
index 7f5ce2c..399bb6e 100644
--- a/third_party/blink/renderer/core/frame/local_frame_ukm_aggregator.h
+++ b/third_party/blink/renderer/core/frame/local_frame_ukm_aggregator.h
@@ -310,9 +310,12 @@
 
   // Inform the aggregator that we have reached First Contentful Paint.
   // The UKM event for the pre-FCP period will be recorded and UMA for
-  // aggregated contributions to FCP are reported if are_painting_main_frame
-  // is true.
-  void DidReachFirstContentfulPaint(bool are_painting_main_frame);
+  // aggregated contributions to FCP are reported.
+  // TODO(crbug.com/1330675): This is called for the main frame or local frame
+  // roots only, depending on features::kLocalFrameRootPrePostFCPMetrics. When
+  // the experiment finishes, we should let only local frame roots use this
+  // class.
+  void DidReachFirstContentfulPaint();
 
   bool InMainFrameUpdate() { return in_main_frame_update_; }
 
@@ -322,6 +325,8 @@
   // RecordEndOfFrameMetrics.
   std::unique_ptr<cc::BeginMainFrameMetrics> GetBeginMainFrameMetrics();
 
+  bool IsBeforeFCPForTesting() const;
+
  private:
   struct AbsoluteMetricRecord {
     std::unique_ptr<CustomCountHistogram> pre_fcp_uma_counter;
@@ -368,9 +373,6 @@
   void ChooseNextFrameForTest();
   void DoNotChooseNextFrameForTest();
 
-  // Used to check that we record only for the MainFrame of a document.
-  bool AllMetricsAreZero();
-
   // The caller is the owner of the |clock|. The |clock| must outlive the
   // LocalFrameUkmAggregator.
   void SetTickClockForTesting(const base::TickClock* clock);
diff --git a/third_party/blink/renderer/core/frame/local_frame_ukm_aggregator_test.cc b/third_party/blink/renderer/core/frame/local_frame_ukm_aggregator_test.cc
index e8e948f..5e7d6784 100644
--- a/third_party/blink/renderer/core/frame/local_frame_ukm_aggregator_test.cc
+++ b/third_party/blink/renderer/core/frame/local_frame_ukm_aggregator_test.cc
@@ -6,12 +6,14 @@
 
 #include "base/metrics/statistics_recorder.h"
 #include "base/test/metrics/histogram_tester.h"
+#include "base/test/scoped_feature_list.h"
 #include "base/test/test_mock_time_task_runner.h"
 #include "cc/metrics/begin_main_frame_metrics.h"
 #include "components/ukm/test_ukm_recorder.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/blink/public/common/metrics/document_update_reason.h"
 #include "third_party/blink/renderer/bindings/core/v8/v8_intersection_observer_init.h"
+#include "third_party/blink/renderer/core/paint/paint_timing.h"
 #include "third_party/blink/renderer/core/testing/intersection_observer_test_helper.h"
 #include "third_party/blink/renderer/core/testing/sim/sim_request.h"
 #include "third_party/blink/renderer/core/testing/sim/sim_test.h"
@@ -159,7 +161,7 @@
     for (int i = 0; i < LocalFrameUkmAggregator::kForcedStyleAndLayout; ++i) {
       auto timer = aggregator().GetScopedTimer(i);
       if (mark_fcp && i == static_cast<int>(LocalFrameUkmAggregator::kPaint))
-        aggregator().DidReachFirstContentfulPaint(true);
+        aggregator().DidReachFirstContentfulPaint();
       test_task_runner_->FastForwardBy(
           base::Milliseconds(millisecond_per_step));
     }
@@ -639,4 +641,34 @@
           "Blink.IntersectionObservationJavascriptCount.UpdateTime.PreFCP"),
       2);
 }
+
+static void TestLocalFrameRootPrePostFCPMetrics(
+    const LocalFrame& local_frame_root) {
+  ASSERT_FALSE(local_frame_root.IsMainFrame());
+  ASSERT_TRUE(local_frame_root.IsLocalRoot());
+  auto& ukm_aggregator = local_frame_root.View()->EnsureUkmAggregator();
+  EXPECT_TRUE(ukm_aggregator.IsBeforeFCPForTesting());
+  // Simulate the first contentful paint.
+  PaintTiming::From(*local_frame_root.GetDocument()).MarkFirstContentfulPaint();
+  EXPECT_EQ(
+      base::FeatureList::IsEnabled(features::kLocalFrameRootPrePostFCPMetrics),
+      !ukm_aggregator.IsBeforeFCPForTesting());
+}
+
+TEST_F(LocalFrameUkmAggregatorSimTest, LocalFrameRootPrePostFCPMetrics) {
+  base::test::ScopedFeatureList feature_list;
+  feature_list.InitAndEnableFeature(features::kLocalFrameRootPrePostFCPMetrics);
+  InitializeRemote();
+  TestLocalFrameRootPrePostFCPMetrics(*LocalFrameRoot().GetFrame());
+}
+
+TEST_F(LocalFrameUkmAggregatorSimTest,
+       LocalFrameRootPrePostFCPMetricsDisabled) {
+  base::test::ScopedFeatureList feature_list;
+  feature_list.InitAndDisableFeature(
+      features::kLocalFrameRootPrePostFCPMetrics);
+  InitializeRemote();
+  TestLocalFrameRootPrePostFCPMetrics(*LocalFrameRoot().GetFrame());
+}
+
 }  // namespace blink
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 91c4dffb..a8a1f6f 100644
--- a/third_party/blink/renderer/core/frame/local_frame_view.cc
+++ b/third_party/blink/renderer/core/frame/local_frame_view.cc
@@ -4726,12 +4726,20 @@
 }
 
 void LocalFrameView::OnFirstContentfulPaint() {
-  GetPage()->GetChromeClient().StopDeferringCommits(
-      *frame_, cc::PaintHoldingCommitTrigger::kFirstContentfulPaint);
-  const bool is_main_frame = frame_->IsMainFrame();
-  if (frame_->GetDocument()->ShouldMarkFontPerformance())
-    FontPerformance::MarkFirstContentfulPaint();
-  EnsureUkmAggregator().DidReachFirstContentfulPaint(is_main_frame);
+  if (frame_->IsMainFrame()) {
+    // Restart commits that may have been deferred.
+    GetPage()->GetChromeClient().StopDeferringCommits(
+        *frame_, cc::PaintHoldingCommitTrigger::kFirstContentfulPaint);
+    if (frame_->GetDocument()->ShouldMarkFontPerformance())
+      FontPerformance::MarkFirstContentfulPaint();
+  }
+
+  if (base::FeatureList::IsEnabled(features::kLocalFrameRootPrePostFCPMetrics)
+          ? frame_->IsLocalRoot()
+          : frame_->IsMainFrame()) {
+    // See crbug.com/1330675.
+    EnsureUkmAggregator().DidReachFirstContentfulPaint();
+  }
 }
 
 void LocalFrameView::RegisterForLifecycleNotifications(
diff --git a/third_party/blink/renderer/core/html/html_object_element.cc b/third_party/blink/renderer/core/html/html_object_element.cc
index f81729a..d659692c 100644
--- a/third_party/blink/renderer/core/html/html_object_element.cc
+++ b/third_party/blink/renderer/core/html/html_object_element.cc
@@ -126,44 +126,41 @@
 // serviceType!
 void HTMLObjectElement::ParametersForPlugin(PluginParameters& plugin_params) {
   HashSet<StringImpl*, CaseFoldingHash> unique_param_names;
+  if (RuntimeEnabledFeatures::HTMLParamElementUrlSupportEnabled()) {
+    // Scan the PARAM children and store their name/value pairs.
+    // Get the URL and type from the params if we don't already have them.
+    // Only scan <param> children if this functionality hasn't been disabled.
+    for (HTMLParamElement* p = Traversal<HTMLParamElement>::FirstChild(*this);
+         p; p = Traversal<HTMLParamElement>::NextSibling(*p)) {
+      String name = p->GetName();
+      if (name.IsEmpty())
+        continue;
 
-  if (!RuntimeEnabledFeatures::HTMLParamElementUrlSupportEnabled()) {
-    // The <param> element functionality has been deprecated/removed.
-    return;
-  }
+      unique_param_names.insert(name.Impl());
+      plugin_params.AppendNameWithValue(p->GetName(), p->Value());
 
-  // Scan the PARAM children and store their name/value pairs.
-  // Get the URL and type from the params if we don't already have them.
-  for (HTMLParamElement* p = Traversal<HTMLParamElement>::FirstChild(*this); p;
-       p = Traversal<HTMLParamElement>::NextSibling(*p)) {
-    String name = p->GetName();
-    if (name.IsEmpty())
-      continue;
-
-    unique_param_names.insert(name.Impl());
-    plugin_params.AppendNameWithValue(p->GetName(), p->Value());
-
-    // TODO(schenney): crbug.com/572908 url adjustment does not belong in this
-    // function.
-    // HTML5 says that an object resource's URL is specified by the object's
-    // data attribute, not by a param element with a name of "data". However,
-    // for compatibility, allow the resource's URL to be given by a param
-    // element with one of the common names if we know that resource points
-    // to a plugin.
-    if (url_.IsEmpty() && !EqualIgnoringASCIICase(name, "data") &&
-        HTMLParamElement::IsURLParameter(name)) {
-      UseCounter::Count(GetDocument(),
-                        WebFeature::kHTMLParamElementURLParameter);
-      // Use count this <param> usage, if it loads a PDF.
-      should_use_count_param_url_ = true;
-      SetUrl(StripLeadingAndTrailingHTMLSpaces(p->Value()));
-    }
-    // TODO(schenney): crbug.com/572908 serviceType calculation does not belong
-    // in this function.
-    if (service_type_.IsEmpty() && EqualIgnoringASCIICase(name, "type")) {
-      wtf_size_t pos = p->Value().Find(";");
-      if (pos != kNotFound)
-        SetServiceType(p->Value().GetString().Left(pos));
+      // TODO(schenney): crbug.com/572908 url adjustment does not belong in this
+      // function.
+      // HTML5 says that an object resource's URL is specified by the object's
+      // data attribute, not by a param element with a name of "data". However,
+      // for compatibility, allow the resource's URL to be given by a param
+      // element with one of the common names if we know that resource points
+      // to a plugin.
+      if (url_.IsEmpty() && !EqualIgnoringASCIICase(name, "data") &&
+          HTMLParamElement::IsURLParameter(name)) {
+        UseCounter::Count(GetDocument(),
+                          WebFeature::kHTMLParamElementURLParameter);
+        // Use count this <param> usage, if it loads a PDF.
+        should_use_count_param_url_ = true;
+        SetUrl(StripLeadingAndTrailingHTMLSpaces(p->Value()));
+      }
+      // TODO(schenney): crbug.com/572908 serviceType calculation does not
+      // belong in this function.
+      if (service_type_.IsEmpty() && EqualIgnoringASCIICase(name, "type")) {
+        wtf_size_t pos = p->Value().Find(";");
+        if (pos != kNotFound)
+          SetServiceType(p->Value().GetString().Left(pos));
+      }
     }
   }
 
@@ -172,8 +169,11 @@
   AttributeCollection attributes = Attributes();
   for (const Attribute& attribute : attributes) {
     const AtomicString& name = attribute.GetName().LocalName();
-    if (!unique_param_names.Contains(name.Impl()))
+    if (unique_param_names.Contains(name.Impl())) {
+      DCHECK(RuntimeEnabledFeatures::HTMLParamElementUrlSupportEnabled());
+    } else {
       plugin_params.AppendAttribute(attribute);
+    }
   }
 
   // Some plugins don't understand the "data" attribute of the OBJECT tag (i.e.
diff --git a/third_party/blink/renderer/core/paint/box_painter_base.cc b/third_party/blink/renderer/core/paint/box_painter_base.cc
index 3a69078..da7aba3 100644
--- a/third_party/blink/renderer/core/paint/box_painter_base.cc
+++ b/third_party/blink/renderer/core/paint/box_painter_base.cc
@@ -582,7 +582,8 @@
                          Image* image,
                          const BackgroundImageGeometry& geometry,
                          SkBlendMode op,
-                         RespectImageOrientationEnum respect_orientation) {
+                         RespectImageOrientationEnum respect_orientation,
+                         bool image_may_be_lcp_candidate) {
   DCHECK(!geometry.TileSize().IsEmpty());
 
   const gfx::RectF dest_rect(geometry.SnappedDestRect());
@@ -598,7 +599,8 @@
     auto image_auto_dark_mode = ImageClassifierHelper::GetImageAutoDarkMode(
         *frame, style, dest_rect, *single_tile_src);
     context.DrawImage(image, Image::kSyncDecode, image_auto_dark_mode,
-                      dest_rect, &*single_tile_src, op, respect_orientation);
+                      dest_rect, &*single_tile_src, op, respect_orientation,
+                      image_may_be_lcp_candidate);
     return;
   }
 
@@ -644,7 +646,7 @@
   // it into the snapped_dest_rect using phase from one_tile_rect and the
   // given repeat spacing. Note the phase is already scaled.
   context.DrawImageTiled(image, dest_rect, tiling_info, image_auto_dark_mode,
-                         op, respect_orientation);
+                         op, respect_orientation, image_may_be_lcp_candidate);
 }
 
 scoped_refptr<Image> GetBGColorPaintWorkletImage(const Document* document,
@@ -705,7 +707,7 @@
   return true;
 }
 
-void DidDrawImage(
+bool WillDrawImage(
     Node* node,
     const Image& image,
     const StyleImage& style_image,
@@ -713,17 +715,19 @@
     const gfx::RectF& image_rect) {
   Node* generating_node = GeneratingNode(node);
   if (!generating_node || !style_image.IsImageResource())
-    return;
+    return false;
   const gfx::Rect enclosing_rect = gfx::ToEnclosingRect(image_rect);
-  PaintTimingDetector::NotifyBackgroundImagePaint(
-      *generating_node, image, To<StyleFetchedImage>(style_image),
-      current_paint_chunk_properties, enclosing_rect);
+  bool image_may_be_lcp_candidate =
+      PaintTimingDetector::NotifyBackgroundImagePaint(
+          *generating_node, image, To<StyleFetchedImage>(style_image),
+          current_paint_chunk_properties, enclosing_rect);
 
   LocalDOMWindow* window = node->GetDocument().domWindow();
   DCHECK(window);
   ImageElementTiming::From(*window).NotifyBackgroundImagePainted(
       *generating_node, To<StyleFetchedImage>(style_image),
       current_paint_chunk_properties, enclosing_rect);
+  return image_may_be_lcp_candidate;
 }
 
 inline bool PaintFastBottomLayer(const Document* document,
@@ -826,17 +830,19 @@
       inspector_paint_image_event::Data, node, *info.image,
       gfx::RectF(image->Rect()), gfx::RectF(image_border.Rect()));
 
+  bool may_be_lcp_candidate =
+      WillDrawImage(node, *image, *info.image,
+                    context.GetPaintController().CurrentPaintChunkProperties(),
+                    image_border.Rect());
+
   auto image_auto_dark_mode = ImageClassifierHelper::GetImageAutoDarkMode(
       *document->GetFrame(), style, image_border.Rect(), src_rect);
   // Since there is no way for the developer to specify decode behavior, use
   // kSync by default.
   context.DrawImageRRect(image, Image::kSyncDecode, image_auto_dark_mode,
                          image_border, src_rect, composite_op,
-                         info.respect_image_orientation);
+                         info.respect_image_orientation, may_be_lcp_candidate);
 
-  DidDrawImage(node, *image, *info.image,
-               context.GetPaintController().CurrentPaintChunkProperties(),
-               image_border.Rect());
   return true;
 }
 
@@ -955,11 +961,13 @@
         TRACE_DISABLED_BY_DEFAULT("devtools.timeline"), "PaintImage",
         inspector_paint_image_event::Data, node, *info.image,
         gfx::RectF(image->Rect()), gfx::RectF(scrolled_paint_rect));
+    bool may_be_lcp_candidate = WillDrawImage(
+        node, *image, *info.image,
+        context.GetPaintController().CurrentPaintChunkProperties(),
+        gfx::RectF(geometry.SnappedDestRect()));
     DrawTiledBackground(document->GetFrame(), context, style, image, geometry,
-                        composite_op, info.respect_image_orientation);
-    DidDrawImage(node, *image, *info.image,
-                 context.GetPaintController().CurrentPaintChunkProperties(),
-                 gfx::RectF(geometry.SnappedDestRect()));
+                        composite_op, info.respect_image_orientation,
+                        may_be_lcp_candidate);
   }
 }
 
diff --git a/third_party/blink/renderer/core/paint/image_paint_timing_detector.cc b/third_party/blink/renderer/core/paint/image_paint_timing_detector.cc
index 4d2708d..6c901ca 100644
--- a/third_party/blink/renderer/core/paint/image_paint_timing_detector.cc
+++ b/third_party/blink/renderer/core/paint/image_paint_timing_detector.cc
@@ -277,7 +277,7 @@
   }
 }
 
-void ImagePaintTimingDetector::RecordImage(
+bool ImagePaintTimingDetector::RecordImage(
     const LayoutObject& object,
     const gfx::Size& intrinsic_size,
     const MediaTiming& media_timing,
@@ -287,12 +287,12 @@
   Node* node = object.GetNode();
 
   if (!node)
-    return;
+    return false;
 
   // Before the image resource starts loading, <img> has no size info. We wait
   // until the size is known.
   if (image_border.IsEmpty())
-    return;
+    return false;
 
   RecordId record_id = std::make_pair(&object, &media_timing);
 
@@ -311,14 +311,14 @@
       records_manager_.MaybeUpdateLargestIgnoredImage(
           record_id, rect_size, image_border, mapped_visual_rect);
     }
-    return;
+    return false;
   }
 
   if (records_manager_.IsRecordedImage(record_id)) {
     base::WeakPtr<ImageRecord> record =
         records_manager_.GetPendingImage(record_id);
     if (!record)
-      return;
+      return false;
     if (ShouldReportAnimatedImages() && media_timing.IsPaintedFirstFrame()) {
       added_entry_in_latest_frame_ |=
           records_manager_.OnFirstAnimatedFramePainted(record_id, frame_index_);
@@ -334,8 +334,9 @@
         visualizer->DumpImageDebuggingRect(object, mapped_visual_rect,
                                            media_timing);
       }
+      return true;
     }
-    return;
+    return false;
   }
 
   gfx::RectF mapped_visual_rect =
@@ -352,7 +353,7 @@
   bool added_pending = records_manager_.RecordFirstPaintAndReturnIsPending(
       record_id, rect_size, image_border, mapped_visual_rect, bpp);
   if (!added_pending)
-    return;
+    return false;
 
   if (ShouldReportAnimatedImages() && media_timing.IsPaintedFirstFrame()) {
     added_entry_in_latest_frame_ |=
@@ -361,7 +362,9 @@
   if (media_timing.IsSufficientContentLoadedForPaint()) {
     records_manager_.OnImageLoaded(record_id, frame_index_, style_image);
     added_entry_in_latest_frame_ = true;
+    return true;
   }
+  return false;
 }
 
 uint64_t ImagePaintTimingDetector::ComputeImageRectSize(
diff --git a/third_party/blink/renderer/core/paint/image_paint_timing_detector.h b/third_party/blink/renderer/core/paint/image_paint_timing_detector.h
index da089d85..c281da5 100644
--- a/third_party/blink/renderer/core/paint/image_paint_timing_detector.h
+++ b/third_party/blink/renderer/core/paint/image_paint_timing_detector.h
@@ -243,8 +243,10 @@
   // Record an image paint. This method covers both img and background image. In
   // the case of a normal img, the last parameter will be nullptr. This
   // parameter is needed only for the purposes of plumbing the correct loadTime
-  // value to the ImageRecord.
-  void RecordImage(const LayoutObject&,
+  // value to the ImageRecord. The method returns true if the image is a
+  // candidate for LargestContentfulPaint. That is, if the image is larger
+  // on screen than the current best candidate.
+  bool RecordImage(const LayoutObject&,
                    const gfx::Size& intrinsic_size,
                    const MediaTiming&,
                    const PropertyTreeStateOrAlias& current_paint_properties,
diff --git a/third_party/blink/renderer/core/paint/image_painter.cc b/third_party/blink/renderer/core/paint/image_painter.cc
index d041b4c..6d103200 100644
--- a/third_party/blink/renderer/core/paint/image_painter.cc
+++ b/third_party/blink/renderer/core/paint/image_painter.cc
@@ -260,10 +260,10 @@
       *layout_image_.GetFrame(), layout_image_.StyleRef(),
       gfx::RectF(pixel_snapped_dest_rect), src_rect);
 
-  context.DrawImage(image.get(), decode_mode, image_auto_dark_mode,
-                    gfx::RectF(pixel_snapped_dest_rect), &src_rect,
-                    SkBlendMode::kSrcOver, respect_orientation);
-
+  // At this point we have all the necessary information to report paint
+  // timing data. Do so now in order to mark the resulting PaintImage as
+  // an LCP candidate.
+  bool image_may_be_lcp_candidate = false;
   if (ImageResourceContent* image_content = image_resource.CachedImage()) {
     if ((IsA<HTMLImageElement>(node) || IsA<HTMLVideoElement>(node)) &&
         image_content->IsLoaded()) {
@@ -274,11 +274,16 @@
           context.GetPaintController().CurrentPaintChunkProperties(),
           pixel_snapped_dest_rect);
     }
-    PaintTimingDetector::NotifyImagePaint(
+    image_may_be_lcp_candidate = PaintTimingDetector::NotifyImagePaint(
         layout_image_, image->Size(), *image_content,
         context.GetPaintController().CurrentPaintChunkProperties(),
         pixel_snapped_dest_rect);
   }
+
+  context.DrawImage(image.get(), decode_mode, image_auto_dark_mode,
+                    gfx::RectF(pixel_snapped_dest_rect), &src_rect,
+                    SkBlendMode::kSrcOver, respect_orientation,
+                    image_may_be_lcp_candidate);
 }
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/paint/paint_timing.cc b/third_party/blink/renderer/core/paint/paint_timing.cc
index 0d6de59..d7db2e4 100644
--- a/third_party/blink/renderer/core/paint/paint_timing.cc
+++ b/third_party/blink/renderer/core/paint/paint_timing.cc
@@ -251,13 +251,12 @@
   first_contentful_paint_ = stamp;
   RegisterNotifyPresentationTime(PaintEvent::kFirstContentfulPaint);
 
-  // Restart commits that may have been deferred.
   LocalFrame* frame = GetFrame();
-  if (!frame || !frame->IsMainFrame())
+  if (!frame)
     return;
   frame->View()->OnFirstContentfulPaint();
 
-  if (frame->GetFrameScheduler())
+  if (frame->IsMainFrame() && frame->GetFrameScheduler())
     frame->GetFrameScheduler()->OnFirstContentfulPaintInMainFrame();
 
   NotifyPaintTimingChanged();
diff --git a/third_party/blink/renderer/core/paint/paint_timing_detector.cc b/third_party/blink/renderer/core/paint/paint_timing_detector.cc
index 36c51e3d..aa38d69f 100644
--- a/third_party/blink/renderer/core/paint/paint_timing_detector.cc
+++ b/third_party/blink/renderer/core/paint/paint_timing_detector.cc
@@ -113,7 +113,7 @@
 }
 
 // static
-void PaintTimingDetector::NotifyBackgroundImagePaint(
+bool PaintTimingDetector::NotifyBackgroundImagePaint(
     const Node& node,
     const Image& image,
     const StyleFetchedImage& style_image,
@@ -122,48 +122,49 @@
   DCHECK(style_image.CachedImage());
   LayoutObject* object = node.GetLayoutObject();
   if (!object)
-    return;
+    return false;
   LocalFrameView* frame_view = object->GetFrameView();
   if (!frame_view)
-    return;
+    return false;
 
   ImagePaintTimingDetector* detector =
       frame_view->GetPaintTimingDetector().GetImagePaintTimingDetector();
   if (!detector)
-    return;
+    return false;
 
   if (!IsBackgroundImageContentful(*object, image))
-    return;
+    return false;
 
   ImageResourceContent* cached_image = style_image.CachedImage();
   DCHECK(cached_image);
   // TODO(yoav): |image| and |cached_image.GetImage()| are not the same here in
   // the case of SVGs. Figure out why and if we can remove this footgun.
 
-  detector->RecordImage(*object, image.Size(), *cached_image,
-                        current_paint_chunk_properties, &style_image,
-                        image_border);
+  return detector->RecordImage(*object, image.Size(), *cached_image,
+                               current_paint_chunk_properties, &style_image,
+                               image_border);
 }
 
 // static
-void PaintTimingDetector::NotifyImagePaint(
+bool PaintTimingDetector::NotifyImagePaint(
     const LayoutObject& object,
     const gfx::Size& intrinsic_size,
     const MediaTiming& media_timing,
     const PropertyTreeStateOrAlias& current_paint_chunk_properties,
     const gfx::Rect& image_border) {
   if (IgnorePaintTimingScope::ShouldIgnore())
-    return;
+    return false;
   LocalFrameView* frame_view = object.GetFrameView();
   if (!frame_view)
-    return;
+    return false;
   ImagePaintTimingDetector* detector =
       frame_view->GetPaintTimingDetector().GetImagePaintTimingDetector();
   if (!detector)
-    return;
+    return false;
 
-  detector->RecordImage(object, intrinsic_size, media_timing,
-                        current_paint_chunk_properties, nullptr, image_border);
+  return detector->RecordImage(object, intrinsic_size, media_timing,
+                               current_paint_chunk_properties, nullptr,
+                               image_border);
 }
 
 void PaintTimingDetector::NotifyImageFinished(const LayoutObject& object,
diff --git a/third_party/blink/renderer/core/paint/paint_timing_detector.h b/third_party/blink/renderer/core/paint/paint_timing_detector.h
index 2405d30..3ade99b9 100644
--- a/third_party/blink/renderer/core/paint/paint_timing_detector.h
+++ b/third_party/blink/renderer/core/paint/paint_timing_detector.h
@@ -123,13 +123,19 @@
  public:
   PaintTimingDetector(LocalFrameView*);
 
-  static void NotifyBackgroundImagePaint(
+  // Returns true if the image might ultimately be a candidate for largest
+  // paint, otherwise false. When this method is called we do not know the
+  // largest status for certain, because we need to wait for presentation.
+  // Hence the "maybe" return value.
+  static bool NotifyBackgroundImagePaint(
       const Node&,
       const Image&,
       const StyleFetchedImage&,
       const PropertyTreeStateOrAlias& current_paint_chunk_properties,
       const gfx::Rect& image_border);
-  static void NotifyImagePaint(
+  // Returns true if the image is a candidate for largest paint, otherwise
+  // false. See the comment for NotifyBackgroundImagePaint(...).
+  static bool NotifyImagePaint(
       const LayoutObject&,
       const gfx::Size& intrinsic_size,
       const MediaTiming& media_timing,
diff --git a/third_party/blink/renderer/core/paint/svg_image_painter.cc b/third_party/blink/renderer/core/paint/svg_image_painter.cc
index a73cccf..99644d1 100644
--- a/third_party/blink/renderer/core/paint/svg_image_painter.cc
+++ b/third_party/blink/renderer/core/paint/svg_image_painter.cc
@@ -85,6 +85,23 @@
         dest_rect, src_rect);
   }
 
+  ImageResourceContent* image_content = image_resource.CachedImage();
+  bool image_may_be_lcp_candidate = false;
+  if (image_content->IsLoaded()) {
+    LocalDOMWindow* window = layout_svg_image_.GetDocument().domWindow();
+    DCHECK(window);
+    ImageElementTiming::From(*window).NotifyImagePainted(
+        layout_svg_image_, *image_content,
+        paint_info.context.GetPaintController().CurrentPaintChunkProperties(),
+        gfx::ToEnclosingRect(dest_rect));
+  }
+  image_may_be_lcp_candidate = PaintTimingDetector::NotifyImagePaint(
+      layout_svg_image_, image->Size(), *image_content,
+      paint_info.context.GetPaintController().CurrentPaintChunkProperties(),
+      gfx::ToEnclosingRect(dest_rect));
+  PaintTiming& timing = PaintTiming::From(layout_svg_image_.GetDocument());
+  timing.MarkFirstContentfulPaint();
+
   ScopedInterpolationQuality interpolation_quality_scope(
       paint_info.context,
       layout_svg_image_.StyleRef().GetInterpolationQuality());
@@ -95,23 +112,7 @@
       src_rect);
   paint_info.context.DrawImage(image.get(), decode_mode, image_auto_dark_mode,
                                dest_rect, &src_rect, SkBlendMode::kSrcOver,
-                               respect_orientation);
-
-  ImageResourceContent* image_content = image_resource.CachedImage();
-  if (image_content->IsLoaded()) {
-    LocalDOMWindow* window = layout_svg_image_.GetDocument().domWindow();
-    DCHECK(window);
-    ImageElementTiming::From(*window).NotifyImagePainted(
-        layout_svg_image_, *image_content,
-        paint_info.context.GetPaintController().CurrentPaintChunkProperties(),
-        gfx::ToEnclosingRect(dest_rect));
-  }
-  PaintTimingDetector::NotifyImagePaint(
-      layout_svg_image_, image->Size(), *image_content,
-      paint_info.context.GetPaintController().CurrentPaintChunkProperties(),
-      gfx::ToEnclosingRect(dest_rect));
-  PaintTiming& timing = PaintTiming::From(layout_svg_image_.GetDocument());
-  timing.MarkFirstContentfulPaint();
+                               respect_orientation, image_may_be_lcp_candidate);
 }
 
 gfx::SizeF SVGImagePainter::ComputeImageViewportSize() const {
diff --git a/third_party/blink/renderer/modules/webgpu/gpu_adapter.cc b/third_party/blink/renderer/modules/webgpu/gpu_adapter.cc
index f2f6de1..2f30ad4 100644
--- a/third_party/blink/renderer/modules/webgpu/gpu_adapter.cc
+++ b/third_party/blink/renderer/modules/webgpu/gpu_adapter.cc
@@ -98,6 +98,7 @@
     device_ = String::Format("0x%08x", properties.deviceID);
   }
   description_ = properties.name;
+  driver_ = properties.driverDescription;
 
   WGPUSupportedLimits limits = {};
   GetProcs().adapterGetLimits(handle_, &limits);
@@ -238,11 +239,20 @@
     }
   }
 
-  // TODO(dawn:1427): If unmask_hints are given ask the user for consent to
-  // expose more information and, if given, include device_ and description_ in
-  // the returned GPUAdapterInfo.
-  auto* adapter_info =
-      MakeGarbageCollected<GPUAdapterInfo>(vendor_, architecture_, "", "");
+  GPUAdapterInfo* adapter_info;
+  if (RuntimeEnabledFeatures::WebGPUDeveloperFeaturesEnabled()) {
+    // If WebGPU developer features have been enabled then provide unmasked
+    // versions of all available adapter info values, including some that are
+    // only available when the flag is enabled.
+    adapter_info = MakeGarbageCollected<GPUAdapterInfo>(
+        vendor_, architecture_, device_, description_, driver_);
+  } else {
+    // TODO(dawn:1427): If unmask_hints are given ask the user for consent to
+    // expose more information and, if given, include device_ and description_
+    // in the returned GPUAdapterInfo.
+    adapter_info = MakeGarbageCollected<GPUAdapterInfo>(vendor_, architecture_);
+  }
+
   resolver->Resolve(adapter_info);
 
   return promise;
diff --git a/third_party/blink/renderer/modules/webgpu/gpu_adapter.h b/third_party/blink/renderer/modules/webgpu/gpu_adapter.h
index 983a644..19cd8e4b 100644
--- a/third_party/blink/renderer/modules/webgpu/gpu_adapter.h
+++ b/third_party/blink/renderer/modules/webgpu/gpu_adapter.h
@@ -73,6 +73,7 @@
   String architecture_;
   String device_;
   String description_;
+  String driver_;
 
   static constexpr int kMaxAllowedConsoleWarnings = 50;
   int allowed_console_warnings_remaining_ = kMaxAllowedConsoleWarnings;
diff --git a/third_party/blink/renderer/modules/webgpu/gpu_adapter_info.cc b/third_party/blink/renderer/modules/webgpu/gpu_adapter_info.cc
index f4b92cd..97f9f77 100644
--- a/third_party/blink/renderer/modules/webgpu/gpu_adapter_info.cc
+++ b/third_party/blink/renderer/modules/webgpu/gpu_adapter_info.cc
@@ -9,11 +9,13 @@
 GPUAdapterInfo::GPUAdapterInfo(const String& vendor,
                                const String& architecture,
                                const String& device,
-                               const String& description)
+                               const String& description,
+                               const String& driver)
     : vendor_(vendor),
       architecture_(architecture),
       device_(device),
-      description_(description) {}
+      description_(description),
+      driver_(driver) {}
 
 const String& GPUAdapterInfo::vendor() const {
   return vendor_;
@@ -31,4 +33,8 @@
   return description_;
 }
 
+const String& GPUAdapterInfo::driver() const {
+  return driver_;
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/modules/webgpu/gpu_adapter_info.h b/third_party/blink/renderer/modules/webgpu/gpu_adapter_info.h
index 8eba07b..09f11be2 100644
--- a/third_party/blink/renderer/modules/webgpu/gpu_adapter_info.h
+++ b/third_party/blink/renderer/modules/webgpu/gpu_adapter_info.h
@@ -16,8 +16,9 @@
  public:
   GPUAdapterInfo(const String& vendor,
                  const String& architecture,
-                 const String& device,
-                 const String& description);
+                 const String& device = String(),
+                 const String& description = String(),
+                 const String& driver = String());
 
   GPUAdapterInfo(const GPUAdapterInfo&) = delete;
   GPUAdapterInfo& operator=(const GPUAdapterInfo&) = delete;
@@ -27,12 +28,14 @@
   const String& architecture() const;
   const String& device() const;
   const String& description() const;
+  const String& driver() const;
 
  private:
   String vendor_;
   String architecture_;
   String device_;
   String description_;
+  String driver_;
 };
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/modules/webgpu/gpu_adapter_info.idl b/third_party/blink/renderer/modules/webgpu/gpu_adapter_info.idl
index b19383b96..de59c4a 100644
--- a/third_party/blink/renderer/modules/webgpu/gpu_adapter_info.idl
+++ b/third_party/blink/renderer/modules/webgpu/gpu_adapter_info.idl
@@ -12,4 +12,5 @@
   readonly attribute DOMString architecture;
   readonly attribute DOMString device;
   readonly attribute DOMString description;
+  [RuntimeEnabled=WebGPUDeveloperFeatures] readonly attribute DOMString driver;
 };
diff --git a/third_party/blink/renderer/platform/exported/web_runtime_features.cc b/third_party/blink/renderer/platform/exported/web_runtime_features.cc
index a387e3e7..e7ca7c2e 100644
--- a/third_party/blink/renderer/platform/exported/web_runtime_features.cc
+++ b/third_party/blink/renderer/platform/exported/web_runtime_features.cc
@@ -409,6 +409,10 @@
   RuntimeEnabledFeatures::SetWebGPUEnabled(enable);
 }
 
+void WebRuntimeFeatures::EnableWebGPUDeveloperFeatures(bool enable) {
+  RuntimeEnabledFeatures::SetWebGPUDeveloperFeaturesEnabled(enable);
+}
+
 void WebRuntimeFeatures::EnableWebXR(bool enable) {
   RuntimeEnabledFeatures::SetWebXREnabled(enable);
 }
diff --git a/third_party/blink/renderer/platform/fonts/font_selector.cc b/third_party/blink/renderer/platform/fonts/font_selector.cc
index 4d996e6..0df0d4f 100644
--- a/third_party/blink/renderer/platform/fonts/font_selector.cc
+++ b/third_party/blink/renderer/platform/fonts/font_selector.cc
@@ -33,13 +33,10 @@
     return g_empty_atom;
 
   if (IsWebkitBodyFamily(font_description)) {
-    // TODO(yosin): We should make |use_counter| available for font threads.
-    if (use_counter) {
-      // TODO(crbug.com/1065468): Remove this counter when it's no longer
-      // necessary.
-      UseCounter::Count(use_counter,
-                        WebFeature::kFontSelectorCSSFontFamilyWebKitPrefixBody);
-    }
+    // TODO(crbug.com/1065468): Remove this counter when it's no longer
+    // necessary.
+    UseCounter::Count(use_counter,
+                      WebFeature::kFontSelectorCSSFontFamilyWebKitPrefixBody);
   } else if (generic_family_name == font_family_names::kWebkitStandard &&
              !generic_family.FamilyIsGeneric()) {
     // -webkit-standard is set internally only with a kGenericFamily type in
diff --git a/third_party/blink/renderer/platform/fonts/font_selector.h b/third_party/blink/renderer/platform/fonts/font_selector.h
index da32354..fc2d7c85 100644
--- a/third_party/blink/renderer/platform/fonts/font_selector.h
+++ b/third_party/blink/renderer/platform/fonts/font_selector.h
@@ -92,7 +92,7 @@
   virtual void ReportFontLookupByUniqueOrFamilyName(
       const AtomicString& name,
       const FontDescription& font_description,
-      scoped_refptr<SimpleFontData> resulting_font_data) = 0;
+      SimpleFontData* resulting_font_data) = 0;
 
   // Called whenever a page attempts to find a local font based on a name. This
   // only includes lookups where the name is allowed to match PostScript names
@@ -100,7 +100,7 @@
   virtual void ReportFontLookupByUniqueNameOnly(
       const AtomicString& name,
       const FontDescription& font_description,
-      scoped_refptr<SimpleFontData> resulting_font_data,
+      SimpleFontData* resulting_font_data,
       bool is_loading_fallback = false) = 0;
 
   // Called whenever a page attempts to find a local font based on a fallback
@@ -109,12 +109,12 @@
       UChar32 fallback_character,
       FontFallbackPriority fallback_priority,
       const FontDescription& font_description,
-      scoped_refptr<SimpleFontData> resulting_font_data) = 0;
+      SimpleFontData* resulting_font_data) = 0;
 
   // Called whenever a page attempts to find a last-resort font.
   virtual void ReportLastResortFallbackFontLookup(
       const FontDescription& font_description,
-      scoped_refptr<SimpleFontData> resulting_font_data) = 0;
+      SimpleFontData* resulting_font_data) = 0;
 
   virtual void ReportNotDefGlyph() const = 0;
 
@@ -137,8 +137,6 @@
       const FontDescription&,
       const FontFamily& passed_family) = 0;
 
-  virtual bool IsContextThread() const = 0;
-
   FontFallbackMap& GetFontFallbackMap();
 
   void Trace(Visitor* visitor) const override;
diff --git a/third_party/blink/renderer/platform/graphics/accelerated_static_bitmap_image.cc b/third_party/blink/renderer/platform/graphics/accelerated_static_bitmap_image.cc
index 1e45dfc..53ea5f8 100644
--- a/third_party/blink/renderer/platform/graphics/accelerated_static_bitmap_image.cc
+++ b/third_party/blink/renderer/platform/graphics/accelerated_static_bitmap_image.cc
@@ -225,10 +225,13 @@
     return;
   auto paint_image_decoding_mode =
       ToPaintImageDecodingMode(draw_options.decode_mode);
-  if (paint_image.decoding_mode() != paint_image_decoding_mode) {
-    paint_image = PaintImageBuilder::WithCopy(std::move(paint_image))
-                      .set_decoding_mode(paint_image_decoding_mode)
-                      .TakePaintImage();
+  if (paint_image.decoding_mode() != paint_image_decoding_mode ||
+      paint_image.may_be_lcp_candidate() != draw_options.may_be_lcp_candidate) {
+    paint_image =
+        PaintImageBuilder::WithCopy(std::move(paint_image))
+            .set_decoding_mode(paint_image_decoding_mode)
+            .set_may_be_lcp_candidate(draw_options.may_be_lcp_candidate)
+            .TakePaintImage();
   }
   StaticBitmapImage::DrawHelper(canvas, flags, dst_rect, src_rect, draw_options,
                                 paint_image);
diff --git a/third_party/blink/renderer/platform/graphics/bitmap_image.cc b/third_party/blink/renderer/platform/graphics/bitmap_image.cc
index 46f42d7..bec446e 100644
--- a/third_party/blink/renderer/platform/graphics/bitmap_image.cc
+++ b/third_party/blink/renderer/platform/graphics/bitmap_image.cc
@@ -263,9 +263,11 @@
 
   auto paint_image_decoding_mode =
       ToPaintImageDecodingMode(draw_options.decode_mode);
-  if (image.decoding_mode() != paint_image_decoding_mode) {
+  if (image.decoding_mode() != paint_image_decoding_mode ||
+      image.may_be_lcp_candidate() != draw_options.may_be_lcp_candidate) {
     image = PaintImageBuilder::WithCopy(std::move(image))
                 .set_decoding_mode(paint_image_decoding_mode)
+                .set_may_be_lcp_candidate(draw_options.may_be_lcp_candidate)
                 .TakePaintImage();
   }
 
diff --git a/third_party/blink/renderer/platform/graphics/compositing/paint_artifact_compositor.cc b/third_party/blink/renderer/platform/graphics/compositing/paint_artifact_compositor.cc
index 4d67711..848153b6 100644
--- a/third_party/blink/renderer/platform/graphics/compositing/paint_artifact_compositor.cc
+++ b/third_party/blink/renderer/platform/graphics/compositing/paint_artifact_compositor.cc
@@ -587,12 +587,6 @@
     ids.overscroll_elasticity_transform =
         property_tree_manager.EnsureCompositorTransformNode(
             *properties.overscroll_elasticity_transform);
-
-    if (RuntimeEnabledFeatures::FixedElementsDontOverscrollEnabled()) {
-      property_tree_manager.SetOverscrollTransformNodeId(
-          ids.overscroll_elasticity_transform);
-      property_tree_manager.SetFixedElementsDontOverscroll(true);
-    }
   }
   if (properties.overscroll_elasticity_effect) {
     ids.overscroll_elasticity_effect =
@@ -616,6 +610,11 @@
     }
   }
 
+  if (RuntimeEnabledFeatures::FixedElementsDontOverscrollEnabled()) {
+    property_tree_manager.SetOverscrollTransformNodeId(
+        ids.overscroll_elasticity_transform);
+    property_tree_manager.SetFixedElementsDontOverscroll(true);
+  }
   layer_tree_host->RegisterViewportPropertyIds(ids);
 }
 
@@ -691,6 +690,10 @@
     int effect_id = property_tree_manager.SwitchToEffectNodeWithSynthesizedClip(
         effect, clip, layer.draws_content());
 
+    if (RuntimeEnabledFeatures::FixedElementsDontOverscrollEnabled() &&
+        transform.RequiresCompositingForFixedToViewport())
+      property_tree_manager.SetOverscrollClipNodeId(clip_id);
+
     // We need additional bookkeeping for backdrop-filter mask.
     if (effect.RequiresCompositingForBackdropFilterMask() &&
         effect.CcNodeId(g_s_property_tree_sequence_number) == effect_id) {
diff --git a/third_party/blink/renderer/platform/graphics/compositing/property_tree_manager.cc b/third_party/blink/renderer/platform/graphics/compositing/property_tree_manager.cc
index 13c189bc..9926830 100644
--- a/third_party/blink/renderer/platform/graphics/compositing/property_tree_manager.cc
+++ b/third_party/blink/renderer/platform/graphics/compositing/property_tree_manager.cc
@@ -386,6 +386,10 @@
   transform_tree_.set_overscroll_node_id(id);
 }
 
+void PropertyTreeManager::SetOverscrollClipNodeId(const int id) {
+  clip_tree_.set_overscroll_node_id(id);
+}
+
 void PropertyTreeManager::SetFixedElementsDontOverscroll(const bool value) {
   transform_tree_.set_fixed_elements_dont_overscroll(value);
 }
diff --git a/third_party/blink/renderer/platform/graphics/compositing/property_tree_manager.h b/third_party/blink/renderer/platform/graphics/compositing/property_tree_manager.h
index 478d956a..1efabd8c 100644
--- a/third_party/blink/renderer/platform/graphics/compositing/property_tree_manager.h
+++ b/third_party/blink/renderer/platform/graphics/compositing/property_tree_manager.h
@@ -115,6 +115,7 @@
 
   // Used to offset the scroll translation during overscroll.
   void SetOverscrollTransformNodeId(const int id);
+  void SetOverscrollClipNodeId(const int id);
   void SetFixedElementsDontOverscroll(const bool value);
 
   // This function is expected to be invoked right before emitting each layer.
diff --git a/third_party/blink/renderer/platform/graphics/graphics_context.cc b/third_party/blink/renderer/platform/graphics/graphics_context.cc
index 63ce53a..8f967d93 100644
--- a/third_party/blink/renderer/platform/graphics/graphics_context.cc
+++ b/third_party/blink/renderer/platform/graphics/graphics_context.cc
@@ -751,7 +751,8 @@
     const gfx::RectF& dest,
     const gfx::RectF* src_ptr,
     SkBlendMode op,
-    RespectImageOrientationEnum should_respect_image_orientation) {
+    RespectImageOrientationEnum should_respect_image_orientation,
+    bool image_may_be_lcp_candidate) {
   if (!image)
     return;
 
@@ -764,7 +765,8 @@
   DarkModeFilter* dark_mode_filter = GetDarkModeFilterForImage(auto_dark_mode);
   ImageDrawOptions draw_options(
       dark_mode_filter, sampling, should_respect_image_orientation,
-      Image::kClampImageToSourceRect, decode_mode, auto_dark_mode.enabled);
+      Image::kClampImageToSourceRect, decode_mode, auto_dark_mode.enabled,
+      image_may_be_lcp_candidate);
 
   image->Draw(canvas_, image_flags, dest, src, draw_options);
   paint_controller_.SetImagePainted();
@@ -777,13 +779,14 @@
     const FloatRoundedRect& dest,
     const gfx::RectF& src_rect,
     SkBlendMode op,
-    RespectImageOrientationEnum respect_orientation) {
+    RespectImageOrientationEnum respect_orientation,
+    bool image_may_be_lcp_candidate) {
   if (!image)
     return;
 
   if (!dest.IsRounded()) {
     DrawImage(image, decode_mode, auto_dark_mode, dest.Rect(), &src_rect, op,
-              respect_orientation);
+              respect_orientation, image_may_be_lcp_candidate);
     return;
   }
 
@@ -803,7 +806,8 @@
   DarkModeFilter* dark_mode_filter = GetDarkModeFilterForImage(auto_dark_mode);
   ImageDrawOptions draw_options(dark_mode_filter, sampling, respect_orientation,
                                 Image::kClampImageToSourceRect, decode_mode,
-                                auto_dark_mode.enabled);
+                                auto_dark_mode.enabled,
+                                image_may_be_lcp_candidate);
 
   bool use_shader = (visible_src == src_rect) &&
                     (respect_orientation == kDoNotRespectImageOrientation ||
@@ -865,7 +869,8 @@
     const ImageTilingInfo& tiling_info,
     const ImageAutoDarkMode& auto_dark_mode,
     SkBlendMode op,
-    RespectImageOrientationEnum respect_orientation) {
+    RespectImageOrientationEnum respect_orientation,
+    bool image_may_be_lcp_candidate) {
   if (!image)
     return;
 
@@ -875,7 +880,8 @@
   DarkModeFilter* dark_mode_filter = GetDarkModeFilterForImage(auto_dark_mode);
   ImageDrawOptions draw_options(dark_mode_filter, sampling, respect_orientation,
                                 Image::kClampImageToSourceRect,
-                                Image::kSyncDecode, auto_dark_mode.enabled);
+                                Image::kSyncDecode, auto_dark_mode.enabled,
+                                image_may_be_lcp_candidate);
 
   image->DrawPattern(*this, image_flags, dest_rect, tiling_info, draw_options);
   paint_controller_.SetImagePainted();
diff --git a/third_party/blink/renderer/platform/graphics/graphics_context.h b/third_party/blink/renderer/platform/graphics/graphics_context.h
index 7804b25..329f05d 100644
--- a/third_party/blink/renderer/platform/graphics/graphics_context.h
+++ b/third_party/blink/renderer/platform/graphics/graphics_context.h
@@ -93,19 +93,22 @@
                             RespectImageOrientationEnum respect_orientation,
                             Image::ImageClampingMode clamping_mode,
                             Image::ImageDecodingMode decode_mode,
-                            bool apply_dark_mode)
+                            bool apply_dark_mode,
+                            bool may_be_lcp_candidate)
       : dark_mode_filter(dark_mode_filter),
         sampling_options(sampling_options),
         respect_orientation(respect_orientation),
         clamping_mode(clamping_mode),
         decode_mode(decode_mode),
-        apply_dark_mode(apply_dark_mode) {}
+        apply_dark_mode(apply_dark_mode),
+        may_be_lcp_candidate(may_be_lcp_candidate) {}
   DarkModeFilter* dark_mode_filter = nullptr;
   SkSamplingOptions sampling_options;
   RespectImageOrientationEnum respect_orientation = kRespectImageOrientation;
   Image::ImageClampingMode clamping_mode = Image::kClampImageToSourceRect;
   Image::ImageDecodingMode decode_mode = Image::kSyncDecode;
   bool apply_dark_mode = false;
+  bool may_be_lcp_candidate = false;
 };
 
 struct AutoDarkMode {
@@ -319,20 +322,23 @@
                  const gfx::RectF& dest_rect,
                  const gfx::RectF* src_rect = nullptr,
                  SkBlendMode = SkBlendMode::kSrcOver,
-                 RespectImageOrientationEnum = kRespectImageOrientation);
+                 RespectImageOrientationEnum = kRespectImageOrientation,
+                 bool image_may_be_lcp_candidate = false);
   void DrawImageRRect(Image*,
                       Image::ImageDecodingMode,
                       const ImageAutoDarkMode& auto_dark_mode,
                       const FloatRoundedRect& dest,
                       const gfx::RectF& src_rect,
                       SkBlendMode = SkBlendMode::kSrcOver,
-                      RespectImageOrientationEnum = kRespectImageOrientation);
+                      RespectImageOrientationEnum = kRespectImageOrientation,
+                      bool image_may_be_lcp_candidate = false);
   void DrawImageTiled(Image* image,
                       const gfx::RectF& dest_rect,
                       const ImageTilingInfo& tiling_info,
                       const ImageAutoDarkMode& auto_dark_mode,
                       SkBlendMode = SkBlendMode::kSrcOver,
-                      RespectImageOrientationEnum = kRespectImageOrientation);
+                      RespectImageOrientationEnum = kRespectImageOrientation,
+                      bool image_may_be_lcp_candidate = false);
 
   // These methods write to the canvas.
   // Also drawLine(const gfx::Point& point1, const gfx::Point& point2) and
diff --git a/third_party/blink/renderer/platform/graphics/paint/cull_rect.cc b/third_party/blink/renderer/platform/graphics/paint/cull_rect.cc
index 04b3e26c..cc546196 100644
--- a/third_party/blink/renderer/platform/graphics/paint/cull_rect.cc
+++ b/third_party/blink/renderer/platform/graphics/paint/cull_rect.cc
@@ -137,7 +137,11 @@
 
   for (const auto* t = &destination.Transform(); t != &source.Transform();
        t = t->UnaliasedParent()) {
-    DCHECK(t);
+    // TODO(wangxianzhu): This should be DCHECK(t), but for now we need to
+    // work around crbug.com/1262837 etc. Also see the TODO in
+    // FragmentData::LocalBorderBoxProperties().
+    if (!t)
+      return false;
     if (t == &root.Transform()) {
       abnormal_hierarchy = true;
       break;
diff --git a/third_party/blink/renderer/platform/graphics/unaccelerated_static_bitmap_image.cc b/third_party/blink/renderer/platform/graphics/unaccelerated_static_bitmap_image.cc
index 968d2bd..3fadffd8 100644
--- a/third_party/blink/renderer/platform/graphics/unaccelerated_static_bitmap_image.cc
+++ b/third_party/blink/renderer/platform/graphics/unaccelerated_static_bitmap_image.cc
@@ -80,8 +80,14 @@
     const gfx::RectF& src_rect,
     const ImageDrawOptions& draw_options) {
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+  auto image = PaintImageForCurrentFrame();
+  if (image.may_be_lcp_candidate() != draw_options.may_be_lcp_candidate) {
+    image = PaintImageBuilder::WithCopy(std::move(image))
+                .set_may_be_lcp_candidate(draw_options.may_be_lcp_candidate)
+                .TakePaintImage();
+  }
   StaticBitmapImage::DrawHelper(canvas, flags, dst_rect, src_rect, draw_options,
-                                PaintImageForCurrentFrame());
+                                image);
 }
 
 PaintImage UnacceleratedStaticBitmapImage::PaintImageForCurrentFrame() {
diff --git a/third_party/blink/renderer/platform/network/parsed_content_header_field_parameters.cc b/third_party/blink/renderer/platform/network/parsed_content_header_field_parameters.cc
index fca608a..29705e5 100644
--- a/third_party/blink/renderer/platform/network/parsed_content_header_field_parameters.cc
+++ b/third_party/blink/renderer/platform/network/parsed_content_header_field_parameters.cc
@@ -4,6 +4,7 @@
 
 #include "third_party/blink/renderer/platform/network/parsed_content_header_field_parameters.h"
 
+#include "base/containers/adapters.h"
 #include "base/logging.h"
 #include "third_party/blink/renderer/platform/network/header_field_tokenizer.h"
 #include "third_party/blink/renderer/platform/wtf/hash_set.h"
@@ -71,9 +72,9 @@
     return String();
   String lower_name = name.LowerASCII();
 
-  for (auto i = rbegin(); i != rend(); ++i) {
-    if (i->name.LowerASCII() == lower_name)
-      return i->value;
+  for (const NameValue& param : base::Reversed(*this)) {
+    if (param.name.LowerASCII() == lower_name)
+      return param.value;
   }
   return String();
 }
diff --git a/third_party/blink/renderer/platform/runtime_enabled_features.json5 b/third_party/blink/renderer/platform/runtime_enabled_features.json5
index 9d0ce2d6..7e000f9 100644
--- a/third_party/blink/renderer/platform/runtime_enabled_features.json5
+++ b/third_party/blink/renderer/platform/runtime_enabled_features.json5
@@ -1102,7 +1102,7 @@
     },
     {
       name: "FixedElementsDontOverscroll",
-      status: "stable",
+      status: "test",
     },
     {
       name: "Fledge",
@@ -2677,6 +2677,11 @@
       origin_trial_feature_name: "WebGPU",
     },
     {
+      // WebGPU developer features are deliberately not enabled by experimental
+      // web platform features.
+      name: "WebGPUDeveloperFeatures",
+    },
+    {
       // Requires both WebGPU and --enable-experimental-web-platform-features
       name: "WebGPUImportTexture",
       status: "experimental",
diff --git a/third_party/blink/renderer/platform/testing/font_test_helpers.cc b/third_party/blink/renderer/platform/testing/font_test_helpers.cc
index 46c4f45..8f7e6046 100644
--- a/third_party/blink/renderer/platform/testing/font_test_helpers.cc
+++ b/third_party/blink/renderer/platform/testing/font_test_helpers.cc
@@ -68,25 +68,24 @@
   void ReportFontLookupByUniqueOrFamilyName(
       const AtomicString& name,
       const FontDescription& font_description,
-      scoped_refptr<SimpleFontData> resulting_font_data) override {}
+      SimpleFontData* resulting_font_data) override {}
   void ReportFontLookupByUniqueNameOnly(
       const AtomicString& name,
       const FontDescription& font_description,
-      scoped_refptr<SimpleFontData> resulting_font_data,
+      SimpleFontData* resulting_font_data,
       bool is_loading_fallback = false) override {}
   void ReportFontLookupByFallbackCharacter(
       UChar32 hint,
       FontFallbackPriority fallback_priority,
       const FontDescription& font_description,
-      scoped_refptr<SimpleFontData> resulting_font_data) override {}
+      SimpleFontData* resulting_font_data) override {}
   void ReportLastResortFallbackFontLookup(
       const FontDescription& font_description,
-      scoped_refptr<SimpleFontData> resulting_font_data) override {}
+      SimpleFontData* resulting_font_data) override {}
   void ReportNotDefGlyph() const override {}
   void ReportEmojiSegmentGlyphCoverage(unsigned, unsigned) override {}
   ExecutionContext* GetExecutionContext() const override { return nullptr; }
   FontFaceCache* GetFontFaceCache() override { return nullptr; }
-  bool IsContextThread() const override { return true; }
 
   void RegisterForInvalidationCallbacks(FontSelectorClient*) override {}
   void UnregisterForInvalidationCallbacks(FontSelectorClient*) override {}
diff --git a/third_party/blink/tools/blinkpy/presubmit/audit_non_blink_usage.py b/third_party/blink/tools/blinkpy/presubmit/audit_non_blink_usage.py
index e6f5913..2ce26e1 100755
--- a/third_party/blink/tools/blinkpy/presubmit/audit_non_blink_usage.py
+++ b/third_party/blink/tools/blinkpy/presubmit/audit_non_blink_usage.py
@@ -128,6 +128,9 @@
             # //base/task/bind_post_task.h
             'base::BindPostTask',
 
+            # //base/bind.h
+            'base::IgnoreResult',
+
             # //base/bits.h
             'base::bits::.+',
 
diff --git a/third_party/blink/tools/blinkpy/style/checker.py b/third_party/blink/tools/blinkpy/style/checker.py
index 1b25a305..20a313a 100644
--- a/third_party/blink/tools/blinkpy/style/checker.py
+++ b/third_party/blink/tools/blinkpy/style/checker.py
@@ -41,7 +41,6 @@
 from blinkpy.style.checkers.jsonchecker import JSONChecker
 from blinkpy.style.checkers.png import PNGChecker
 from blinkpy.style.checkers.python import PythonChecker
-from blinkpy.style.checkers.test_expectations import TestExpectationsChecker
 from blinkpy.style.checkers.text import TextChecker
 from blinkpy.style.checkers.xcodeproj import XcodeProjectFileChecker
 from blinkpy.style.checkers.xml import XMLChecker
@@ -196,7 +195,6 @@
     # Take the union across all checkers.
     categories = CommonCategories.union(CppChecker.categories)
     categories = categories.union(JSONChecker.categories)
-    categories = categories.union(TestExpectationsChecker.categories)
     categories = categories.union(PNGChecker.categories)
     return categories
 
@@ -360,14 +358,6 @@
         """Return whether the given file should be skipped without a warning."""
         if not self._file_type(file_path):  # FileType.NONE.
             return True
-        # Since "web_tests" is in _SKIPPED_FILES_WITHOUT_WARNING, make
-        # an exception to prevent files like 'TestExpectations' from being skipped.
-        #
-        # FIXME: Figure out a good way to avoid having to add special logic
-        #        for this special case.
-        basename = os.path.basename(file_path)
-        if basename == 'TestExpectations':
-            return False
         for skipped_file in _SKIPPED_FILES_WITHOUT_WARNING:
             if self._should_skip_file_path(file_path, skipped_file):
                 return True
@@ -399,8 +389,7 @@
             return FileType.XCODEPROJ
         elif file_extension == _PNG_FILE_EXTENSION:
             return FileType.PNG
-        elif (file_extension in _TEXT_FILE_EXTENSIONS
-              or os.path.basename(file_path) == 'TestExpectations'):
+        elif (file_extension in _TEXT_FILE_EXTENSIONS):
             return FileType.TEXT
         else:
             return FileType.NONE
@@ -425,12 +414,7 @@
         elif file_type == FileType.PNG:
             checker = PNGChecker(file_path, handle_style_error)
         elif file_type == FileType.TEXT:
-            basename = os.path.basename(file_path)
-            if basename == 'TestExpectations':
-                checker = TestExpectationsChecker(file_path,
-                                                  handle_style_error)
-            else:
-                checker = TextChecker(file_path, handle_style_error)
+            checker = TextChecker(file_path, handle_style_error)
         else:
             raise ValueError(
                 'Invalid file type "%(file_type)s": the only valid file types '
diff --git a/third_party/blink/tools/blinkpy/style/checker_unittest.py b/third_party/blink/tools/blinkpy/style/checker_unittest.py
index ef55ac3..23d98b52 100644
--- a/third_party/blink/tools/blinkpy/style/checker_unittest.py
+++ b/third_party/blink/tools/blinkpy/style/checker_unittest.py
@@ -263,14 +263,9 @@
 
     def test_should_skip_without_warning__false(self):
         """Test should_skip_without_warning() for False return values."""
-        paths = [
-            'foo.txt',
-            os.path.join('web_tests', 'TestExpectations'),
-        ]
-
-        for path in paths:
-            self._assert_should_skip_without_warning(
-                path, is_checker_none=False, expected=False)
+        self._assert_should_skip_without_warning('foo.txt',
+                                                 is_checker_none=False,
+                                                 expected=False)
 
 
 class CheckerDispatcherCarriageReturnTest(unittest.TestCase):
diff --git a/third_party/blink/tools/blinkpy/style/checkers/test_expectations.py b/third_party/blink/tools/blinkpy/style/checkers/test_expectations.py
deleted file mode 100644
index 48d4e546..0000000
--- a/third_party/blink/tools/blinkpy/style/checkers/test_expectations.py
+++ /dev/null
@@ -1,73 +0,0 @@
-# Copyright (C) 2010 Google Inc. All rights reserved.
-#
-# Redistribution and use in source and binary forms, with or without
-# modification, are permitted provided that the following conditions are
-# met:
-#
-#     * Redistributions of source code must retain the above copyright
-# notice, this list of conditions and the following disclaimer.
-#     * Redistributions in binary form must reproduce the above
-# copyright notice, this list of conditions and the following disclaimer
-# in the documentation and/or other materials provided with the
-# distribution.
-#     * Neither the name of Google Inc. nor the names of its
-# contributors may be used to endorse or promote products derived from
-# this software without specific prior written permission.
-#
-# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-"""Checks WebKit style for test_expectations files."""
-
-import logging
-
-from blinkpy.style.checkers.common import TabChecker
-from blinkpy.common.host import Host
-from blinkpy.web_tests.models.typ_types import TestExpectations
-
-
-class TestExpectationsChecker(object):
-    """Processes TestExpectations lines for validating the syntax."""
-
-    categories = set(['test/expectations'])
-
-    def __init__(self, file_path, handle_style_error, host=None):
-        self._file_path = file_path
-        self._handle_style_error = handle_style_error
-        self._tab_checker = TabChecker(file_path, handle_style_error)
-
-        # FIXME: host should be a required parameter, not an optional one.
-        host = host or Host()
-
-        self._port_obj = host.port_factory.get()
-
-        # Suppress error messages of test_expectations module since they will be reported later.
-        log = logging.getLogger(
-            'blinkpy.web_tests.layout_package.test_expectations')
-        log.setLevel(logging.CRITICAL)
-
-    def check_test_expectations(self, expectations_str, tests=None):
-        expectations = TestExpectations()
-        ret, errors = expectations.parse_tagged_list(expectations_str)
-        if ret:
-            self._handle_style_error(message=errors)
-
-    def check_tabs(self, lines):
-        self._tab_checker.check(lines)
-
-    def check(self, lines):
-        expectations = '\n'.join(lines)
-        if self._port_obj:
-            self.check_test_expectations(
-                expectations_str=expectations, tests=None)
-
-        # Warn tabs in lines as well
-        self.check_tabs(lines)
diff --git a/third_party/blink/tools/blinkpy/style/checkers/test_expectations_unittest.py b/third_party/blink/tools/blinkpy/style/checkers/test_expectations_unittest.py
deleted file mode 100644
index 35a6131..0000000
--- a/third_party/blink/tools/blinkpy/style/checkers/test_expectations_unittest.py
+++ /dev/null
@@ -1,121 +0,0 @@
-# Copyright (C) 2010 Google Inc. All rights reserved.
-#
-# Redistribution and use in source and binary forms, with or without
-# modification, are permitted provided that the following conditions are
-# met:
-#
-#     * Redistributions of source code must retain the above copyright
-# notice, this list of conditions and the following disclaimer.
-#     * Redistributions in binary form must reproduce the above
-# copyright notice, this list of conditions and the following disclaimer
-# in the documentation and/or other materials provided with the
-# distribution.
-#     * Neither the name of Google Inc. nor the names of its
-# contributors may be used to endorse or promote products derived from
-# this software without specific prior written permission.
-#
-# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-import unittest
-
-from blinkpy.common.host_mock import MockHost
-from blinkpy.style.checkers.test_expectations import TestExpectationsChecker
-
-
-class ErrorCollector(object):
-    """An error handler class for unit tests."""
-
-    def __init__(self):
-        self._errors = []
-        self.turned_off_filtering = False
-
-    def turn_off_line_filtering(self):
-        self.turned_off_filtering = True
-
-    def __call__(self,
-                 lineno=0,
-                 category='test/expectations',
-                 confidence=100,
-                 message=''):
-        self._errors.append('%s\n[%s] [%d]' % (message, category, confidence))
-        return True
-
-    def get_errors(self):
-        return ''.join(self._errors)
-
-    def reset_errors(self):
-        self._errors = []
-        self.turned_off_filtering = False
-
-
-class TestExpectationsTestCase(unittest.TestCase):
-    """TestCase for test_expectations.py"""
-
-    def setUp(self):
-        self._error_collector = ErrorCollector()
-        self._test_file = 'passes/text.html'
-
-    def assert_lines_lint(self, lines, should_pass, expected_output=None):
-        self._error_collector.reset_errors()
-
-        host = MockHost()
-        checker = TestExpectationsChecker(
-            'test/TestExpectations', self._error_collector, host=host)
-
-        # We should have a valid port, but override it with a test port so we
-        # can check the lines.
-        self.assertIsNotNone(checker._port_obj)
-        checker._port_obj = host.port_factory.get('test-mac-mac10.10')
-        checker.check_test_expectations(
-            expectations_str='\n'.join(lines), tests=[self._test_file])
-        checker.check_tabs(lines)
-        if should_pass:
-            self.assertEqual('', self._error_collector.get_errors())
-        elif expected_output:
-            self.assertEqual(expected_output,
-                             self._error_collector.get_errors())
-        else:
-            self.assertNotEquals('', self._error_collector.get_errors())
-
-        # Note that a patch might change a line that introduces errors elsewhere, but we
-        # don't want to lint the whole file (it can unfairly punish patches for pre-existing errors).
-        # We rely on a separate webkit_lint step on the bots to keep the whole file okay.
-        # FIXME: See https://bugs.webkit.org/show_bug.cgi?id=104712 .
-        self.assertFalse(self._error_collector.turned_off_filtering)
-
-    def test_valid_expectations(self):
-        self.assert_lines_lint([
-            '# tags: [ Mac ]\n'
-            '# results: [ Pass Failure ]\n'
-            'crbug.com/1234 [ Mac ] passes/text.html [ Pass Failure ]'
-        ],
-                               should_pass=True)
-
-    def test_invalid_expectations(self):
-        self.assert_lines_lint(['Bug(me) passes/text.html [ Give Up]'],
-                               should_pass=False)
-
-    def test_tab(self):
-        self.assert_lines_lint(
-            [
-                '# results: [ Pass ]\n'
-                '\tcrbug.com/b/1 passes/text.html [ Pass ]'
-            ],
-            should_pass=False,
-            expected_output='Line contains tab character.\n[whitespace/tab] [5]'
-        )
-
-    def test_missing_expectation_not_allowed(self):
-        self.assert_lines_lint(
-            ['crbug.com/1234 [ Mac ] passes/text.html [ Missing ]'],
-            should_pass=False)
diff --git a/third_party/blink/tools/blinkpy/style/main.py b/third_party/blink/tools/blinkpy/style/main.py
index 80ac53c8..3addc55 100644
--- a/third_party/blink/tools/blinkpy/style/main.py
+++ b/third_party/blink/tools/blinkpy/style/main.py
@@ -102,9 +102,12 @@
     def _engage_awesome_stderr_hacks(self):
         # Change stderr to write with replacement characters so we don't die
         # if we try to print something containing non-ASCII characters.
-        stderr = codecs.StreamReaderWriter(sys.stderr,
-                                           codecs.getreader('utf8'),
-                                           codecs.getwriter('utf8'), 'replace')
+        # We use sys.stderr.buffer in Python 3, since StreamReaderWriter writes
+        # bytes to the specified stream (this fix copied from cpplint.py).
+        stderr = codecs.StreamReaderWriter(
+            getattr(sys.stderr, 'buffer', sys.stderr),
+            codecs.getreader('utf8'), codecs.getwriter('utf8'), 'replace')
+
         # Setting an "encoding" attribute on the stream is necessary to
         # prevent the logging module from raising an error.  See
         # the checker.configure_logging() function for more information.
diff --git a/third_party/blink/web_tests/NeverFixTests b/third_party/blink/web_tests/NeverFixTests
index a70f443..5b7af635 100644
--- a/third_party/blink/web_tests/NeverFixTests
+++ b/third_party/blink/web_tests/NeverFixTests
@@ -1984,3 +1984,6 @@
 # virtual test suite.
 wpt_internal/origin-agent-cluster-default/* [ Skip ]
 virtual/origin-agent-cluster-default/wpt_internal/origin-agent-cluster-default/* [ Pass ]
+
+# Skip manual tests, see crbug/1114920
+external/wpt/html/semantics/forms/the-input-element/disabled-click-picker-manual.html [ Skip ]
diff --git a/third_party/blink/web_tests/TestExpectations b/third_party/blink/web_tests/TestExpectations
index e7ddf09..f46eaba 100644
--- a/third_party/blink/web_tests/TestExpectations
+++ b/third_party/blink/web_tests/TestExpectations
@@ -1416,10 +1416,7 @@
 
 # Comparing variable font rendering to static font rendering fails on systems that use FreeType for variable fonts
 crbug.com/1067242 [ Mac10.13 ] external/wpt/css/css-text-decor/text-underline-position-from-font-variable.html [ Failure ]
-crbug.com/1067242 [ Mac10.13 ] external/wpt/css/css-text-decor/text-decoration-thickness-fixed.html [ Failure ]
 crbug.com/1067242 [ Mac10.13 ] external/wpt/css/css-text-decor/text-decoration-thickness-from-font-variable.html [ Failure ]
-# Windows 10 bots are not on high enough a Windows version to render variable fonts in DirectWrite
-crbug.com/1068947 [ Win ] external/wpt/css/css-text-decor/text-decoration-thickness-fixed.html [ Failure ]
 # Windows 10 bot upgrade now causes these failures on Windows-10-18363
 crbug.com/1140324 [ Win11 ] external/wpt/css/css-text-decor/text-underline-offset-variable.html [ Failure ]
 crbug.com/1140324 [ Win10.20h2 ] external/wpt/css/css-text-decor/text-underline-offset-variable.html [ Failure ]
diff --git a/third_party/blink/web_tests/external/wpt/css/css-text-decor/reference/text-decoration-thickness-fixed-ref.html b/third_party/blink/web_tests/external/wpt/css/css-text-decor/reference/text-decoration-thickness-fixed-ref.html
index b155eb9c..88f7b46 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-text-decor/reference/text-decoration-thickness-fixed-ref.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-text-decor/reference/text-decoration-thickness-fixed-ref.html
@@ -19,7 +19,7 @@
 
 .test {
 text-underline-position: from-font;
-font-size: 62px;
+font-size: 64px;
 line-height: 1.8;
 }
 
@@ -38,9 +38,9 @@
 </head>
 <body>
     <p>Test passes if underlines are thin, thick, thin, thick in this order and match the reference rendering.</p>
-    <div class="test"><span class="thin_underline">aagaa</span></div>
-    <div class="test"><span class="thick_underline">aagaa</span></div>
-    <div class="test"><span class="thin_underline">aagaa</span></div>
-    <div class="test"><span class="thick_underline">aagaa</span></div>
+    <div class="test"><span class="thin_underline">aaGaa</span></div>
+    <div class="test"><span class="thick_underline">aaGaa</span></div>
+    <div class="test"><span class="thin_underline">aaGaa</span></div>
+    <div class="test"><span class="thick_underline">aaGaa</span></div>
 </body>
 </html>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-text-decor/text-decoration-thickness-fixed.html b/third_party/blink/web_tests/external/wpt/css/css-text-decor/text-decoration-thickness-fixed.html
index 3437c430..07dab0b9 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-text-decor/text-decoration-thickness-fixed.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-text-decor/text-decoration-thickness-fixed.html
@@ -15,7 +15,7 @@
 
 .test {
 text-underline-position: from-font;
-font-size: 62px;
+font-size: 64px;
 line-height: 1.8;
 }
 
@@ -28,7 +28,7 @@
 .thick_underline {
 font-family: underline-variable;
 text-decoration: underline;
-text-decoration-thickness: 6.2px;
+text-decoration-thickness: 6.4px;
 }
 
 .thin_underline_percent {
@@ -46,9 +46,9 @@
 </head>
 <body>
     <p>Test passes if underlines are thin, thick, thin, thick in this order and match the reference rendering.</p>
-    <div class="test"><span class="thin_underline">aagaa</span></div>
-    <div class="test"><span class="thick_underline">aagaa</span></div>
-    <div class="test"><span class="thin_underline_percent">aagaa</span></div>
-    <div class="test"><span class="thick_underline_percent">aagaa</span></div>
+    <div class="test"><span class="thin_underline">aaGaa</span></div>
+    <div class="test"><span class="thick_underline">aaGaa</span></div>
+    <div class="test"><span class="thin_underline_percent">aaGaa</span></div>
+    <div class="test"><span class="thick_underline_percent">aaGaa</span></div>
 </body>
 </html>
diff --git a/third_party/blink/web_tests/external/wpt/html/canvas/element/manual/layers/layers-alpha-expected.html b/third_party/blink/web_tests/external/wpt/html/canvas/element/manual/layers/layers-alpha-expected.html
index 876eacc..03fe48a 100644
--- a/third_party/blink/web_tests/external/wpt/html/canvas/element/manual/layers/layers-alpha-expected.html
+++ b/third_party/blink/web_tests/external/wpt/html/canvas/element/manual/layers/layers-alpha-expected.html
@@ -1,4 +1,3 @@
-<body>
 <p class="desc"> Test to ensure beginlayer works for alpha. </p>
 <script>
 var canvas, ctx;
@@ -22,4 +21,3 @@
 
 ctx.drawImage(canvas2,0,0);
 </script>
-</body>
diff --git a/third_party/blink/web_tests/external/wpt/html/canvas/element/manual/layers/layers-alpha-filter-expected.html b/third_party/blink/web_tests/external/wpt/html/canvas/element/manual/layers/layers-alpha-filter-expected.html
index bd0affb..5b54384a 100644
--- a/third_party/blink/web_tests/external/wpt/html/canvas/element/manual/layers/layers-alpha-filter-expected.html
+++ b/third_party/blink/web_tests/external/wpt/html/canvas/element/manual/layers/layers-alpha-filter-expected.html
@@ -1,4 +1,3 @@
-<body>
 <p class="desc"> Test to ensure beginlayer works when both alpha and filter are applied. </p>
 <script>
     var canvas, ctx;
@@ -23,4 +22,3 @@
 
     ctx.drawImage(canvas2,0,0);
 </script>
-</body>
\ No newline at end of file
diff --git a/third_party/blink/web_tests/external/wpt/html/canvas/element/manual/layers/layers-alpha-filter-globalcompositeoperation-expected.html b/third_party/blink/web_tests/external/wpt/html/canvas/element/manual/layers/layers-alpha-filter-globalcompositeoperation-expected.html
index bd9b5ae..8d84dcb7 100644
--- a/third_party/blink/web_tests/external/wpt/html/canvas/element/manual/layers/layers-alpha-filter-globalcompositeoperation-expected.html
+++ b/third_party/blink/web_tests/external/wpt/html/canvas/element/manual/layers/layers-alpha-filter-globalcompositeoperation-expected.html
@@ -1,4 +1,3 @@
-<body>
 <p class="desc"> Test to ensure beginlayer works with global alpha, filter, and global composite operation. </p>
 <script>
     var canvas, ctx;
@@ -27,4 +26,3 @@
     ctx.drawImage(canvas2,0,0);
 
 </script>
-</body>
\ No newline at end of file
diff --git a/third_party/blink/web_tests/external/wpt/html/canvas/element/manual/layers/layers-alpha-filter-globalcompositeoperation.html b/third_party/blink/web_tests/external/wpt/html/canvas/element/manual/layers/layers-alpha-filter-globalcompositeoperation.html
index dd1af3d..6912d6a1 100644
--- a/third_party/blink/web_tests/external/wpt/html/canvas/element/manual/layers/layers-alpha-filter-globalcompositeoperation.html
+++ b/third_party/blink/web_tests/external/wpt/html/canvas/element/manual/layers/layers-alpha-filter-globalcompositeoperation.html
@@ -1,4 +1,4 @@
-<body>
+<link rel="match" href="layers-alpha-filter-globalcompositeoperation-expected.html">
 <p class="desc"> Test to ensure beginlayer works with global alpha, filter, and global composite operation. </p>
 <script>
     var canvas, ctx;
@@ -26,4 +26,3 @@
 
     ctx.endLayer();
 </script>
-</body>
\ No newline at end of file
diff --git a/third_party/blink/web_tests/external/wpt/html/canvas/element/manual/layers/layers-alpha-filter-shadow-expected.html b/third_party/blink/web_tests/external/wpt/html/canvas/element/manual/layers/layers-alpha-filter-shadow-expected.html
index bfdb26e..89c3f64 100644
--- a/third_party/blink/web_tests/external/wpt/html/canvas/element/manual/layers/layers-alpha-filter-shadow-expected.html
+++ b/third_party/blink/web_tests/external/wpt/html/canvas/element/manual/layers/layers-alpha-filter-shadow-expected.html
@@ -1,4 +1,3 @@
-<body>
 <p class="desc"> Test to ensure beginlayer works for filter, alpha and shadow. </p>
 <script>
 var canvas, ctx;
@@ -26,4 +25,3 @@
 
 ctx.drawImage(canvas2,0,0);
 </script>
-</body>
diff --git a/third_party/blink/web_tests/external/wpt/html/canvas/element/manual/layers/layers-alpha-filter-shadow.html b/third_party/blink/web_tests/external/wpt/html/canvas/element/manual/layers/layers-alpha-filter-shadow.html
index ae97240..9c4a6fc 100644
--- a/third_party/blink/web_tests/external/wpt/html/canvas/element/manual/layers/layers-alpha-filter-shadow.html
+++ b/third_party/blink/web_tests/external/wpt/html/canvas/element/manual/layers/layers-alpha-filter-shadow.html
@@ -1,4 +1,5 @@
-<body>
+<link rel="match" href="layers-alpha-filter-shadow-expected.html">
+<meta name=fuzzy content="maxDifference=0-2; totalPixels=0-18000">
 <p class="desc"> Test to ensure beginlayer works for filter, alpha and shadow. </p>
 <script>
 var canvas, ctx;
@@ -27,4 +28,3 @@
 ctx.endLayer();
 
 </script>
-</body>
diff --git a/third_party/blink/web_tests/external/wpt/html/canvas/element/manual/layers/layers-alpha-filter.html b/third_party/blink/web_tests/external/wpt/html/canvas/element/manual/layers/layers-alpha-filter.html
index d45b1d5..7a895cf 100644
--- a/third_party/blink/web_tests/external/wpt/html/canvas/element/manual/layers/layers-alpha-filter.html
+++ b/third_party/blink/web_tests/external/wpt/html/canvas/element/manual/layers/layers-alpha-filter.html
@@ -1,4 +1,4 @@
-<body>
+<link rel="match" href="layers-alpha-filter-expected.html">
 <p class="desc"> Test to ensure beginlayer works when both alpha and filter are applied. </p>
 <script>
     var canvas, ctx;
@@ -22,4 +22,3 @@
 
     ctx.endLayer();
 </script>
-</body>
\ No newline at end of file
diff --git a/third_party/blink/web_tests/external/wpt/html/canvas/element/manual/layers/layers-alpha-shadow-expected.html b/third_party/blink/web_tests/external/wpt/html/canvas/element/manual/layers/layers-alpha-shadow-expected.html
index 89343e78..f29fb736 100644
--- a/third_party/blink/web_tests/external/wpt/html/canvas/element/manual/layers/layers-alpha-shadow-expected.html
+++ b/third_party/blink/web_tests/external/wpt/html/canvas/element/manual/layers/layers-alpha-shadow-expected.html
@@ -1,4 +1,3 @@
-<body>
 <p class="desc"> Test to ensure beginlayer works for alpha and shadow. </p>
 <script>
 var canvas, ctx;
@@ -25,4 +24,3 @@
 
 ctx.drawImage(canvas2,0,0);
 </script>
-</body>
diff --git a/third_party/blink/web_tests/external/wpt/html/canvas/element/manual/layers/layers-alpha-shadow.html b/third_party/blink/web_tests/external/wpt/html/canvas/element/manual/layers/layers-alpha-shadow.html
index bfe7241..e7d791d8 100644
--- a/third_party/blink/web_tests/external/wpt/html/canvas/element/manual/layers/layers-alpha-shadow.html
+++ b/third_party/blink/web_tests/external/wpt/html/canvas/element/manual/layers/layers-alpha-shadow.html
@@ -1,4 +1,5 @@
-<body>
+<link rel="match" href="layers-alpha-shadow-expected.html">
+<meta name=fuzzy content="maxDifference=0-2; totalPixels=0-18000">
 <p class="desc"> Test to ensure beginlayer works for alpha and shadow. </p>
 <script>
 var canvas, ctx;
@@ -26,4 +27,3 @@
 ctx.endLayer();
 
 </script>
-</body>
diff --git a/third_party/blink/web_tests/external/wpt/html/canvas/element/manual/layers/layers-alpha.html b/third_party/blink/web_tests/external/wpt/html/canvas/element/manual/layers/layers-alpha.html
index fe871e7e..7be5bdb0 100644
--- a/third_party/blink/web_tests/external/wpt/html/canvas/element/manual/layers/layers-alpha.html
+++ b/third_party/blink/web_tests/external/wpt/html/canvas/element/manual/layers/layers-alpha.html
@@ -1,4 +1,4 @@
-<body>
+<link rel="match" href="layers-alpha-expected.html">
 <p class="desc"> Test to ensure beginlayer works for alpha. </p>
 <script>
 var canvas, ctx;
@@ -23,4 +23,3 @@
 ctx.endLayer();
 
 </script>
-</body>
diff --git a/third_party/blink/web_tests/external/wpt/html/canvas/element/manual/layers/layers-endlayer-noop-expected.html b/third_party/blink/web_tests/external/wpt/html/canvas/element/manual/layers/layers-endlayer-noop-expected.html
index e3c9571..7f4937ec 100644
--- a/third_party/blink/web_tests/external/wpt/html/canvas/element/manual/layers/layers-endlayer-noop-expected.html
+++ b/third_party/blink/web_tests/external/wpt/html/canvas/element/manual/layers/layers-endlayer-noop-expected.html
@@ -1,4 +1,3 @@
-<body>
 <p class="desc"> A test to make sure an unmatched endLayer is a no-op and has no effect on the code following it. </p>
 <script>
 var canvas, ctx;
@@ -23,4 +22,3 @@
 ctx.endLayer();
 
 </script>
-</body>
\ No newline at end of file
diff --git a/third_party/blink/web_tests/external/wpt/html/canvas/element/manual/layers/layers-endlayer-noop.html b/third_party/blink/web_tests/external/wpt/html/canvas/element/manual/layers/layers-endlayer-noop.html
index d9d6200..aae72cf 100644
--- a/third_party/blink/web_tests/external/wpt/html/canvas/element/manual/layers/layers-endlayer-noop.html
+++ b/third_party/blink/web_tests/external/wpt/html/canvas/element/manual/layers/layers-endlayer-noop.html
@@ -1,4 +1,4 @@
-<body>
+<link rel="match" href="layers-endlayer-noop-expected.html">
 <p class="desc"> A test to make sure an unmatched endLayer is a no-op and has no effect on the code following it. </p>
 <script>
 var canvas, ctx;
@@ -25,4 +25,3 @@
 ctx.endLayer();
 
 </script>
-</body>
\ No newline at end of file
diff --git a/third_party/blink/web_tests/external/wpt/html/canvas/element/manual/layers/layers-filter-expected.html b/third_party/blink/web_tests/external/wpt/html/canvas/element/manual/layers/layers-filter-expected.html
index a65f52b..7fe4d2c 100644
--- a/third_party/blink/web_tests/external/wpt/html/canvas/element/manual/layers/layers-filter-expected.html
+++ b/third_party/blink/web_tests/external/wpt/html/canvas/element/manual/layers/layers-filter-expected.html
@@ -1,4 +1,3 @@
-<body>
 <p class="desc"> Test to ensure beginlayer works for filter. </p>
 <script>
     var canvas, ctx;
@@ -22,4 +21,3 @@
 
     ctx.drawImage(canvas2,0,0);
 </script>
-</body>
\ No newline at end of file
diff --git a/third_party/blink/web_tests/external/wpt/html/canvas/element/manual/layers/layers-filter-globalcompositeoperation-expected.html b/third_party/blink/web_tests/external/wpt/html/canvas/element/manual/layers/layers-filter-globalcompositeoperation-expected.html
index 3e9fbee..0676d7e1 100644
--- a/third_party/blink/web_tests/external/wpt/html/canvas/element/manual/layers/layers-filter-globalcompositeoperation-expected.html
+++ b/third_party/blink/web_tests/external/wpt/html/canvas/element/manual/layers/layers-filter-globalcompositeoperation-expected.html
@@ -1,4 +1,3 @@
-<body>
 <p class="desc"> Test to ensure beginlayer works for filter and global composition. </p>
 <script>
     var canvas, ctx;
@@ -27,4 +26,3 @@
 
     ctx.drawImage(canvas2,0,0);
 </script>
-</body>
\ No newline at end of file
diff --git a/third_party/blink/web_tests/external/wpt/html/canvas/element/manual/layers/layers-filter-globalcompositeoperation.html b/third_party/blink/web_tests/external/wpt/html/canvas/element/manual/layers/layers-filter-globalcompositeoperation.html
index 51c79c8..03d04d09 100644
--- a/third_party/blink/web_tests/external/wpt/html/canvas/element/manual/layers/layers-filter-globalcompositeoperation.html
+++ b/third_party/blink/web_tests/external/wpt/html/canvas/element/manual/layers/layers-filter-globalcompositeoperation.html
@@ -1,4 +1,4 @@
-<body>
+<link rel="match" href="layers-filter-globalcompositeoperation-expected.html">
 <p class="desc"> Test to ensure beginlayer works for filter and global composition. </p>
 <script>
     var canvas, ctx;
@@ -26,4 +26,3 @@
 
     ctx.endLayer();
 </script>
-</body>
\ No newline at end of file
diff --git a/third_party/blink/web_tests/external/wpt/html/canvas/element/manual/layers/layers-filter-shadow-expected.html b/third_party/blink/web_tests/external/wpt/html/canvas/element/manual/layers/layers-filter-shadow-expected.html
index 85a5bea..5d141c3 100644
--- a/third_party/blink/web_tests/external/wpt/html/canvas/element/manual/layers/layers-filter-shadow-expected.html
+++ b/third_party/blink/web_tests/external/wpt/html/canvas/element/manual/layers/layers-filter-shadow-expected.html
@@ -1,4 +1,3 @@
-<body>
 <p class="desc"> Test to ensure beginlayer works for filter and shadow. </p>
 <script>
 var canvas, ctx;
@@ -25,4 +24,3 @@
 
 ctx.drawImage(canvas2,0,0);
 </script>
-</body>
diff --git a/third_party/blink/web_tests/external/wpt/html/canvas/element/manual/layers/layers-filter-shadow.html b/third_party/blink/web_tests/external/wpt/html/canvas/element/manual/layers/layers-filter-shadow.html
index e4da5b8..31ce671 100644
--- a/third_party/blink/web_tests/external/wpt/html/canvas/element/manual/layers/layers-filter-shadow.html
+++ b/third_party/blink/web_tests/external/wpt/html/canvas/element/manual/layers/layers-filter-shadow.html
@@ -1,4 +1,4 @@
-<body>
+<link rel="match" href="layers-filter-shadow-expected.html">
 <p class="desc"> Test to ensure beginlayer works for filter and shadow. </p>
 <script>
 var canvas, ctx;
@@ -26,4 +26,3 @@
 ctx.endLayer();
 
 </script>
-</body>
diff --git a/third_party/blink/web_tests/external/wpt/html/canvas/element/manual/layers/layers-filter.html b/third_party/blink/web_tests/external/wpt/html/canvas/element/manual/layers/layers-filter.html
index 9dbb09f6..c4d63cd 100644
--- a/third_party/blink/web_tests/external/wpt/html/canvas/element/manual/layers/layers-filter.html
+++ b/third_party/blink/web_tests/external/wpt/html/canvas/element/manual/layers/layers-filter.html
@@ -1,4 +1,4 @@
-<body>
+<link rel="match" href="layers-filter-expected.html">
 <p class="desc"> Test to ensure beginlayer works for filter. </p>
 <script>
     var canvas, ctx;
@@ -21,4 +21,3 @@
 
     ctx.endLayer();
 </script>
-</body>
\ No newline at end of file
diff --git a/third_party/blink/web_tests/external/wpt/html/canvas/element/manual/layers/layers-globalcompositeoperation-expected.html b/third_party/blink/web_tests/external/wpt/html/canvas/element/manual/layers/layers-globalcompositeoperation-expected.html
index 234c1f9e..d2d4317 100644
--- a/third_party/blink/web_tests/external/wpt/html/canvas/element/manual/layers/layers-globalcompositeoperation-expected.html
+++ b/third_party/blink/web_tests/external/wpt/html/canvas/element/manual/layers/layers-globalcompositeoperation-expected.html
@@ -1,4 +1,3 @@
-<body>
 <p class="desc"> Test to ensure beginlayer works for globalCompositeOperation. </p>
 <script>
 var canvas, ctx;
@@ -22,4 +21,3 @@
 
 ctx.drawImage(canvas2,0,0);
 </script>
-</body>
diff --git a/third_party/blink/web_tests/external/wpt/html/canvas/element/manual/layers/layers-globalcompositeoperation.html b/third_party/blink/web_tests/external/wpt/html/canvas/element/manual/layers/layers-globalcompositeoperation.html
index 20ce0ca..6d4fde3 100644
--- a/third_party/blink/web_tests/external/wpt/html/canvas/element/manual/layers/layers-globalcompositeoperation.html
+++ b/third_party/blink/web_tests/external/wpt/html/canvas/element/manual/layers/layers-globalcompositeoperation.html
@@ -1,4 +1,4 @@
-<body>
+<link rel="match" href="layers-globalcompositeoperation-expected.html">
 <p class="desc"> Test to ensure beginlayer works for globalCompositeOperation. </p>
 <script>
 var canvas, ctx;
@@ -23,4 +23,3 @@
 ctx.endLayer();
 
 </script>
-</body>
diff --git a/third_party/blink/web_tests/external/wpt/html/canvas/element/manual/layers/layers-loneendlayer-expected.html b/third_party/blink/web_tests/external/wpt/html/canvas/element/manual/layers/layers-loneendlayer-expected.html
index d6e06cd..cec5a54 100644
--- a/third_party/blink/web_tests/external/wpt/html/canvas/element/manual/layers/layers-loneendlayer-expected.html
+++ b/third_party/blink/web_tests/external/wpt/html/canvas/element/manual/layers/layers-loneendlayer-expected.html
@@ -1,4 +1,3 @@
-<body>
 <p class="desc"> A test to make sure a single endLayer with no beginLayer is a no-op. </p>
 <script>
 var canvas, ctx;
@@ -17,4 +16,3 @@
 ctx.fillStyle = 'rgba(0,255,0,1)';
 ctx.fillRect(70,70,75,50);
 </script>
-</body>
\ No newline at end of file
diff --git a/third_party/blink/web_tests/external/wpt/html/canvas/element/manual/layers/layers-loneendlayer.html b/third_party/blink/web_tests/external/wpt/html/canvas/element/manual/layers/layers-loneendlayer.html
index e762c5f..f0584d3 100644
--- a/third_party/blink/web_tests/external/wpt/html/canvas/element/manual/layers/layers-loneendlayer.html
+++ b/third_party/blink/web_tests/external/wpt/html/canvas/element/manual/layers/layers-loneendlayer.html
@@ -1,4 +1,4 @@
-<body>
+<link rel="match" href="layers-loneendlayer-expected.html">
 <p class="desc"> A test to make sure a single endLayer with no beginLayer is a no-op. </p>
 <script>
 var canvas, ctx;
@@ -19,4 +19,3 @@
 
 ctx.endLayer();
 </script>
-</body>
\ No newline at end of file
diff --git a/third_party/blink/web_tests/external/wpt/html/canvas/element/manual/layers/layers-nested-expected.html b/third_party/blink/web_tests/external/wpt/html/canvas/element/manual/layers/layers-nested-expected.html
index c74f1e6..4647996 100644
--- a/third_party/blink/web_tests/external/wpt/html/canvas/element/manual/layers/layers-nested-expected.html
+++ b/third_party/blink/web_tests/external/wpt/html/canvas/element/manual/layers/layers-nested-expected.html
@@ -1,5 +1,3 @@
-<meta name=fuzzy content="maxDifference=0-5;totalPixels=0-100">
-<body>
 <script>
 var canvas, ctx;
 canvas = document.createElement("canvas");
@@ -34,4 +32,3 @@
 ctx2.drawImage(canvas3,0,0);
 ctx.drawImage(canvas2,0,0);
 </script>
-</body>
\ No newline at end of file
diff --git a/third_party/blink/web_tests/external/wpt/html/canvas/element/manual/layers/layers-nested.html b/third_party/blink/web_tests/external/wpt/html/canvas/element/manual/layers/layers-nested.html
index b1bbdbe..fb52976 100644
--- a/third_party/blink/web_tests/external/wpt/html/canvas/element/manual/layers/layers-nested.html
+++ b/third_party/blink/web_tests/external/wpt/html/canvas/element/manual/layers/layers-nested.html
@@ -1,5 +1,5 @@
-<meta name=fuzzy content="maxDifference=0-5;totalPixels=0-100">
-<body>
+<link rel="match" href="layers-nested-expected.html">
+<meta name=fuzzy content="maxDifference=0-2; totalPixels=0-14000">
 <script>
 var canvas, ctx;
 canvas = document.createElement("canvas");
@@ -32,4 +32,3 @@
 ctx.endLayer();
 ctx.endLayer();
 </script>
-</body>
\ No newline at end of file
diff --git a/third_party/blink/web_tests/external/wpt/html/canvas/element/manual/layers/layers-restorestyle-expected.html b/third_party/blink/web_tests/external/wpt/html/canvas/element/manual/layers/layers-restorestyle-expected.html
index 6372dfe..f67a457 100644
--- a/third_party/blink/web_tests/external/wpt/html/canvas/element/manual/layers/layers-restorestyle-expected.html
+++ b/third_party/blink/web_tests/external/wpt/html/canvas/element/manual/layers/layers-restorestyle-expected.html
@@ -1,4 +1,3 @@
-<body>
 <p class="desc"> Test that ensure layers restores style values upon endLayer. </p>
 <script>
 var canvas, ctx;
@@ -22,4 +21,3 @@
 ctx.fillRect(70,70,75,50);
 
 </script>
-</body>
diff --git a/third_party/blink/web_tests/external/wpt/html/canvas/element/manual/layers/layers-restorestyle.html b/third_party/blink/web_tests/external/wpt/html/canvas/element/manual/layers/layers-restorestyle.html
index 9a20aa07..659b6c0 100644
--- a/third_party/blink/web_tests/external/wpt/html/canvas/element/manual/layers/layers-restorestyle.html
+++ b/third_party/blink/web_tests/external/wpt/html/canvas/element/manual/layers/layers-restorestyle.html
@@ -1,4 +1,5 @@
-<body>
+<link rel="match" href="layers-restorestyle-expected.html">
+<meta name=fuzzy content="maxDifference=0-1; totalPixels=0-14000">
 <p class="desc"> Test that ensure layers restores style values upon endLayer. </p>
 <script>
 var canvas, ctx;
@@ -22,4 +23,3 @@
 ctx.fillRect(70,70,75,50);
 
 </script>
-</body>
diff --git a/third_party/blink/web_tests/external/wpt/html/canvas/element/manual/layers/layers-several-complex-expected.html b/third_party/blink/web_tests/external/wpt/html/canvas/element/manual/layers/layers-several-complex-expected.html
index 60fffec..eca81cd 100644
--- a/third_party/blink/web_tests/external/wpt/html/canvas/element/manual/layers/layers-several-complex-expected.html
+++ b/third_party/blink/web_tests/external/wpt/html/canvas/element/manual/layers/layers-several-complex-expected.html
@@ -1,4 +1,3 @@
-<body>
 <p class="desc"> Test to ensure beginlayer works for filter, alpha and shadow, even with consecutive layers. </p>
 <script>
 var canvas;
@@ -32,4 +31,3 @@
   ctx.drawImage(canvas2[i],i,i);
 }
 </script>
-</body>
diff --git a/third_party/blink/web_tests/external/wpt/html/canvas/element/manual/layers/layers-several-complex.html b/third_party/blink/web_tests/external/wpt/html/canvas/element/manual/layers/layers-several-complex.html
index 586e1f0..66840bba 100644
--- a/third_party/blink/web_tests/external/wpt/html/canvas/element/manual/layers/layers-several-complex.html
+++ b/third_party/blink/web_tests/external/wpt/html/canvas/element/manual/layers/layers-several-complex.html
@@ -1,4 +1,5 @@
-<body>
+<link rel="match" href="layers-several-complex-expected.html">
+<meta name=fuzzy content="maxDifference=0-3; totalPixels=0-19000">
 <p class="desc"> Test to ensure beginlayer works for filter, alpha and shadow, even with consecutive layers. </p>
 <script>
 var canvas, ctx;
@@ -30,4 +31,3 @@
 }
 
 </script>
-</body>
diff --git a/third_party/blink/web_tests/external/wpt/html/canvas/element/manual/layers/layers-shadow-expected.html b/third_party/blink/web_tests/external/wpt/html/canvas/element/manual/layers/layers-shadow-expected.html
index c58bc50..7696045 100644
--- a/third_party/blink/web_tests/external/wpt/html/canvas/element/manual/layers/layers-shadow-expected.html
+++ b/third_party/blink/web_tests/external/wpt/html/canvas/element/manual/layers/layers-shadow-expected.html
@@ -1,4 +1,3 @@
-<body>
 <p class="desc"> A test to make sure shadow works with layers. </p>
 <script>
 var canvas, ctx;
@@ -24,4 +23,3 @@
 
 ctx.drawImage(canvas2,0,0);
 </script>
-</body>
diff --git a/third_party/blink/web_tests/external/wpt/html/canvas/element/manual/layers/layers-shadow.html b/third_party/blink/web_tests/external/wpt/html/canvas/element/manual/layers/layers-shadow.html
index a605ef4..59768746 100644
--- a/third_party/blink/web_tests/external/wpt/html/canvas/element/manual/layers/layers-shadow.html
+++ b/third_party/blink/web_tests/external/wpt/html/canvas/element/manual/layers/layers-shadow.html
@@ -1,4 +1,4 @@
-<body>
+<link rel="match" href="layers-shadow-expected.html">
 <p class="desc"> A test to make sure shadow works with layers. </p>
 <script>
 
@@ -26,4 +26,3 @@
 ctx.endLayer();
 
 </script>
-</body>
diff --git a/third_party/blink/web_tests/http/tests/security/contentSecurityPolicy/object-src-param-code-blocked.html b/third_party/blink/web_tests/http/tests/security/contentSecurityPolicy/object-src-param-code-blocked.html
deleted file mode 100644
index e7ba845..0000000
--- a/third_party/blink/web_tests/http/tests/security/contentSecurityPolicy/object-src-param-code-blocked.html
+++ /dev/null
@@ -1,13 +0,0 @@
-<!DOCTYPE html>
-<html>
-<head>
-<script src="resources/object-src-param.js"></script>
-<meta http-equiv="Content-Security-Policy" content="object-src http://localhost:8080">
-</head>
-<body>
-This test passes if there is a console message saying the plugin was blocked.
-<script>
-    appendObjectElement('code');
-</script>
-</body>
-</html>
diff --git a/third_party/blink/web_tests/http/tests/security/contentSecurityPolicy/object-src-param-movie-blocked.html b/third_party/blink/web_tests/http/tests/security/contentSecurityPolicy/object-src-param-movie-blocked.html
deleted file mode 100644
index e9f938c4..0000000
--- a/third_party/blink/web_tests/http/tests/security/contentSecurityPolicy/object-src-param-movie-blocked.html
+++ /dev/null
@@ -1,13 +0,0 @@
-<!DOCTYPE html>
-<html>
-<head>
-<script src="resources/object-src-param.js"></script>
-<meta http-equiv="Content-Security-Policy" content="object-src http://localhost:8080">
-</head>
-<body>
-This test passes if there is a console message saying the plugin was blocked.
-<script>
-    appendObjectElement('movie');
-</script>
-</body>
-</html>
diff --git a/third_party/blink/web_tests/http/tests/security/contentSecurityPolicy/object-src-param-src-blocked.html b/third_party/blink/web_tests/http/tests/security/contentSecurityPolicy/object-src-param-src-blocked.html
deleted file mode 100644
index def6f517..0000000
--- a/third_party/blink/web_tests/http/tests/security/contentSecurityPolicy/object-src-param-src-blocked.html
+++ /dev/null
@@ -1,13 +0,0 @@
-<!DOCTYPE html>
-<html>
-<head>
-<script src="resources/object-src-param.js"></script>
-<meta http-equiv="Content-Security-Policy" content="object-src http://localhost:8080">
-</head>
-<body>
-This test passes if there is a console message saying the plugin was blocked.
-<script>
-    appendObjectElement('src');
-</script>
-</body>
-</html>
diff --git a/third_party/blink/web_tests/http/tests/security/contentSecurityPolicy/object-src-param-url-blocked.html b/third_party/blink/web_tests/http/tests/security/contentSecurityPolicy/object-src-param-url-blocked.html
deleted file mode 100644
index d4e78d2..0000000
--- a/third_party/blink/web_tests/http/tests/security/contentSecurityPolicy/object-src-param-url-blocked.html
+++ /dev/null
@@ -1,13 +0,0 @@
-<!DOCTYPE html>
-<html>
-<head>
-<script src="resources/object-src-param.js"></script>
-<meta http-equiv="Content-Security-Policy" content="object-src http://localhost:8080">
-</head>
-<body>
-This test passes if there is a console message saying the plugin was blocked.
-<script>
-    appendObjectElement('url');
-</script>
-</body>
-</html>
diff --git a/third_party/blink/web_tests/http/tests/security/contentSecurityPolicy/resources/object-src-param.js b/third_party/blink/web_tests/http/tests/security/contentSecurityPolicy/resources/object-src-param.js
deleted file mode 100644
index 812aadd..0000000
--- a/third_party/blink/web_tests/http/tests/security/contentSecurityPolicy/resources/object-src-param.js
+++ /dev/null
@@ -1,29 +0,0 @@
-if (window.testRunner) {
-    testRunner.dumpAsText();
-    testRunner.waitUntilDone();
-}
-
-function appendObjectElement(type) {
-    window.onload = function () {
-        var o = document.createElement('object');
-        o.setAttribute('type', 'application/x-blink-test-plugin');
-        o.addEventListener('load', function () {
-            console.log('FAIL: The object should have been blocked.');
-            if (window.testRunner)
-                testRunner.notifyDone();
-        });
-        o.addEventListener('error', function () {
-            console.log('PASS: Error occurred, so load was correctly blocked.');
-            if (window.testRunner)
-                testRunner.notifyDone();
-        });
-
-        var p = document.createElement('param');
-        p.setAttribute('value', 'http://127.0.0.1:8080/plugins/resources/mock-plugin.pl?' + type);
-        p.setAttribute('name', type);
-
-        o.appendChild(p);
-
-        document.body.appendChild(o);
-    };
-}
diff --git a/third_party/blink/web_tests/wpt_internal/trust-tokens/README b/third_party/blink/web_tests/wpt_internal/trust-tokens/README
deleted file mode 100644
index c243eaf..0000000
--- a/third_party/blink/web_tests/wpt_internal/trust-tokens/README
+++ /dev/null
@@ -1,2 +0,0 @@
-This directory contains files for testing trust tokens with WPT using a Python
-implementation of a trust token issuer.
diff --git a/third_party/blink/web_tests/wpt_internal/trust-tokens/README.md b/third_party/blink/web_tests/wpt_internal/trust-tokens/README.md
new file mode 100644
index 0000000..a43f8fa
--- /dev/null
+++ b/third_party/blink/web_tests/wpt_internal/trust-tokens/README.md
@@ -0,0 +1,75 @@
+# Trust Token Tests
+
+## Table of Contents
+
+1. [Overview](#overview)
+1. [General Guidelines](#general-guidelines)
+1. [File Descriptions](#file-descriptions)
+1. [Running Tests](#running-tests)
+
+## Overview
+
+The trust token WPTs in this directory are JavaScript tests that interact with a Python implementation of a trust token issuer.
+[Python file handlers](https://web-platform-tests.org/writing-tests/python-handlers/index.html) implement the server-side logic for trust token issuance and redemption.
+The supported issuer protocol is `TrustTokenV3VOPRF`.
+
+Please refer to the [trust token API explainer](https://github.com/WICG/trust-token-api) for details about the API.
+
+## General Guidelines
+
+- These tests are currently internal only. New tests should be added to `wpt_internal/trust-tokens/`. When trust tokens are implemented by other browser vendors, these files can be transitioned to `third_party/blink/web_tests/external/wpt/trust-tokens/`
+- All tests should be [JavaScript tests](https://web-platform-tests.org/writing-tests/testharness.html)
+- The key commitment and other arguments to the test runner are defined in `third_party/blink/web_tests/VirtualTestSuites`. The contents of the key commitment are as follows:
+```json
+{
+    "https://web-platform.test:8444": {
+        "TrustTokenV3VOPRF": {
+            "protocol_version": "TrustTokenV3VOPRF",
+            "id": 1,
+            "batchsize": 1,
+            "keys": {
+                "0": {
+                    "Y": "AAAAAASqh8oivosFN46xxx7zIK10bh07Younm5hZ90HgglQqOFUC8l2/VSlsOlReOHJ2CrfJ6CG1adnTkKJhZ0BtbSPWBwviQtdl64MWJc7sSg9HPvWfTjDigX5ihbzihG8V8aA=",
+                    // The timestamp here is equivalent to
+                    // Friday, December 31, 9999 11:59:59 PM GMT
+                    "expiry": "253402300799000000"
+                }
+            }
+        }
+    }
+}
+```
+
+## File Descriptions
+
+- `resources/hash_to_field.py`
+  - Helper module used to implement [hash-to-scalar](https://github.com/WICG/trust-token-api/blob/main/ISSUER_PROTOCOL.md#serializationhashing-1)
+- `resources/trust_token_issuance.py`
+  - Python file handler for token issuance
+  - Generates a valid response including a DLEQ proof, which is verified by Chromium
+  - The response is stripped from the `Sec-Trust-Token` header by the browser and is not accessible to JavaScript
+- `resources/trust_token_redemption.py`
+  - Python file handler for token redemption
+  - The redemption record in the response is an arbitrary byte string, and it is also stripped from the `Sec-Trust-Token` header by the browser
+- `resources/trust_token_send_redemption_record.py`
+  - Python file handler for `send-redemption-record` requests
+  - Checks for the presence of the `Sec-Redemption-Record` header
+- `resources/trust_token_voprf.py`
+  - Helper module that implements the server side of the issuer protocol
+- `trust-token-api-e2e.https.html`
+  - JavaScript WPT that tests the browser's interaction with the issuer
+  - Tests `token-request`, `token-redemption`, and `send-redemption-record`
+
+## Running Tests
+
+The WPTs run as [virtual tests](https://source.chromium.org/chromium/chromium/src/+/main:third_party/blink/web_tests/VirtualTestSuites). This enables the issuer's key commitment to be passed to Chromium via a command line argument.
+
+```bash
+# Build web tests
+autoninja -C out/Default blink_tests
+
+# Run a single test
+third_party/blink/tools/run_web_tests.py -t Default virtual/trust-tokens/wpt_internal/trust-tokens/trust-token-api-e2e.https.html
+```
+
+See the [web tests doc](https://chromium.googlesource.com/chromium/src/+/HEAD/docs/testing/web_tests.md#running-the-tests) for more details on using the test runner.
diff --git a/third_party/zxcvbn-cpp/native-src/zxcvbn/util.cpp b/third_party/zxcvbn-cpp/native-src/zxcvbn/util.cpp
index 69d69bf..440f1e93 100644
--- a/third_party/zxcvbn-cpp/native-src/zxcvbn/util.cpp
+++ b/third_party/zxcvbn-cpp/native-src/zxcvbn/util.cpp
@@ -43,8 +43,8 @@
 std::pair<char32_t, It> _utf8_decode(It it, It end) {
   assert(it != end);
   const char* src = &*it;
-  int32_t src_len = std::distance(it, end);
-  int32_t char_index = 0;
+  size_t src_len = static_cast<size_t>(std::distance(it, end));
+  size_t char_index = 0;
   base_icu::UChar32 code_point_out;
 
   base::ReadUnicodeCharacter(src, src_len, &char_index, &code_point_out);
diff --git a/third_party/zxcvbn-cpp/patches/base_utf8.diff b/third_party/zxcvbn-cpp/patches/base_utf8.diff
index c7a81c5b..d15bf4a8 100644
--- a/third_party/zxcvbn-cpp/patches/base_utf8.diff
+++ b/third_party/zxcvbn-cpp/patches/base_utf8.diff
@@ -56,8 +56,8 @@
 +std::pair<char32_t, It> _utf8_decode(It it, It end) {
 +  assert(it != end);
 +  const char* src = &*it;
-+  int32_t src_len = std::distance(it, end);
-+  int32_t char_index = 0;
++  size_t src_len = static_cast<size_t>(std::distance(it, end));
++  size_t char_index = 0;
 +  base_icu::UChar32 code_point_out;
  
 -bool utf8_valid(std::string::const_iterator start,
diff --git a/tools/android/native_lib_memory/PRESUBMIT.py b/tools/android/native_lib_memory/PRESUBMIT.py
index 73f80c8..b5a8c19 100644
--- a/tools/android/native_lib_memory/PRESUBMIT.py
+++ b/tools/android/native_lib_memory/PRESUBMIT.py
@@ -18,7 +18,8 @@
   output.extend(
       input_api.canned_checks.RunPylint(input_api,
                                         output_api,
-                                        files_to_skip=files_to_skip))
+                                        files_to_skip=files_to_skip,
+                                        version='1.5'))
   # These tests don't run on Windows and give verbose and cryptic failure
   # messages.
   if input_api.sys.platform != 'win32':
diff --git a/tools/browserbench-webdriver/browserbench.py b/tools/browserbench-webdriver/browserbench.py
index a09c7f8..9c69670 100644
--- a/tools/browserbench-webdriver/browserbench.py
+++ b/tools/browserbench-webdriver/browserbench.py
@@ -7,6 +7,7 @@
 
 import json
 import logging
+import platform
 import selenium
 import subprocess
 import sys
@@ -170,6 +171,13 @@
 
     logging.info('Script starting')
 
+    caffeinate_process = None
+    if platform.system() == 'Darwin':
+      logging.info('Starting caffeinate')
+      # Caffeinate ensures the machine is not sleeping/idle.
+      caffeinate_process = subprocess.Popen(
+          ['/usr/bin/caffeinate', '-uims', '-t', '300'])
+
     parser = OptionParser()
     parser.add_option('-b',
                       '--browser',
@@ -235,6 +243,8 @@
         else:
           logging.critical('Got exception running, retried too many times, '
                            'giving up')
+          if caffeinate_process:
+            caffeinate_process.kill()
           raise e
       # When rerunning, first try killing the browser in hopes of state
       # resetting.
@@ -242,6 +252,8 @@
 
     logging.info('Test completed')
     self._ProduceOutput(measurements, extra_key_values)
+    if caffeinate_process:
+      caffeinate_process.kill()
 
   def AddExtraParserOptions(self, parser):
     pass
diff --git a/tools/checkperms/PRESUBMIT.py b/tools/checkperms/PRESUBMIT.py
index 8f4efcb..f2a8640 100644
--- a/tools/checkperms/PRESUBMIT.py
+++ b/tools/checkperms/PRESUBMIT.py
@@ -14,7 +14,8 @@
 
 def CommonChecks(input_api, output_api):
   output = []
-  output.extend(input_api.canned_checks.RunPylint(input_api, output_api))
+  output.extend(
+      input_api.canned_checks.RunPylint(input_api, output_api, version='1.5'))
   # Run it like if it were a unit test.
   output.extend(
       input_api.canned_checks.RunUnitTests(input_api,
diff --git a/tools/code_coverage/create_js_source_maps/PRESUBMIT.py b/tools/code_coverage/create_js_source_maps/PRESUBMIT.py
index c4d5f23..0cfee788 100644
--- a/tools/code_coverage/create_js_source_maps/PRESUBMIT.py
+++ b/tools/code_coverage/create_js_source_maps/PRESUBMIT.py
@@ -11,7 +11,9 @@
 
 
 def CheckLint(input_api, output_api):
-  results = input_api.canned_checks.RunPylint(input_api, output_api)
+  results = input_api.canned_checks.RunPylint(input_api,
+                                              output_api,
+                                              version='1.5')
   results += input_api.canned_checks.CheckPatchFormatted(input_api,
                                                          output_api,
                                                          check_js=True)
diff --git a/tools/cygprofile/PRESUBMIT.py b/tools/cygprofile/PRESUBMIT.py
index 8efde4c3..8c9edd7 100644
--- a/tools/cygprofile/PRESUBMIT.py
+++ b/tools/cygprofile/PRESUBMIT.py
@@ -18,7 +18,8 @@
   output.extend(
       input_api.canned_checks.RunPylint(input_api,
                                         output_api,
-                                        files_to_skip=files_to_skip))
+                                        files_to_skip=files_to_skip,
+                                        version='1.5'))
 
   # These tests don't run on Windows and give verbose and cryptic failure
   # messages.
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
index a5bb677b..65163031 100644
--- a/tools/metrics/histograms/enums.xml
+++ b/tools/metrics/histograms/enums.xml
@@ -13870,6 +13870,11 @@
   <int value="3" label="CAPS Service started"/>
 </enum>
 
+<enum name="CaptionBubbleErrorType">
+  <int value="0" label="Generic"/>
+  <int value="1" label="Media foundation renderer unsupported"/>
+</enum>
+
 <enum name="CaptivePortalBlockingPageEvent">
   <int value="0" label="SHOW_ALL"/>
   <int value="1" label="OPEN_LOGIN_PAGE"/>
@@ -21217,6 +21222,8 @@
   <int value="22" label="SMART_REPLACE_PHRASE"/>
   <int value="23" label="SMART_INSERT_BEFORE"/>
   <int value="24" label="SMART_SELECT_BTWN_INCL"/>
+  <int value="25" label="NAV_NEXT_SENT"/>
+  <int value="26" label="NAV_PREV_SENT"/>
 </enum>
 
 <enum name="CrosDictationToggleDictationMethod">
@@ -55363,6 +55370,7 @@
   <int value="-1804411756" label="ClipboardSuggestionContentHidden:enabled"/>
   <int value="-1802559277" label="RunTasksByBatches:disabled"/>
   <int value="-1802502753" label="enable-manual-password-generation:enabled"/>
+  <int value="-1801515363" label="ThreadedPreloadScanner:enabled"/>
   <int value="-1799801575" label="CookieDeprecationMessages:enabled"/>
   <int value="-1798337879" label="enable-md-downloads"/>
   <int value="-1798258607" label="AdaptiveCharging:disabled"/>
@@ -58320,6 +58328,7 @@
   <int value="128323385" label="WebUIOmniboxPopup:enabled"/>
   <int value="129246815" label="AudioSettingsPage:disabled"/>
   <int value="129458360" label="LiteVideo:disabled"/>
+  <int value="130059719" label="ThreadedPreloadScanner:disabled"/>
   <int value="130939792" label="FilesJsModules:disabled"/>
   <int value="131881947" label="D3DVsync:enabled"/>
   <int value="132560299"
@@ -58959,6 +58968,7 @@
   <int value="549483647" label="EnableUnifiedMultiDeviceSettings:disabled"/>
   <int value="550378029" label="reset-app-list-install-state"/>
   <int value="550387510" label="NTPAssetDownloadSuggestions:disabled"/>
+  <int value="550411207" label="enable-webgpu-developer-features"/>
   <int value="550741462" label="WebAppBorderless:disabled"/>
   <int value="552317551" label="MediaAppHandlesPdf:disabled"/>
   <int value="552421509" label="AssistMultiWordExpanded:enabled"/>
@@ -91791,6 +91801,7 @@
   <int value="49" label="Autofill Wallet Offer"/>
   <int value="50" label="Workspace Desk"/>
   <int value="51" label="History"/>
+  <int value="52" label="Printers Authorization Servers"/>
 </enum>
 
 <enum name="SyncModelTypeStoreInitResult">
@@ -98087,6 +98098,7 @@
   <int value="1804492839" label="Colors"/>
   <int value="1894705443" label="Leisure"/>
   <int value="1914850019" label="Heritage"/>
+  <int value="2026958207" label="LGBTQ Artists"/>
   <int value="2084467495" label="Dessert time"/>
   <int value="2094027636" label="Solid colors"/>
 </enum>
@@ -98135,6 +98147,8 @@
   <int value="-1971674273" label="Leisure - 18438465517979870047"/>
   <int value="-1953555516" label="Floral - 18439301194079738820"/>
   <int value="-1938230489" label="Element - 18440708087942279975"/>
+  <int value="-1934316364" label="LGBTQ Artists - 3223345251527860"/>
+  <int value="-1927352346" label="LGBTQ Artists - 18438382509191262182"/>
   <int value="-1916450445" label="Art - 18446537445211457907"/>
   <int value="-1910580223" label="Solid Colors - 18444350009783540737"/>
   <int value="-1890583571" label="Heritage - 8492875030461421"/>
@@ -98182,6 +98196,7 @@
   <int value="-1389216897" label="Landscapes - 3716322142862207"/>
   <int value="-1382225891" label="Landscapes - 7848789358209053"/>
   <int value="-1379111552" label="Heritage - 6939063748423040"/>
+  <int value="-1349646083" label="LGBTQ Artists - 18440020408432199933"/>
   <int value="-1346329828" label="Collage - 302823912809244"/>
   <int value="-1315221908" label="Cityscapes - 18438039904557157996"/>
   <int value="-1308168417" label="Solid Colors - 737212648320799"/>
@@ -98229,10 +98244,14 @@
   <int value="-755562342" label="Art - 18442108141089261722"/>
   <int value="-753216621" label="Colors - 6786829258511251"/>
   <int value="-732111196" label="Solid Colors - 6325085935558308"/>
+  <int value="-692767506" label="LGBTQ Artists - 18440369104598938862"/>
   <int value="-671868169" label="Art - 18441859488336321271"/>
   <int value="-663382773" label="Landscapes - 543123721000203"/>
+  <int value="-651763227" label="LGBTQ Artists - 18444260070132212197"/>
   <int value="-650608223" label="Composition - 1914403712172449"/>
+  <int value="-648909746" label="LGBTQ Artists - 18445559868972756046"/>
   <int value="-646744548" label="Togetherness - 4852535008654876"/>
+  <int value="-629215657" label="LGBTQ Artists - 8024729151532631"/>
   <int value="-628255755" label="Dessert Time - 18439774784268899317"/>
   <int value="-620910477" label="Cityscapes - 18441185517824223347"/>
   <int value="-610816140" label="Landscapes - 8626157735358324"/>
@@ -98252,6 +98271,7 @@
   <int value="-435188694" label="Art - 18444631610789694506"/>
   <int value="-387775828" label="Cityscapes - 18438479018645979820"/>
   <int value="-359510740" label="Solid Colors - 18440625901026823468"/>
+  <int value="-353243221" label="LGBTQ Artists - 18439187396226576299"/>
   <int value="-347674537" label="Heritage - 6951507300116567"/>
   <int value="-344702098" label="Landscapes - 18446494149217895278"/>
   <int value="-335909288" label="Art - 6686963096251992"/>
@@ -98328,7 +98348,9 @@
   <int value="519669485" label="Made by Canvas - 1162381878724333"/>
   <int value="519705872" label="Cityscapes - 5269745003271440"/>
   <int value="523171359" label="Landscapes - 456362273207839"/>
+  <int value="542400471" label="LGBTQ Artists - 8628186113138647"/>
   <int value="582571507" label="Cityscapes - 18437958065853126131"/>
+  <int value="602985801" label="LGBTQ Artists - 18442751988110251337"/>
   <int value="627281636" label="Cityscapes - 18443580607585029860"/>
   <int value="632498960" label="Element - 18442926110408911632"/>
   <int value="636241240" label="Imaginary - 18446507769540134232"/>
@@ -98374,6 +98396,7 @@
   <int value="1040676664" label="Landscapes - 858440449095480"/>
   <int value="1123537047" label="Composition - 18445642548865650839"/>
   <int value="1141632188" label="Cityscapes - 18443148044763131068"/>
+  <int value="1165024101" label="LGBTQ Artists - 91127486143333"/>
   <int value="1173629942" label="Colors - 6465464552401910"/>
   <int value="1192252038" label="Cityscapes - 6488081868804742"/>
   <int value="1206215167" label="Art - 18445129029773517311"/>
@@ -98388,8 +98411,10 @@
   <int value="1354190091" label="Landscapes - 8257179059965195"/>
   <int value="1358808705" label="Cityscapes - 18438573333579417217"/>
   <int value="1361171564" label="Collage - 18446268433212494956"/>
+  <int value="1398130163" label="LGBTQ Artists - 18438014764531959283"/>
   <int value="1404576511" label="Collage - 1050009239298815"/>
   <int value="1414735641" label="Solid Colors - 2615791116822297"/>
+  <int value="1446838327" label="LGBTQ Artists - 18441755303138361399"/>
   <int value="1447770551" label="Composition - 18441221902560868791"/>
   <int value="1456247150" label="Togetherness - 18444641211933036910"/>
   <int value="1457778021" label="Cityscapes - 18438203687318056293"/>
@@ -98403,6 +98428,7 @@
   <int value="1520811358" label="Colors - 273157145869662"/>
   <int value="1535000622" label="Imaginary - 1900923995439150"/>
   <int value="1551836957" label="Heritage - 990184787093277"/>
+  <int value="1559927711" label="LGBTQ Artists - 18441607573556337567"/>
   <int value="1561201240" label="Solid Colors - 18440201375495096920"/>
   <int value="1567507477" label="Art - 2184344624841749"/>
   <int value="1573305199" label="Collage - 18445466155008572271"/>
@@ -98418,6 +98444,7 @@
   <int value="1650555304" label="Cityscapes - 8018267505523112"/>
   <int value="1679563589" label="Colors - 3148839772756805"/>
   <int value="1693366509" label="Cityscapes - 18440922723974559981"/>
+  <int value="1694862057" label="LGBTQ Artists - 4207995903118057"/>
   <int value="1696499200" label="Collage - 8802726148212224"/>
   <int value="1697916989" label="Floral - 4641671638951997"/>
   <int value="1708825347" label="Leisure - 2442472300649219"/>
diff --git a/tools/metrics/histograms/metadata/accessibility/histograms.xml b/tools/metrics/histograms/metadata/accessibility/histograms.xml
index 4b601203..f314e4a 100644
--- a/tools/metrics/histograms/metadata/accessibility/histograms.xml
+++ b/tools/metrics/histograms/metadata/accessibility/histograms.xml
@@ -1133,6 +1133,17 @@
   </summary>
 </histogram>
 
+<histogram name="Accessibility.LiveCaption.CaptionBubbleError"
+    enum="CaptionBubbleErrorType" expires_after="2023-06-13">
+  <owner>abigailbklein@google.com</owner>
+  <owner>evliu@google.com</owner>
+  <owner>chrome-a11y-core@google.com</owner>
+  <summary>
+    Records when the caption bubble displays an error. This is logged once each
+    time the caption bubble model encounters an error.
+  </summary>
+</histogram>
+
 <histogram name="Accessibility.LiveCaption.Duration.CaptionBubble{Visibility}2"
     units="ms" expires_after="2022-07-26">
   <owner>abigailbklein@google.com</owner>
@@ -1230,7 +1241,7 @@
   </summary>
 </histogram>
 
-<histogram name="Accessibility.LiveCaption.Session"
+<histogram name="Accessibility.LiveCaption.Session2"
     enum="LiveCaptionSessionEvent" expires_after="2022-11-27">
   <owner>katie@chromium.org</owner>
   <owner>abigailbklein@google.com</owner>
diff --git a/tools/metrics/histograms/metadata/blink/histograms.xml b/tools/metrics/histograms/metadata/blink/histograms.xml
index 7733c09..fdb5993b 100644
--- a/tools/metrics/histograms/metadata/blink/histograms.xml
+++ b/tools/metrics/histograms/metadata/blink/histograms.xml
@@ -2292,6 +2292,44 @@
   </summary>
 </histogram>
 
+<histogram name="Blink.SetInnerHtml.ConversionTime" units="microseconds"
+    expires_after="2022-12-12">
+  <owner>sky@chromium.org</owner>
+  <owner>swarm-team@chromium.org</owner>
+  <summary>
+    Time spent converting the v8 string to a blink string when Element.innerHTML
+    is set.
+
+    This histogram does not record metrics on machines with low-resolution
+    clocks.
+  </summary>
+</histogram>
+
+<histogram name="Blink.SetInnerHtml.ExecutionTime" units="microseconds"
+    expires_after="2022-12-12">
+  <owner>sky@chromium.org</owner>
+  <owner>swarm-team@chromium.org</owner>
+  <summary>
+    Time spent processing html string supplied to Element.innerHTML. This
+    specifically captures the time in Element.SetInnerHTML.
+
+    This histogram does not record metrics on machines with low-resolution
+    clocks.
+  </summary>
+</histogram>
+
+<histogram name="Blink.SetInnerHtml.StringLength" units="length"
+    expires_after="2022-12-12">
+  <owner>sky@chromium.org</owner>
+  <owner>swarm-team@chromium.org</owner>
+  <summary>
+    Length of the string supplied to Element.innerHTML.
+
+    This histogram does not record metrics on machines with low-resolution
+    clocks.
+  </summary>
+</histogram>
+
 <histogram name="Blink.Sms.BackendAvailability"
     enum="WebOTPBackendAvailability" expires_after="2022-10-30">
   <owner>yigu@chromium.org</owner>
diff --git a/tools/metrics/histograms/metadata/sync/histograms.xml b/tools/metrics/histograms/metadata/sync/histograms.xml
index be7b2591..db7fe69 100644
--- a/tools/metrics/histograms/metadata/sync/histograms.xml
+++ b/tools/metrics/histograms/metadata/sync/histograms.xml
@@ -45,6 +45,8 @@
   <variant name=".PASSWORD" summary="PASSWORD"/>
   <variant name=".PREFERENCE" summary="PREFERENCE"/>
   <variant name=".PRINTERS" summary="PRINTERS"/>
+  <variant name=".PRINTERS_AUTHORIZATION_SERVERS"
+      summary="PRINTERS_AUTHORIZATION_SERVERS"/>
   <variant name=".PRIORITY_PREFERENCE" summary="PRIORITY_PREFERENCE"/>
   <variant name=".PROXY_TABS" summary="PROXY_TABS"/>
   <variant name=".READING_LIST" summary="READING_LIST"/>
diff --git a/tools/perf/PRESUBMIT.py b/tools/perf/PRESUBMIT.py
index 46c55b6..20d8140 100644
--- a/tools/perf/PRESUBMIT.py
+++ b/tools/perf/PRESUBMIT.py
@@ -32,9 +32,14 @@
   results.extend(_CheckWprShaFiles(input_api, output_api))
   results.extend(_CheckShardMaps(input_api, output_api, block_on_failure))
   results.extend(_CheckVersionsInSmokeTests(input_api, output_api))
-  results.extend(input_api.RunTests(input_api.canned_checks.GetPylint(
-      input_api, output_api, extra_paths_list=_GetPathsToPrepend(input_api),
-      pylintrc='pylintrc')))
+  results.extend(
+      input_api.RunTests(
+          input_api.canned_checks.GetPylint(
+              input_api,
+              output_api,
+              extra_paths_list=_GetPathsToPrepend(input_api),
+              pylintrc='pylintrc',
+              version='1.5')))
   return results
 
 
diff --git a/tools/perf/core/perfetto_binary_roller/binary_deps.json b/tools/perf/core/perfetto_binary_roller/binary_deps.json
index 9667b6b..a8eccb14 100644
--- a/tools/perf/core/perfetto_binary_roller/binary_deps.json
+++ b/tools/perf/core/perfetto_binary_roller/binary_deps.json
@@ -5,16 +5,16 @@
             "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/linux_arm64/49b4b5dcbc312d8d2c3751cf29238b8efeb4e494/trace_processor_shell"
         },
         "win": {
-            "hash": "5c26236044a135f11d8afc32a4e512aab2e8bb16",
-            "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/win/ddc88694c41b71562428bd08b168666c8d42eaf3/trace_processor_shell.exe"
+            "hash": "bc2c442fac332b89ad0f9ff5c244b64fec3c2b0c",
+            "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/win/6007fadd7cb1b558e89799b409d20529abb8c37f/trace_processor_shell.exe"
         },
         "linux_arm": {
             "hash": "58893933be305d3bfe0a72ebebcacde2ac3ca893",
             "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/linux_arm/49b4b5dcbc312d8d2c3751cf29238b8efeb4e494/trace_processor_shell"
         },
         "mac": {
-            "hash": "43adeda14c6a325073dff3752e4b67bcf83862c8",
-            "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/mac/74fabc35bb05d6d0795387df62cc30c81d517952/trace_processor_shell"
+            "hash": "37ad669f8e361ec25d649544e36abe883d11385d",
+            "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/mac/6007fadd7cb1b558e89799b409d20529abb8c37f/trace_processor_shell"
         },
         "mac_arm64": {
             "hash": "e1ad4861384b06d911a65f035317914b8cc975c6",
@@ -22,7 +22,7 @@
         },
         "linux": {
             "hash": "9e2523358d3118333a637c080edf185cb0613e4a",
-            "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/linux/397f8c5e50d7d4d7106e69a8ac378dafc22b1334/trace_processor_shell"
+            "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/linux/6007fadd7cb1b558e89799b409d20529abb8c37f/trace_processor_shell"
         }
     },
     "power_profile.sql": {
diff --git a/ui/accessibility/platform/ax_platform_node_auralinux.cc b/ui/accessibility/platform/ax_platform_node_auralinux.cc
index 908f508..fdf9741 100644
--- a/ui/accessibility/platform/ax_platform_node_auralinux.cc
+++ b/ui/accessibility/platform/ax_platform_node_auralinux.cc
@@ -1013,10 +1013,11 @@
     return 0;
 
   std::u16string text = obj->GetHypertext();
-  int32_t text_length = text.length();
+  size_t text_length = text.length();
 
   offset = obj->UnicodeToUTF16OffsetInText(offset);
-  int32_t limited_offset = base::clamp(offset, 0, text_length);
+  offset = std::max(offset, 0);
+  size_t limited_offset = std::min(static_cast<size_t>(offset), text_length);
 
   base_icu::UChar32 code_point;
   base::ReadUnicodeCharacter(text.c_str(), text_length + 1, &limited_offset,
@@ -4343,8 +4344,8 @@
   text_unicode_adjustments_.emplace();
 
   std::u16string text = GetHypertext();
-  int32_t text_length = text.size();
-  for (int32_t i = 0; i < text_length; i++) {
+  size_t text_length = text.size();
+  for (size_t i = 0; i < text_length; i++) {
     base_icu::UChar32 code_point;
     size_t original_i = i;
     base::ReadUnicodeCharacter(text.c_str(), text_length + 1, &i, &code_point);
diff --git a/ui/events/keycodes/dom/keycode_converter.cc b/ui/events/keycodes/dom/keycode_converter.cc
index d0f8c2a..f5d61aa 100644
--- a/ui/events/keycodes/dom/keycode_converter.cc
+++ b/ui/events/keycodes/dom/keycode_converter.cc
@@ -336,8 +336,8 @@
   }
   // Otherwise, if the string contains a single Unicode character,
   // the key value is that character.
-  const auto key_length = static_cast<int32_t>(key.length());
-  int32_t char_index = 0;
+  const size_t key_length = key.length();
+  size_t char_index = 0;
   base_icu::UChar32 character;
   if (base::ReadUnicodeCharacter(key.data(), key_length, &char_index,
                                  &character) &&
diff --git a/ui/file_manager/file_manager/foreground/js/BUILD.gn b/ui/file_manager/file_manager/foreground/js/BUILD.gn
index 43a0255..2fb16b9 100644
--- a/ui/file_manager/file_manager/foreground/js/BUILD.gn
+++ b/ui/file_manager/file_manager/foreground/js/BUILD.gn
@@ -471,13 +471,13 @@
 js_library("directory_tree_naming_controller") {
   deps = [
     ":directory_model",
+    "ui:dialogs",
     "ui:directory_tree",
     "//ui/file_manager/file_manager/common/js:api",
     "//ui/file_manager/file_manager/common/js:util",
     "//ui/file_manager/file_manager/externs:volume_info",
     "//ui/file_manager/file_manager/foreground/js:file_rename",
     "//ui/webui/resources/js:assert.m",
-    "//ui/webui/resources/js/cr/ui:dialogs.m",
   ]
 }
 
@@ -757,6 +757,7 @@
     "ui:directory_tree",
     "ui:drag_selector",
     "ui:list_container",
+    "ui:tree",
     "//ui/file_manager/file_manager/common/js:api",
     "//ui/file_manager/file_manager/common/js:file_type",
     "//ui/file_manager/file_manager/common/js:progress_center_common",
@@ -773,7 +774,6 @@
     "//ui/webui/resources/js:util.m",
     "//ui/webui/resources/js/cr/ui:command.m",
     "//ui/webui/resources/js/cr/ui:list.m",
-    "//ui/webui/resources/js/cr/ui:tree",
   ]
 }
 
@@ -1042,12 +1042,12 @@
     ":directory_contents",
     ":directory_model",
     ":file_selection",
+    "ui:dialogs",
     "ui:list_container",
     "//ui/file_manager/file_manager/common/js:api",
     "//ui/file_manager/file_manager/common/js:util",
     "//ui/file_manager/file_manager/foreground/js:file_rename",
     "//ui/webui/resources/js:assert.m",
-    "//ui/webui/resources/js/cr/ui:dialogs.m",
   ]
 }
 
diff --git a/ui/file_manager/file_manager/foreground/js/file_transfer_controller.js b/ui/file_manager/file_manager/foreground/js/file_transfer_controller.js
index e1eb28b..b006bfb 100644
--- a/ui/file_manager/file_manager/foreground/js/file_transfer_controller.js
+++ b/ui/file_manager/file_manager/foreground/js/file_transfer_controller.js
@@ -5,7 +5,6 @@
 import {assert, assertNotReached} from 'chrome://resources/js/assert.m.js';
 import {Command} from 'chrome://resources/js/cr/ui/command.m.js';
 import {List} from 'chrome://resources/js/cr/ui/list.m.js';
-import {TreeItem} from 'chrome://resources/js/cr/ui/tree.js';
 import {queryRequiredElement} from 'chrome://resources/js/util.m.js';
 
 import {getDirectory, getDisallowedTransfers, startIOTask} from '../../common/js/api.js';
@@ -28,6 +27,7 @@
 import {DirectoryItem, DirectoryTree} from './ui/directory_tree.js';
 import {DragSelector} from './ui/drag_selector.js';
 import {ListContainer} from './ui/list_container.js';
+import {TreeItem} from './ui/tree.js';
 
 /**
  * Global (placed in the window object) variable name to hold internal
diff --git a/ui/file_manager/file_manager/foreground/js/naming_controller.js b/ui/file_manager/file_manager/foreground/js/naming_controller.js
index 62400d3..b74ad25 100644
--- a/ui/file_manager/file_manager/foreground/js/naming_controller.js
+++ b/ui/file_manager/file_manager/foreground/js/naming_controller.js
@@ -3,7 +3,6 @@
 // found in the LICENSE file.
 
 import {assert} from 'chrome://resources/js/assert.m.js';
-import {ConfirmDialog} from 'chrome://resources/js/cr/ui/dialogs.m.js';
 
 import {getFile} from '../../common/js/api.js';
 import {strf, UserCanceledError, util} from '../../common/js/util.js';
@@ -13,6 +12,7 @@
 import {DirectoryModel} from './directory_model.js';
 import {renameEntry, validateEntryName, validateFileName} from './file_rename.js';
 import {FileSelectionHandler} from './file_selection.js';
+import {ConfirmDialog} from './ui/dialogs.js';
 import {FilesAlertDialog} from './ui/files_alert_dialog.js';
 import {ListContainer} from './ui/list_container.js';
 
diff --git a/ui/file_manager/file_manager/foreground/js/ui/BUILD.gn b/ui/file_manager/file_manager/foreground/js/ui/BUILD.gn
index be7a2b3..1043055 100644
--- a/ui/file_manager/file_manager/foreground/js/ui/BUILD.gn
+++ b/ui/file_manager/file_manager/foreground/js/ui/BUILD.gn
@@ -30,6 +30,7 @@
     ":commandbutton",
     ":default_task_dialog",
     ":dialog_footer",
+    ":dialogs",
     ":directory_tree",
     ":drag_selector",
     ":file_grid",
@@ -52,6 +53,7 @@
     ":progress_center_panel",
     ":providers_menu",
     ":search_box",
+    ":tree",
   ]
 }
 
@@ -80,6 +82,7 @@
     ":commandbutton",
     ":default_task_dialog",
     ":dialog_footer",
+    ":dialogs",
     ":directory_tree",
     ":drag_selector",
     ":file_grid",
@@ -102,6 +105,7 @@
     ":progress_center_panel",
     ":providers_menu",
     ":search_box",
+    ":tree",
     "table:table",
     "table:table_column",
     "table:table_column_model",
@@ -199,8 +203,12 @@
   ]
 }
 
+js_library("dialogs") {
+}
+
 js_library("directory_tree") {
   deps = [
+    ":tree",
     "//ui/file_manager/file_manager/common/js:file_type",
     "//ui/file_manager/file_manager/common/js:metrics",
     "//ui/file_manager/file_manager/common/js:util",
@@ -219,7 +227,6 @@
     "//ui/webui/resources/js/cr/ui:command.m",
     "//ui/webui/resources/js/cr/ui:context_menu_handler.m",
     "//ui/webui/resources/js/cr/ui:menu.m",
-    "//ui/webui/resources/js/cr/ui:tree",
   ]
   visibility +=
       [ "//ui/file_manager/file_manager/externs:command_handler_deps" ]
@@ -294,9 +301,9 @@
 
 js_library("file_manager_dialog_base") {
   deps = [
+    ":dialogs",
     "//ui/file_manager/file_manager/common/js:util",
     "//ui/file_manager/file_manager/common/js:xfm",
-    "//ui/webui/resources/js/cr/ui:dialogs.m",
   ]
 }
 
@@ -318,6 +325,7 @@
     ":combobutton",
     ":default_task_dialog",
     ":dialog_footer",
+    ":dialogs",
     ":directory_tree",
     ":file_grid",
     ":file_table",
@@ -347,7 +355,6 @@
     "//ui/webui/resources/js:util.m",
     "//ui/webui/resources/js/cr:ui.m",
     "//ui/webui/resources/js/cr/ui:context_menu_handler.m",
-    "//ui/webui/resources/js/cr/ui:dialogs.m",
     "//ui/webui/resources/js/cr/ui:menu.m",
     "//ui/webui/resources/js/cr/ui:menu_item.m",
     "//ui/webui/resources/js/cr/ui:splitter",
@@ -456,8 +463,8 @@
 
 js_library("files_alert_dialog") {
   deps = [
+    ":dialogs",
     "//ui/file_manager/file_manager/common/js:util",
-    "//ui/webui/resources/js/cr/ui:dialogs.m",
   ]
 }
 
@@ -465,8 +472,8 @@
 
 js_library("files_confirm_dialog") {
   deps = [
+    ":dialogs",
     "//ui/file_manager/file_manager/common/js:util",
-    "//ui/webui/resources/js/cr/ui:dialogs.m",
   ]
 }
 
@@ -507,8 +514,8 @@
 
 js_library("import_crostini_image_dialog") {
   deps = [
+    ":dialogs",
     "//ui/file_manager/file_manager/common/js:util",
-    "//ui/webui/resources/js/cr/ui:dialogs.m",
   ]
 }
 
@@ -615,6 +622,14 @@
   externs_list = [ "//ui/file_manager/file_manager/externs/search_item.js" ]
 }
 
+js_library("tree") {
+  deps = [
+    "//ui/webui/resources/js:assert.m",
+    "//ui/webui/resources/js:cr.m",
+    "//ui/webui/resources/js/cr:ui.m",
+  ]
+}
+
 js_library("autocomplete_list") {
   deps = [
     "//ui/webui/resources/js:cr.m",
diff --git a/ui/webui/resources/js/cr/ui/dialogs.js b/ui/file_manager/file_manager/foreground/js/ui/dialogs.js
similarity index 95%
rename from ui/webui/resources/js/cr/ui/dialogs.js
rename to ui/file_manager/file_manager/foreground/js/ui/dialogs.js
index 4ae3b36..68745270 100644
--- a/ui/webui/resources/js/cr/ui/dialogs.js
+++ b/ui/file_manager/file_manager/foreground/js/ui/dialogs.js
@@ -2,13 +2,12 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-// #import {isRTL} from 'chrome://resources/js/util.m.js'
+import {isRTL} from 'chrome://resources/js/util.m.js';
 
-cr.define('cr.ui.dialogs', function() {
   /**
    * @constructor
    */
-  /* #export */ function BaseDialog(parentNode) {
+  export function BaseDialog(parentNode) {
     this.parentNode_ = parentNode;
     this.document_ = parentNode.ownerDocument;
 
@@ -356,9 +355,9 @@
   /**
    * AlertDialog contains just a message and an ok button.
    * @constructor
-   * @extends {cr.ui.dialogs.BaseDialog}
+   * @extends {BaseDialog}
    */
-  /* #export */ function AlertDialog(parentNode) {
+  export function AlertDialog(parentNode) {
     BaseDialog.call(this, parentNode);
     this.cancelButton.style.display = 'none';
   }
@@ -378,19 +377,11 @@
   /**
    * ConfirmDialog contains a message, an ok button, and a cancel button.
    * @constructor
-   * @extends {cr.ui.dialogs.BaseDialog}
+   * @extends {BaseDialog}
    */
-  /* #export */ function ConfirmDialog(parentNode) {
+  export function ConfirmDialog(parentNode) {
     BaseDialog.call(this, parentNode);
   }
 
   ConfirmDialog.prototype = {__proto__: BaseDialog.prototype};
 
-  // #cr_define_end
-  console.warn('crbug/1173575, non-JS module files deprecated.');
-  return {
-    BaseDialog: BaseDialog,
-    AlertDialog: AlertDialog,
-    ConfirmDialog: ConfirmDialog,
-  };
-});
diff --git a/ui/file_manager/file_manager/foreground/js/ui/directory_tree.js b/ui/file_manager/file_manager/foreground/js/ui/directory_tree.js
index 8117195..9c55553 100644
--- a/ui/file_manager/file_manager/foreground/js/ui/directory_tree.js
+++ b/ui/file_manager/file_manager/foreground/js/ui/directory_tree.js
@@ -7,7 +7,6 @@
 import {Command} from 'chrome://resources/js/cr/ui/command.m.js';
 import {contextMenuHandler} from 'chrome://resources/js/cr/ui/context_menu_handler.m.js';
 import {Menu} from 'chrome://resources/js/cr/ui/menu.m.js';
-import {Tree, TreeItem} from 'chrome://resources/js/cr/ui/tree.js';
 
 import {FileType} from '../../../common/js/file_type.js';
 import {metrics} from '../../../common/js/metrics.js';
@@ -23,6 +22,8 @@
 import {MetadataModel} from '../metadata/metadata_model.js';
 import {NavigationListModel, NavigationModelAndroidAppItem, NavigationModelFakeItem, NavigationModelItem, NavigationModelItemType, NavigationModelShortcutItem, NavigationModelVolumeItem, NavigationSection} from '../navigation_list_model.js';
 
+import {Tree, TreeItem} from './tree.js';
+
 // Namespace
 const directorytree = {};
 
diff --git a/ui/file_manager/file_manager/foreground/js/ui/file_manager_dialog_base.js b/ui/file_manager/file_manager/foreground/js/ui/file_manager_dialog_base.js
index b5d1d1e..0db9f99 100644
--- a/ui/file_manager/file_manager/foreground/js/ui/file_manager_dialog_base.js
+++ b/ui/file_manager/file_manager/foreground/js/ui/file_manager_dialog_base.js
@@ -2,11 +2,11 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import {BaseDialog} from 'chrome://resources/js/cr/ui/dialogs.m.js';
-
 import {util} from '../../../common/js/util.js';
 import {xfm} from '../../../common/js/xfm.js';
 
+import {BaseDialog} from './dialogs.js';
+
 /**
  * This class is an extended class, to manage the status of the dialogs.
  */
diff --git a/ui/file_manager/file_manager/foreground/js/ui/file_manager_ui.js b/ui/file_manager/file_manager/foreground/js/ui/file_manager_ui.js
index 22379ef..682b736 100644
--- a/ui/file_manager/file_manager/foreground/js/ui/file_manager_ui.js
+++ b/ui/file_manager/file_manager/foreground/js/ui/file_manager_ui.js
@@ -5,7 +5,6 @@
 import {assertInstanceof} from 'chrome://resources/js/assert.m.js';
 import {decorate, define as crUiDefine} from 'chrome://resources/js/cr/ui.m.js';
 import {contextMenuHandler} from 'chrome://resources/js/cr/ui/context_menu_handler.m.js';
-import {BaseDialog} from 'chrome://resources/js/cr/ui/dialogs.m.js';
 import {Menu} from 'chrome://resources/js/cr/ui/menu.m.js';
 import {MenuItem} from 'chrome://resources/js/cr/ui/menu_item.m.js';
 import {Splitter} from 'chrome://resources/js/cr/ui/splitter.js';
@@ -29,6 +28,7 @@
 import {ComboButton} from './combobutton.js';
 import {DefaultTaskDialog} from './default_task_dialog.js';
 import {DialogFooter} from './dialog_footer.js';
+import {BaseDialog} from './dialogs.js';
 import {DirectoryTree} from './directory_tree.js';
 import {FileGrid} from './file_grid.js';
 import {FileTable} from './file_table.js';
diff --git a/ui/file_manager/file_manager/foreground/js/ui/files_alert_dialog.js b/ui/file_manager/file_manager/foreground/js/ui/files_alert_dialog.js
index b73d586..8e43cc8 100644
--- a/ui/file_manager/file_manager/foreground/js/ui/files_alert_dialog.js
+++ b/ui/file_manager/file_manager/foreground/js/ui/files_alert_dialog.js
@@ -2,10 +2,10 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import {AlertDialog} from 'chrome://resources/js/cr/ui/dialogs.m.js';
-
 import {util} from '../../../common/js/util.js';
 
+import {AlertDialog} from './dialogs.js';
+
 /**
  * Alert dialog.
  */
diff --git a/ui/file_manager/file_manager/foreground/js/ui/files_confirm_dialog.js b/ui/file_manager/file_manager/foreground/js/ui/files_confirm_dialog.js
index 41a82556..d0376804 100644
--- a/ui/file_manager/file_manager/foreground/js/ui/files_confirm_dialog.js
+++ b/ui/file_manager/file_manager/foreground/js/ui/files_confirm_dialog.js
@@ -2,10 +2,10 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import {ConfirmDialog} from 'chrome://resources/js/cr/ui/dialogs.m.js';
-
 import {util} from '../../../common/js/util.js';
 
+import {ConfirmDialog} from './dialogs.js';
+
 /**
  * Confirm dialog.
  */
diff --git a/ui/file_manager/file_manager/foreground/js/ui/import_crostini_image_dialog.js b/ui/file_manager/file_manager/foreground/js/ui/import_crostini_image_dialog.js
index 3d852191..4ce8d273 100644
--- a/ui/file_manager/file_manager/foreground/js/ui/import_crostini_image_dialog.js
+++ b/ui/file_manager/file_manager/foreground/js/ui/import_crostini_image_dialog.js
@@ -2,10 +2,11 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import {ConfirmDialog} from 'chrome://resources/js/cr/ui/dialogs.m.js';
-
 import {str, util} from '../../../common/js/util.js';
 
+import {ConfirmDialog} from './dialogs.js';
+
+
 
 /**
  * ImportCrostiniImageDialog is used as the handler for .tini files.
diff --git a/ui/webui/resources/js/cr/ui/tree.js b/ui/file_manager/file_manager/foreground/js/ui/tree.js
similarity index 95%
rename from ui/webui/resources/js/cr/ui/tree.js
rename to ui/file_manager/file_manager/foreground/js/ui/tree.js
index 446adff..1dca296 100644
--- a/ui/webui/resources/js/cr/ui/tree.js
+++ b/ui/file_manager/file_manager/foreground/js/ui/tree.js
@@ -2,16 +2,11 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-// NOTE: This file depends on ui.js (or the autogenerated ui.m.js module
-// version). These files and all files that depend on them are deprecated, and
-// should only be used by legacy UIs that have not yet been updated to new
-// patterns. Use Web Components in any new code.
-
 // clang-format off
-import {assert, assertInstanceof} from '../../assert.m.js';
-import {dispatchSimpleEvent, getPropertyDescriptor, isMac, PropertyKind} from '../../cr.m.js';
-import {getTrustedHTML} from '../../static_types.js';
-import {define as crUiDefine, limitInputWidth} from '../ui.m.js';
+import {assert, assertInstanceof} from 'chrome://resources/js/assert.m.js';
+import {dispatchSimpleEvent, getPropertyDescriptor, PropertyKind} from 'chrome://resources/js/cr.m.js';
+import {getTrustedHTML} from 'chrome://resources/js/static_types.js';
+import {define as crUiDefine, limitInputWidth} from 'chrome://resources/js/cr/ui.m.js';
 // clang-format on
 
 /**
@@ -202,7 +197,7 @@
       case 'ArrowLeft':
       case 'ArrowRight':
         // Don't let back/forward keyboard shortcuts be used.
-        if (!isMac && e.altKey || isMac && e.metaKey) {
+        if (e.altKey) {
           break;
         }
 
@@ -659,9 +654,9 @@
       }
 
       input.addEventListener('keydown', handleKeydown);
-      input.addEventListener('blur', (function() {
-                                       this.editing = false;
-                                     }).bind(this));
+      input.addEventListener('blur', () => {
+        this.editing = false;
+      });
 
       // Make sure that double clicks do not expand and collapse the tree
       // item.
diff --git a/ui/file_manager/file_names.gni b/ui/file_manager/file_names.gni
index febc286..b4e76ef 100644
--- a/ui/file_manager/file_names.gni
+++ b/ui/file_manager/file_names.gni
@@ -221,6 +221,7 @@
   "file_manager/foreground/js/ui/commandbutton.js",
   "file_manager/foreground/js/ui/default_task_dialog.js",
   "file_manager/foreground/js/ui/dialog_footer.js",
+  "file_manager/foreground/js/ui/dialogs.js",
   "file_manager/foreground/js/ui/directory_tree.js",
   "file_manager/foreground/js/ui/drag_selector.js",
   "file_manager/foreground/js/ui/file_grid.js",
@@ -250,6 +251,7 @@
   "file_manager/foreground/js/ui/table/table_header.js",
   "file_manager/foreground/js/ui/table/table_list.js",
   "file_manager/foreground/js/ui/table/table_splitter.js",
+  "file_manager/foreground/js/ui/tree.js",
 ]
 
 # END: static_js_files.
diff --git a/ui/file_manager/tsconfig_base.json b/ui/file_manager/tsconfig_base.json
index f79e3053..9844915 100644
--- a/ui/file_manager/tsconfig_base.json
+++ b/ui/file_manager/tsconfig_base.json
@@ -2,6 +2,12 @@
   "extends": "../../tools/typescript/tsconfig_base.json",
   "compilerOptions": {
     "allowJs": true,
-    "importsNotUsedAsValues": "preserve"
+    "importsNotUsedAsValues": "preserve",
+    "typeRoots": [
+      "../../third_party/node/node_modules/@types"
+    ],
+    "types": [
+      "trusted-types"
+    ]
   }
 }
diff --git a/ui/ozone/platform/README b/ui/ozone/platform/README
index cc91d3b7..70ca9c1 100644
--- a/ui/ozone/platform/README
+++ b/ui/ozone/platform/README
@@ -1,2 +1,4 @@
 This directory contains implementations of platforms. Each platform implements
 the interfaces from ui/ozone/public for the rest of chromium.
+
+See docs/ozone_overview.md for details.
diff --git a/ui/shell_dialogs/execute_select_file_win.cc b/ui/shell_dialogs/execute_select_file_win.cc
index 063d4c7c..162dbc3 100644
--- a/ui/shell_dialogs/execute_select_file_win.cc
+++ b/ui/shell_dialogs/execute_select_file_win.cc
@@ -10,6 +10,7 @@
 #include "base/callback.h"
 #include "base/files/file.h"
 #include "base/files/file_util.h"
+#include "base/strings/string_tokenizer.h"
 #include "base/strings/string_util.h"
 #include "base/win/com_init_util.h"
 #include "base/win/registry.h"
@@ -50,7 +51,7 @@
     default_folder = default_path;
   } else {
     default_folder = default_path.DirName();
-    default_file_name = default_path.BaseName();
+    default_file_name = GetSanitizedFileName(default_path.BaseName());
   }
 
   // Do not fail the file dialog operation if the specified folder is invalid.
@@ -371,6 +372,33 @@
   return return_value;
 }
 
+base::FilePath GetSanitizedFileName(const base::FilePath& file_name) {
+  base::StringTokenizerT<std::wstring, std::wstring::const_iterator> t(
+      file_name.value(), L"%");
+  t.set_options(base::StringTokenizer::RETURN_EMPTY_TOKENS);
+  std::wstring result;
+  bool token_valid = t.GetNext();
+  while (token_valid) {
+    // Append substring before the first "%".
+    result.append(t.token());
+    // Done if we are reaching the end delimiter,
+    if (!t.GetNext()) {
+      break;
+    }
+    std::wstring string_after_first_percent = t.token();
+    token_valid = t.GetNext();
+    // If there are no other "%", append the string after
+    // the first "%". Otherwise, remove the string between
+    // the "%" and continue handing the remaining string.
+    if (!token_valid) {
+      result.append(L"%");
+      result.append(string_after_first_percent);
+      break;
+    }
+  }
+  return base::FilePath(result);
+}
+
 void ExecuteSelectFile(
     SelectFileDialog::Type type,
     const std::u16string& title,
diff --git a/ui/shell_dialogs/execute_select_file_win.h b/ui/shell_dialogs/execute_select_file_win.h
index dc808af3..8fa96906 100644
--- a/ui/shell_dialogs/execute_select_file_win.h
+++ b/ui/shell_dialogs/execute_select_file_win.h
@@ -26,6 +26,12 @@
     const std::wstring& filter_selected,
     const std::wstring& suggested_ext);
 
+// Given a file name, return the sanitized version by removing substrings that
+// are embedded in double '%' characters as those are reserved for environment
+// variables. Implementation detail exported for unit tests.
+SHELL_DIALOGS_EXPORT base::FilePath GetSanitizedFileName(
+    const base::FilePath& file_name);
+
 // Describes a filter for a file dialog.
 struct FileFilterSpec {
   // A human readable description of this filter. E.g. "HTML Files."
diff --git a/ui/shell_dialogs/execute_select_file_win_unittest.cc b/ui/shell_dialogs/execute_select_file_win_unittest.cc
index df5e8a8..b6c604f 100644
--- a/ui/shell_dialogs/execute_select_file_win_unittest.cc
+++ b/ui/shell_dialogs/execute_select_file_win_unittest.cc
@@ -50,3 +50,38 @@
                                           test_cases[i].suggested_ext));
   }
 }
+
+TEST(ShellDialogsWin, GetSanitizedFileName) {
+  struct GetSanitizedFileNameTestCase {
+    const wchar_t* filename;
+    const wchar_t* sanitized_filename;
+  } test_cases[] = {
+      {L"", L""},
+      {L"a.txt", L"a.txt"},
+
+      // Only 1 "%" in file name.
+      {L"%", L"%"},
+      {L"%.txt", L"%.txt"},
+      {L"ab%c.txt", L"ab%c.txt"},
+      {L"abc.t%", L"abc.t%"},
+
+      // 2 "%" in file name.
+      {L"%%", L""},
+      {L"%c%", L""},
+      {L"%c%d", L"d"},
+      {L"d%c%.txt", L"d.txt"},
+      {L"ab%c.t%", L"ab"},
+      {L"abc.%t%", L"abc."},
+
+      // More than 2 "%" in file name.
+      {L"%ab%c%.txt", L"c%.txt"},
+      {L"%abc%.%txt%", L"."},
+      {L"%ab%c%.%txt%", L"ctxt%"},
+  };
+
+  for (size_t i = 0; i < std::size(test_cases); ++i) {
+    SCOPED_TRACE(base::StringPrintf("i=%zu", i));
+    EXPECT_EQ(base::FilePath(test_cases[i].sanitized_filename),
+              ui::GetSanitizedFileName(base::FilePath(test_cases[i].filename)));
+  }
+}
diff --git a/ui/webui/resources/BUILD.gn b/ui/webui/resources/BUILD.gn
index 8c979f96..33c5c7d 100644
--- a/ui/webui/resources/BUILD.gn
+++ b/ui/webui/resources/BUILD.gn
@@ -206,7 +206,6 @@
     "cr_components/chromeos/smb_shares/add_smb_share_dialog.d.ts",
     "cr_components/chromeos/smb_shares/smb_browser_proxy.d.ts",
     "js/cr/ui/context_menu_handler.m.d.ts",
-    "js/cr/ui/dialogs.m.d.ts",
     "js/cr/ui/grid.m.d.ts",
     "js/cr/ui/list.m.d.ts",
     "js/cr/ui/menu_button.m.d.ts",
@@ -290,7 +289,6 @@
       "js/cr/ui/position_util.m.js",
       "js/cr/ui/splitter.js",
       "js/cr/ui/store_client.js",
-      "js/cr/ui/tree.js",
     ]
   }
 }
@@ -299,6 +297,10 @@
   generate_definitions_js_files += [ "js/cr/ui/focus_without_ink.m.js" ]
 }
 
+if (is_ios) {
+  generate_definitions_js_files += [ "js/ios/web_ui.js" ]
+}
+
 ts_library("library") {
   root_dir = preprocessed_folder
   out_dir = preprocessed_folder
@@ -306,6 +308,7 @@
   tsconfig_base = "tsconfig_base.json"
 
   in_files = [
+    "cr_elements/cr_splitter/cr_splitter.ts",
     "cr_elements/cr_tab_box/cr_tab_box.ts",
     "cr_elements/cr_tab_box/cr_tab_box.html.ts",
     "cr_elements/cr_tree/cr_tree.ts",
@@ -351,7 +354,6 @@
       "cr_elements/cr_search_field/cr_search_field.ts",
       "cr_elements/cr_slider/cr_slider.html.ts",
       "cr_elements/cr_slider/cr_slider.ts",
-      "cr_elements/cr_splitter/cr_splitter.ts",
       "cr_elements/cr_tabs/cr_tabs.html.ts",
       "cr_elements/cr_tabs/cr_tabs.ts",
       "cr_elements/cr_toast/cr_toast_manager.html.ts",
diff --git a/ui/webui/resources/cr_elements/BUILD.gn b/ui/webui/resources/cr_elements/BUILD.gn
index 9a0313f6..617cf46 100644
--- a/ui/webui/resources/cr_elements/BUILD.gn
+++ b/ui/webui/resources/cr_elements/BUILD.gn
@@ -79,6 +79,7 @@
   in_folder = "."
   out_folder = preprocess_folder
   in_files = [
+    "cr_splitter/cr_splitter.ts",
     "cr_tab_box/cr_tab_box.ts",
     "cr_tree/cr_tree_base.ts",
     "cr_tree/cr_tree_item.ts",
@@ -99,7 +100,6 @@
       "cr_search_field/cr_search_field_mixin.ts",
       "cr_search_field/cr_search_field.ts",
       "cr_slider/cr_slider.ts",
-      "cr_splitter/cr_splitter.ts",
       "cr_tabs/cr_tabs.ts",
       "cr_toast/cr_toast_manager.ts",
       "cr_toast/cr_toast.ts",
diff --git a/ui/webui/resources/cr_elements/cr_splitter/cr_splitter.ts b/ui/webui/resources/cr_elements/cr_splitter/cr_splitter.ts
index 7b2ae6ab..b1785b4 100644
--- a/ui/webui/resources/cr_elements/cr_splitter/cr_splitter.ts
+++ b/ui/webui/resources/cr_elements/cr_splitter/cr_splitter.ts
@@ -3,41 +3,28 @@
 // found in the LICENSE file.
 
 import {assert} from 'chrome://resources/js/assert_ts.js';
-import {PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 
-export class CrSplitterElement extends PolymerElement {
+export class CrSplitterElement extends HTMLElement {
   static get is() {
     return 'cr-splitter';
   }
 
-  static get template() {
-    return null;
-  }
-
-  static get properties() {
-    return {
-      resizeNextElement: Boolean,
-    };
-  }
-
   private handlers_: Map<string, (e: any) => void>|null = null;
   private startX_: number = 0;
   private startWidth_: number = -1;
   resizeNextElement: boolean = false;
 
-  override ready() {
-    super.ready();
+  constructor() {
+    super();
     this.addEventListener('mousedown', e => this.onMouseDown_(e));
     this.addEventListener('touchstart', e => this.onTouchStart_(e));
   }
 
-  override connectedCallback() {
-    super.connectedCallback();
+  connectedCallback() {
     this.handlers_ = new Map();
   }
 
-  override disconnectedCallback() {
-    super.disconnectedCallback();
+  disconnectedCallback() {
     this.removeAllHandlers_();
     this.handlers_ = null;
   }
diff --git a/ui/webui/resources/css/BUILD.gn b/ui/webui/resources/css/BUILD.gn
index 3801cb7..985218e 100644
--- a/ui/webui/resources/css/BUILD.gn
+++ b/ui/webui/resources/css/BUILD.gn
@@ -36,7 +36,6 @@
     "text_defaults.css",
     "text_defaults_md.css",
     "throbber.css",
-    "tree.css",
     "widgets.css",
   ]
 
diff --git a/ui/webui/resources/css/tree.css b/ui/webui/resources/css/tree.css
deleted file mode 100644
index d0f7939c..0000000
--- a/ui/webui/resources/css/tree.css
+++ /dev/null
@@ -1,185 +0,0 @@
-/* Copyright (c) 2012 The Chromium Authors. All rights reserved.
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file. */
-
-tree {
-  display: block;
-  outline: none;
-  overflow: auto;
-}
-
-.tree-item > .tree-row {
-  align-items: center;
-  background-color: rgba(255, 255, 255, 0);
-  border: 1px solid rgba(255, 255, 255, 0); /* transparent white */
-  border-radius: 2px;
-  color: black;
-  cursor: default;
-  display: flex;
-  line-height: 28px;
-  padding: 0 3px;
-  position: relative;
-  user-select: none;
-  white-space: nowrap;
-}
-
-.expand-icon {
-  background: url(../images/tree_triangle.svg) no-repeat center center;
-  background-size: 8px 5.5px;
-  height: 16px;
-  min-width: 16px;
-  opacity: .6;
-  transform: rotate(-90deg);
-  transition: all 150ms;
-  width: 16px;
-}
-
-html[dir=rtl] .expand-icon {
-  transform: rotate(90deg);
-}
-
-.tree-item[expanded] > .tree-row > .expand-icon {
-  background-image: url(../images/tree_triangle.svg);
-  opacity: .5;
-  transform: rotate(0);
-}
-
-.tree-row .expand-icon {
-  visibility: hidden;
-}
-
-.tree-row[may-have-children] .expand-icon {
-  visibility: visible;
-}
-
-.tree-row[has-children=false] .expand-icon {
-  visibility: hidden;
-}
-
-.tree-row:hover {
-  background-color: hsl(214, 91%, 97%);
-  border-color: hsl(214, 91%, 85%);
-  z-index: 1;
-}
-
-/* WebKit has a bug with attribute selectors so we apply selected to the tree
- * row as well. https://bugs.webkit.org/show_bug.cgi?id=12519. */
-.tree-row[selected] {
-  background-image: -webkit-linear-gradient(rgba(255, 255, 255, 0.8),
-                                            rgba(255,255,255,0));
-}
-
-.tree-row[selected] {
-  background-color: hsl(0, 0%, 90%);
-  border-color: hsl(0, 0%, 85%);
-  z-index: 2;
-}
-
-.tree-row[selected]:hover,
-tree:focus .tree-row[selected] {
-  background-color: hsl(214, 91%, 89%);
-  border-color: rgb(125, 162, 206);
-}
-
-.tree-children[expanded] {
-  display: block;
-}
-
-.tree-children {
-  display: none;
-}
-
-.tree-item > .tree-row > * {
-  box-sizing: border-box;
-  display: inline-block;
-}
-
-.tree-label-icon {
-  background-position: 0 50%;
-  background-repeat: no-repeat;
-  height: 20px;
-  min-width: 20px;
-  width: 20px;
-}
-
-.tree-label {
-  white-space: pre;
-}
-
-.tree-label-icon,
-.tree-row[may-have-children] > .tree-label-icon {
-  background-image: url(chrome://theme/IDR_FOLDER_CLOSED);
-}
-
-<if expr="is_macosx or is_ios">
-@media (prefers-color-scheme: dark) {
-  .tree-label-icon,
-  .tree-row[may-have-children] > .tree-label-icon {
-    background-image: url(chrome://theme/IDR_FOLDER_CLOSED_WHITE);
-  }
-}
-</if>
-
-<if expr="not is_macosx and not is_ios">
-.tree-item[expanded] > .tree-row > .tree-label-icon {
-  background-image: url(chrome://theme/IDR_FOLDER_OPEN);
-}
-</if>
-
-html[dir=rtl] .tree-label-icon,
-html[dir=rtl] .tree-row[may-have-children] > .tree-label-icon,
-html[dir=rtl] .tree-item[expanded] > .tree-row > .tree-label-icon {
-  transform: scaleX(-1);
-}
-
-tree[icon-visibility=hidden] .tree-label-icon {
-  display: none;
-}
-
-tree[icon-visibility=parent] .tree-label-icon,
-tree[icon-visibility=parent] .tree-row[has-children=false] > .tree-label-icon {
-  background-image: none;
-}
-
-/* We need to ensure that even empty labels take up space */
-.tree-label:empty::after {
-  content: ' ';
-  white-space: pre;
-}
-
-.tree-rename > .tree-row > .tree-label {
-  -webkit-user-modify: read-write-plaintext-only;
-  background: white;
-  color: black;
-  outline: 1px solid black;
-  user-select: auto;
-}
-
-.tree-item[editing] input {
-  /* Do not inherit the line-height */
-  font-family: inherit;
-  font-size: inherit;
-  font-weight: inherit;
-  margin: -2px -8px -2px -3px;
-<if expr="not is_macosx and not is_ios">
-  outline: none;
-</if>
-  padding: 1px 7px 1px 1px;
-}
-
-html[dir=rtl] .tree-item[editing] input {
-  margin: -2px -3px -2px -8px;
-  padding: 1px 1px 1px 7px;
-}
-
-@media(forced-colors) {

-  .tree-row[selected],

-  .tree-row:hover,

-  .tree-row[selected]:hover,

-  tree:focus .tree-row[selected] {

-    background-color: Highlight;

-    background-image: none;

-    color: HighlightText;

-    forced-color-adjust: none;

-  }

-}
diff --git a/ui/webui/resources/js/cr.gni b/ui/webui/resources/js/cr.gni
index 1a20b8a..7fb7ae5 100644
--- a/ui/webui/resources/js/cr.gni
+++ b/ui/webui/resources/js/cr.gni
@@ -10,9 +10,6 @@
   "cr.ui.Command|Command",
   "cr.ui.contextMenuHandler|contextMenuHandler",
   "cr.ui.decorate|decorate",
-  "cr.ui.dialogs.BaseDialog|BaseDialog",
-  "cr.ui.dialogs.AlertDialog|AlertDialog",
-  "cr.ui.dialogs.ConfirmDialog|ConfirmDialog",
   "cr.ui.Action|Action",
   "cr.ui.AnchorType|AnchorType",
   "cr.ui.ArrayDataModel|ArrayDataModel",
@@ -35,8 +32,5 @@
   "cr.ui.Size|Size",
   "cr.ui.Splitter|Splitter",
   "cr.ui.StoreObserver|StoreObserver",
-  "cr.ui.Tab|Tab",
-  "cr.ui.Tree|Tree",
-  "cr.ui.TreeItem|TreeItem",
   "cr.ui.VirtualFocusRow|VirtualFocusRow",
 ]
diff --git a/ui/webui/resources/js/cr/ui/BUILD.gn b/ui/webui/resources/js/cr/ui/BUILD.gn
index 4114d43..672dbf7 100644
--- a/ui/webui/resources/js/cr/ui/BUILD.gn
+++ b/ui/webui/resources/js/cr/ui/BUILD.gn
@@ -41,7 +41,6 @@
     "focus_outline_manager.js",
     "splitter.js",
     "store.js",
-    "tree.js",
   ]
 
   if (is_chromeos_ash) {
@@ -49,7 +48,6 @@
       "array_data_model.js",
       "command.js",
       "context_menu_handler.js",
-      "dialogs.js",
       "focus_manager.js",
       "focus_row.js",
       "focus_row_behavior.js",
@@ -101,7 +99,6 @@
 
   if (is_chromeos_ash) {
     in_files += [
-      "dialogs.m.js",
       "grid.m.js",
       "list_single_selection_model.m.js",
     ]
@@ -121,7 +118,6 @@
     ":array_data_model",
     ":command",
     ":context_menu_handler",
-    ":dialogs",
     ":focus_manager",
     ":focus_outline_manager",
     ":focus_row",
@@ -172,13 +168,6 @@
   ]
 }
 
-js_library("dialogs") {
-  deps = [
-    "../..:cr",
-    "../..:util",
-  ]
-}
-
 js_library("focus_manager") {
   deps = [ "../..:cr" ]
 }
@@ -295,7 +284,6 @@
     "array_data_model.js",
     "command.js",
     "context_menu_handler.js",
-    "dialogs.js",
     "focus_outline_manager.js",
     "focus_row.js",
     "focus_row_behavior.js",
@@ -321,7 +309,6 @@
     ":array_data_model.m",
     ":command.m",
     ":context_menu_handler.m",
-    ":dialogs.m",
     ":drag_wrapper",
     ":focus_grid",
     ":focus_outline_manager.m",
@@ -342,7 +329,6 @@
     ":splitter",
     ":store",
     ":store_client",
-    ":tree",
   ]
 }
 
@@ -387,11 +373,6 @@
   extra_deps = [ ":modulize" ]
 }
 
-js_library("dialogs.m") {
-  sources = [ "$root_gen_dir/ui/webui/resources/js/cr/ui/dialogs.m.js" ]
-  extra_deps = [ ":modulize" ]
-}
-
 js_library("drag_wrapper") {
 }
 
@@ -566,11 +547,3 @@
     "../..:assert.m",
   ]
 }
-
-js_library("tree") {
-  deps = [
-    "../:ui.m",
-    "../..:assert.m",
-    "../..:cr.m",
-  ]
-}
diff --git a/ui/webui/resources/js/cr/ui/dialogs.m.d.ts b/ui/webui/resources/js/cr/ui/dialogs.m.d.ts
deleted file mode 100644
index b78a1d5..0000000
--- a/ui/webui/resources/js/cr/ui/dialogs.m.d.ts
+++ /dev/null
@@ -1,36 +0,0 @@
-// Copyright 2022 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.
-
-export class BaseDialog {
-  constructor(parentNode: any);
-  setOkLabel(label: string): void;
-  setCancelLabel(label: string): void;
-  setInitialFocusOnCancel(): void;
-  show(
-      message: string, onOk?: Function|undefined, onCancel?: Function|undefined,
-      onShow?: Function|undefined): void;
-  showHtml(
-      title: string, message: string, onOk?: Function|undefined,
-      onCancel?: Function|undefined, onShow?: Function|undefined): void;
-  showWithTitle(
-      title: string, message: string, onOk?: Function|undefined,
-      onCancel?: Function|undefined, onShow?: Function|undefined): void;
-  hide(onHide?: Function|undefined): void;
-
-  /* eslint-disable @typescript-eslint/naming-convention */
-  OK_LABEL: string;
-  CANCEL_LABEL: string;
-  ANIMATE_STABLE_DURATION: number;
-  /* eslint-enable @typescript-eslint/naming-convention */
-}
-
-export class AlertDialog extends BaseDialog {
-  constructor(parentNode: any);
-  show(message: any, onOk?: Function|undefined, onShow?: Function|undefined):
-      any;
-}
-
-export class ConfirmDialog extends BaseDialog {
-  constructor(parentNode: any);
-}
diff --git a/url/url_canon_etc.cc b/url/url_canon_etc.cc
index 851926da..53b71ea 100644
--- a/url/url_canon_etc.cc
+++ b/url/url_canon_etc.cc
@@ -101,7 +101,7 @@
               const Component& scheme,
               CanonOutput* output,
               Component* out_scheme) {
-  if (scheme.len <= 0) {
+  if (!scheme.is_nonempty()) {
     // Scheme is unspecified or empty, convert to empty by appending a colon.
     *out_scheme = Component(output->length(), 0);
     output->push_back(':');
@@ -117,12 +117,13 @@
   // FindAndCompareScheme, which could cause some security checks on
   // schemes to be incorrect.
   bool success = true;
-  int end = scheme.end();
-  for (int i = scheme.begin; i < end; i++) {
+  size_t begin = static_cast<size_t>(scheme.begin);
+  size_t end = static_cast<size_t>(scheme.end());
+  for (size_t i = begin; i < end; i++) {
     UCHAR ch = static_cast<UCHAR>(spec[i]);
     char replacement = 0;
     if (ch < 0x80) {
-      if (i == scheme.begin) {
+      if (i == begin) {
         // Need to do a special check for the first letter of the scheme.
         if (IsSchemeFirstChar(static_cast<unsigned char>(ch)))
           replacement = kSchemeCanonical[ch];
@@ -179,8 +180,9 @@
   out_username->begin = output->length();
   if (username.len > 0) {
     // This will escape characters not valid for the username.
-    AppendStringOfType(&username_spec[username.begin], username.len,
-                       CHAR_USERINFO, output);
+    AppendStringOfType(&username_spec[username.begin],
+                       static_cast<size_t>(username.len), CHAR_USERINFO,
+                       output);
   }
   out_username->len = output->length() - out_username->begin;
 
@@ -189,8 +191,9 @@
   if (password.len > 0) {
     output->push_back(':');
     out_password->begin = output->length();
-    AppendStringOfType(&password_spec[password.begin], password.len,
-                       CHAR_USERINFO, output);
+    AppendStringOfType(&password_spec[password.begin],
+                       static_cast<size_t>(password.len), CHAR_USERINFO,
+                       output);
     out_password->len = output->length() - out_password->begin;
   } else {
     *out_password = Component();
@@ -223,7 +226,8 @@
     // what the error was, and mark the URL as invalid by returning false.
     output->push_back(':');
     out_port->begin = output->length();
-    AppendInvalidNarrowString(spec, port.begin, port.end(), output);
+    AppendInvalidNarrowString(spec, static_cast<size_t>(port.begin),
+                              static_cast<size_t>(port.end()), output);
     out_port->len = output->length() - out_port->begin;
     return false;
   }
@@ -285,7 +289,7 @@
                        const Component& ref,
                        CanonOutput* output,
                        Component* out_ref) {
-  if (ref.len < 0) {
+  if (!ref.is_valid()) {
     // Common case of no ref.
     *out_ref = Component();
     return;
@@ -297,8 +301,8 @@
   out_ref->begin = output->length();
 
   // Now iterate through all the characters, converting to UTF-8 and validating.
-  int end = ref.end();
-  for (int i = ref.begin; i < end; i++) {
+  size_t end = static_cast<size_t>(ref.end());
+  for (size_t i = static_cast<size_t>(ref.begin); i < end; i++) {
     UCHAR current_char = static_cast<UCHAR>(spec[i]);
     if (current_char < 0x80) {
       if (kShouldEscapeCharInFragment[current_char])
diff --git a/url/url_canon_host.cc b/url/url_canon_host.cc
index c2cd9d1..5f7bf71 100644
--- a/url/url_canon_host.cc
+++ b/url/url_canon_host.cc
@@ -123,15 +123,15 @@
 //    |*has_non_ascii| flag.
 //
 // The return value indicates if the output is a potentially valid host name.
-template<typename INCHAR, typename OUTCHAR>
+template <typename INCHAR, typename OUTCHAR>
 bool DoSimpleHost(const INCHAR* host,
-                  int host_len,
+                  size_t host_len,
                   CanonOutputT<OUTCHAR>* output,
                   bool* has_non_ascii) {
   *has_non_ascii = false;
 
   bool success = true;
-  for (int i = 0; i < host_len; ++i) {
+  for (size_t i = 0; i < host_len; ++i) {
     unsigned int source = host[i];
     if (source == '%') {
       // Unescape first, if possible.
@@ -175,7 +175,7 @@
 }
 
 // Canonicalizes a host that requires IDN conversion. Returns true on success
-bool DoIDNHost(const char16_t* src, int src_len, CanonOutput* output) {
+bool DoIDNHost(const char16_t* src, size_t src_len, CanonOutput* output) {
   int original_output_len = output->length();  // So we can rewind below.
 
   // We need to escape URL before doing IDN conversion, since punicode strings
@@ -202,8 +202,8 @@
   // unescaping. Although we unescaped everything before this function call, if
   // somebody does %00 as fullwidth, ICU will convert this to ASCII.
   bool success = DoSimpleHost(wide_output.data(),
-                              wide_output.length(),
-                              output, &has_non_ascii);
+                              static_cast<size_t>(wide_output.length()), output,
+                              &has_non_ascii);
   if (has_non_ascii) {
     // ICU generated something that DoSimpleHost didn't think looked like
     // ASCII. This is quite rare, but ICU might convert some characters to
@@ -220,7 +220,8 @@
     // ASCII isn't strictly necessary, but DoSimpleHost handles this case
     // anyway so we handle it/
     output->set_length(original_output_len);
-    AppendInvalidNarrowString(wide_output.data(), 0, wide_output.length(),
+    AppendInvalidNarrowString(wide_output.data(), 0,
+                              static_cast<size_t>(wide_output.length()),
                               output);
     return false;
   }
@@ -230,8 +231,11 @@
 // 8-bit convert host to its ASCII version: this converts the UTF-8 input to
 // UTF-16. The has_escaped flag should be set if the input string requires
 // unescaping.
-bool DoComplexHost(const char* host, int host_len,
-                   bool has_non_ascii, bool has_escaped, CanonOutput* output) {
+bool DoComplexHost(const char* host,
+                   size_t host_len,
+                   bool has_non_ascii,
+                   bool has_escaped,
+                   CanonOutput* output) {
   // Save the current position in the output. We may write stuff and rewind it
   // below, so we need to know where to rewind to.
   int begin_length = output->length();
@@ -239,7 +243,7 @@
   // Points to the UTF-8 data we want to convert. This will either be the
   // input or the unescaped version written to |*output| if necessary.
   const char* utf8_source;
-  int utf8_source_len;
+  size_t utf8_source_len;
   bool are_all_escaped_valid = true;
   if (has_escaped) {
     // Unescape before converting to UTF-16 for IDN. We write this into the
@@ -264,7 +268,7 @@
     // Save the pointer into the data was just converted (it may be appended to
     // other data in the output buffer).
     utf8_source = &output->data()[begin_length];
-    utf8_source_len = output->length() - begin_length;
+    utf8_source_len = static_cast<size_t>(output->length() - begin_length);
   } else {
     // We don't need to unescape, use input for IDNization later. (We know the
     // input has non-ASCII, or the simple version would have been called
@@ -280,17 +284,18 @@
   if (!ConvertUTF8ToUTF16(utf8_source, utf8_source_len, &utf16)) {
     // In this error case, the input may or may not be the output.
     StackBuffer utf8;
-    for (int i = 0; i < utf8_source_len; i++)
+    for (size_t i = 0; i < utf8_source_len; i++)
       utf8.push_back(utf8_source[i]);
     output->set_length(begin_length);
-    AppendInvalidNarrowString(utf8.data(), 0, utf8.length(), output);
+    AppendInvalidNarrowString(utf8.data(), 0,
+                              static_cast<size_t>(utf8.length()), output);
     return false;
   }
   output->set_length(begin_length);
 
   // This will call DoSimpleHost which will do normal ASCII canonicalization
   // and also check for IP addresses in the outpt.
-  return DoIDNHost(utf16.data(), utf16.length(), output) &&
+  return DoIDNHost(utf16.data(), static_cast<size_t>(utf16.length()), output) &&
          are_all_escaped_valid;
 }
 
@@ -298,7 +303,7 @@
 // the backend, so we just pass through. The has_escaped flag should be set if
 // the input string requires unescaping.
 bool DoComplexHost(const char16_t* host,
-                   int host_len,
+                   size_t host_len,
                    bool has_non_ascii,
                    bool has_escaped,
                    CanonOutput* output) {
@@ -319,8 +324,8 @@
 
     // Once we convert to UTF-8, we can use the 8-bit version of the complex
     // host handling code above.
-    return DoComplexHost(utf8.data(), utf8.length(), has_non_ascii,
-                         has_escaped, output);
+    return DoComplexHost(utf8.data(), static_cast<size_t>(utf8.length()),
+                         has_non_ascii, has_escaped, output);
   }
 
   // No unescaping necessary, we can safely pass the input to ICU. This
@@ -334,16 +339,18 @@
 bool DoHostSubstring(const CHAR* spec,
                      const Component& host,
                      CanonOutput* output) {
+  DCHECK(host.is_valid());
+
   bool has_non_ascii, has_escaped;
   ScanHostname<CHAR, UCHAR>(spec, host, &has_non_ascii, &has_escaped);
 
   if (has_non_ascii || has_escaped) {
-    return DoComplexHost(&spec[host.begin], host.len, has_non_ascii,
-                         has_escaped, output);
+    return DoComplexHost(&spec[host.begin], static_cast<size_t>(host.len),
+                         has_non_ascii, has_escaped, output);
   }
 
-  const bool success =
-      DoSimpleHost(&spec[host.begin], host.len, output, &has_non_ascii);
+  const bool success = DoSimpleHost(
+      &spec[host.begin], static_cast<size_t>(host.len), output, &has_non_ascii);
   DCHECK(!has_non_ascii);
   return success;
 }
@@ -353,7 +360,7 @@
             const Component& host,
             CanonOutput* output,
             CanonHostInfo* host_info) {
-  if (host.len <= 0) {
+  if (!host.is_nonempty()) {
     // Empty hosts don't need anything.
     host_info->family = CanonHostInfo::NEUTRAL;
     host_info->out_host = Component();
diff --git a/url/url_canon_internal.cc b/url/url_canon_internal.cc
index 48a1e74b1..192feb9 100644
--- a/url/url_canon_internal.cc
+++ b/url/url_canon_internal.cc
@@ -11,17 +11,19 @@
 #include <cstdio>
 #include <string>
 
+#include "base/numerics/safe_conversions.h"
 #include "base/strings/utf_string_conversion_utils.h"
 
 namespace url {
 
 namespace {
 
-template<typename CHAR, typename UCHAR>
-void DoAppendStringOfType(const CHAR* source, int length,
+template <typename CHAR, typename UCHAR>
+void DoAppendStringOfType(const CHAR* source,
+                          size_t length,
                           SharedCharTypes type,
                           CanonOutput* output) {
-  for (int i = 0; i < length; i++) {
+  for (size_t i = 0; i < length; i++) {
     if (static_cast<UCHAR>(source[i]) >= 0x80) {
       // ReadChar will fill the code point with kUnicodeReplacementCharacter
       // when the input is invalid, which is what we want.
@@ -41,10 +43,12 @@
 
 // This function assumes the input values are all contained in 8-bit,
 // although it allows any type. Returns true if input is valid, false if not.
-template<typename CHAR, typename UCHAR>
-void DoAppendInvalidNarrowString(const CHAR* spec, int begin, int end,
+template <typename CHAR, typename UCHAR>
+void DoAppendInvalidNarrowString(const CHAR* spec,
+                                 size_t begin,
+                                 size_t end,
                                  CanonOutput* output) {
-  for (int i = begin; i < end; i++) {
+  for (size_t i = begin; i < end; i++) {
     UCHAR uch = static_cast<UCHAR>(spec[i]);
     if (uch >= 0x80) {
       // Handle UTF-8/16 encodings. This call will correctly handle the error
@@ -98,7 +102,8 @@
       // Convert to UTF-8.
       dest_component->begin = utf8_buffer->length();
       success = ConvertUTF16ToUTF8(&override_source[override_component.begin],
-                                   override_component.len, utf8_buffer);
+                                   static_cast<size_t>(override_component.len),
+                                   utf8_buffer);
       dest_component->len = utf8_buffer->length() - dest_component->begin;
     }
   }
@@ -235,26 +240,24 @@
 
 const base_icu::UChar32 kUnicodeReplacementCharacter = 0xfffd;
 
-void AppendStringOfType(const char* source, int length,
+void AppendStringOfType(const char* source,
+                        size_t length,
                         SharedCharTypes type,
                         CanonOutput* output) {
   DoAppendStringOfType<char, unsigned char>(source, length, type, output);
 }
 
 void AppendStringOfType(const char16_t* source,
-                        int length,
+                        size_t length,
                         SharedCharTypes type,
                         CanonOutput* output) {
   DoAppendStringOfType<char16_t, char16_t>(source, length, type, output);
 }
 
 bool ReadUTFChar(const char* str,
-                 int* begin,
-                 int length,
+                 size_t* begin,
+                 size_t length,
                  base_icu::UChar32* code_point_out) {
-  // This depends on ints and int32s being the same thing. If they're not, it
-  // will fail to compile.
-  // TODO(mmenke): This should probably be fixed.
   if (!base::ReadUnicodeCharacter(str, length, begin, code_point_out) ||
       !base::IsValidCharacter(*code_point_out)) {
     *code_point_out = kUnicodeReplacementCharacter;
@@ -264,12 +267,9 @@
 }
 
 bool ReadUTFChar(const char16_t* str,
-                 int* begin,
-                 int length,
+                 size_t* begin,
+                 size_t length,
                  base_icu::UChar32* code_point_out) {
-  // This depends on ints and int32s being the same thing. If they're not, it
-  // will fail to compile.
-  // TODO(mmenke): This should probably be fixed.
   if (!base::ReadUnicodeCharacter(str, length, begin, code_point_out) ||
       !base::IsValidCharacter(*code_point_out)) {
     *code_point_out = kUnicodeReplacementCharacter;
@@ -278,23 +278,25 @@
   return true;
 }
 
-void AppendInvalidNarrowString(const char* spec, int begin, int end,
+void AppendInvalidNarrowString(const char* spec,
+                               size_t begin,
+                               size_t end,
                                CanonOutput* output) {
   DoAppendInvalidNarrowString<char, unsigned char>(spec, begin, end, output);
 }
 
 void AppendInvalidNarrowString(const char16_t* spec,
-                               int begin,
-                               int end,
+                               size_t begin,
+                               size_t end,
                                CanonOutput* output) {
   DoAppendInvalidNarrowString<char16_t, char16_t>(spec, begin, end, output);
 }
 
 bool ConvertUTF16ToUTF8(const char16_t* input,
-                        int input_len,
+                        size_t input_len,
                         CanonOutput* output) {
   bool success = true;
-  for (int i = 0; i < input_len; i++) {
+  for (size_t i = 0; i < input_len; i++) {
     base_icu::UChar32 code_point;
     success &= ReadUTFChar(input, &i, input_len, &code_point);
     AppendUTF8Value(code_point, output);
@@ -303,10 +305,10 @@
 }
 
 bool ConvertUTF8ToUTF16(const char* input,
-                        int input_len,
+                        size_t input_len,
                         CanonOutputT<char16_t>* output) {
   bool success = true;
-  for (int i = 0; i < input_len; i++) {
+  for (size_t i = 0; i < input_len; i++) {
     base_icu::UChar32 code_point;
     success &= ReadUTFChar(input, &i, input_len, &code_point);
     AppendUTF16Value(code_point, output);
diff --git a/url/url_canon_internal.h b/url/url_canon_internal.h
index def4636..ec5b61cb4 100644
--- a/url/url_canon_internal.h
+++ b/url/url_canon_internal.h
@@ -77,11 +77,12 @@
 
 // Appends the given string to the output, escaping characters that do not
 // match the given |type| in SharedCharTypes.
-void AppendStringOfType(const char* source, int length,
+void AppendStringOfType(const char* source,
+                        size_t length,
                         SharedCharTypes type,
                         CanonOutput* output);
 void AppendStringOfType(const char16_t* source,
-                        int length,
+                        size_t length,
                         SharedCharTypes type,
                         CanonOutput* output);
 
@@ -107,8 +108,8 @@
 // Indicates if the given character is a dot or dot equivalent, returning the
 // number of characters taken by it. This will be one for a literal dot, 3 for
 // an escaped dot. If the character is not a dot, this will return 0.
-template<typename CHAR>
-inline int IsDot(const CHAR* spec, int offset, int end) {
+template <typename CHAR>
+inline size_t IsDot(const CHAR* spec, size_t offset, size_t end) {
   if (spec[offset] == '.') {
     return 1;
   } else if (spec[offset] == '%' && offset + 3 <= end &&
@@ -154,8 +155,8 @@
 // (for a single-byte ASCII character, it will not be changed).
 COMPONENT_EXPORT(URL)
 bool ReadUTFChar(const char* str,
-                 int* begin,
-                 int length,
+                 size_t* begin,
+                 size_t length,
                  base_icu::UChar32* code_point_out);
 
 // Generic To-UTF-8 converter. This will call the given append method for each
@@ -231,8 +232,8 @@
 // (for a single-16-bit-word character, it will not be changed).
 COMPONENT_EXPORT(URL)
 bool ReadUTFChar(const char16_t* str,
-                 int* begin,
-                 int length,
+                 size_t* begin,
+                 size_t length,
                  base_icu::UChar32* code_point_out);
 
 // Equivalent to U16_APPEND_UNSAFE in ICU but uses our output method.
@@ -268,8 +269,8 @@
 // Assumes that ch[begin] is within range in the array, but does not assume
 // that any following characters are.
 inline bool AppendUTF8EscapedChar(const char16_t* str,
-                                  int* begin,
-                                  int length,
+                                  size_t* begin,
+                                  size_t length,
                                   CanonOutput* output) {
   // UTF-16 input. ReadUTFChar will handle invalid characters for us and give
   // us the kUnicodeReplacementCharacter, so we don't have to do special
@@ -281,7 +282,9 @@
 }
 
 // Handles UTF-8 input. See the wide version above for usage.
-inline bool AppendUTF8EscapedChar(const char* str, int* begin, int length,
+inline bool AppendUTF8EscapedChar(const char* str,
+                                  size_t* begin,
+                                  size_t length,
                                   CanonOutput* output) {
   // ReadUTF8Char will handle invalid characters for us and give us the
   // kUnicodeReplacementCharacter, so we don't have to do special checking
@@ -308,8 +311,10 @@
   return c <= 255;
 }
 
-template<typename CHAR>
-inline bool DecodeEscaped(const CHAR* spec, int* begin, int end,
+template <typename CHAR>
+inline bool DecodeEscaped(const CHAR* spec,
+                          size_t* begin,
+                          size_t end,
                           unsigned char* unescaped_value) {
   if (*begin + 3 > end ||
       !Is8BitChar(spec[*begin + 1]) || !Is8BitChar(spec[*begin + 2])) {
@@ -338,11 +343,13 @@
 // This is used in error cases to append invalid output so that it looks
 // approximately correct. Non-error cases should not call this function since
 // the escaping rules are not guaranteed!
-void AppendInvalidNarrowString(const char* spec, int begin, int end,
+void AppendInvalidNarrowString(const char* spec,
+                               size_t begin,
+                               size_t end,
                                CanonOutput* output);
 void AppendInvalidNarrowString(const char16_t* spec,
-                               int begin,
-                               int end,
+                               size_t begin,
+                               size_t end,
                                CanonOutput* output);
 
 // Misc canonicalization helpers ----------------------------------------------
@@ -357,11 +364,11 @@
 // normal.
 COMPONENT_EXPORT(URL)
 bool ConvertUTF16ToUTF8(const char16_t* input,
-                        int input_len,
+                        size_t input_len,
                         CanonOutput* output);
 COMPONENT_EXPORT(URL)
 bool ConvertUTF8ToUTF16(const char* input,
-                        int input_len,
+                        size_t input_len,
                         CanonOutputT<char16_t>* output);
 
 // Converts from UTF-16 to 8-bit using the character set converter. If the
diff --git a/url/url_canon_mailtourl.cc b/url/url_canon_mailtourl.cc
index f4fe2b4e..ff62bea5 100644
--- a/url/url_canon_mailtourl.cc
+++ b/url/url_canon_mailtourl.cc
@@ -57,8 +57,8 @@
     // Copy the path using path URL's more lax escaping rules.
     // We convert to UTF-8 and escape non-ASCII, but leave most
     // ASCII characters alone.
-    int end = parsed.path.end();
-    for (int i = parsed.path.begin; i < end; ++i) {
+    size_t end = static_cast<size_t>(parsed.path.end());
+    for (size_t i = static_cast<size_t>(parsed.path.begin); i < end; ++i) {
       UCHAR uch = static_cast<UCHAR>(source.path[i]);
       if (ShouldEncodeMailboxCharacter<UCHAR>(uch))
         success &= AppendUTF8EscapedChar(source.path, &i, end, output);
diff --git a/url/url_canon_path.cc b/url/url_canon_path.cc
index d6fb64b..f50e107 100644
--- a/url/url_canon_path.cc
+++ b/url/url_canon_path.cc
@@ -101,9 +101,11 @@
 // If the input is "../foo", |after_dot| = 1, |end| = 6, and
 // at the end, |*consumed_len| = 2 for the "./" this function consumed. The
 // original dot length should be handled by the caller.
-template<typename CHAR>
-DotDisposition ClassifyAfterDot(const CHAR* spec, int after_dot,
-                                int end, int* consumed_len) {
+template <typename CHAR>
+DotDisposition ClassifyAfterDot(const CHAR* spec,
+                                size_t after_dot,
+                                size_t end,
+                                size_t* consumed_len) {
   if (after_dot == end) {
     // Single dot at the end.
     *consumed_len = 0;
@@ -115,9 +117,9 @@
     return DIRECTORY_CUR;
   }
 
-  int second_dot_len = IsDot(spec, after_dot, end);
+  size_t second_dot_len = IsDot(spec, after_dot, end);
   if (second_dot_len) {
-    int after_second_dot = after_dot + second_dot_len;
+    size_t after_second_dot = after_dot + second_dot_len;
     if (after_second_dot == end) {
       // Double dot at the end.
       *consumed_len = second_dot_len;
@@ -193,10 +195,10 @@
 // ends with a '%' followed by one or two characters, and the '%' is the one
 // pointed to by |last_invalid_percent_index|.  The last character in the string
 // was just unescaped.
-template<typename CHAR>
+template <typename CHAR>
 void CheckForNestedEscapes(const CHAR* spec,
-                           int next_input_index,
-                           int input_len,
+                           size_t next_input_index,
+                           size_t input_len,
                            int last_invalid_percent_index,
                            CanonOutput* output) {
   const int length = output->length();
@@ -218,9 +220,10 @@
   }
 
   // Now output ends like "%cc".  Try to unescape this.
-  int begin = last_invalid_percent_index;
+  size_t begin = static_cast<size_t>(last_invalid_percent_index);
   unsigned char temp;
-  if (DecodeEscaped(output->data(), &begin, output->length(), &temp)) {
+  if (DecodeEscaped(output->data(), &begin,
+                    static_cast<size_t>(output->length()), &temp)) {
     // New escape sequence found.  Overwrite the characters following the '%'
     // with "25", and push_back() the one or two characters that were following
     // the '%' when we were called.
@@ -252,7 +255,10 @@
                            const Component& path,
                            int path_begin_in_output,
                            CanonOutput* output) {
-  int end = path.end();
+  if (!path.is_nonempty())
+    return true;
+
+  size_t end = static_cast<size_t>(path.end());
 
   // We use this variable to minimize the amount of work done when unescaping --
   // we'll only call CheckForNestedEscapes() when this points at one of the last
@@ -260,7 +266,7 @@
   int last_invalid_percent_index = INT_MIN;
 
   bool success = true;
-  for (int i = path.begin; i < end; i++) {
+  for (size_t i = static_cast<size_t>(path.begin); i < end; i++) {
     DCHECK_LT(last_invalid_percent_index, output->length());
     UCHAR uch = static_cast<UCHAR>(spec[i]);
     if (sizeof(CHAR) > 1 && uch >= 0x80) {
@@ -276,7 +282,7 @@
       unsigned char flags = kPathCharLookup[out_ch];
       if (flags & SPECIAL) {
         // Needs special handling of some sort.
-        int dotlen;
+        size_t dotlen;
         if ((dotlen = IsDot(spec, i, end)) > 0) {
           // See if this dot was preceded by a slash in the output.
           //
@@ -287,7 +293,7 @@
           if (output->length() > path_begin_in_output &&
               output->at(output->length() - 1) == '/') {
             // Slash followed by a dot, check to see if this is means relative
-            int consumed_len;
+            size_t consumed_len;
             switch (ClassifyAfterDot<CHAR>(spec, i + dotlen, end,
                                            &consumed_len)) {
               case NOT_A_DIRECTORY:
diff --git a/url/url_canon_pathurl.cc b/url/url_canon_pathurl.cc
index e726cfb..d8d65f33 100644
--- a/url/url_canon_pathurl.cc
+++ b/url/url_canon_pathurl.cc
@@ -32,8 +32,8 @@
     // https://url.spec.whatwg.org/#cannot-be-a-base-url-path-state
     // https://url.spec.whatwg.org/#c0-control-percent-encode-set
     new_component->begin = output->length();
-    int end = component.end();
-    for (int i = component.begin; i < end; i++) {
+    size_t end = static_cast<size_t>(component.end());
+    for (size_t i = static_cast<size_t>(component.begin); i < end; i++) {
       UCHAR uch = static_cast<UCHAR>(source[i]);
       if (uch < 0x20 || uch > 0x7E)
         AppendUTF8EscapedChar(source, &i, end, output);
diff --git a/url/url_canon_query.cc b/url/url_canon_query.cc
index b3a1118..d23b45f 100644
--- a/url/url_canon_query.cc
+++ b/url/url_canon_query.cc
@@ -72,10 +72,12 @@
                   const Component& query,
                   CharsetConverter* converter,
                   CanonOutput* output) {
+  DCHECK(query.is_valid());
   // This function will replace any misencoded values with the invalid
   // character. This is what we want so we don't have to check for error.
   RawCanonOutputW<1024> utf16;
-  ConvertUTF8ToUTF16(&spec[query.begin], query.len, &utf16);
+  ConvertUTF8ToUTF16(&spec[query.begin], static_cast<size_t>(query.len),
+                     &utf16);
   converter->ConvertFromUTF16(utf16.data(), utf16.length(), output);
 }
 
@@ -86,7 +88,9 @@
                   const Component& query,
                   CharsetConverter* converter,
                   CanonOutput* output) {
-  converter->ConvertFromUTF16(&spec[query.begin], query.len, output);
+  DCHECK(query.is_valid());
+  converter->ConvertFromUTF16(&spec[query.begin],
+                              static_cast<size_t>(query.len), output);
 }
 
 template<typename CHAR, typename UCHAR>
@@ -109,7 +113,8 @@
 
     } else {
       // No converter, do our own UTF-8 conversion.
-      AppendStringOfType(&spec[query.begin], query.len, CHAR_QUERY, output);
+      AppendStringOfType(&spec[query.begin], static_cast<size_t>(query.len),
+                         CHAR_QUERY, output);
     }
   }
 }
diff --git a/url/url_canon_unittest.cc b/url/url_canon_unittest.cc
index f6ac9d4..96aa55a 100644
--- a/url/url_canon_unittest.cc
+++ b/url/url_canon_unittest.cc
@@ -173,9 +173,9 @@
       out_str.clear();
       StdStringCanonOutput output(&out_str);
 
-      int input_len = static_cast<int>(strlen(utf_cases[i].input8));
+      size_t input_len = strlen(utf_cases[i].input8);
       bool success = true;
-      for (int ch = 0; ch < input_len; ch++) {
+      for (size_t ch = 0; ch < input_len; ch++) {
         success &= AppendUTF8EscapedChar(utf_cases[i].input8, &ch, input_len,
                                          &output);
       }
@@ -189,9 +189,9 @@
 
       std::u16string input_str(
           test_utils::TruncateWStringToUTF16(utf_cases[i].input16));
-      int input_len = static_cast<int>(input_str.length());
+      size_t input_len = input_str.length();
       bool success = true;
-      for (int ch = 0; ch < input_len; ch++) {
+      for (size_t ch = 0; ch < input_len; ch++) {
         success &= AppendUTF8EscapedChar(input_str.c_str(), &ch, input_len,
                                          &output);
       }
diff --git a/url/url_util.cc b/url/url_util.cc
index 1f0de84..cd8e2e1 100644
--- a/url/url_util.cc
+++ b/url/url_util.cc
@@ -811,11 +811,15 @@
                               int length,
                               DecodeURLMode mode,
                               CanonOutputW* output) {
+  if (length <= 0)
+    return;
+
   STACK_UNINITIALIZED RawCanonOutputT<char> unescaped_chars;
-  for (int i = 0; i < length; i++) {
+  size_t length_size_t = static_cast<size_t>(length);
+  for (size_t i = 0; i < length_size_t; i++) {
     if (input[i] == '%') {
       unsigned char ch;
-      if (DecodeEscaped(input, &i, length, &ch)) {
+      if (DecodeEscaped(input, &i, length_size_t, &ch)) {
         unescaped_chars.push_back(ch);
       } else {
         // Invalid escape sequence, copy the percent literal.
@@ -830,18 +834,20 @@
   int output_initial_length = output->length();
   // Convert that 8-bit to UTF-16. It's not clear IE does this at all to
   // JavaScript URLs, but Firefox and Safari do.
-  for (int i = 0; i < unescaped_chars.length(); i++) {
-    unsigned char uch = static_cast<unsigned char>(unescaped_chars.at(i));
+  size_t unescaped_length = static_cast<size_t>(unescaped_chars.length());
+  for (size_t i = 0; i < unescaped_length; i++) {
+    unsigned char uch =
+        static_cast<unsigned char>(unescaped_chars.at(static_cast<int>(i)));
     if (uch < 0x80) {
       // Non-UTF-8, just append directly
       output->push_back(uch);
     } else {
       // next_ch will point to the last character of the decoded
       // character.
-      int next_character = i;
+      size_t next_character = i;
       base_icu::UChar32 code_point;
-      if (ReadUTFChar(unescaped_chars.data(), &next_character,
-                      unescaped_chars.length(), &code_point)) {
+      if (ReadUTFChar(unescaped_chars.data(), &next_character, unescaped_length,
+                      &code_point)) {
         // Valid UTF-8 character, convert to UTF-16.
         AppendUTF16Value(code_point, output);
         i = next_character;