diff --git a/DEPS b/DEPS
index 987bfbe..a7448c1 100644
--- a/DEPS
+++ b/DEPS
@@ -228,7 +228,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling Skia
   # and whatever else without interference from each other.
-  'skia_revision': 'f61ec43f84dd73508a566c36ef085156b40285f0',
+  'skia_revision': '0733d484289faa5ea51b97bd19e6afadc82ab042',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling V8
   # and whatever else without interference from each other.
@@ -240,11 +240,11 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling ANGLE
   # and whatever else without interference from each other.
-  'angle_revision': 'e3f2acb978d84b9da06e66515d2183b82fa29865',
+  'angle_revision': 'f2c968874230190cc1d7719f77b07abccd46a20a',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling SwiftShader
   # and whatever else without interference from each other.
-  'swiftshader_revision': '803ceb58c92cc4c817685efa896c8e7421a86cc2',
+  'swiftshader_revision': 'f2bf63b016782359ca581dded874bb3eda79cbce',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling PDFium
   # and whatever else without interference from each other.
@@ -275,11 +275,11 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling NaCl
   # and whatever else without interference from each other.
-  'nacl_revision': '63b3cb448cf22e589a4d125ee1829c894cb582c2',
+  'nacl_revision': '0c1b812efbe7731df5b40858689f857d80c6372b',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling freetype
   # and whatever else without interference from each other.
-  'freetype_revision': 'f44c2d586064bcccdb504bf098b9dc78e660269e',
+  'freetype_revision': 'e2cceed857f0b4f3f3fd48681d2f3009e62d1194',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling freetype
   # and whatever else without interference from each other.
@@ -307,7 +307,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling devtools-frontend
   # and whatever else without interference from each other.
-  'devtools_frontend_revision': '7f672f7f8446ca82c02ad16bc5231c7ef512dd88',
+  'devtools_frontend_revision': 'f423747da6817c98e025e0044dfe3dbb276dd004',
   # 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.
@@ -347,7 +347,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
-  'dawn_revision': '71d7c2e6707e76fbdd96087c57d34657b0103b52',
+  'dawn_revision': '41e3931b04b694dec2b5e0952e7837d9db227ea1',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
@@ -610,7 +610,7 @@
   },
 
   'src/ios/third_party/material_components_ios/src': {
-      'url': Var('chromium_git') + '/external/github.com/material-components/material-components-ios.git' + '@' + '93ccaa53c1fce39f8223f2be5ebccebc7214aa4c',
+      'url': Var('chromium_git') + '/external/github.com/material-components/material-components-ios.git' + '@' + 'da985561a1c5611756d27f6d8f2998899ee24c5c',
       'condition': 'checkout_ios',
   },
 
@@ -932,7 +932,7 @@
   },
 
   'src/third_party/breakpad/breakpad':
-    Var('chromium_git') + '/breakpad/breakpad.git' + '@' + 'b95c4868b10f69e642666742233aede1eb653012',
+    Var('chromium_git') + '/breakpad/breakpad.git' + '@' + '524a6249f0b4dc4e24d38a29a36e1c8ae611d28f',
 
   'src/third_party/byte_buddy': {
       'packages': [
@@ -983,7 +983,7 @@
   # Tools used when building Chrome for Chrome OS. This affects both the Simple
   # Chrome workflow, as well as the chromeos-chrome ebuild.
   'src/third_party/chromite': {
-      'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + '16cd88facdb0b74c17adb8498b7411315bcbc1c5',
+      'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + '4a2e809002c96046f1583b3a5c6db9660eb473c4',
       'condition': 'checkout_chromeos',
   },
 
@@ -1003,7 +1003,7 @@
   },
 
   'src/third_party/depot_tools':
-    Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + '9107458ff61fae58c97b0523714dd73211a02758',
+    Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + '67574d7a19b6dd25af19ed83d4822695169e3a22',
 
   'src/third_party/devtools-frontend/src':
     Var('chromium_git') + '/devtools/devtools-frontend' + '@' + Var('devtools_frontend_revision'),
@@ -1503,7 +1503,7 @@
       'packages': [
           {
               'package': 'chromium/third_party/r8',
-              'version': 'Nu_mvQJe34CotIXadFlA3w732CJ9EvQGuVs4udcZedAC',
+              'version': 'version:2@3.1.16',
           },
       ],
       'condition': 'checkout_android',
@@ -1621,7 +1621,7 @@
     Var('chromium_git') + '/external/github.com/gpuweb/cts.git' + '@' + 'a03fcf00c1a1c40c1dd9a0dd2c8071333b882a09',
 
   'src/third_party/webrtc':
-    Var('webrtc_git') + '/src.git' + '@' + '124889f6f9a2fcdcf54eebfea3ba2ccb7a352ea5',
+    Var('webrtc_git') + '/src.git' + '@' + 'b6f19d7dfd29209b0a45fff629c1c0ed02d3b6d7',
 
   'src/third_party/libgifcodec':
      Var('skia_git') + '/libgifcodec' + '@'+  Var('libgifcodec_revision'),
@@ -1682,7 +1682,7 @@
     Var('chromium_git') + '/v8/v8.git' + '@' +  Var('v8_revision'),
 
   'src-internal': {
-    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@aa168d0bfa1740dd9faf01381f340852655e3f18',
+    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@7807e2400514d2035c6c4d4f19c2d8bff5a9db7b',
     'condition': 'checkout_src_internal',
   },
 
@@ -1701,7 +1701,7 @@
     'packages': [
       {
         'package': 'chromeos_internal/apps/help_app/app',
-        'version': '-mUXyaeW3TjdvF9VQ0soGGdiAla8v5-nZoFd9lcPoLsC',
+        'version': 'vEd6WxCBEatoB1gAVQ1u73pT32gi5xR0RB-ikMBZ2LUC',
       },
     ],
     'condition': 'checkout_chromeos and checkout_src_internal',
diff --git a/android_webview/BUILD.gn b/android_webview/BUILD.gn
index fe9d70e2..fcdbf54d 100644
--- a/android_webview/BUILD.gn
+++ b/android_webview/BUILD.gn
@@ -774,6 +774,7 @@
   deps = [
     "//base:base_java",
     "//cc/base:cc_base_java",
+    "//components/autofill/android:autofill_features_java",
     "//components/metrics:metrics_java",
     "//components/power_scheduler:power_scheduler_java",
     "//components/viz:viz_java",
diff --git a/android_webview/glue/java/src/com/android/webview/chromium/WebViewChromiumFactoryProvider.java b/android_webview/glue/java/src/com/android/webview/chromium/WebViewChromiumFactoryProvider.java
index 51cd297..bed9ebc 100644
--- a/android_webview/glue/java/src/com/android/webview/chromium/WebViewChromiumFactoryProvider.java
+++ b/android_webview/glue/java/src/com/android/webview/chromium/WebViewChromiumFactoryProvider.java
@@ -418,12 +418,16 @@
                             actions.size());
                     RecordHistogram.recordCount100Histogram(
                             "Android.WebView.SafeMode.ActionsCount", actions.size());
-                    controller.executeActions(actions);
+                    boolean success = controller.executeActions(actions);
                     long safeModeQueryExecuteEnd = SystemClock.elapsedRealtime();
                     RecordHistogram.recordTimesHistogram(
                             "Android.WebView.SafeMode.QueryAndExecuteBlockingTime",
                             safeModeQueryExecuteEnd - safeModeQueryExecuteStart);
-                    logSafeModeExecutionResult(SafeModeExecutionResult.SUCCESS);
+                    if (success) {
+                        logSafeModeExecutionResult(SafeModeExecutionResult.SUCCESS);
+                    } else {
+                        logSafeModeExecutionResult(SafeModeExecutionResult.ACTION_FAILED);
+                    }
                 } catch (Throwable t) {
                     // Don't let SafeMode crash WebView. Instead just log the error.
                     Log.e(TAG, "WebViewSafeMode threw exception: ", t);
@@ -460,11 +464,13 @@
 
     // These values are persisted to logs. Entries should not be renumbered and
     // numeric values should never be reused.
-    @IntDef({SafeModeExecutionResult.SUCCESS, SafeModeExecutionResult.UNKNOWN_ERROR})
+    @IntDef({SafeModeExecutionResult.SUCCESS, SafeModeExecutionResult.UNKNOWN_ERROR,
+            SafeModeExecutionResult.ACTION_FAILED})
     private @interface SafeModeExecutionResult {
         int SUCCESS = 0;
         int UNKNOWN_ERROR = 1;
-        int COUNT = 2;
+        int ACTION_FAILED = 2;
+        int COUNT = 3;
     }
 
     private static void logSafeModeExecutionResult(@SafeModeExecutionResult int result) {
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 dc25dbb..98f01f8 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
@@ -7,6 +7,7 @@
 import org.chromium.base.BaseSwitches;
 import org.chromium.blink_public.common.BlinkFeatures;
 import org.chromium.cc.base.CcSwitches;
+import org.chromium.components.autofill.AutofillFeatures;
 import org.chromium.components.metrics.MetricsSwitches;
 import org.chromium.components.power_scheduler.PowerSchedulerFeatures;
 import org.chromium.components.viz.common.VizFeatures;
@@ -141,6 +142,13 @@
             Flag.baseFeature(BlinkFeatures.GMS_CORE_EMOJI,
                     "Enables retrieval of the emoji font through GMS Core "
                             + "improving emoji glyph coverage."),
+            Flag.baseFeature(AutofillFeatures.AUTOFILL_ENABLE_SUPPORT_FOR_MORE_STRUCTURE_IN_NAMES,
+                    "Enables support for names with a rich structure including multiple last "
+                            + "names."),
+            Flag.baseFeature(
+                    AutofillFeatures.AUTOFILL_ENABLE_SUPPORT_FOR_MORE_STRUCTURE_IN_ADDRESSES,
+                    "Enables support for address with a rich structure including separate street "
+                            + "names and house numberse."),
             Flag.baseFeature(
                     NetworkServiceFeatures.TRUST_TOKENS, "Enables the prototype Trust Tokens API."),
             Flag.baseFeature(AwFeatures.WEBVIEW_APPS_PACKAGE_NAMES_ALLOWLIST,
diff --git a/android_webview/java/src/org/chromium/android_webview/common/SafeModeAction.java b/android_webview/java/src/org/chromium/android_webview/common/SafeModeAction.java
index adf048e..5e41eed8 100644
--- a/android_webview/java/src/org/chromium/android_webview/common/SafeModeAction.java
+++ b/android_webview/java/src/org/chromium/android_webview/common/SafeModeAction.java
@@ -18,8 +18,11 @@
     public String getId();
 
     /**
-     * Executes the given action. Implementations of this method should be  Java-only (no JNI/C++)
-     * because the native library may not yet be loaded.
+     * Executes the given action. Implementations of this method should be Java-only (no JNI/C++)
+     * because the native library may not yet be loaded. The return status is used for logging
+     * purposes only.
+     *
+     * @return {@code true} if the action succeeded, {@code false} otherwise.
      */
-    void execute();
+    boolean execute();
 }
diff --git a/android_webview/java/src/org/chromium/android_webview/common/SafeModeController.java b/android_webview/java/src/org/chromium/android_webview/common/SafeModeController.java
index 3789920..b7f14981 100644
--- a/android_webview/java/src/org/chromium/android_webview/common/SafeModeController.java
+++ b/android_webview/java/src/org/chromium/android_webview/common/SafeModeController.java
@@ -118,24 +118,32 @@
      * Executes the given set of {@link SafeModeAction}s. Execution order is determined by the order
      * of the array registered by {@link registerActions}.
      *
+     * @return {@code true} if <b>all</b> actions succeeded, {@code false} otherwise.
      * @throws IllegalStateException if this is called before {@link registerActions}.
      */
-    public void executeActions(Set<String> actionsToExecute) {
+    public boolean executeActions(Set<String> actionsToExecute) {
         // Execute SafeModeActions in a deterministic order.
         if (mRegisteredActions == null) {
             throw new IllegalStateException(
                     "Must registerActions() before calling executeActions()");
         }
+        boolean overallStatus = true;
         for (SafeModeAction action : mRegisteredActions) {
             if (actionsToExecute.contains(action.getId())) {
                 // Allow SafeModeActions in general to perform disk reads and writes.
                 try (StrictModeContext ignored = StrictModeContext.allowDiskWrites()) {
                     Log.i(TAG, "Starting to execute %s", action.getId());
-                    action.execute();
-                    Log.i(TAG, "Finished executing %s", action.getId());
+                    boolean status = action.execute();
+                    overallStatus &= status;
+                    if (status) {
+                        Log.i(TAG, "Finished executing %s (%s)", action.getId(), "success");
+                    } else {
+                        Log.e(TAG, "Finished executing %s (%s)", action.getId(), "failure");
+                    }
                 }
             }
         }
+        return overallStatus;
     }
 
     /**
diff --git a/android_webview/java/src/org/chromium/android_webview/variations/VariationsSeedSafeModeAction.java b/android_webview/java/src/org/chromium/android_webview/variations/VariationsSeedSafeModeAction.java
index 5e58b47..3b1787a 100644
--- a/android_webview/java/src/org/chromium/android_webview/variations/VariationsSeedSafeModeAction.java
+++ b/android_webview/java/src/org/chromium/android_webview/variations/VariationsSeedSafeModeAction.java
@@ -28,21 +28,27 @@
     }
 
     @Override
-    public void execute() {
-        deleteIfExists(VariationsUtils.getSeedFile());
-        deleteIfExists(VariationsUtils.getNewSeedFile());
-        deleteIfExists(VariationsUtils.getStampFile());
+    public boolean execute() {
+        boolean success = true;
+        // Try deleting each file even if a previous step failed, but indicate the overall success
+        // of all steps.
+        success &= deleteIfExists(VariationsUtils.getSeedFile());
+        success &= deleteIfExists(VariationsUtils.getNewSeedFile());
+        success &= deleteIfExists(VariationsUtils.getStampFile());
+        return success;
     }
 
-    private static void deleteIfExists(File file) {
+    private static boolean deleteIfExists(File file) {
         if (!file.exists()) {
             Log.i(TAG, "File does not exist (skipping): %s", file);
-            return;
+            return true;
         }
         if (file.delete()) {
             Log.i(TAG, "Successfully deleted %s", file);
+            return true;
         } else {
             Log.e(TAG, "Failed to delete %s", file);
+            return false;
         }
     }
 }
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/SafeModeTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/SafeModeTest.java
index 138039d..c5681f0 100644
--- a/android_webview/javatests/src/org/chromium/android_webview/test/SafeModeTest.java
+++ b/android_webview/javatests/src/org/chromium/android_webview/test/SafeModeTest.java
@@ -287,9 +287,15 @@
         private int mCallCount;
         private int mExecutionOrder;
         private final String mId;
+        private final boolean mSuccess;
 
         TestSafeModeAction(String id) {
+            this(id, true);
+        }
+
+        TestSafeModeAction(String id, boolean success) {
             mId = id;
+            mSuccess = success;
         }
 
         @Override
@@ -299,9 +305,10 @@
         }
 
         @Override
-        public void execute() {
+        public boolean execute() {
             mCallCount++;
             mExecutionOrder = mTestSafeModeActionExecutionCounter.incrementAndGet();
+            return mSuccess;
         }
 
         public int getCallCount() {
@@ -449,6 +456,42 @@
     }
 
     @Test
+    @SmallTest
+    @Feature({"AndroidWebView"})
+    public void testSafeModeAction_overallSuccessStatus() throws Throwable {
+        TestSafeModeAction successAction1 = new TestSafeModeAction("successAction1");
+        TestSafeModeAction successAction2 = new TestSafeModeAction("successAction2");
+        Set<String> allSuccessful = asSet(successAction1.getId(), successAction2.getId());
+        SafeModeController.getInstance().registerActions(
+                new SafeModeAction[] {successAction1, successAction2});
+        boolean success = SafeModeController.getInstance().executeActions(allSuccessful);
+        Assert.assertTrue(
+                "Overall status should be successful if all actions are successful", success);
+        Assert.assertEquals("successAction1 should have been executed exactly 1 time", 1,
+                successAction1.getCallCount());
+        Assert.assertEquals("successAction2 should have been executed exactly 1 time", 1,
+                successAction2.getCallCount());
+
+        // Register a new set of actions where at least one indicates failure.
+        SafeModeController.getInstance().unregisterActionsForTesting();
+        TestSafeModeAction failAction = new TestSafeModeAction("failAction", false);
+        Set<String> oneFailure =
+                asSet(successAction1.getId(), failAction.getId(), successAction2.getId());
+        SafeModeController.getInstance().registerActions(
+                new SafeModeAction[] {successAction1, failAction, successAction2});
+        success = SafeModeController.getInstance().executeActions(oneFailure);
+        Assert.assertFalse(
+                "Overall status should be failure if at least one action fails", success);
+        // One step failing should not block subsequent steps from executing.
+        Assert.assertEquals(
+                "successAction1 should have been executed again", 2, successAction1.getCallCount());
+        Assert.assertEquals("failAction should have been executed exactly 1 time", 1,
+                failAction.getCallCount());
+        Assert.assertEquals(
+                "successAction2 should have been executed again", 2, successAction2.getCallCount());
+    }
+
+    @Test
     @MediumTest
     public void testSafeModeAction_canRegisterBrowserActions() throws Exception {
         // Validity check: verify we can register the production SafeModeAction list. As long as
@@ -468,7 +511,8 @@
             VariationsTestUtils.writeMockSeed(oldFile);
             VariationsTestUtils.writeMockSeed(newFile);
             VariationsSeedSafeModeAction action = new VariationsSeedSafeModeAction();
-            action.execute();
+            boolean success = action.execute();
+            Assert.assertTrue("VariationsSeedSafeModeAction should indicate success", success);
             Assert.assertFalse(
                     "Old seed should have been deleted but it still exists", oldFile.exists());
             Assert.assertFalse(
@@ -489,7 +533,8 @@
             VariationsTestUtils.writeMockSeed(oldFile);
             VariationsTestUtils.writeMockSeed(newFile);
             VariationsSeedSafeModeAction action = new VariationsSeedSafeModeAction();
-            action.execute();
+            boolean success = action.execute();
+            Assert.assertTrue("VariationsSeedSafeModeAction should indicate success", success);
 
             TestLoader loader = new TestLoader(new TestLoaderResult());
             loader.startVariationsInit();
@@ -509,7 +554,8 @@
             File oldFile = VariationsUtils.getSeedFile();
             File newFile = VariationsUtils.getNewSeedFile();
             VariationsSeedSafeModeAction action = new VariationsSeedSafeModeAction();
-            action.execute();
+            boolean success = action.execute();
+            Assert.assertTrue("VariationsSeedSafeModeAction should indicate success", success);
             Assert.assertFalse("Old seed should never have existed", oldFile.exists());
             Assert.assertFalse("New seed should never have existed", newFile.exists());
         } finally {
diff --git a/android_webview/nonembedded/java/src/org/chromium/android_webview/nonembedded/ComponentUpdaterResetSafeModeAction.java b/android_webview/nonembedded/java/src/org/chromium/android_webview/nonembedded/ComponentUpdaterResetSafeModeAction.java
index 9348979..d21f88a6 100644
--- a/android_webview/nonembedded/java/src/org/chromium/android_webview/nonembedded/ComponentUpdaterResetSafeModeAction.java
+++ b/android_webview/nonembedded/java/src/org/chromium/android_webview/nonembedded/ComponentUpdaterResetSafeModeAction.java
@@ -24,8 +24,9 @@
     }
 
     @Override
-    public void execute() {
+    public boolean execute() {
         // Each Component Updater Service will take requisite steps on startup.
         // Do nothing here.
+        return true;
     }
 }
diff --git a/ash/BUILD.gn b/ash/BUILD.gn
index dfcfa8e..0e76b55 100644
--- a/ash/BUILD.gn
+++ b/ash/BUILD.gn
@@ -2554,6 +2554,7 @@
     "//components/arc:notification_test_support",
     "//components/full_restore",
     "//components/media_message_center",
+    "//components/onc",
     "//components/password_manager/core/browser:hash_password_manager",
     "//components/prefs:test_support",
     "//components/quirks",
diff --git a/ash/accelerators/accelerator_controller_impl.cc b/ash/accelerators/accelerator_controller_impl.cc
index 4ecf67ef..b6f555e6 100644
--- a/ash/accelerators/accelerator_controller_impl.cc
+++ b/ash/accelerators/accelerator_controller_impl.cc
@@ -2341,8 +2341,6 @@
       break;
   }
 
-  NotifyActionPerformed(action);
-
   // Reset any in progress composition.
   if (::features::IsImprovedKeyboardShortcutsEnabled()) {
     auto* input_method =
diff --git a/ash/accelerators/accelerator_controller_unittest.cc b/ash/accelerators/accelerator_controller_unittest.cc
index 6cebdd8..b7a7a4a 100644
--- a/ash/accelerators/accelerator_controller_unittest.cc
+++ b/ash/accelerators/accelerator_controller_unittest.cc
@@ -252,13 +252,6 @@
               (override));
 };
 
-class MockAcceleratorObserver
-    : public testing::NiceMock<AcceleratorController::Observer> {
- public:
-  // AcceleratorController::Observer:
-  MOCK_METHOD(void, OnActionPerformed, (AcceleratorAction action), (override));
-};
-
 }  // namespace
 
 // Note AcceleratorControllerTest can't be in the anonymous namespace because
@@ -2112,10 +2105,6 @@
 }
 
 TEST_F(AcceleratorControllerTest, CalculatorKey) {
-  auto observer = std::make_unique<MockAcceleratorObserver>();
-  auto* accelerator_controller = ash::AcceleratorController::Get();
-  accelerator_controller->AddObserver(observer.get());
-
   // Verify that the launch calculator key (VKEY_MEDIA_LAUNCH_APP2) is
   // registered.
   ui::Accelerator accelerator(ui::VKEY_MEDIA_LAUNCH_APP2, ui::EF_NONE);
@@ -2124,9 +2113,6 @@
   // Verify that the delegate to open the app is called.
   EXPECT_CALL(*new_window_delegate_, OpenCalculator)
       .WillOnce(testing::Return());
-  EXPECT_CALL(*observer, OnActionPerformed)
-      .WillOnce(
-          [](AcceleratorAction action) { EXPECT_EQ(OPEN_CALCULATOR, action); });
   EXPECT_TRUE(ProcessInController(accelerator));
 }
 
diff --git a/ash/constants/ash_features.cc b/ash/constants/ash_features.cc
index 187ecbd..1cb86916 100644
--- a/ash/constants/ash_features.cc
+++ b/ash/constants/ash_features.cc
@@ -726,7 +726,7 @@
 // minor mode(e.g. under the age of 18) to provide unnecessary consents to share
 // personal data.
 const base::Feature kMinorModeRestriction{"MinorModeRestriction",
-                                          base::FEATURE_DISABLED_BY_DEFAULT};
+                                          base::FEATURE_ENABLED_BY_DEFAULT};
 
 // Enables the use of Mojo by Chrome-process code to communicate with Power
 // Manager. In order to use mojo, this feature must be turned on and a callsite
diff --git a/ash/keyboard/keyboard_controller_impl.cc b/ash/keyboard/keyboard_controller_impl.cc
index b67461e..f25dc84 100644
--- a/ash/keyboard/keyboard_controller_impl.cc
+++ b/ash/keyboard/keyboard_controller_impl.cc
@@ -73,8 +73,7 @@
   if (!features)
     return false;
 
-  bool feature_value = false;
-  return features->GetBoolean(feature_path, &feature_value) && feature_value;
+  return features->FindBoolPath(feature_path).value_or(false);
 }
 
 }  // namespace
diff --git a/ash/login/ui/lock_screen_media_controls_view.cc b/ash/login/ui/lock_screen_media_controls_view.cc
index 5049e865..c73870d 100644
--- a/ash/login/ui/lock_screen_media_controls_view.cc
+++ b/ash/login/ui/lock_screen_media_controls_view.cc
@@ -134,6 +134,7 @@
     case MediaSessionAction::kToggleCamera:
     case MediaSessionAction::kHangUp:
     case MediaSessionAction::kRaise:
+    case MediaSessionAction::kSetMute:
       NOTREACHED();
       break;
   }
diff --git a/ash/public/cpp/accelerators.cc b/ash/public/cpp/accelerators.cc
index 8c247ec..2d27b38 100644
--- a/ash/public/cpp/accelerators.cc
+++ b/ash/public/cpp/accelerators.cc
@@ -325,14 +325,6 @@
     GetVolumeAdjustmentCallback()->Run();
 }
 
-void AcceleratorController::AddObserver(Observer* observer) {
-  observers_.AddObserver(observer);
-}
-
-void AcceleratorController::RemoveObserver(Observer* observer) {
-  observers_.RemoveObserver(observer);
-}
-
 AcceleratorController::AcceleratorController() {
   DCHECK_EQ(nullptr, g_instance);
   g_instance = this;
@@ -343,9 +335,4 @@
   g_instance = nullptr;
 }
 
-void AcceleratorController::NotifyActionPerformed(AcceleratorAction action) {
-  for (Observer& observer : observers_)
-    observer.OnActionPerformed(action);
-}
-
 }  // namespace ash
diff --git a/ash/public/cpp/accelerators.h b/ash/public/cpp/accelerators.h
index b97d8bda..bd57004 100644
--- a/ash/public/cpp/accelerators.h
+++ b/ash/public/cpp/accelerators.h
@@ -9,7 +9,6 @@
 
 #include "ash/public/cpp/ash_public_export.h"
 #include "base/callback_forward.h"
-#include "base/observer_list.h"
 #include "ui/events/event_constants.h"
 #include "ui/events/keycodes/keyboard_codes.h"
 
@@ -186,12 +185,6 @@
 // implement.
 class ASH_PUBLIC_EXPORT AcceleratorController {
  public:
-  class Observer : public base::CheckedObserver {
-   public:
-    // Invoked when `action` is performed.
-    virtual void OnActionPerformed(AcceleratorAction action) = 0;
-  };
-
   // Returns the singleton instance.
   static AcceleratorController* Get();
 
@@ -235,15 +228,9 @@
   virtual bool DoesAcceleratorMatchAction(const ui::Accelerator& accelerator,
                                           const AcceleratorAction action) = 0;
 
-  void AddObserver(Observer* observer);
-  void RemoveObserver(Observer* observer);
-
  protected:
   AcceleratorController();
   virtual ~AcceleratorController();
-  void NotifyActionPerformed(AcceleratorAction action);
-
-  base::ObserverList<Observer> observers_;
 };
 
 // The public facing interface for AcceleratorHistory, which is implemented in
diff --git a/ash/quick_pair/common/log_buffer.cc b/ash/quick_pair/common/log_buffer.cc
index fbe7dd4..63c923c 100644
--- a/ash/quick_pair/common/log_buffer.cc
+++ b/ash/quick_pair/common/log_buffer.cc
@@ -4,7 +4,7 @@
 
 #include "ash/quick_pair/common/log_buffer.h"
 
-#include "base/lazy_instance.h"
+#include "base/no_destructor.h"
 
 namespace ash {
 namespace quick_pair {
@@ -14,9 +14,6 @@
 // The maximum number of logs that can be stored in the buffer.
 const size_t kMaxBufferSize = 1000;
 
-// The global instance returned by LogBuffer::GetInstance().
-base::LazyInstance<LogBuffer>::Leaky g_log_buffer = LAZY_INSTANCE_INITIALIZER;
-
 }  // namespace
 
 LogBuffer::LogMessage::LogMessage(const std::string& text,
@@ -28,7 +25,8 @@
 
 // static
 LogBuffer* LogBuffer::GetInstance() {
-  return &g_log_buffer.Get();
+  static base::NoDestructor<LogBuffer> log_buffer;
+  return log_buffer.get();
 }
 
 LogBuffer::LogBuffer() {}
diff --git a/ash/system/media/unified_media_controls_view.cc b/ash/system/media/unified_media_controls_view.cc
index 87f2201..80faac0 100644
--- a/ash/system/media/unified_media_controls_view.cc
+++ b/ash/system/media/unified_media_controls_view.cc
@@ -90,6 +90,7 @@
     case MediaSessionAction::kToggleCamera:
     case MediaSessionAction::kHangUp:
     case MediaSessionAction::kRaise:
+    case MediaSessionAction::kSetMute:
       NOTREACHED();
       break;
   }
diff --git a/ash/system/message_center/unified_message_list_view.cc b/ash/system/message_center/unified_message_list_view.cc
index e8e8cc8b..9ed381b 100644
--- a/ash/system/message_center/unified_message_list_view.cc
+++ b/ash/system/message_center/unified_message_list_view.cc
@@ -415,6 +415,13 @@
       ->set_notification_id(new_grouped_notification_id);
 }
 
+void UnifiedMessageListView::ConvertGroupedNotificationViewToNotificationView(
+    const std::string& grouped_notification_id,
+    const std::string& new_single_notification_id) {
+  GetMessageViewForNotificationId(grouped_notification_id)
+      ->set_notification_id(new_single_notification_id);
+}
+
 void UnifiedMessageListView::OnNotificationAdded(const std::string& id) {
   if (ash::features::IsNotificationsRefreshEnabled())
     message_center::NotificationViewController::OnNotificationAdded(id);
diff --git a/ash/system/message_center/unified_message_list_view.h b/ash/system/message_center/unified_message_list_view.h
index adb33a5..c65c4c2 100644
--- a/ash/system/message_center/unified_message_list_view.h
+++ b/ash/system/message_center/unified_message_list_view.h
@@ -91,6 +91,9 @@
   void ConvertNotificationViewToGroupedNotificationView(
       const std::string& ungrouped_notification_id,
       const std::string& new_grouped_notification_id) override;
+  void ConvertGroupedNotificationViewToNotificationView(
+      const std::string& grouped_notification_id,
+      const std::string& new_single_notification_id) override;
   void OnNotificationAdded(const std::string& id) override;
   void OnNotificationRemoved(const std::string& id, bool by_user) override;
   void OnNotificationUpdated(const std::string& id) override;
diff --git a/ash/webui/common/resources/navigation_view_panel.html b/ash/webui/common/resources/navigation_view_panel.html
index fac17f7..a98fe4e 100644
--- a/ash/webui/common/resources/navigation_view_panel.html
+++ b/ash/webui/common/resources/navigation_view_panel.html
@@ -5,26 +5,61 @@
     position: fixed;
   }
 
-  #container {
-    align-items: flex-start;
+  :host([show-tool-bar]) #container {
+    display: grid;
+    grid-template-areas: "head head" "sideNav main";
+    grid-template-columns: 1fr 4fr;
+    grid-template-rows: 1fr 9fr;
+    width: 100%;
+  }
+
+  :host(:not([show-tool-bar])) #container {
+    display: grid;
+    grid-template-areas: "sideNav main";
+    grid-template-columns: 1fr 4fr;
+  }
+
+  :host([show-tool-bar]) #topNav {
+    grid-area: head;
+    position: sticky;
+    top: 0;
+  }
+
+  #sideNav {
+    grid-area: sideNav;
     display: flex;
-    flex: 1;
-    position: relative;
+    flex-direction: column;
+    justify-content: flex-start;
+  }
+
+  #navigationBody {
+    grid-area: main;
   }
 
   .left-aligned {
-    height: 100%;
     position: sticky;
     top: 75px;
   }
+
+  page-toolbar {
+    font-size: 0.7rem;
+  }
 </style>
 
 <div id="container">
-  <div id="navigationSelector" class="left-aligned">
-    <navigation-selector menu-items={{menuItems_}}
-        selected-item={{selectedItem}}>
-    </navigation-selector>
-    <slot name="bottom-nav-content"></slot>
+  <template is="dom-if" if="[[showToolBar]]">
+    <div id="topNav">
+      <page-toolbar title="[[title]]"></page-toolbar>
+    </div>
+  </template>
+
+  <div id="sideNav">
+    <div id="navigationSelector" class="left-aligned">
+      <navigation-selector menu-items={{menuItems_}}
+          selected-item={{selectedItem}}>
+      </navigation-selector>
+      <slot name="bottom-nav-content"></slot>
+    </div>
   </div>
 
   <div id="navigationBody" class="right-aligned"></div>
diff --git a/ash/webui/common/resources/navigation_view_panel.js b/ash/webui/common/resources/navigation_view_panel.js
index 64dfa6a..0a370a9 100644
--- a/ash/webui/common/resources/navigation_view_panel.js
+++ b/ash/webui/common/resources/navigation_view_panel.js
@@ -2,6 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+import './page_toolbar.js';
+
 import {assert} from 'chrome://resources/js/assert.m.js';
 import {html, PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 
@@ -43,7 +45,7 @@
        */
       selectedItem: {
         type: Object,
-        observer: "selectedItemChanged_",
+        observer: 'selectedItemChanged_',
         value: null,
       },
 
@@ -54,7 +56,28 @@
       menuItems_: {
         type: Array,
         value: () => [],
-      }
+      },
+
+      /**
+       * This title only appears if |showToolBar| is True. Is otherwise a
+       * no-opt if title is set and |showToolbar| is False.
+       */
+      title: {
+        type: String,
+        value: '',
+      },
+
+      /**
+       * Can only be set to True if specified from the parent element by adding
+       * show-tool-bar as an attribute to <navigation-view-panel>. If True,
+       * a toolbar will appear at the top of the navigation view panel with
+       * a 2 column view below it (sidebar + page). If False, navigation view
+       * panel will only be a 2 column view (sidebar + page).
+       */
+      showToolBar: {
+        type: Boolean,
+        value: false,
+      },
     }
   }
 
diff --git a/ash/webui/diagnostics_ui/resources/diagnostics_app.html b/ash/webui/diagnostics_ui/resources/diagnostics_app.html
index e89e1ebc..5db2e438 100644
--- a/ash/webui/diagnostics_ui/resources/diagnostics_app.html
+++ b/ash/webui/diagnostics_ui/resources/diagnostics_app.html
@@ -18,8 +18,8 @@
 </style>
 <div id="diagnosticsAppContainer">
   <div hidden$="[[!showNavPanel_]]">
-    <page-toolbar title="[[i18n('diagnosticsTitle')]]"></page-toolbar>
-    <navigation-view-panel id="navigationPanel">
+    <navigation-view-panel id="navigationPanel"
+        title="[[i18n('diagnosticsTitle')]]" show-tool-bar>
       <div slot="bottom-nav-content" class="session-log-container">
         <cr-button on-click="onSessionLogClick_" class="session-log-button"
             hidden="[[!isLoggedIn_]]">
diff --git a/ash/webui/diagnostics_ui/resources/diagnostics_shared_css.html b/ash/webui/diagnostics_ui/resources/diagnostics_shared_css.html
index 9da66b4..8207e0f 100644
--- a/ash/webui/diagnostics_ui/resources/diagnostics_shared_css.html
+++ b/ash/webui/diagnostics_ui/resources/diagnostics_shared_css.html
@@ -54,7 +54,6 @@
       align-items: center;
       display: flex;
       flex-direction: column;
-      padding-inline: var(--container-padding);
       width: var(--content-container-width);
     }
 
diff --git a/ash/webui/scanning/scanning_handler.cc b/ash/webui/scanning/scanning_handler.cc
index 1fbb084..b159c24 100644
--- a/ash/webui/scanning/scanning_handler.cc
+++ b/ash/webui/scanning/scanning_handler.cc
@@ -11,6 +11,9 @@
 #include "base/check_op.h"
 #include "base/feature_list.h"
 #include "base/files/file_util.h"
+#include "base/sequenced_task_runner.h"
+#include "base/task/task_traits.h"
+#include "base/task/thread_pool.h"
 #include "base/values.h"
 #include "chromeos/strings/grit/chromeos_strings.h"
 #include "content/public/browser/web_contents.h"
@@ -29,7 +32,10 @@
 
 ScanningHandler::ScanningHandler(
     std::unique_ptr<ScanningAppDelegate> scanning_app_delegate)
-    : scanning_app_delegate_(std::move(scanning_app_delegate)) {}
+    : scanning_app_delegate_(std::move(scanning_app_delegate)),
+      task_runner_(base::ThreadPool::CreateSequencedTaskRunner(
+          {base::MayBlock(), base::TaskPriority::USER_VISIBLE,
+           base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN})) {}
 
 ScanningHandler::~ScanningHandler() = default;
 
@@ -230,13 +236,22 @@
   const std::string callback = args->GetList()[0].GetString();
   const base::FilePath file_path(args->GetList()[1].GetString());
 
+  task_runner_->PostTaskAndReplyWithResult(
+      FROM_HERE, base::BindOnce(&base::PathExists, file_path),
+      base::BindOnce(&ScanningHandler::OnPathExists, base::Unretained(this),
+                     file_path, callback));
+}
+
+void ScanningHandler::OnPathExists(const base::FilePath& file_path,
+                                   const std::string& callback,
+                                   bool file_path_exists) {
   // When |file_path| is not valid, return a dictionary with an empty file path.
-  const bool filePathValid =
-      scanning_app_delegate_->IsFilePathSupported(file_path) &&
-      base::PathExists(file_path);
+  const bool file_path_valid =
+      file_path_exists &&
+      scanning_app_delegate_->IsFilePathSupported(file_path);
   ResolveJavascriptCallback(
       base::Value(callback),
-      CreateSelectedPathValue(filePathValid ? file_path : base::FilePath()));
+      CreateSelectedPathValue(file_path_valid ? file_path : base::FilePath()));
 }
 
 }  // namespace ash
diff --git a/ash/webui/scanning/scanning_handler.h b/ash/webui/scanning/scanning_handler.h
index 2248ff2..416b055 100644
--- a/ash/webui/scanning/scanning_handler.h
+++ b/ash/webui/scanning/scanning_handler.h
@@ -17,6 +17,7 @@
 
 namespace base {
 class ListValue;
+class SequencedTaskRunner;
 }  // namespace base
 
 namespace content {
@@ -87,6 +88,11 @@
   // display name. If the file path doesn't exist, return an empty file path.
   void HandleEnsureValidFilePath(const base::ListValue* args);
 
+  // Callback for HandleEnsureValidFilePath().
+  void OnPathExists(const base::FilePath& file_path,
+                    const std::string& callback,
+                    bool file_path_exists);
+
   std::string scan_location_callback_id_;
 
   scoped_refptr<ui::SelectFileDialog> select_file_dialog_;
@@ -95,6 +101,9 @@
   std::unique_ptr<ScanningAppDelegate> scanning_app_delegate_;
 
   std::map<std::string, int> string_id_map_;
+
+  // Task runner for the I/O function base::FilePath().
+  scoped_refptr<base::SequencedTaskRunner> task_runner_;
 };
 
 }  // namespace ash
diff --git a/ash/webui/scanning/scanning_handler_unittest.cc b/ash/webui/scanning/scanning_handler_unittest.cc
index 73b88275..39ed3df 100644
--- a/ash/webui/scanning/scanning_handler_unittest.cc
+++ b/ash/webui/scanning/scanning_handler_unittest.cc
@@ -356,6 +356,7 @@
   args.Append(kHandlerFunctionName);
   args.Append(myScanPath.value());
   web_ui_.HandleReceivedMessage("ensureValidFilePath", &args);
+  task_environment_.RunUntilIdle();
 
   const content::TestWebUI::CallData& call_data =
       GetCallData(call_data_count_before_call);
@@ -376,6 +377,7 @@
   args.Append(kHandlerFunctionName);
   args.Append(invalidFilePath);
   web_ui_.HandleReceivedMessage("ensureValidFilePath", &args);
+  task_environment_.RunUntilIdle();
 
   const content::TestWebUI::CallData& call_data =
       GetCallData(call_data_count_before_call);
diff --git a/ash/webui/shortcut_customization_ui/resources/shortcut_customization_app.html b/ash/webui/shortcut_customization_ui/resources/shortcut_customization_app.html
index 8475727..ec1fb405 100644
--- a/ash/webui/shortcut_customization_ui/resources/shortcut_customization_app.html
+++ b/ash/webui/shortcut_customization_ui/resources/shortcut_customization_app.html
@@ -2,14 +2,11 @@
   :host {
     @apply --shortcut-customization-default-font;
   }
-
-  page-toolbar {
-    font-size: 0.7rem;
-  }
 </style>
 <!-- TODO(jimmyxgong): Localize this title -->
-<page-toolbar title="Shortcut Customization"></page-toolbar>
-<navigation-view-panel id="navigationPanel"></navigation-view-panel>
+<navigation-view-panel id="navigationPanel" title="Shortcut Customization"
+    show-tool-bar>
+</navigation-view-panel>
 
 <template is="dom-if" if="[[showEditDialog_]]" restamp>
   <accelerator-edit-dialog id="editDialog"
diff --git a/ash/wm/toplevel_window_event_handler.cc b/ash/wm/toplevel_window_event_handler.cc
index 43e8764e..37eada04 100644
--- a/ash/wm/toplevel_window_event_handler.cc
+++ b/ash/wm/toplevel_window_event_handler.cc
@@ -322,7 +322,8 @@
   if (window_resizer_ && !in_gesture_drag_)
     return;
 
-  if (window_resizer_ && window_resizer_->resizer()->GetTarget() != target)
+  if (window_resizer_ && window_resizer_->resizer()->GetTarget() != target &&
+      !target->bounds().IsEmpty())
     return;
 
   if (event->details().touch_points() > 2) {
diff --git a/base/allocator/partition_allocator/extended_api.cc b/base/allocator/partition_allocator/extended_api.cc
index 67f2255..0321c3b2a 100644
--- a/base/allocator/partition_allocator/extended_api.cc
+++ b/base/allocator/partition_allocator/extended_api.cc
@@ -6,14 +6,11 @@
 
 #include "base/allocator/allocator_shim_default_dispatch_to_partition_alloc.h"
 #include "base/allocator/buildflags.h"
-#include "base/allocator/partition_allocator/partition_root.h"
 #include "base/allocator/partition_allocator/thread_cache.h"
 
 namespace base {
 
 namespace internal {
-
-#if BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC)
 void DisableThreadCacheForRootIfEnabled(ThreadSafePartitionRoot* root) {
   // Some platforms don't have a thread cache, or it could already have been
   // disabled.
@@ -26,10 +23,40 @@
   // will be collected (and free cached memory) at thread destruction
   // time. For the main thread, we leak it.
 }
-#endif  // BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC)
+
+void EnablePartitionAllocThreadCacheForRootIfDisabled(
+    ThreadSafePartitionRoot* root) {
+  if (!root)
+    return;
+  root->with_thread_cache = true;
+}
 
 }  // namespace internal
 
+void SwapOutProcessThreadCacheForTesting(ThreadSafePartitionRoot* root) {
+#if BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC) && \
+    defined(PA_THREAD_CACHE_SUPPORTED)
+  DisablePartitionAllocThreadCacheForProcess();
+  internal::ThreadCache::SwapForTesting(root);
+  internal::EnablePartitionAllocThreadCacheForRootIfDisabled(root);
+#endif  // BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC) &&
+        // defined(PA_THREAD_CACHE_SUPPORTED)
+}
+
+void SwapInProcessThreadCacheForTesting(ThreadSafePartitionRoot* root) {
+#if BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC) && \
+    defined(PA_THREAD_CACHE_SUPPORTED)
+  // First, disable the test thread cache we have.
+  internal::DisableThreadCacheForRootIfEnabled(root);
+
+  auto* regular_allocator = base::internal::PartitionAllocMalloc::Allocator();
+  internal::EnablePartitionAllocThreadCacheForRootIfDisabled(regular_allocator);
+
+  internal::ThreadCache::SwapForTesting(regular_allocator);
+#endif  // BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC) &&
+        // defined(PA_THREAD_CACHE_SUPPORTED)
+}
+
 void DisablePartitionAllocThreadCacheForProcess() {
 #if BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC)
   auto* regular_allocator = base::internal::PartitionAllocMalloc::Allocator();
diff --git a/base/allocator/partition_allocator/extended_api.h b/base/allocator/partition_allocator/extended_api.h
index b8aeb78..5513e4c 100644
--- a/base/allocator/partition_allocator/extended_api.h
+++ b/base/allocator/partition_allocator/extended_api.h
@@ -5,9 +5,23 @@
 #ifndef BASE_ALLOCATOR_PARTITION_ALLOCATOR_EXTENDED_API_H_
 #define BASE_ALLOCATOR_PARTITION_ALLOCATOR_EXTENDED_API_H_
 
+#include "base/allocator/partition_allocator/partition_root.h"
+#include "base/allocator/partition_allocator/thread_cache.h"
 #include "base/base_export.h"
 
 namespace base {
+// These two functions are unsafe to run if there are multiple threads running
+// in the process.
+//
+// Disables the thread cache for the entire process, and replaces it with a
+// thread cache for |root|.
+BASE_EXPORT void SwapOutProcessThreadCacheForTesting(
+    ThreadSafePartitionRoot* root);
+// Disables the current thread cache, and replaces it with the default for the
+// process.
+BASE_EXPORT void SwapInProcessThreadCacheForTesting(
+    ThreadSafePartitionRoot* root);
+
 // Disables the thread cache for the entire process.
 //
 // Saves memory but slows down the allocator *significantly*. Only use for
diff --git a/base/allocator/partition_allocator/thread_cache.cc b/base/allocator/partition_allocator/thread_cache.cc
index ff28466..4ed0f399 100644
--- a/base/allocator/partition_allocator/thread_cache.cc
+++ b/base/allocator/partition_allocator/thread_cache.cc
@@ -303,6 +303,21 @@
 }
 
 // static
+void ThreadCache::DeleteForTesting(ThreadCache* tcache) {
+  ThreadCache::Delete(tcache);
+}
+
+// static
+void ThreadCache::SwapForTesting(PartitionRoot<ThreadSafe>* root) {
+  auto* old_tcache = ThreadCache::Get();
+  g_thread_cache_root.store(nullptr, std::memory_order_relaxed);
+  if (old_tcache)
+    ThreadCache::DeleteForTesting(old_tcache);
+  Init(root);
+  Create(root);
+}
+
+// static
 void ThreadCache::Init(PartitionRoot<ThreadSafe>* root) {
 #if defined(OS_NACL)
   IMMEDIATE_CRASH();
diff --git a/base/allocator/partition_allocator/thread_cache.h b/base/allocator/partition_allocator/thread_cache.h
index 29d073dc..7f0670da 100644
--- a/base/allocator/partition_allocator/thread_cache.h
+++ b/base/allocator/partition_allocator/thread_cache.h
@@ -178,6 +178,11 @@
   static void Init(PartitionRoot<ThreadSafe>* root);
   static void Init(PartitionRoot<NotThreadSafe>* root) { IMMEDIATE_CRASH(); }
 
+  static void DeleteForTesting(ThreadCache* tcache);
+
+  // Deletes existing thread cache and creates a new one for |root|.
+  static void SwapForTesting(PartitionRoot<ThreadSafe>* root);
+
   // Can be called several times, must be called before any ThreadCache
   // interactions.
   static void EnsureThreadSpecificDataInitialized();
diff --git a/base/allocator/partition_allocator/thread_cache_unittest.cc b/base/allocator/partition_allocator/thread_cache_unittest.cc
index 8707eb25..ea89ff7 100644
--- a/base/allocator/partition_allocator/thread_cache_unittest.cc
+++ b/base/allocator/partition_allocator/thread_cache_unittest.cc
@@ -9,6 +9,7 @@
 #include <vector>
 
 #include "base/allocator/buildflags.h"
+#include "base/allocator/partition_allocator/extended_api.h"
 #include "base/allocator/partition_allocator/partition_address_space.h"
 #include "base/allocator/partition_allocator/partition_alloc.h"
 #include "base/bind.h"
@@ -20,15 +21,11 @@
 #include "build/build_config.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
-// Only a single partition can have a thread cache at a time. When
-// PartitionAlloc is malloc(), it is already in use.
-//
 // With *SAN, PartitionAlloc is replaced in partition_alloc.h by ASAN, so we
 // cannot test the thread cache.
 //
 // Finally, the thread cache is not supported on all platforms.
-#if !BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC) && \
-    !defined(MEMORY_TOOL_REPLACES_ALLOCATOR) &&  \
+#if !defined(MEMORY_TOOL_REPLACES_ALLOCATOR) && \
     defined(PA_THREAD_CACHE_SUPPORTED)
 
 namespace base {
@@ -77,7 +74,11 @@
 // Forbid extras, since they make finding out which bucket is used harder.
 NoDestructor<ThreadSafePartitionRoot> g_root{
     PartitionOptions{PartitionOptions::AlignedAlloc::kAllowed,
+#if !BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC)
                      PartitionOptions::ThreadCache::kEnabled,
+#else
+                     PartitionOptions::ThreadCache::kDisabled,
+#endif  // BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC)
                      PartitionOptions::Quarantine::kAllowed,
                      PartitionOptions::Cookies::kDisallowed,
                      PartitionOptions::RefCount::kDisallowed}};
@@ -120,10 +121,11 @@
     // Another test can uninitialize the pools, so make sure they are
     // initialized.
     PartitionAddressSpace::Init();
-#endif
+#endif  // defined(PA_HAS_64_BITS_POINTERS)
     ThreadCacheRegistry::Instance().SetThreadCacheMultiplier(
         ThreadCache::kDefaultMultiplier);
     ThreadCache::SetLargestCachedSize(ThreadCache::kLargeSizeThreshold);
+
     // Make sure that enough slot spans have been touched, otherwise cache fill
     // becomes unpredictable (because it doesn't take slow paths in the
     // allocator), which is an issue for tests.
@@ -143,6 +145,24 @@
     ASSERT_EQ(0u, task_env_.GetPendingMainThreadTaskCount());
   }
 
+  // Used to disable the thread cache for the lifetime of a scope (in this case
+  // the lifetime of the PartitionAllocThreadCacheTest).
+  class ScopedThreadCacheDisabler {
+   public:
+    ScopedThreadCacheDisabler() {
+      SwapOutProcessThreadCacheForTesting(g_root.get());
+    }
+    ~ScopedThreadCacheDisabler() {
+      ThreadCache::SetLargestCachedSize(ThreadCache::kDefaultSizeThreshold);
+      SwapInProcessThreadCacheForTesting(g_root.get());
+    }
+  };
+
+  // We do this here instead of in SetUp()/TearDown() because we need this to
+  // run before the task environment (which creates threads and hence is racy
+  // with attempting to disable the thread cache).
+  ScopedThreadCacheDisabler scoped_thread_cache_disabler_{};
+
   base::test::TaskEnvironment task_env_{
       base::test::TaskEnvironment::TimeSource::MOCK_TIME};
 };
@@ -324,10 +344,24 @@
     g_root->Free(ptr);
 }
 
-TEST_F(PartitionAllocThreadCacheTest, ThreadCacheRegistry) {
+// On Android with PartitionAlloc as malloc, we have extra thread caches being
+// created, causing this test to fail.
+#if BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC) && defined(OS_ANDROID)
+#define MAYBE_ThreadCacheRegistry DISABLED_ThreadCacheRegistry
+#else
+#define MAYBE_ThreadCacheRegistry ThreadCacheRegistry
+#endif  // BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC) && defined(OS_ANDROID)
+
+TEST_F(PartitionAllocThreadCacheTest, MAYBE_ThreadCacheRegistry) {
   auto* parent_thread_tcache = g_root->thread_cache_for_testing();
   ASSERT_TRUE(parent_thread_tcache);
 
+  {
+    PartitionAutoLock lock(ThreadCacheRegistry::GetLock());
+    EXPECT_EQ(parent_thread_tcache->prev_, nullptr);
+    EXPECT_EQ(parent_thread_tcache->next_, nullptr);
+  }
+
   LambdaThreadDelegate delegate{BindLambdaForTesting([&]() {
     EXPECT_FALSE(g_root->thread_cache_for_testing());  // No allocations yet.
     FillThreadCacheAndReturnIndex(kSmallSize);
@@ -855,6 +889,5 @@
 }  // namespace internal
 }  // namespace base
 
-#endif  // !BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC) &&
-        // !defined(MEMORY_TOOL_REPLACES_ALLOCATOR) &&
+#endif  // !defined(MEMORY_TOOL_REPLACES_ALLOCATOR) &&
         // defined(PA_THREAD_CACHE_SUPPORTED)
diff --git a/base/i18n/break_iterator.cc b/base/i18n/break_iterator.cc
index 0471763..32786974 100644
--- a/base/i18n/break_iterator.cc
+++ b/base/i18n/break_iterator.cc
@@ -5,6 +5,7 @@
 #include "base/i18n/break_iterator.h"
 
 #include <stdint.h>
+#include <ostream>
 
 #include "base/check.h"
 #include "base/lazy_instance.h"
diff --git a/base/mac/mach_port_rendezvous_unittest.cc b/base/mac/mach_port_rendezvous_unittest.cc
index 94a353b..7676342 100644
--- a/base/mac/mach_port_rendezvous_unittest.cc
+++ b/base/mac/mach_port_rendezvous_unittest.cc
@@ -9,6 +9,7 @@
 #include <utility>
 
 #include "base/at_exit.h"
+#include "base/cxx17_backports.h"
 #include "base/mac/foundation_util.h"
 #include "base/mac/mach_logging.h"
 #include "base/strings/stringprintf.h"
diff --git a/base/process/memory_unittest.cc b/base/process/memory_unittest.cc
index 2c0216e..51c5ae3 100644
--- a/base/process/memory_unittest.cc
+++ b/base/process/memory_unittest.cc
@@ -179,8 +179,9 @@
 #if defined(OS_ANDROID) && defined(ARCH_CPU_X86)
     return base::android::BuildInfo::GetInstance()->sdk_int() <
            base::android::SDK_VERSION_NOUGAT;
-#endif
+#else
     return false;
+#endif
   }
 };
 
diff --git a/base/synchronization/lock_impl.h b/base/synchronization/lock_impl.h
index cfe53db2..0d05dce 100644
--- a/base/synchronization/lock_impl.h
+++ b/base/synchronization/lock_impl.h
@@ -106,9 +106,12 @@
 
 #elif defined(OS_POSIX) || defined(OS_FUCHSIA)
 
+BASE_EXPORT std::string SystemErrorCodeToString(int error_code);
+
 bool LockImpl::Try() {
   int rv = pthread_mutex_trylock(&native_handle_);
-  DCHECK(rv == 0 || rv == EBUSY) << ". " << strerror(rv);
+  DCHECK(rv == 0 || rv == EBUSY)
+      << ". " << base::internal::SystemErrorCodeToString(rv);
   return rv == 0;
 }
 
diff --git a/base/synchronization/lock_impl_posix.cc b/base/synchronization/lock_impl_posix.cc
index 217808d..069bd5294 100644
--- a/base/synchronization/lock_impl_posix.cc
+++ b/base/synchronization/lock_impl_posix.cc
@@ -6,6 +6,7 @@
 
 #include <string>
 
+#include "base/base_export.h"
 #include "base/check_op.h"
 #include "base/debug/activity_tracker.h"
 #include "base/posix/safe_strerror.h"
@@ -29,7 +30,9 @@
 }
 #endif  // DCHECK_IS_ON()
 
-std::string SystemErrorCodeToString(int error_code) {
+}  // namespace
+
+BASE_EXPORT std::string SystemErrorCodeToString(int error_code) {
 #if DCHECK_IS_ON()
   return base::safe_strerror(error_code) + ". " +
          AdditionalHintForSystemErrorCode(error_code);
@@ -38,8 +41,6 @@
 #endif  // DCHECK_IS_ON()
 }
 
-}  // namespace
-
 // Determines which platforms can consider using priority inheritance locks. Use
 // this define for platform code that may not compile if priority inheritance
 // locks aren't available. For this platform code,
diff --git a/base/system/sys_info.cc b/base/system/sys_info.cc
index b0e5f815..84a2e7a 100644
--- a/base/system/sys_info.cc
+++ b/base/system/sys_info.cc
@@ -16,6 +16,7 @@
 #include "base/task/post_task.h"
 #include "base/task/task_traits.h"
 #include "base/task/thread_pool.h"
+#include "base/threading/sequenced_task_runner_handle.h"
 #include "base/time/time.h"
 #include "build/build_config.h"
 #include "build/chromeos_buildflags.h"
@@ -97,8 +98,8 @@
       std::move(callback));
 #else
   NOTIMPLEMENTED();
-  base::ThreadPool::PostTask(
-      FROM_HERE, {}, base::BindOnce(std::move(callback), HardwareInfo()));
+  base::SequencedTaskRunnerHandle::Get()->PostTask(
+      FROM_HERE, base::BindOnce(std::move(callback), HardwareInfo()));
 #endif
 }
 
diff --git a/base/threading/sequence_local_storage_map.cc b/base/threading/sequence_local_storage_map.cc
index 26ce4b17..c152566 100644
--- a/base/threading/sequence_local_storage_map.cc
+++ b/base/threading/sequence_local_storage_map.cc
@@ -4,6 +4,7 @@
 
 #include "base/threading/sequence_local_storage_map.h"
 
+#include <ostream>
 #include <utility>
 
 #include "base/check_op.h"
diff --git a/base/threading/thread_restrictions.cc b/base/threading/thread_restrictions.cc
index b4cf3c9..1259fa12 100644
--- a/base/threading/thread_restrictions.cc
+++ b/base/threading/thread_restrictions.cc
@@ -4,6 +4,7 @@
 
 #include "base/threading/thread_restrictions.h"
 
+#include "base/threading/hang_watcher.h"
 #include "base/trace_event/base_tracing.h"
 
 #if DCHECK_IS_ON()
diff --git a/base/threading/thread_restrictions.h b/base/threading/thread_restrictions.h
index 67f44e5..dedab0bc 100644
--- a/base/threading/thread_restrictions.h
+++ b/base/threading/thread_restrictions.h
@@ -14,7 +14,9 @@
 #include "base/gtest_prod_util.h"
 #include "base/location.h"
 #include "base/macros.h"
-#include "base/threading/hang_watcher.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/threading/platform_thread.h"
+#include "base/time/time.h"
 #include "build/build_config.h"
 
 // -----------------------------------------------------------------------------
diff --git a/base/win/win_util.cc b/base/win/win_util.cc
index 4e4ec94..42d3fa3 100644
--- a/base/win/win_util.cc
+++ b/base/win/win_util.cc
@@ -37,6 +37,7 @@
 #include "base/command_line.h"
 #include "base/files/file_path.h"
 #include "base/logging.h"
+#include "base/notreached.h"
 #include "base/scoped_native_library.h"
 #include "base/strings/string_util.h"
 #include "base/strings/string_util_win.h"
diff --git a/build/android/adb_logcat_monitor.py b/build/android/adb_logcat_monitor.py
index 5cdbfc7..6230db4d 100755
--- a/build/android/adb_logcat_monitor.py
+++ b/build/android/adb_logcat_monitor.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
 #
 # Copyright (c) 2012 The Chromium Authors. All rights reserved.
 # Use of this source code is governed by a BSD-style license that can be
diff --git a/build/android/adb_logcat_printer.py b/build/android/adb_logcat_printer.py
index 63a84af..f80845a 100755
--- a/build/android/adb_logcat_printer.py
+++ b/build/android/adb_logcat_printer.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
 #
 # Copyright (c) 2012 The Chromium Authors. All rights reserved.
 # Use of this source code is governed by a BSD-style license that can be
diff --git a/build/android/apk_operations.pydeps b/build/android/apk_operations.pydeps
index 60b1289..0bd7b7f9 100644
--- a/build/android/apk_operations.pydeps
+++ b/build/android/apk_operations.pydeps
@@ -65,6 +65,9 @@
 ../../third_party/catapult/third_party/six/six.py
 ../../third_party/jinja2/__init__.py
 ../../third_party/jinja2/_compat.py
+../../third_party/jinja2/_identifier.py
+../../third_party/jinja2/asyncfilters.py
+../../third_party/jinja2/asyncsupport.py
 ../../third_party/jinja2/bccache.py
 ../../third_party/jinja2/compiler.py
 ../../third_party/jinja2/defaults.py
diff --git a/build/android/asan_symbolize.py b/build/android/asan_symbolize.py
index a10ac49..60b00d00 100755
--- a/build/android/asan_symbolize.py
+++ b/build/android/asan_symbolize.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
 #
 # Copyright 2013 The Chromium Authors. All rights reserved.
 # Use of this source code is governed by a BSD-style license that can be
diff --git a/build/android/diff_resource_sizes.py b/build/android/diff_resource_sizes.py
index e0a23a1..0bd2c47 100755
--- a/build/android/diff_resource_sizes.py
+++ b/build/android/diff_resource_sizes.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
 # 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.
diff --git a/build/android/download_doclava.py b/build/android/download_doclava.py
index 1982fdb8..059d1cb 100755
--- a/build/android/download_doclava.py
+++ b/build/android/download_doclava.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
 # Copyright 2016 The Chromium Authors. All rights reserved.
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
diff --git a/build/android/gradle/gn_to_cmake.py b/build/android/gradle/gn_to_cmake.py
index d3e80ae..7289825 100755
--- a/build/android/gradle/gn_to_cmake.py
+++ b/build/android/gradle/gn_to_cmake.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
 # Copyright 2016 The Chromium Authors. All rights reserved.
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
diff --git a/build/android/gyp/compile_resources.pydeps b/build/android/gyp/compile_resources.pydeps
index 174b5269..9076014 100644
--- a/build/android/gyp/compile_resources.pydeps
+++ b/build/android/gyp/compile_resources.pydeps
@@ -2,6 +2,7 @@
 #   build/print_python_deps.py --root build/android/gyp --output build/android/gyp/compile_resources.pydeps build/android/gyp/compile_resources.py
 ../../../third_party/jinja2/__init__.py
 ../../../third_party/jinja2/_compat.py
+../../../third_party/jinja2/_identifier.py
 ../../../third_party/jinja2/asyncfilters.py
 ../../../third_party/jinja2/asyncsupport.py
 ../../../third_party/jinja2/bccache.py
@@ -23,29 +24,6 @@
 ../../../third_party/markupsafe/__init__.py
 ../../../third_party/markupsafe/_compat.py
 ../../../third_party/markupsafe/_native.py
-../../../third_party/protobuf/python/google/__init__.py
-../../../third_party/protobuf/python/google/protobuf/__init__.py
-../../../third_party/protobuf/python/google/protobuf/descriptor.py
-../../../third_party/protobuf/python/google/protobuf/descriptor_database.py
-../../../third_party/protobuf/python/google/protobuf/descriptor_pool.py
-../../../third_party/protobuf/python/google/protobuf/internal/__init__.py
-../../../third_party/protobuf/python/google/protobuf/internal/api_implementation.py
-../../../third_party/protobuf/python/google/protobuf/internal/containers.py
-../../../third_party/protobuf/python/google/protobuf/internal/decoder.py
-../../../third_party/protobuf/python/google/protobuf/internal/encoder.py
-../../../third_party/protobuf/python/google/protobuf/internal/enum_type_wrapper.py
-../../../third_party/protobuf/python/google/protobuf/internal/extension_dict.py
-../../../third_party/protobuf/python/google/protobuf/internal/message_listener.py
-../../../third_party/protobuf/python/google/protobuf/internal/python_message.py
-../../../third_party/protobuf/python/google/protobuf/internal/type_checkers.py
-../../../third_party/protobuf/python/google/protobuf/internal/well_known_types.py
-../../../third_party/protobuf/python/google/protobuf/internal/wire_format.py
-../../../third_party/protobuf/python/google/protobuf/message.py
-../../../third_party/protobuf/python/google/protobuf/message_factory.py
-../../../third_party/protobuf/python/google/protobuf/reflection.py
-../../../third_party/protobuf/python/google/protobuf/symbol_database.py
-../../../third_party/protobuf/python/google/protobuf/text_encoding.py
-../../../third_party/protobuf/python/google/protobuf/text_format.py
 ../../../third_party/six/src/six.py
 ../../gn_helpers.py
 compile_resources.py
diff --git a/build/android/gyp/create_apk_operations_script.py b/build/android/gyp/create_apk_operations_script.py
index 660567f0..82a6e5be 100755
--- a/build/android/gyp/create_apk_operations_script.py
+++ b/build/android/gyp/create_apk_operations_script.py
@@ -12,7 +12,7 @@
 from util import build_utils
 
 SCRIPT_TEMPLATE = string.Template("""\
-#!/usr/bin/env python
+#!/usr/bin/env python3
 #
 # This file was generated by build/android/gyp/create_apk_operations_script.py
 
diff --git a/build/android/gyp/create_app_bundle.pydeps b/build/android/gyp/create_app_bundle.pydeps
index cbb471a..503dfb0 100644
--- a/build/android/gyp/create_app_bundle.pydeps
+++ b/build/android/gyp/create_app_bundle.pydeps
@@ -14,6 +14,7 @@
 ../../../third_party/catapult/devil/devil/utils/cmd_helper.py
 ../../../third_party/jinja2/__init__.py
 ../../../third_party/jinja2/_compat.py
+../../../third_party/jinja2/_identifier.py
 ../../../third_party/jinja2/asyncfilters.py
 ../../../third_party/jinja2/asyncsupport.py
 ../../../third_party/jinja2/bccache.py
diff --git a/build/android/gyp/create_app_bundle_apks.pydeps b/build/android/gyp/create_app_bundle_apks.pydeps
index 20d8ffe8..5e04dae 100644
--- a/build/android/gyp/create_app_bundle_apks.pydeps
+++ b/build/android/gyp/create_app_bundle_apks.pydeps
@@ -2,6 +2,7 @@
 #   build/print_python_deps.py --root build/android/gyp --output build/android/gyp/create_app_bundle_apks.pydeps build/android/gyp/create_app_bundle_apks.py
 ../../../third_party/jinja2/__init__.py
 ../../../third_party/jinja2/_compat.py
+../../../third_party/jinja2/_identifier.py
 ../../../third_party/jinja2/asyncfilters.py
 ../../../third_party/jinja2/asyncsupport.py
 ../../../third_party/jinja2/bccache.py
diff --git a/build/android/gyp/create_bundle_wrapper_script.py b/build/android/gyp/create_bundle_wrapper_script.py
index 282e206..1bdb767 100755
--- a/build/android/gyp/create_bundle_wrapper_script.py
+++ b/build/android/gyp/create_bundle_wrapper_script.py
@@ -13,7 +13,7 @@
 from util import build_utils
 
 SCRIPT_TEMPLATE = string.Template("""\
-#!/usr/bin/env python
+#!/usr/bin/env python3
 #
 # This file was generated by build/android/gyp/create_bundle_wrapper_script.py
 
diff --git a/build/android/gyp/create_java_binary_script.py b/build/android/gyp/create_java_binary_script.py
index 5bc9d08ab1..91fe600 100755
--- a/build/android/gyp/create_java_binary_script.py
+++ b/build/android/gyp/create_java_binary_script.py
@@ -21,7 +21,7 @@
 # to the directory that the script is written in and then, when run, must
 # recalculate the paths relative to the current directory.
 script_template = """\
-#!/usr/bin/env python
+#!/usr/bin/env python3
 #
 # This file was generated by build/android/gyp/create_java_binary_script.py
 
diff --git a/build/android/gyp/create_r_java.pydeps b/build/android/gyp/create_r_java.pydeps
index 45121e3..b259751 100644
--- a/build/android/gyp/create_r_java.pydeps
+++ b/build/android/gyp/create_r_java.pydeps
@@ -2,6 +2,7 @@
 #   build/print_python_deps.py --root build/android/gyp --output build/android/gyp/create_r_java.pydeps build/android/gyp/create_r_java.py
 ../../../third_party/jinja2/__init__.py
 ../../../third_party/jinja2/_compat.py
+../../../third_party/jinja2/_identifier.py
 ../../../third_party/jinja2/asyncfilters.py
 ../../../third_party/jinja2/asyncsupport.py
 ../../../third_party/jinja2/bccache.py
diff --git a/build/android/gyp/create_r_txt.pydeps b/build/android/gyp/create_r_txt.pydeps
index c7698eef..54e5670 100644
--- a/build/android/gyp/create_r_txt.pydeps
+++ b/build/android/gyp/create_r_txt.pydeps
@@ -2,6 +2,7 @@
 #   build/print_python_deps.py --root build/android/gyp --output build/android/gyp/create_r_txt.pydeps build/android/gyp/create_r_txt.py
 ../../../third_party/jinja2/__init__.py
 ../../../third_party/jinja2/_compat.py
+../../../third_party/jinja2/_identifier.py
 ../../../third_party/jinja2/asyncfilters.py
 ../../../third_party/jinja2/asyncsupport.py
 ../../../third_party/jinja2/bccache.py
diff --git a/build/android/gyp/create_ui_locale_resources.pydeps b/build/android/gyp/create_ui_locale_resources.pydeps
index 6bb98dd2..a147237 100644
--- a/build/android/gyp/create_ui_locale_resources.pydeps
+++ b/build/android/gyp/create_ui_locale_resources.pydeps
@@ -2,6 +2,7 @@
 #   build/print_python_deps.py --root build/android/gyp --output build/android/gyp/create_ui_locale_resources.pydeps build/android/gyp/create_ui_locale_resources.py
 ../../../third_party/jinja2/__init__.py
 ../../../third_party/jinja2/_compat.py
+../../../third_party/jinja2/_identifier.py
 ../../../third_party/jinja2/asyncfilters.py
 ../../../third_party/jinja2/asyncsupport.py
 ../../../third_party/jinja2/bccache.py
diff --git a/build/android/gyp/jinja_template.pydeps b/build/android/gyp/jinja_template.pydeps
index af22c40..98de9329b 100644
--- a/build/android/gyp/jinja_template.pydeps
+++ b/build/android/gyp/jinja_template.pydeps
@@ -11,6 +11,7 @@
 ../../../third_party/catapult/devil/devil/constants/exit_codes.py
 ../../../third_party/jinja2/__init__.py
 ../../../third_party/jinja2/_compat.py
+../../../third_party/jinja2/_identifier.py
 ../../../third_party/jinja2/asyncfilters.py
 ../../../third_party/jinja2/asyncsupport.py
 ../../../third_party/jinja2/bccache.py
diff --git a/build/android/gyp/prepare_resources.pydeps b/build/android/gyp/prepare_resources.pydeps
index b225918..8136e73 100644
--- a/build/android/gyp/prepare_resources.pydeps
+++ b/build/android/gyp/prepare_resources.pydeps
@@ -2,6 +2,7 @@
 #   build/print_python_deps.py --root build/android/gyp --output build/android/gyp/prepare_resources.pydeps build/android/gyp/prepare_resources.py
 ../../../third_party/jinja2/__init__.py
 ../../../third_party/jinja2/_compat.py
+../../../third_party/jinja2/_identifier.py
 ../../../third_party/jinja2/asyncfilters.py
 ../../../third_party/jinja2/asyncsupport.py
 ../../../third_party/jinja2/bccache.py
diff --git a/build/android/gyp/unused_resources.pydeps b/build/android/gyp/unused_resources.pydeps
index 4753ec3..b821d70 100644
--- a/build/android/gyp/unused_resources.pydeps
+++ b/build/android/gyp/unused_resources.pydeps
@@ -2,6 +2,7 @@
 #   build/print_python_deps.py --root build/android/gyp --output build/android/gyp/unused_resources.pydeps build/android/gyp/unused_resources.py
 ../../../third_party/jinja2/__init__.py
 ../../../third_party/jinja2/_compat.py
+../../../third_party/jinja2/_identifier.py
 ../../../third_party/jinja2/asyncfilters.py
 ../../../third_party/jinja2/asyncsupport.py
 ../../../third_party/jinja2/bccache.py
diff --git a/build/android/gyp/write_build_config.pydeps b/build/android/gyp/write_build_config.pydeps
index b1276bc..e9c7d9fc 100644
--- a/build/android/gyp/write_build_config.pydeps
+++ b/build/android/gyp/write_build_config.pydeps
@@ -2,6 +2,7 @@
 #   build/print_python_deps.py --root build/android/gyp --output build/android/gyp/write_build_config.pydeps build/android/gyp/write_build_config.py
 ../../../third_party/jinja2/__init__.py
 ../../../third_party/jinja2/_compat.py
+../../../third_party/jinja2/_identifier.py
 ../../../third_party/jinja2/asyncfilters.py
 ../../../third_party/jinja2/asyncsupport.py
 ../../../third_party/jinja2/bccache.py
diff --git a/build/android/incremental_install/generate_android_manifest.py b/build/android/incremental_install/generate_android_manifest.py
index c4912837..67feaa5a 100755
--- a/build/android/incremental_install/generate_android_manifest.py
+++ b/build/android/incremental_install/generate_android_manifest.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
 #
 # Copyright 2015 The Chromium Authors. All rights reserved.
 # Use of this source code is governed by a BSD-style license that can be
diff --git a/build/android/incremental_install/generate_android_manifest.pydeps b/build/android/incremental_install/generate_android_manifest.pydeps
index 568ea1e2..b28c307 100644
--- a/build/android/incremental_install/generate_android_manifest.pydeps
+++ b/build/android/incremental_install/generate_android_manifest.pydeps
@@ -2,6 +2,9 @@
 #   build/print_python_deps.py --root build/android/incremental_install --output build/android/incremental_install/generate_android_manifest.pydeps build/android/incremental_install/generate_android_manifest.py
 ../../../third_party/jinja2/__init__.py
 ../../../third_party/jinja2/_compat.py
+../../../third_party/jinja2/_identifier.py
+../../../third_party/jinja2/asyncfilters.py
+../../../third_party/jinja2/asyncsupport.py
 ../../../third_party/jinja2/bccache.py
 ../../../third_party/jinja2/compiler.py
 ../../../third_party/jinja2/defaults.py
diff --git a/build/android/incremental_install/write_installer_json.py b/build/android/incremental_install/write_installer_json.py
index cf1d2d4..ce88e8a 100755
--- a/build/android/incremental_install/write_installer_json.py
+++ b/build/android/incremental_install/write_installer_json.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
 
 # Copyright 2017 The Chromium Authors. All rights reserved.
 # Use of this source code is governed by a BSD-style license that can be
diff --git a/build/android/lighttpd_server.py b/build/android/lighttpd_server.py
index ce2afbe..561174d 100755
--- a/build/android/lighttpd_server.py
+++ b/build/android/lighttpd_server.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
 #
 # Copyright (c) 2012 The Chromium Authors. All rights reserved.
 # Use of this source code is governed by a BSD-style license that can be
diff --git a/build/android/method_count.py b/build/android/method_count.py
index a39a390..80d0073 100755
--- a/build/android/method_count.py
+++ b/build/android/method_count.py
@@ -1,4 +1,4 @@
-#! /usr/bin/env python
+#! /usr/bin/env python3
 # Copyright 2015 The Chromium Authors. All rights reserved.
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
diff --git a/build/android/native_flags/argcapture.py b/build/android/native_flags/argcapture.py
index 159b03ab..b0e2acd9 100755
--- a/build/android/native_flags/argcapture.py
+++ b/build/android/native_flags/argcapture.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
 # Copyright 2021 The Chromium Authors. All rights reserved.
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
diff --git a/build/android/pylib/dex/dex_parser.py b/build/android/pylib/dex/dex_parser.py
index be5f1af..1ff8d2527 100755
--- a/build/android/pylib/dex/dex_parser.py
+++ b/build/android/pylib/dex/dex_parser.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
 # 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.
diff --git a/build/android/pylib/results/presentation/standard_gtest_merge.py b/build/android/pylib/results/presentation/standard_gtest_merge.py
index 64f40e6..d458223 100755
--- a/build/android/pylib/results/presentation/standard_gtest_merge.py
+++ b/build/android/pylib/results/presentation/standard_gtest_merge.py
@@ -1,4 +1,4 @@
-#! /usr/bin/env python
+#! /usr/bin/env python3
 #
 # Copyright 2017 The Chromium Authors. All rights reserved.
 # Use of this source code is governed by a BSD-style license that can be
diff --git a/build/android/pylib/results/presentation/test_results_presentation.py b/build/android/pylib/results/presentation/test_results_presentation.py
index 2fd98b11..fc14b8b 100755
--- a/build/android/pylib/results/presentation/test_results_presentation.py
+++ b/build/android/pylib/results/presentation/test_results_presentation.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
 #
 # Copyright 2017 The Chromium Authors. All rights reserved.
 # Use of this source code is governed by a BSD-style license that can be
diff --git a/build/android/pylib/symbols/apk_lib_dump.py b/build/android/pylib/symbols/apk_lib_dump.py
index 933a0ab..f40c758 100755
--- a/build/android/pylib/symbols/apk_lib_dump.py
+++ b/build/android/pylib/symbols/apk_lib_dump.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
 
 # Copyright 2018 The Chromium Authors. All rights reserved.
 # Use of this source code is governed by a BSD-style license that can be
diff --git a/build/android/test_runner.pydeps b/build/android/test_runner.pydeps
index 3ee560c..87b39ef 100644
--- a/build/android/test_runner.pydeps
+++ b/build/android/test_runner.pydeps
@@ -95,6 +95,7 @@
 ../../third_party/catapult/devil/devil/utils/timeout_retry.py
 ../../third_party/catapult/devil/devil/utils/watchdog_timer.py
 ../../third_party/catapult/devil/devil/utils/zip_utils.py
+../../third_party/catapult/third_party/six/six.py
 ../../third_party/colorama/src/colorama/__init__.py
 ../../third_party/colorama/src/colorama/ansi.py
 ../../third_party/colorama/src/colorama/ansitowin32.py
diff --git a/build/android/update_deps/update_third_party_deps.py b/build/android/update_deps/update_third_party_deps.py
index 3a869c4..c03fec5d 100755
--- a/build/android/update_deps/update_third_party_deps.py
+++ b/build/android/update_deps/update_third_party_deps.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
 # Copyright 2016 The Chromium Authors. All rights reserved.
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
diff --git a/build/config/ios/BUILD.gn b/build/config/ios/BUILD.gn
index 5309e82..1626097c 100644
--- a/build/config/ios/BUILD.gn
+++ b/build/config/ios/BUILD.gn
@@ -16,11 +16,6 @@
   # Mimicking how Xcode handles it, the production builds (is_debug = false)
   # get real bitcode sections added, while the debug builds (is_debug = true)
   # only get bitcode-section "markers" added in them.
-  # NOTE: This option is ignored when building versions for the iOS simulator,
-  # where a part of libvpx is compiled from the assembly code written using
-  # Intel assembly syntax; Yasm / Nasm do not support emitting bitcode parts.
-  # That is not a limitation for now as Xcode mandates the presence of bitcode
-  # only when building bitcode-enabled projects for real devices (ARM CPUs).
   enable_ios_bitcode = false
 }
 
diff --git a/chrome/android/BUILD.gn b/chrome/android/BUILD.gn
index 5a39db4..a4b7306c3 100644
--- a/chrome/android/BUILD.gn
+++ b/chrome/android/BUILD.gn
@@ -341,6 +341,7 @@
     "//chrome/browser/consent_auditor/android:java",
     "//chrome/browser/contextmenu:java",
     "//chrome/browser/continuous_search:java",
+    "//chrome/browser/dependency_injection:java",
     "//chrome/browser/device:java",
     "//chrome/browser/download/android:factory_java",
     "//chrome/browser/download/android:file_provider_java",
@@ -357,6 +358,7 @@
     "//chrome/browser/history_clusters:java",
     "//chrome/browser/image_descriptions:java",
     "//chrome/browser/image_editor/public:java",
+    "//chrome/browser/incognito:java",
     "//chrome/browser/language/android:base_module_java",
     "//chrome/browser/language/android:java",
     "//chrome/browser/lens:java",
@@ -776,6 +778,7 @@
     "//chrome/browser/flags:jni_headers",
     "//chrome/browser/history_clusters:jni_headers",
     "//chrome/browser/image_descriptions:jni_headers",
+    "//chrome/browser/incognito:jni_headers",
     "//chrome/browser/locale:jni_headers",
     "//chrome/browser/performance_hints/android:jni_headers",
     "//chrome/browser/preferences:jni_headers",
@@ -926,6 +929,7 @@
     "//chrome/browser/contextmenu:java",
     "//chrome/browser/continuous_search:junit",
     "//chrome/browser/continuous_search/internal:junit",
+    "//chrome/browser/dependency_injection:java",
     "//chrome/browser/device:java",
     "//chrome/browser/device:junit",
     "//chrome/browser/download/android:java",
@@ -945,6 +949,7 @@
     "//chrome/browser/gsa:java",
     "//chrome/browser/image_descriptions:java",
     "//chrome/browser/image_editor/public:java",
+    "//chrome/browser/incognito:java",
     "//chrome/browser/lens:delegate_public_impl_java",
     "//chrome/browser/lens:java",
     "//chrome/browser/locale:java",
@@ -1287,6 +1292,7 @@
     "//chrome/browser/content_creation/notes/internal/android:javatests",
     "//chrome/browser/contextmenu:java",
     "//chrome/browser/continuous_search:javatests",
+    "//chrome/browser/dependency_injection:java",
     "//chrome/browser/device:java",
     "//chrome/browser/download/android:file_provider_java",
     "//chrome/browser/download/android:java",
@@ -1301,6 +1307,7 @@
     "//chrome/browser/fullscreen/android:java",
     "//chrome/browser/gsa:java",
     "//chrome/browser/image_descriptions:javatests",
+    "//chrome/browser/incognito:java",
     "//chrome/browser/language/android:base_module_java",
     "//chrome/browser/language/android:java",
     "//chrome/browser/language/android:javatests",
@@ -3753,7 +3760,6 @@
     "java/src/org/chromium/chrome/browser/history/HistoryDeletionBridge.java",
     "java/src/org/chromium/chrome/browser/history/HistoryDeletionInfo.java",
     "java/src/org/chromium/chrome/browser/historyreport/HistoryReportJniBridge.java",
-    "java/src/org/chromium/chrome/browser/incognito/IncognitoUtils.java",
     "java/src/org/chromium/chrome/browser/infobar/AutofillCreditCardFillingInfoBar.java",
     "java/src/org/chromium/chrome/browser/infobar/AutofillOfferNotificationInfoBar.java",
     "java/src/org/chromium/chrome/browser/infobar/AutofillSaveCardInfoBar.java",
diff --git a/chrome/android/chrome_java_sources.gni b/chrome/android/chrome_java_sources.gni
index dd58dafd..fb91a5a 100644
--- a/chrome/android/chrome_java_sources.gni
+++ b/chrome/android/chrome_java_sources.gni
@@ -488,7 +488,6 @@
   "java/src/org/chromium/chrome/browser/datareduction/settings/DataReductionPreferenceFragment.java",
   "java/src/org/chromium/chrome/browser/datareduction/settings/DataReductionSiteBreakdownView.java",
   "java/src/org/chromium/chrome/browser/datareduction/settings/DataReductionStatsPreference.java",
-  "java/src/org/chromium/chrome/browser/dependency_injection/ActivityScope.java",
   "java/src/org/chromium/chrome/browser/dependency_injection/ChromeActivityCommonsModule.java",
   "java/src/org/chromium/chrome/browser/dependency_injection/ChromeActivityComponent.java",
   "java/src/org/chromium/chrome/browser/dependency_injection/ChromeAppComponent.java",
@@ -669,15 +668,12 @@
   "java/src/org/chromium/chrome/browser/homepage/settings/HomepageSettings.java",
   "java/src/org/chromium/chrome/browser/homepage/settings/RadioButtonGroupHomepagePreference.java",
   "java/src/org/chromium/chrome/browser/identity_disc/IdentityDiscController.java",
-  "java/src/org/chromium/chrome/browser/incognito/IncognitoCctProfileManager.java",
   "java/src/org/chromium/chrome/browser/incognito/IncognitoNotificationManager.java",
   "java/src/org/chromium/chrome/browser/incognito/IncognitoNotificationPresenceController.java",
   "java/src/org/chromium/chrome/browser/incognito/IncognitoNotificationServiceImpl.java",
   "java/src/org/chromium/chrome/browser/incognito/IncognitoProfileDestroyer.java",
   "java/src/org/chromium/chrome/browser/incognito/IncognitoTabLauncher.java",
-  "java/src/org/chromium/chrome/browser/incognito/IncognitoTabPersistence.java",
   "java/src/org/chromium/chrome/browser/incognito/IncognitoTabSnapshotController.java",
-  "java/src/org/chromium/chrome/browser/incognito/IncognitoUtils.java",
   "java/src/org/chromium/chrome/browser/infobar/AutofillCreditCardFillingInfoBar.java",
   "java/src/org/chromium/chrome/browser/infobar/AutofillOfferNotificationInfoBar.java",
   "java/src/org/chromium/chrome/browser/infobar/AutofillSaveCardInfoBar.java",
diff --git a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/details/AssistantDetailsAdapter.java b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/details/AssistantDetailsAdapter.java
index 24bdfb2..c5e373bd 100644
--- a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/details/AssistantDetailsAdapter.java
+++ b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/details/AssistantDetailsAdapter.java
@@ -456,8 +456,8 @@
         if (!clickthroughData.getDescription().isEmpty()) {
             builder.with(ModalDialogProperties.MESSAGE, clickthroughData.getDescription());
         } else {
-            builder.with(ModalDialogProperties.MESSAGE, resources,
-                    R.string.autofill_assistant_view_original_image_desc);
+            builder.with(ModalDialogProperties.MESSAGE,
+                    resources.getString(R.string.autofill_assistant_view_original_image_desc));
         }
 
         if (!clickthroughData.getPositiveText().isEmpty()) {
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogMenuCoordinator.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogMenuCoordinator.java
index e78cb15..cd08cf1 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogMenuCoordinator.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogMenuCoordinator.java
@@ -146,9 +146,11 @@
         itemList.add(new ListItem(ListItemType.MENU_ITEM,
                 buildPropertyModel(context, R.string.tab_grid_dialog_toolbar_remove_from_group,
                         R.id.ungroup_tab)));
-        itemList.add(new ListItem(ListItemType.MENU_ITEM,
-                buildPropertyModel(context, R.string.tab_grid_dialog_toolbar_share_group,
-                        R.id.share_tab_group)));
+        if (TabUiFeatureUtilities.ENABLE_TAB_GROUP_SHARING.getValue()) {
+            itemList.add(new ListItem(ListItemType.MENU_ITEM,
+                    buildPropertyModel(context, R.string.tab_grid_dialog_toolbar_share_group,
+                            R.id.share_tab_group)));
+        }
         if (TabUiFeatureUtilities.isLaunchPolishEnabled()) {
             itemList.add(new ListItem(ListItemType.MENU_ITEM,
                     buildPropertyModel(context, R.string.tab_grid_dialog_toolbar_edit_group_name,
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabUiFeatureUtilities.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabUiFeatureUtilities.java
index 5a05bc8..7bac445 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabUiFeatureUtilities.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabUiFeatureUtilities.java
@@ -92,6 +92,11 @@
             new BooleanCachedFieldTrialParameter(
                     ChromeFeatureList.TAB_GRID_LAYOUT_ANDROID, TAB_GROUP_AUTO_CREATION_PARAM, true);
 
+    private static final String TAB_GROUP_SHARING_PARAM = "enable_tab_group_sharing";
+    public static final BooleanCachedFieldTrialParameter ENABLE_TAB_GROUP_SHARING =
+            new BooleanCachedFieldTrialParameter(ChromeFeatureList.TAB_GROUPS_CONTINUATION_ANDROID,
+                    TAB_GROUP_SHARING_PARAM, false);
+
     private static Boolean sTabManagementModuleSupportedForTesting;
 
     /**
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 f53a58e..87ae142 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
@@ -367,8 +367,12 @@
 
     @Test
     @MediumTest
-    @Features.EnableFeatures(ChromeFeatureList.TAB_GROUPS_CONTINUATION_ANDROID)
+    // clang-format off
+    @Features.EnableFeatures({ChromeFeatureList.TAB_GROUPS_CONTINUATION_ANDROID + "<Study"})
+    @CommandLineFlags.Add({"force-fieldtrials=Study/Group",
+            "force-fieldtrial-params=Study.Group:enable_tab_group_sharing/true"})
     public void testDialogToolbarMenuShareGroup() {
+        // clang-format on
         final ChromeTabbedActivity cta = mActivityTestRule.getActivity();
         createTabs(cta, false, 2);
         enterTabSwitcher(cta);
@@ -391,7 +395,9 @@
     @Test
     @MediumTest
     // clang-format off
-    @Features.EnableFeatures({ChromeFeatureList.TAB_GROUPS_CONTINUATION_ANDROID})
+    @Features.EnableFeatures({ChromeFeatureList.TAB_GROUPS_CONTINUATION_ANDROID + "<Study"})
+    @CommandLineFlags.Add({"force-fieldtrials=Study/Group",
+        "force-fieldtrial-params=Study.Group:enable_tab_group_sharing/true"})
     public void testDialogToolbarMenuShareGroup_WithSharingHub() {
         // clang-format on
         final ChromeTabbedActivity cta = mActivityTestRule.getActivity();
@@ -1131,8 +1137,10 @@
                     ListView listView = (ListView) v;
                     verifyTabGridDialogToolbarMenuItem(listView, 0,
                             cta.getString(R.string.tab_grid_dialog_toolbar_remove_from_group));
-                    verifyTabGridDialogToolbarMenuItem(listView, 1,
-                            cta.getString(R.string.tab_grid_dialog_toolbar_share_group));
+                    if (TabUiFeatureUtilities.ENABLE_TAB_GROUP_SHARING.getValue()) {
+                        verifyTabGridDialogToolbarMenuItem(listView, 1,
+                                cta.getString(R.string.tab_grid_dialog_toolbar_share_group));
+                    }
                     if (TabUiFeatureUtilities.isLaunchPolishEnabled()) {
                         assertEquals(3, listView.getCount());
                         verifyTabGridDialogToolbarMenuItem(listView, 2,
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java
index fc40ac6a..a9d8268 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java
@@ -98,6 +98,7 @@
 import org.chromium.chrome.browser.incognito.IncognitoNotificationManager;
 import org.chromium.chrome.browser.incognito.IncognitoNotificationPresenceController;
 import org.chromium.chrome.browser.incognito.IncognitoProfileDestroyer;
+import org.chromium.chrome.browser.incognito.IncognitoStartup;
 import org.chromium.chrome.browser.incognito.IncognitoTabLauncher;
 import org.chromium.chrome.browser.incognito.IncognitoTabSnapshotController;
 import org.chromium.chrome.browser.incognito.IncognitoUtils;
@@ -910,15 +911,8 @@
     public void onResumeWithNative() {
         super.onResumeWithNative();
 
-        if (IncognitoUtils.shouldDestroyIncognitoProfileOnStartup(
-                    getTabModelSelector().getCurrentModel().isIncognito(),
-                    TABBED_MODE_COMPONENT_NAMES)) {
-            Profile.getLastUsedRegularProfile()
-                    .getPrimaryOTRProfile(/*createIfNeeded=*/true)
-                    .destroyWhenAppropriate();
-        } else {
-            CookiesFetcher.restoreCookies();
-        }
+        IncognitoStartup.onResumeWithNative(
+                getTabModelSelectorSupplier(), TABBED_MODE_COMPONENT_NAMES);
 
         mLocaleManager.setSnackbarManager(getSnackbarManager());
         mLocaleManager.startObservingPhoneChanges();
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/DEPS b/chrome/android/java/src/org/chromium/chrome/browser/DEPS
index 1b5dcd6..6feddbb 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/DEPS
+++ b/chrome/android/java/src/org/chromium/chrome/browser/DEPS
@@ -110,9 +110,6 @@
   "IncognitoNotificationServiceImpl\.java": [
     "+chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java"
   ],
-  "IncognitoUtils\.java": [
-    "+chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java"
-  ],
   "StartupTabPreloader\.java": [
     "+chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.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 edfb5a9..5de202c 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
@@ -144,6 +144,7 @@
                         add(TabUiFeatureUtilities.ENABLE_SEARCH_CHIP);
                         add(TabUiFeatureUtilities.ENABLE_SEARCH_CHIP_ADAPTIVE);
                         add(TabUiFeatureUtilities.ENABLE_TAB_GROUP_AUTO_CREATION);
+                        add(TabUiFeatureUtilities.ENABLE_TAB_GROUP_SHARING);
                         add(TabUiFeatureUtilities.ZOOMING_MIN_MEMORY);
                         add(TabUiFeatureUtilities.ZOOMING_MIN_SDK);
                         add(TabUiFeatureUtilities.SKIP_SLOW_ZOOMING);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/app/tab_activity_glue/ActivityTabWebContentsDelegateAndroid.java b/chrome/android/java/src/org/chromium/chrome/browser/app/tab_activity_glue/ActivityTabWebContentsDelegateAndroid.java
index c94d78b9..6e1bcb57 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/app/tab_activity_glue/ActivityTabWebContentsDelegateAndroid.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/app/tab_activity_glue/ActivityTabWebContentsDelegateAndroid.java
@@ -424,7 +424,8 @@
                         .with(ModalDialogProperties.CONTROLLER, dialogController)
                         .with(ModalDialogProperties.TITLE, resources,
                                 R.string.http_post_warning_title)
-                        .with(ModalDialogProperties.MESSAGE, resources, R.string.http_post_warning)
+                        .with(ModalDialogProperties.MESSAGE,
+                                resources.getString(R.string.http_post_warning))
                         .with(ModalDialogProperties.POSITIVE_BUTTON_TEXT, resources,
                                 R.string.http_post_warning_resend)
                         .with(ModalDialogProperties.NEGATIVE_BUTTON_TEXT, resources,
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/searchwidget/SearchActivityLocationBarLayout.java b/chrome/android/java/src/org/chromium/chrome/browser/searchwidget/SearchActivityLocationBarLayout.java
index cf5a3f9..bfff9a9 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/searchwidget/SearchActivityLocationBarLayout.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/searchwidget/SearchActivityLocationBarLayout.java
@@ -114,7 +114,7 @@
         assert !mPendingSearchPromoDecision;
         assert !isVoiceSearchIntent || mNativeInitialized;
 
-        if (voiceRecognitionHandler.isVoiceSearchEnabled() && isVoiceSearchIntent) {
+        if (isVoiceSearchIntent && voiceRecognitionHandler.isVoiceSearchEnabled()) {
             voiceRecognitionHandler.startVoiceRecognition(
                     VoiceRecognitionHandler.VoiceInteractionSource.SEARCH_WIDGET);
         } else {
@@ -159,4 +159,9 @@
             mUrlBarFocusRequested = false;
         }
     }
+
+    @Override
+    public void notifyVoiceRecognitionCanceled() {
+        focusTextBox();
+    }
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/usage_stats/UsageStatsConsentDialog.java b/chrome/android/java/src/org/chromium/chrome/browser/usage_stats/UsageStatsConsentDialog.java
index 06febbaf..51b078c7 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/usage_stats/UsageStatsConsentDialog.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/usage_stats/UsageStatsConsentDialog.java
@@ -45,13 +45,13 @@
         if (mIsRevocation) {
             builder.with(ModalDialogProperties.TITLE, resources,
                            R.string.usage_stats_revocation_prompt)
-                    .with(ModalDialogProperties.MESSAGE, resources,
-                            R.string.usage_stats_revocation_explanation)
+                    .with(ModalDialogProperties.MESSAGE,
+                            resources.getString(R.string.usage_stats_revocation_explanation))
                     .with(ModalDialogProperties.POSITIVE_BUTTON_TEXT, resources, R.string.remove);
         } else {
             builder.with(ModalDialogProperties.TITLE, resources, R.string.usage_stats_consent_title)
-                    .with(ModalDialogProperties.MESSAGE, resources,
-                            R.string.usage_stats_consent_prompt)
+                    .with(ModalDialogProperties.MESSAGE,
+                            resources.getString(R.string.usage_stats_consent_prompt))
                     .with(ModalDialogProperties.POSITIVE_BUTTON_TEXT, resources, R.string.show);
         }
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebApkIconNameUpdateDialog.java b/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebApkIconNameUpdateDialog.java
index af2ad8f..6167b31 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebApkIconNameUpdateDialog.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebApkIconNameUpdateDialog.java
@@ -101,8 +101,8 @@
                 new PropertyModel.Builder(ModalDialogProperties.ALL_KEYS)
                         .with(ModalDialogProperties.CONTROLLER, this)
                         .with(ModalDialogProperties.TITLE, resources, titleId)
-                        .with(ModalDialogProperties.MESSAGE, resources,
-                                R.string.webapp_update_explanation)
+                        .with(ModalDialogProperties.MESSAGE,
+                                resources.getString(R.string.webapp_update_explanation))
                         .with(ModalDialogProperties.CUSTOM_VIEW, dialogCustomView)
                         .with(ModalDialogProperties.PRIMARY_BUTTON_FILLED, true)
                         .with(ModalDialogProperties.POSITIVE_BUTTON_TEXT, resources, R.string.ok)
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/omnibox/LocationBarTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/omnibox/LocationBarTest.java
index c369f04..209799f2 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/omnibox/LocationBarTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/omnibox/LocationBarTest.java
@@ -593,8 +593,12 @@
 
     @Test
     @MediumTest
+    @CommandLineFlags.Add({"enable-features=" + ChromeFeatureList.VOICE_BUTTON_IN_TOP_TOOLBAR,
+            "disable-features=" + ChromeFeatureList.ADAPTIVE_BUTTON_IN_TOP_TOOLBAR + ","
+                    + ChromeFeatureList.ADAPTIVE_BUTTON_IN_TOP_TOOLBAR_CUSTOMIZATION_V2})
     @Restriction(UiRestriction.RESTRICTION_TYPE_TABLET)
-    public void testFocusLogic_buttonVisibilityTablet() {
+    public void
+    testFocusLogic_buttonVisibilityTablet() {
         startActivityNormally();
         doReturn(true).when(mVoiceRecognitionHandler).isVoiceSearchEnabled();
         String url = mActivityTestRule.getEmbeddedTestServerRule().getServer().getURLWithHostName(
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/omnibox/voice/VoiceRecognitionHandlerTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/omnibox/voice/VoiceRecognitionHandlerTest.java
index d43161c..d337a00 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/omnibox/voice/VoiceRecognitionHandlerTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/omnibox/voice/VoiceRecognitionHandlerTest.java
@@ -473,6 +473,9 @@
         public String getUrl() {
             return mUrl;
         }
+
+        @Override
+        public void notifyVoiceRecognitionCanceled() {}
     }
 
     /**
diff --git a/chrome/app/os_settings_strings.grdp b/chrome/app/os_settings_strings.grdp
index 41f9644e..217149a 100644
--- a/chrome/app/os_settings_strings.grdp
+++ b/chrome/app/os_settings_strings.grdp
@@ -2025,6 +2025,12 @@
   <message name="IDS_SETTINGS_BLUETOOTH_CHANGE_DEVICE_NAME_DIALOG_DONE" desc="Bluetooth device details page > Change device name dialog: Label for button that initiates device's name change.">
     Done
   </message>
+  <message name="IDS_SETTINGS_BLUETOOTH_DEVICE_DETAIL_CHANGE_DEVICE_SETTINGS_MOUSE" desc="Bluetooth device details page: label for button that changes current mouse device settings.">
+    Change mouse settings
+  </message>
+  <message name="IDS_SETTINGS_BLUETOOTH_DEVICE_DETAIL_CHANGE_DEVICE_SETTINGS_KEYBOARD" desc="Bluetooth device details page: label for button that changes current keyboard device settings.">
+    Change keyboard settings
+  </message>
   <message name="IDS_SETTINGS_BLUETOOTH_TOGGLE_ACCESSIBILITY_LABEL" desc="Accessibility only label for Bluetooth enable/disable toggle.">
     Bluetooth enable
   </message>
@@ -2070,6 +2076,63 @@
   <message name="IDS_SETTINGS_BLUETOOTH_PAIRED_DEVICE_ITEM_BATTERY_PERCENTAGE" desc="Label for a paired Bluetooth device in a list that indicates the battery level of the device.">
     <ph name="BATTERY_PERCENTAGE">$1<ex>32</ex></ph>%
   </message>
+  <message name="IDS_SETTINGS_BLUETOOTH_PAIRED_DEVICE_ITEM_A11Y_LABEL_TYPE_UNKNOWN" desc="Accessibility label for paired Bluetooth device list item with a device type of unknown.">
+    Device <ph name="DEVICE_INDEX">$1<ex>1</ex></ph> of <ph name="DEVICE_COUNT">$2<ex>15</ex></ph>, <ph name="DEVICE_NAME">$3<ex>Beats</ex></ph>, Unknown device type
+  </message>
+  <message name="IDS_SETTINGS_BLUETOOTH_PAIRED_DEVICE_ITEM_A11Y_LABEL_TYPE_UNKNOWN_WITH_BATTERY_INFO" desc="Accessibility label for paired Bluetooth device list item with a device type of unknown and available battery info.">
+    Device <ph name="DEVICE_INDEX">$1<ex>1</ex></ph> of <ph name="DEVICE_COUNT">$2<ex>15</ex></ph>, <ph name="DEVICE_NAME">$3<ex>Beats</ex></ph>, Unknown device type, Battery level <ph name="BATTERY_PERCENTAGE">$4<ex>50</ex></ph>%
+  </message>
+  <message name="IDS_SETTINGS_BLUETOOTH_PAIRED_DEVICE_ITEM_A11Y_LABEL_TYPE_COMPUTER" desc="Accessibility label for paired Bluetooth device list item with a device type of computer.">
+    Device <ph name="DEVICE_INDEX">$1<ex>1</ex></ph> of <ph name="DEVICE_COUNT">$2<ex>15</ex></ph>, <ph name="DEVICE_NAME">$3<ex>Beats</ex></ph>, Computer
+  </message>
+  <message name="IDS_SETTINGS_BLUETOOTH_PAIRED_DEVICE_ITEM_A11Y_LABEL_TYPE_COMPUTER_WITH_BATTERY_INFO" desc="Accessibility label for paired Bluetooth device list item with a device type of computer and available battery info.">
+    Device <ph name="DEVICE_INDEX">$1<ex>1</ex></ph> of <ph name="DEVICE_COUNT">$2<ex>15</ex></ph>, <ph name="DEVICE_NAME">$3<ex>Beats</ex></ph>, Computer, Battery level <ph name="BATTERY_PERCENTAGE">$4<ex>50</ex></ph>%
+  </message>
+  <message name="IDS_SETTINGS_BLUETOOTH_PAIRED_DEVICE_ITEM_A11Y_LABEL_TYPE_PHONE" desc="Accessibility label for paired Bluetooth device list item with a device type of phone.">
+    Device <ph name="DEVICE_INDEX">$1<ex>1</ex></ph> of <ph name="DEVICE_COUNT">$2<ex>15</ex></ph>, <ph name="DEVICE_NAME">$3<ex>Beats</ex></ph>, Phone
+  </message>
+  <message name="IDS_SETTINGS_BLUETOOTH_PAIRED_DEVICE_ITEM_A11Y_LABEL_TYPE_PHONE_WITH_BATTERY_INFO" desc="Accessibility label for paired Bluetooth device list item with a device type of phone and available battery info.">
+    Device <ph name="DEVICE_INDEX">$1<ex>1</ex></ph> of <ph name="DEVICE_COUNT">$2<ex>15</ex></ph>, <ph name="DEVICE_NAME">$3<ex>Beats</ex></ph>, Phone, Battery level <ph name="BATTERY_PERCENTAGE">$4<ex>50</ex></ph>%
+  </message>
+  <message name="IDS_SETTINGS_BLUETOOTH_PAIRED_DEVICE_ITEM_A11Y_LABEL_TYPE_HEADSET" desc="Accessibility label for paired Bluetooth device list item with a device type of audio device.">
+    Device <ph name="DEVICE_INDEX">$1<ex>1</ex></ph> of <ph name="DEVICE_COUNT">$2<ex>15</ex></ph>, <ph name="DEVICE_NAME">$3<ex>Beats</ex></ph>, Audio device
+  </message>
+  <message name="IDS_SETTINGS_BLUETOOTH_PAIRED_DEVICE_ITEM_A11Y_LABEL_TYPE_HEADSET_WITH_BATTERY_INFO" desc="Accessibility label for paired Bluetooth device list item with a device type of audio device and available battery info.">
+    Device <ph name="DEVICE_INDEX">$1<ex>1</ex></ph> of <ph name="DEVICE_COUNT">$2<ex>15</ex></ph>, <ph name="DEVICE_NAME">$3<ex>Beats</ex></ph>, Audio device, Battery level <ph name="BATTERY_PERCENTAGE">$4<ex>50</ex></ph>%
+  </message>
+  <message name="IDS_SETTINGS_BLUETOOTH_PAIRED_DEVICE_ITEM_A11Y_LABEL_TYPE_VIDEO_CAMERA" desc="Accessibility label for paired Bluetooth device list item with a device type of video camera.">
+    Device <ph name="DEVICE_INDEX">$1<ex>1</ex></ph> of <ph name="DEVICE_COUNT">$2<ex>15</ex></ph>, <ph name="DEVICE_NAME">$3<ex>Beats</ex></ph>, Video camera
+  </message>
+  <message name="IDS_SETTINGS_BLUETOOTH_PAIRED_DEVICE_ITEM_A11Y_LABEL_TYPE_VIDEO_CAMERA_WITH_BATTERY_INFO" desc="Accessibility label for paired Bluetooth device list item with a device type of video camera and available battery info.">
+    Device <ph name="DEVICE_INDEX">$1<ex>1</ex></ph> of <ph name="DEVICE_COUNT">$2<ex>15</ex></ph>, <ph name="DEVICE_NAME">$3<ex>Beats</ex></ph>, Video camera, Battery level <ph name="BATTERY_PERCENTAGE">$4<ex>50</ex></ph>%
+  </message>
+  <message name="IDS_SETTINGS_BLUETOOTH_PAIRED_DEVICE_ITEM_A11Y_LABEL_TYPE_GAME_CONTROLLER" desc="Accessibility label for paired Bluetooth device list item with a device type of game controller.">
+    Device <ph name="DEVICE_INDEX">$1<ex>1</ex></ph> of <ph name="DEVICE_COUNT">$2<ex>15</ex></ph>, <ph name="DEVICE_NAME">$3<ex>Beats</ex></ph>, Game controller
+  </message>
+  <message name="IDS_SETTINGS_BLUETOOTH_PAIRED_DEVICE_ITEM_A11Y_LABEL_TYPE_GAME_CONTROLLER_WITH_BATTERY_INFO" desc="Accessibility label for paired Bluetooth device list item with a device type of game controller and available battery info.">
+    Device <ph name="DEVICE_INDEX">$1<ex>1</ex></ph> of <ph name="DEVICE_COUNT">$2<ex>15</ex></ph>, <ph name="DEVICE_NAME">$3<ex>Beats</ex></ph>, Game controller, Battery level <ph name="BATTERY_PERCENTAGE">$4<ex>50</ex></ph>%
+  </message>
+  <message name="IDS_SETTINGS_BLUETOOTH_PAIRED_DEVICE_ITEM_A11Y_LABEL_TYPE_KEYBOARD" desc="Accessibility label for paired Bluetooth device list item with a device type of keyboard.">
+    Device <ph name="DEVICE_INDEX">$1<ex>1</ex></ph> of <ph name="DEVICE_COUNT">$2<ex>15</ex></ph>, <ph name="DEVICE_NAME">$3<ex>Beats</ex></ph>, Keyboard
+  </message>
+  <message name="IDS_SETTINGS_BLUETOOTH_PAIRED_DEVICE_ITEM_A11Y_LABEL_TYPE_KEYBOARD_WITH_BATTERY_INFO" desc="Accessibility label for paired Bluetooth device list item with a device type of keyboard and available battery info.">
+    Device <ph name="DEVICE_INDEX">$1<ex>1</ex></ph> of <ph name="DEVICE_COUNT">$2<ex>15</ex></ph>, <ph name="DEVICE_NAME">$3<ex>Beats</ex></ph>, Keyboard, Battery level <ph name="BATTERY_PERCENTAGE">$4<ex>50</ex></ph>%
+  </message>
+  <message name="IDS_SETTINGS_BLUETOOTH_PAIRED_DEVICE_ITEM_A11Y_LABEL_TYPE_MOUSE" desc="Accessibility label for paired Bluetooth device list item with a device type of mouse.">
+    Device <ph name="DEVICE_INDEX">$1<ex>1</ex></ph> of <ph name="DEVICE_COUNT">$2<ex>15</ex></ph>, <ph name="DEVICE_NAME">$3<ex>Beats</ex></ph>, Mouse
+  </message>
+  <message name="IDS_SETTINGS_BLUETOOTH_PAIRED_DEVICE_ITEM_A11Y_LABEL_TYPE_MOUSE_WITH_BATTERY_INFO" desc="Accessibility label for paired Bluetooth device list item with a device type of mouse and available battery info.">
+    Device <ph name="DEVICE_INDEX">$1<ex>1</ex></ph> of <ph name="DEVICE_COUNT">$2<ex>15</ex></ph>, <ph name="DEVICE_NAME">$3<ex>Beats</ex></ph>, Mouse, Battery level <ph name="BATTERY_PERCENTAGE">$4<ex>50</ex></ph>%
+  </message>
+  <message name="IDS_SETTINGS_BLUETOOTH_PAIRED_DEVICE_ITEM_A11Y_LABEL_TYPE_TABLET" desc="Accessibility label for paired Bluetooth device list item with a device type of tablet.">
+    Device <ph name="DEVICE_INDEX">$1<ex>1</ex></ph> of <ph name="DEVICE_COUNT">$2<ex>15</ex></ph>, <ph name="DEVICE_NAME">$3<ex>Beats</ex></ph>, Tablet
+  </message>
+  <message name="IDS_SETTINGS_BLUETOOTH_PAIRED_DEVICE_ITEM_A11Y_LABEL_TYPE_TABLET_WITH_BATTERY_INFO" desc="Accessibility label for paired Bluetooth device list item with a device type of tablet and available battery info.">
+    Device <ph name="DEVICE_INDEX">$1<ex>1</ex></ph> of <ph name="DEVICE_COUNT">$2<ex>15</ex></ph>, <ph name="DEVICE_NAME">$3<ex>Beats</ex></ph>, Tablet, Battery level <ph name="BATTERY_PERCENTAGE">$4<ex>50</ex></ph>%
+  </message>
+  <message name="IDS_SETTINGS_BLUETOOTH_PAIRED_DEVICE_ITEM_SUBPAGE_BUTTON_A11Y_LABEL" desc="Accessibility label for button in a paired Bluetooth device list item that routes to the detail page for the device.">
+    <ph name="DEVICE">$1<ex>Beats</ex></ph>, Details
+  </message>
   <message name="IDS_SETTINGS_BLUETOOTH_DEVICE_DETAILS" desc="Name of the settings page which displays Bluetooth device details.">
     Bluetooth device details
   </message>
diff --git a/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_BLUETOOTH_DEVICE_DETAIL_CHANGE_DEVICE_SETTINGS_KEYBOARD.png.sha1 b/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_BLUETOOTH_DEVICE_DETAIL_CHANGE_DEVICE_SETTINGS_KEYBOARD.png.sha1
new file mode 100644
index 0000000..7303f4c
--- /dev/null
+++ b/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_BLUETOOTH_DEVICE_DETAIL_CHANGE_DEVICE_SETTINGS_KEYBOARD.png.sha1
@@ -0,0 +1 @@
+2dc524c14a803cfd6e1b7fac0dfcaea98ec1084f
\ No newline at end of file
diff --git a/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_BLUETOOTH_DEVICE_DETAIL_CHANGE_DEVICE_SETTINGS_MOUSE.png.sha1 b/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_BLUETOOTH_DEVICE_DETAIL_CHANGE_DEVICE_SETTINGS_MOUSE.png.sha1
new file mode 100644
index 0000000..6736288
--- /dev/null
+++ b/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_BLUETOOTH_DEVICE_DETAIL_CHANGE_DEVICE_SETTINGS_MOUSE.png.sha1
@@ -0,0 +1 @@
+0c0d756e242386c82a2359fe599e85eddd6607c9
\ No newline at end of file
diff --git a/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_BLUETOOTH_PAIRED_DEVICE_ITEM_A11Y_LABEL_TYPE_COMPUTER.png.sha1 b/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_BLUETOOTH_PAIRED_DEVICE_ITEM_A11Y_LABEL_TYPE_COMPUTER.png.sha1
new file mode 100644
index 0000000..63bb59a
--- /dev/null
+++ b/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_BLUETOOTH_PAIRED_DEVICE_ITEM_A11Y_LABEL_TYPE_COMPUTER.png.sha1
@@ -0,0 +1 @@
+77d711c149e055a401f13707015648435173b3be
\ No newline at end of file
diff --git a/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_BLUETOOTH_PAIRED_DEVICE_ITEM_A11Y_LABEL_TYPE_COMPUTER_WITH_BATTERY_INFO.png.sha1 b/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_BLUETOOTH_PAIRED_DEVICE_ITEM_A11Y_LABEL_TYPE_COMPUTER_WITH_BATTERY_INFO.png.sha1
new file mode 100644
index 0000000..cb1bd81c
--- /dev/null
+++ b/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_BLUETOOTH_PAIRED_DEVICE_ITEM_A11Y_LABEL_TYPE_COMPUTER_WITH_BATTERY_INFO.png.sha1
@@ -0,0 +1 @@
+df2f9b854954a5d4a8a1eb874793897e9b688bac
\ No newline at end of file
diff --git a/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_BLUETOOTH_PAIRED_DEVICE_ITEM_A11Y_LABEL_TYPE_GAME_CONTROLLER.png.sha1 b/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_BLUETOOTH_PAIRED_DEVICE_ITEM_A11Y_LABEL_TYPE_GAME_CONTROLLER.png.sha1
new file mode 100644
index 0000000..48203d87
--- /dev/null
+++ b/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_BLUETOOTH_PAIRED_DEVICE_ITEM_A11Y_LABEL_TYPE_GAME_CONTROLLER.png.sha1
@@ -0,0 +1 @@
+0f03c8c9f2d4b73a68317b3815960d188302c51d
\ No newline at end of file
diff --git a/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_BLUETOOTH_PAIRED_DEVICE_ITEM_A11Y_LABEL_TYPE_GAME_CONTROLLER_WITH_BATTERY_INFO.png.sha1 b/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_BLUETOOTH_PAIRED_DEVICE_ITEM_A11Y_LABEL_TYPE_GAME_CONTROLLER_WITH_BATTERY_INFO.png.sha1
new file mode 100644
index 0000000..ef61b4c1
--- /dev/null
+++ b/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_BLUETOOTH_PAIRED_DEVICE_ITEM_A11Y_LABEL_TYPE_GAME_CONTROLLER_WITH_BATTERY_INFO.png.sha1
@@ -0,0 +1 @@
+236471ea7a3253d9356f05ba96a9f2ad358ca458
\ No newline at end of file
diff --git a/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_BLUETOOTH_PAIRED_DEVICE_ITEM_A11Y_LABEL_TYPE_HEADSET.png.sha1 b/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_BLUETOOTH_PAIRED_DEVICE_ITEM_A11Y_LABEL_TYPE_HEADSET.png.sha1
new file mode 100644
index 0000000..1dde5c58
--- /dev/null
+++ b/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_BLUETOOTH_PAIRED_DEVICE_ITEM_A11Y_LABEL_TYPE_HEADSET.png.sha1
@@ -0,0 +1 @@
+74b63a5fba11bf77b9c74e48818de5058f8c1c70
\ No newline at end of file
diff --git a/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_BLUETOOTH_PAIRED_DEVICE_ITEM_A11Y_LABEL_TYPE_HEADSET_WITH_BATTERY_INFO.png.sha1 b/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_BLUETOOTH_PAIRED_DEVICE_ITEM_A11Y_LABEL_TYPE_HEADSET_WITH_BATTERY_INFO.png.sha1
new file mode 100644
index 0000000..8acd3b6
--- /dev/null
+++ b/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_BLUETOOTH_PAIRED_DEVICE_ITEM_A11Y_LABEL_TYPE_HEADSET_WITH_BATTERY_INFO.png.sha1
@@ -0,0 +1 @@
+d415824b9802d499cc6db252bd19a8db63c8b8ff
\ No newline at end of file
diff --git a/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_BLUETOOTH_PAIRED_DEVICE_ITEM_A11Y_LABEL_TYPE_KEYBOARD.png.sha1 b/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_BLUETOOTH_PAIRED_DEVICE_ITEM_A11Y_LABEL_TYPE_KEYBOARD.png.sha1
new file mode 100644
index 0000000..1f8f6e4
--- /dev/null
+++ b/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_BLUETOOTH_PAIRED_DEVICE_ITEM_A11Y_LABEL_TYPE_KEYBOARD.png.sha1
@@ -0,0 +1 @@
+2be849292e89f74ff78e7c2bc404fc3947c74207
\ No newline at end of file
diff --git a/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_BLUETOOTH_PAIRED_DEVICE_ITEM_A11Y_LABEL_TYPE_KEYBOARD_WITH_BATTERY_INFO.png.sha1 b/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_BLUETOOTH_PAIRED_DEVICE_ITEM_A11Y_LABEL_TYPE_KEYBOARD_WITH_BATTERY_INFO.png.sha1
new file mode 100644
index 0000000..f5ddc5d
--- /dev/null
+++ b/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_BLUETOOTH_PAIRED_DEVICE_ITEM_A11Y_LABEL_TYPE_KEYBOARD_WITH_BATTERY_INFO.png.sha1
@@ -0,0 +1 @@
+7b6643132fac08170adf0da0cd90a6a28879604f
\ No newline at end of file
diff --git a/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_BLUETOOTH_PAIRED_DEVICE_ITEM_A11Y_LABEL_TYPE_MOUSE.png.sha1 b/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_BLUETOOTH_PAIRED_DEVICE_ITEM_A11Y_LABEL_TYPE_MOUSE.png.sha1
new file mode 100644
index 0000000..6707e32
--- /dev/null
+++ b/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_BLUETOOTH_PAIRED_DEVICE_ITEM_A11Y_LABEL_TYPE_MOUSE.png.sha1
@@ -0,0 +1 @@
+025d119796fde76b9ae66e7dcd669f0214b92262
\ No newline at end of file
diff --git a/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_BLUETOOTH_PAIRED_DEVICE_ITEM_A11Y_LABEL_TYPE_MOUSE_WITH_BATTERY_INFO.png.sha1 b/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_BLUETOOTH_PAIRED_DEVICE_ITEM_A11Y_LABEL_TYPE_MOUSE_WITH_BATTERY_INFO.png.sha1
new file mode 100644
index 0000000..82b97d4
--- /dev/null
+++ b/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_BLUETOOTH_PAIRED_DEVICE_ITEM_A11Y_LABEL_TYPE_MOUSE_WITH_BATTERY_INFO.png.sha1
@@ -0,0 +1 @@
+c43dcc75766ed9300c62d624a5340d5753b151fc
\ No newline at end of file
diff --git a/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_BLUETOOTH_PAIRED_DEVICE_ITEM_A11Y_LABEL_TYPE_PHONE.png.sha1 b/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_BLUETOOTH_PAIRED_DEVICE_ITEM_A11Y_LABEL_TYPE_PHONE.png.sha1
new file mode 100644
index 0000000..3311d2fc
--- /dev/null
+++ b/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_BLUETOOTH_PAIRED_DEVICE_ITEM_A11Y_LABEL_TYPE_PHONE.png.sha1
@@ -0,0 +1 @@
+8eefe5c08186a9473717a44dc239e9b7c660a52d
\ No newline at end of file
diff --git a/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_BLUETOOTH_PAIRED_DEVICE_ITEM_A11Y_LABEL_TYPE_PHONE_WITH_BATTERY_INFO.png.sha1 b/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_BLUETOOTH_PAIRED_DEVICE_ITEM_A11Y_LABEL_TYPE_PHONE_WITH_BATTERY_INFO.png.sha1
new file mode 100644
index 0000000..1408d7b
--- /dev/null
+++ b/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_BLUETOOTH_PAIRED_DEVICE_ITEM_A11Y_LABEL_TYPE_PHONE_WITH_BATTERY_INFO.png.sha1
@@ -0,0 +1 @@
+2c7e33bf810d184b0e223976f76153a91ba9ae2a
\ No newline at end of file
diff --git a/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_BLUETOOTH_PAIRED_DEVICE_ITEM_A11Y_LABEL_TYPE_TABLET.png.sha1 b/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_BLUETOOTH_PAIRED_DEVICE_ITEM_A11Y_LABEL_TYPE_TABLET.png.sha1
new file mode 100644
index 0000000..58699a8
--- /dev/null
+++ b/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_BLUETOOTH_PAIRED_DEVICE_ITEM_A11Y_LABEL_TYPE_TABLET.png.sha1
@@ -0,0 +1 @@
+b749c7595996c5b471d803ec88b9a6dc69161fb8
\ No newline at end of file
diff --git a/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_BLUETOOTH_PAIRED_DEVICE_ITEM_A11Y_LABEL_TYPE_TABLET_WITH_BATTERY_INFO.png.sha1 b/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_BLUETOOTH_PAIRED_DEVICE_ITEM_A11Y_LABEL_TYPE_TABLET_WITH_BATTERY_INFO.png.sha1
new file mode 100644
index 0000000..f9d2d29
--- /dev/null
+++ b/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_BLUETOOTH_PAIRED_DEVICE_ITEM_A11Y_LABEL_TYPE_TABLET_WITH_BATTERY_INFO.png.sha1
@@ -0,0 +1 @@
+b7db253df89c7ce360c8dc16beaaab1dba959a06
\ No newline at end of file
diff --git a/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_BLUETOOTH_PAIRED_DEVICE_ITEM_A11Y_LABEL_TYPE_UNKNOWN.png.sha1 b/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_BLUETOOTH_PAIRED_DEVICE_ITEM_A11Y_LABEL_TYPE_UNKNOWN.png.sha1
new file mode 100644
index 0000000..c69328d3
--- /dev/null
+++ b/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_BLUETOOTH_PAIRED_DEVICE_ITEM_A11Y_LABEL_TYPE_UNKNOWN.png.sha1
@@ -0,0 +1 @@
+3dd05a9ab89e3fdff620068255f6b4c56c455385
\ No newline at end of file
diff --git a/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_BLUETOOTH_PAIRED_DEVICE_ITEM_A11Y_LABEL_TYPE_UNKNOWN_WITH_BATTERY_INFO.png.sha1 b/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_BLUETOOTH_PAIRED_DEVICE_ITEM_A11Y_LABEL_TYPE_UNKNOWN_WITH_BATTERY_INFO.png.sha1
new file mode 100644
index 0000000..b31a50f
--- /dev/null
+++ b/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_BLUETOOTH_PAIRED_DEVICE_ITEM_A11Y_LABEL_TYPE_UNKNOWN_WITH_BATTERY_INFO.png.sha1
@@ -0,0 +1 @@
+76ab44de9efc6172f425242f064de8f8e465bd7b
\ No newline at end of file
diff --git a/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_BLUETOOTH_PAIRED_DEVICE_ITEM_A11Y_LABEL_TYPE_VIDEO_CAMERA.png.sha1 b/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_BLUETOOTH_PAIRED_DEVICE_ITEM_A11Y_LABEL_TYPE_VIDEO_CAMERA.png.sha1
new file mode 100644
index 0000000..e4a0ad9
--- /dev/null
+++ b/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_BLUETOOTH_PAIRED_DEVICE_ITEM_A11Y_LABEL_TYPE_VIDEO_CAMERA.png.sha1
@@ -0,0 +1 @@
+dac2dfd7833f1e0a9ee0e0d07b4c73447457d788
\ No newline at end of file
diff --git a/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_BLUETOOTH_PAIRED_DEVICE_ITEM_A11Y_LABEL_TYPE_VIDEO_CAMERA_WITH_BATTERY_INFO.png.sha1 b/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_BLUETOOTH_PAIRED_DEVICE_ITEM_A11Y_LABEL_TYPE_VIDEO_CAMERA_WITH_BATTERY_INFO.png.sha1
new file mode 100644
index 0000000..5250785
--- /dev/null
+++ b/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_BLUETOOTH_PAIRED_DEVICE_ITEM_A11Y_LABEL_TYPE_VIDEO_CAMERA_WITH_BATTERY_INFO.png.sha1
@@ -0,0 +1 @@
+2719e66d6d8e42a0fb67eabd6ba7d99f3e24942e
\ No newline at end of file
diff --git a/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_BLUETOOTH_PAIRED_DEVICE_ITEM_SUBPAGE_BUTTON_A11Y_LABEL.png.sha1 b/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_BLUETOOTH_PAIRED_DEVICE_ITEM_SUBPAGE_BUTTON_A11Y_LABEL.png.sha1
new file mode 100644
index 0000000..18453f9
--- /dev/null
+++ b/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_BLUETOOTH_PAIRED_DEVICE_ITEM_SUBPAGE_BUTTON_A11Y_LABEL.png.sha1
@@ -0,0 +1 @@
+8ea28bdda83d0d5ce6a9a889bcdaae4fe7ceb1b8
\ No newline at end of file
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn
index 0be54dab..ee2cda4d 100644
--- a/chrome/browser/BUILD.gn
+++ b/chrome/browser/BUILD.gn
@@ -3042,6 +3042,7 @@
       "geolocation/geolocation_permission_context_delegate_android.h",
       "history_clusters/history_clusters_tab_helper_android.cc",
       "icon_loader_android.cc",
+      "incognito/android/incognito_utils_android.cc",
       "installable/installed_webapp_bridge.cc",
       "installable/installed_webapp_bridge.h",
       "installable/installed_webapp_geolocation_bridge.cc",
@@ -3172,7 +3173,6 @@
       "profiles/android/profile_manager_utils.cc",
       "profiles/android/profile_resolver.cc",
       "profiles/android/profile_resolver.h",
-      "profiles/incognito_utils_android.cc",
       "profiles/profile_android.cc",
       "profiles/profile_android.h",
       "profiles/profile_key_android.cc",
@@ -6992,7 +6992,6 @@
     deps += [
       "//chrome/browser/resources/chromeos/account_manager:web_components",
       "//chrome/browser/resources/chromeos/account_manager/components:web_components",
-      "//chrome/browser/resources/chromeos/add_supervision:post_message_api",
       "//chrome/browser/resources/chromeos/add_supervision:web_components",
       "//chrome/browser/resources/chromeos/crostini_installer:web_components",
       "//chrome/browser/resources/chromeos/crostini_upgrader:web_components",
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc
index accfb7dc..f2a7c81 100644
--- a/chrome/browser/about_flags.cc
+++ b/chrome/browser/about_flags.cc
@@ -1445,6 +1445,13 @@
      "(electronicexpress.com|zazzle.com|wish.com|homesquare.com)"}};
 const FeatureEntry::FeatureParam kNtpChromeCartModuleHeuristicsImprovement[] = {
     {ntp_features::kNtpChromeCartModuleHeuristicsImprovementParam, "true"}};
+const FeatureEntry::FeatureParam kNtpChromeCartModuleRBDAndCouponDiscount[] = {
+    {ntp_features::kNtpChromeCartModuleHeuristicsImprovementParam, "true"},
+    {ntp_features::kNtpChromeCartModuleAbandonedCartDiscountParam, "true"},
+    {ntp_features::NtpChromeCartModuleAbandonedCartDiscountUseUtmParam, "true"},
+    {"partner-merchant-pattern",
+     "(electronicexpress.com|zazzle.com|wish.com|homesquare.com)"},
+    {ntp_features::kNtpChromeCartModuleCouponParam, "true"}};
 const FeatureEntry::FeatureVariation kNtpChromeCartModuleVariations[] = {
     {"- Fake Data And Discount", kNtpChromeCartModuleFakeData,
      base::size(kNtpChromeCartModuleFakeData), nullptr},
@@ -1452,6 +1459,8 @@
      base::size(kNtpChromeCartModuleAbandonedCartDiscount), nullptr},
     {"- Heuristics Improvement", kNtpChromeCartModuleHeuristicsImprovement,
      base::size(kNtpChromeCartModuleHeuristicsImprovement), nullptr},
+    {"- RBD and Coupons", kNtpChromeCartModuleRBDAndCouponDiscount,
+     base::size(kNtpChromeCartModuleRBDAndCouponDiscount), nullptr},
 };
 
 const FeatureEntry::FeatureParam kNtpRecipeTasksModuleFakeData[] = {
@@ -2588,6 +2597,13 @@
      base::size(kContinuousSearchDoubleRowChip), nullptr}};
 #endif  // defined(OS_ANDROID)
 
+const FeatureEntry::Choice kDocumentTransitionSlowdownFactorChoices[] = {
+    {flags_ui::kGenericExperimentChoiceDefault, "", ""},
+    {"5", switches::kDocumentTransitionSlowdownFactor, "5"},
+    {"10", switches::kDocumentTransitionSlowdownFactor, "10"},
+    {"20", switches::kDocumentTransitionSlowdownFactor, "20"},
+    {"50", switches::kDocumentTransitionSlowdownFactor, "50"}};
+
 // RECORDING USER METRICS FOR FLAGS:
 // -----------------------------------------------------------------------------
 // The first line of the entry is the internal name.
@@ -3779,6 +3795,10 @@
     {"document-transition", flag_descriptions::kDocumentTransitionName,
      flag_descriptions::kDocumentTransitionDescription, kOsAll,
      FEATURE_VALUE_TYPE(blink::features::kDocumentTransition)},
+    {"document-transition-slowdown-factor",
+     flag_descriptions::kDocumentTransitionSlowdownFactorName,
+     flag_descriptions::kDocumentTransitionSlowdownFactorDescription, kOsAll,
+     MULTI_VALUE_TYPE(kDocumentTransitionSlowdownFactorChoices)},
 #if defined(OS_WIN)
     {"use-winrt-midi-api", flag_descriptions::kUseWinrtMidiApiName,
      flag_descriptions::kUseWinrtMidiApiDescription, kOsWin,
@@ -5573,12 +5593,12 @@
     {"enhanced-network-voices", flag_descriptions::kEnhancedNetworkVoicesName,
      flag_descriptions::kEnhancedNetworkVoicesDescription, kOsCrOS,
      FEATURE_VALUE_TYPE(features::kEnhancedNetworkVoices)},
-#endif  // BUILDFLAG(IS_CHROMEOS_ASH)
-
+#else
     {"enable-extended-sync-promos-capability",
      flag_descriptions::kEnableExtendedSyncPromosCapabilityName,
      flag_descriptions::kEnableExtendedSyncPromosCapabilityDescription,
      flags_ui::kOsAndroid, FEATURE_VALUE_TYPE(switches::kMinorModeSupport)},
+#endif  // BUILDFLAG(IS_CHROMEOS_ASH)
 
     {"enable-fenced-frames", flag_descriptions::kEnableFencedFramesName,
      flag_descriptions::kEnableFencedFramesDescription, kOsAll,
@@ -7469,6 +7489,11 @@
      flag_descriptions::kExtensionsMenuAccessControlDescription, kOsDesktop,
      FEATURE_VALUE_TYPE(features::kExtensionsMenuAccessControl)},
 
+    {"persistent-quota-is-temporary-quota",
+     flag_descriptions::kPersistentQuotaIsTemporaryQuotaName,
+     flag_descriptions::kPersistentQuotaIsTemporaryQuotaDescription, kOsAll,
+     FEATURE_VALUE_TYPE(blink::features::kPersistentQuotaIsTemporaryQuota)},
+
     // NOTE: Adding a new flag requires adding a corresponding entry to enum
     // "LoginCustomFlags" in tools/metrics/histograms/enums.xml. See "Flag
     // Histograms" in tools/metrics/histograms/README.md (run the
diff --git a/chrome/browser/android/feed/v2/feed_service_factory.cc b/chrome/browser/android/feed/v2/feed_service_factory.cc
index f6bd6309..9844bff4 100644
--- a/chrome/browser/android/feed/v2/feed_service_factory.cc
+++ b/chrome/browser/android/feed/v2/feed_service_factory.cc
@@ -13,6 +13,7 @@
 #include "chrome/browser/android/feed/v2/feed_service_bridge.h"
 #include "chrome/browser/android/feed/v2/refresh_task_scheduler_impl.h"
 #include "chrome/browser/browser_process.h"
+#include "chrome/browser/flags/android/chrome_feature_list.h"
 #include "chrome/browser/history/history_service_factory.h"
 #include "chrome/browser/metrics/chrome_metrics_service_accessor.h"
 #include "chrome/browser/profiles/profile.h"
@@ -120,6 +121,8 @@
   feed::ChromeInfo chrome_info;
   chrome_info.version = base::Version({CHROME_VERSION});
   chrome_info.channel = chrome::GetChannel();
+  chrome_info.start_surface =
+      base::FeatureList::IsEnabled(chrome::android::kStartSurfaceAndroid);
 
   return new FeedService(
       std::make_unique<FeedServiceDelegateImpl>(),
diff --git a/chrome/browser/apps/app_service/app_service_proxy_factory.h b/chrome/browser/apps/app_service/app_service_proxy_factory.h
index a8ae786..315303c 100644
--- a/chrome/browser/apps/app_service/app_service_proxy_factory.h
+++ b/chrome/browser/apps/app_service/app_service_proxy_factory.h
@@ -6,6 +6,7 @@
 #define CHROME_BROWSER_APPS_APP_SERVICE_APP_SERVICE_PROXY_FACTORY_H_
 
 #include "base/memory/singleton.h"
+#include "build/chromeos_buildflags.h"
 #include "components/keyed_service/content/browser_context_keyed_service_factory.h"
 
 class Profile;
diff --git a/chrome/browser/ash/accessibility/accessibility_input_method_observer.h b/chrome/browser/ash/accessibility/accessibility_input_method_observer.h
index 7fab5bd..53d23a4 100644
--- a/chrome/browser/ash/accessibility/accessibility_input_method_observer.h
+++ b/chrome/browser/ash/accessibility/accessibility_input_method_observer.h
@@ -10,6 +10,7 @@
 
 namespace ui {
 class InputMethod;
+class InputMethodObserver;
 }
 
 namespace ash {
diff --git a/chrome/browser/ash/arc/print_spooler/print_session_impl.cc b/chrome/browser/ash/arc/print_spooler/print_session_impl.cc
index 8516ac8..2ec3e7d 100644
--- a/chrome/browser/ash/arc/print_spooler/print_session_impl.cc
+++ b/chrome/browser/ash/arc/print_spooler/print_session_impl.cc
@@ -24,6 +24,7 @@
 #include "chrome/browser/ash/arc/print_spooler/arc_print_spooler_util.h"
 #include "chrome/browser/printing/print_view_manager_common.h"
 #include "chrome/browser/printing/printing_service.h"
+#include "chrome/services/printing/public/mojom/printing_service.mojom.h"
 #include "components/arc/intent_helper/custom_tab.h"
 #include "components/arc/mojom/print_common.mojom.h"
 #include "content/public/browser/web_contents.h"
diff --git a/chrome/browser/ash/customization/customization_document.h b/chrome/browser/ash/customization/customization_document.h
index 3a01f43..4d8e7526 100644
--- a/chrome/browser/ash/customization/customization_document.h
+++ b/chrome/browser/ash/customization/customization_document.h
@@ -16,6 +16,7 @@
 #include "base/macros.h"
 #include "base/memory/singleton.h"
 #include "base/memory/weak_ptr.h"
+#include "base/time/time.h"
 #include "base/values.h"
 #include "url/gurl.h"
 
diff --git a/chrome/browser/ash/enhanced_network_tts/enhanced_network_tts_impl.cc b/chrome/browser/ash/enhanced_network_tts/enhanced_network_tts_impl.cc
index aac6f8f9..191c1cf 100644
--- a/chrome/browser/ash/enhanced_network_tts/enhanced_network_tts_impl.cc
+++ b/chrome/browser/ash/enhanced_network_tts/enhanced_network_tts_impl.cc
@@ -11,6 +11,8 @@
 #include "base/bind.h"
 #include "base/logging.h"
 #include "base/no_destructor.h"
+#include "base/strings/string_util.h"
+#include "base/strings/utf_string_conversions.h"
 #include "chrome/browser/ash/enhanced_network_tts/enhanced_network_tts_constants.h"
 #include "chrome/browser/ash/enhanced_network_tts/enhanced_network_tts_utils.h"
 #include "components/google/core/common/google_util.h"
@@ -42,7 +44,8 @@
 
 EnhancedNetworkTtsImpl::EnhancedNetworkTtsImpl()
     : api_key_(kApiKey.Get().empty() ? google_apis::GetReadAloudAPIKey()
-                                     : kApiKey.Get()) {}
+                                     : kApiKey.Get()),
+      char_limit_per_request_(mojom::kEnhancedNetworkTtsMaxCharacterSize) {}
 EnhancedNetworkTtsImpl::~EnhancedNetworkTtsImpl() = default;
 
 void EnhancedNetworkTtsImpl::BindReceiverAndURLFactory(
@@ -80,20 +83,50 @@
     return;
   }
 
-  // TODO(crbug.com/1240445): Chop the utterance into text pieces, and queue
-  // them into the |server_requests_|. Currently we send the entire utterance
-  // as a single text piece.
-  std::unique_ptr<network::SimpleURLLoader> url_loader = MakeRequestLoader();
-  url_loader->AttachStringForUpload(FormatJsonRequest(std::move(request)),
-                                    kNetworkRequestUploadType);
-  server_requests_.emplace_back(std::move(url_loader),
-                                0 /* text_piece_start_index */,
-                                true /* is_last_request */);
+  std::u16string utterance_u16string = base::UTF8ToUTF16(request->utterance);
+  // Ignore the whitespaces at start. The ICU break iterator does not work well
+  // with text that has whitespaces at start. We must trim the text before
+  // sending it to |FindTextBreaks|.
+  int start_offset = 0;
+  while (base::IsUnicodeWhitespace(utterance_u16string[start_offset])) {
+    start_offset++;
+  }
+  utterance_u16string = utterance_u16string.substr(start_offset);
+
+  // Chop the utterance into smaller text pieces and queue them into
+  // |server_requests_|.
+  std::vector<uint16_t> text_breaks =
+      FindTextBreaks(utterance_u16string, char_limit_per_request_);
+  uint16_t text_piece_start_index = 0;
+  for (int i = 0; i < text_breaks.size(); i++) {
+    uint16_t text_piece_end_index = text_breaks[i];
+    auto size = text_piece_end_index - text_piece_start_index + 1;
+    const std::string text_piece = base::UTF16ToUTF8(
+        utterance_u16string.substr(text_piece_start_index, size));
+
+    mojom::TtsRequestPtr new_tts_request = mojom::TtsRequest::New(
+        text_piece, request->rate, request->voice, request->lang);
+    std::unique_ptr<network::SimpleURLLoader> url_loader = MakeRequestLoader();
+    const bool last_request = i == text_breaks.size() - 1;
+    url_loader->AttachStringForUpload(
+        FormatJsonRequest(std::move(new_tts_request)),
+        kNetworkRequestUploadType);
+    server_requests_.emplace_back(std::move(url_loader),
+                                  text_piece_start_index + start_offset,
+                                  last_request);
+
+    // Prepare for the next text piece.
+    text_piece_start_index = text_piece_end_index + 1;
+  }
 
   // Kick off the server requests.
   ProcessNextServerRequest();
 }
 
+void EnhancedNetworkTtsImpl::SetCharLimitPerRequestForTesting(int limit) {
+  char_limit_per_request_ = limit;
+}
+
 data_decoder::mojom::JsonParser* EnhancedNetworkTtsImpl::GetJsonParser() {
   // TODO(crbug.com/1217301): Sets an explicit disconnect handler.
   if (!json_parser_) {
diff --git a/chrome/browser/ash/enhanced_network_tts/enhanced_network_tts_impl.h b/chrome/browser/ash/enhanced_network_tts/enhanced_network_tts_impl.h
index 4e363f5..ccc7e46 100644
--- a/chrome/browser/ash/enhanced_network_tts/enhanced_network_tts_impl.h
+++ b/chrome/browser/ash/enhanced_network_tts/enhanced_network_tts_impl.h
@@ -52,6 +52,11 @@
   static constexpr base::Feature kOverrideParams{
       "EnhancedNetworkTtsOverride", base::FEATURE_DISABLED_BY_DEFAULT};
 
+  // Set the character limit of text piece in each |ServerRequest|. Unit tests
+  // can use this method to modify the limit. Otherwise, the limit is set to
+  // |mojom::kEnhancedNetworkTtsMaxCharacterSize|.
+  void SetCharLimitPerRequestForTesting(int limit);
+
  private:
   // An input utterance may be chopped into several text pieces, which will be
   // sent over several |ServerRequest|. A |ServerRequest| contains three
@@ -131,6 +136,11 @@
 
   const std::string api_key_;
 
+  // The character limit of text piece in each |ServerRequest|. The limit is set
+  // to |mojom::kEnhancedNetworkTtsMaxCharacterSize| but can be overridden by
+  // |SetCharLimitPerRequestForTesting|.
+  int char_limit_per_request_;
+
   // Used for all callbacks.
   base::WeakPtrFactory<EnhancedNetworkTtsImpl> weak_factory_{this};
 
diff --git a/chrome/browser/ash/enhanced_network_tts/enhanced_network_tts_impl_unittest.cc b/chrome/browser/ash/enhanced_network_tts/enhanced_network_tts_impl_unittest.cc
index 05734cde..1dfde40 100644
--- a/chrome/browser/ash/enhanced_network_tts/enhanced_network_tts_impl_unittest.cc
+++ b/chrome/browser/ash/enhanced_network_tts/enhanced_network_tts_impl_unittest.cc
@@ -235,6 +235,48 @@
   EXPECT_EQ(timing_data[1].text_offset, 6);
 }
 
+TEST_F(EnhancedNetworkTtsImplTest, GetAudioDataIgnoresWhitespacesAtStart) {
+  const std::string input_text = "    test1 test2";
+  const std::string input_text_trimmed = "test1 test2";
+  const float rate = 1.0;
+  GetTestingInstance().GetAudioData(
+      mojom::TtsRequest::New(input_text, rate, absl::nullopt, absl::nullopt),
+      base::BindOnce(
+          [](TestAudioDataObserverImpl* observer,
+             mojo::PendingReceiver<mojom::AudioDataObserver> pending_receiver) {
+            observer->BindReceiver(std::move(pending_receiver));
+          },
+          GetTestingObserverPtr()));
+  test_task_env_.RunUntilIdle();
+
+  const std::map<std::string, absl::optional<std::string>> expected_headers = {
+      {kGoogApiKeyHeader, google_apis::GetReadAloudAPIKey()}};
+  const std::string expected_body =
+      CreateCorrectRequest(input_text_trimmed, rate);
+  // |expected_output| here is arbitrary, which is encoded into a fake response
+  // sent by the fake server, |TestServerURLLoaderFactory|. In general, we
+  // expect the real server sends the audio data back as a base64 encoded JSON
+  // string.
+  const std::vector<uint8_t> expected_output = {1, 2, 5};
+  test_url_factory_.ExpectRequestAndSimulateResponse(
+      kReadAloudServerUrl, expected_headers, expected_body,
+      CreateServerResponse(expected_output), net::HTTP_OK);
+  test_task_env_.RunUntilIdle();
+
+  // We only get the data after the server's response. We simulate the response
+  // in the code above.
+  absl::optional<mojom::TtsRequestError> error;
+  std::vector<uint8_t> audio_data;
+  std::vector<mojom::TimingInfo> timing_data;
+  UnpackResult(&error, &audio_data, &timing_data,
+               GetTestingObserverPtr()->GetNexResponse());
+  // The text offset will be compensated with whitespaces.
+  EXPECT_EQ(timing_data[0].text, "test1");
+  EXPECT_EQ(timing_data[0].text_offset, 4);
+  EXPECT_EQ(timing_data[1].text, "test2");
+  EXPECT_EQ(timing_data[1].text_offset, 10);
+}
+
 TEST_F(EnhancedNetworkTtsImplTest, GetAudioDataSucceedsWithFasterRate) {
   const std::string input_text = "Rate will be capped to kMaxRate";
   const float rate = kMaxRate + 1.0f;
@@ -307,6 +349,53 @@
   EXPECT_EQ(audio_data, expected_output);
 }
 
+TEST_F(EnhancedNetworkTtsImplTest, GetAudioDataWithLongUtterance) {
+  const std::string input_text = "Sent 1. Hello world!";
+  const float rate = 1.0;
+  // Sets the limit to cover the first sentence and every words in the second
+  // sentence.
+  GetTestingInstance().SetCharLimitPerRequestForTesting(8);
+  GetTestingInstance().GetAudioData(
+      mojom::TtsRequest::New(input_text, rate, absl::nullopt, absl::nullopt),
+      base::BindOnce(
+          [](TestAudioDataObserverImpl* observer,
+             mojo::PendingReceiver<mojom::AudioDataObserver> pending_receiver) {
+            observer->BindReceiver(std::move(pending_receiver));
+          },
+          GetTestingObserverPtr()));
+  test_task_env_.RunUntilIdle();
+
+  const std::map<std::string, absl::optional<std::string>> expected_headers = {
+      {kGoogApiKeyHeader, google_apis::GetReadAloudAPIKey()}};
+  // |expected_output| here is arbitrary, which is encoded into a fake response
+  // sent by the fake server, |TestServerURLLoaderFactory|. In general, we
+  // expect the real server sends the audio data back as a base64 encoded JSON
+  // string.
+  const std::vector<uint8_t> expected_output = {1, 2, 5};
+
+  // The first request contains the first sentence.
+  const std::string first_expected_body =
+      CreateCorrectRequest("Sent 1. ", rate);
+  test_url_factory_.ExpectRequestAndSimulateResponse(
+      kReadAloudServerUrl, expected_headers, first_expected_body,
+      CreateServerResponse(expected_output), net::HTTP_OK);
+  test_task_env_.RunUntilIdle();
+
+  // The second request contains the first word in the second sentence.
+  const std::string second_expected_body = CreateCorrectRequest("Hello", rate);
+  test_url_factory_.ExpectRequestAndSimulateResponse(
+      kReadAloudServerUrl, expected_headers, second_expected_body,
+      CreateServerResponse(expected_output), net::HTTP_OK);
+  test_task_env_.RunUntilIdle();
+
+  // The third request contains the second word in the second sentence.
+  const std::string third_expected_body = CreateCorrectRequest(" world!", rate);
+  test_url_factory_.ExpectRequestAndSimulateResponse(
+      kReadAloudServerUrl, expected_headers, third_expected_body,
+      CreateServerResponse(expected_output), net::HTTP_OK);
+  test_task_env_.RunUntilIdle();
+}
+
 TEST_F(EnhancedNetworkTtsImplTest, EmptyUtteranceError) {
   const std::string input_text("");
   const float rate = 1.0;
diff --git a/chrome/browser/ash/enhanced_network_tts/enhanced_network_tts_utils.cc b/chrome/browser/ash/enhanced_network_tts/enhanced_network_tts_utils.cc
index 713138f..7fdd910 100644
--- a/chrome/browser/ash/enhanced_network_tts/enhanced_network_tts_utils.cc
+++ b/chrome/browser/ash/enhanced_network_tts/enhanced_network_tts_utils.cc
@@ -12,7 +12,7 @@
 #include "base/json/json_writer.h"
 #include "base/logging.h"
 #include "base/numerics/safe_conversions.h"
-#include "base/strings/utf_string_conversions.h"
+#include "base/strings/string_util.h"
 #include "chrome/browser/ash/enhanced_network_tts/enhanced_network_tts_constants.h"
 #include "ui/accessibility/ax_text_utils.h"
 
@@ -93,7 +93,7 @@
   return json_request;
 }
 
-std::vector<uint16_t> FindTextBreaks(const std::string& utterance,
+std::vector<uint16_t> FindTextBreaks(const std::u16string& utterance,
                                      const int length_limit) {
   std::vector<uint16_t> breaks;
   DCHECK_GT(length_limit, 0);
@@ -101,6 +101,11 @@
   if (utterance.empty())
     return breaks;
 
+  // The input utterance must be pre-trimmed so that it does not start with
+  // whitespaces. The ICU break iterator does not work well with text that
+  // has whitespaces at start.
+  DCHECK(!base::IsUnicodeWhitespace(utterance[0]));
+
   const int utterance_length = utterance.length();
   if (utterance_length <= length_limit) {
     breaks.push_back(utterance_length - 1);
@@ -113,12 +118,9 @@
     return breaks;
   }
 
-  const std::u16string utterance_u16string = base::UTF8ToUTF16(utterance);
-
-  std::vector<int> sentence_ends =
-      ui::GetSentenceEndOffsets(utterance_u16string);
+  std::vector<int> sentence_ends = ui::GetSentenceEndOffsets(utterance);
   ConvertOffsetsToIndexes(sentence_ends);
-  std::vector<int> word_ends = ui::GetWordEndOffsets(utterance_u16string);
+  std::vector<int> word_ends = ui::GetWordEndOffsets(utterance);
   ConvertOffsetsToIndexes(word_ends);
 
   const int sentence_ends_length = sentence_ends.size();
@@ -231,10 +233,14 @@
         timing_info.FindStringPath("location.timeLocation.timeOffset");
     const std::string* timing_info_duration_ptr =
         timing_info.FindStringPath("location.timeLocation.duration");
-    // The first item in the timing_info_list does not have a text offset, we
-    // default that to 0.
-    const absl::optional<int> timing_info_text_offset =
-        i == 0 ? 0 : timing_info.FindIntPath("location.textLocation.offset");
+    // If the first item in the timing_info_list does not have a text offset,
+    // we default that to 0. If the first item starts with whitespaces, the
+    // server will send back the text offset for the item.
+    absl::optional<int> timing_info_text_offset =
+        timing_info.FindIntPath("location.textLocation.offset");
+    if (timing_info_text_offset == absl::nullopt && i == 0) {
+      timing_info_text_offset = 0;
+    }
 
     if (timing_info_text_offset == absl::nullopt || !timing_info_text_ptr ||
         !timing_info_timeoffset_ptr || !timing_info_duration_ptr) {
diff --git a/chrome/browser/ash/enhanced_network_tts/enhanced_network_tts_utils.h b/chrome/browser/ash/enhanced_network_tts/enhanced_network_tts_utils.h
index d19e9a7..c9bc844 100644
--- a/chrome/browser/ash/enhanced_network_tts/enhanced_network_tts_utils.h
+++ b/chrome/browser/ash/enhanced_network_tts/enhanced_network_tts_utils.h
@@ -26,8 +26,10 @@
 // contains a over-length word and we calculate text breaks using
 // |length_limit| directly. The returned vector contains the indexes for the
 // breaks. For example, a [2, 4] vector means we should pass the utterance
-// "hello" as: "hel" and "lo".
-std::vector<uint16_t> FindTextBreaks(const std::string& utterance,
+// "hello" as: "hel" and "lo". The |utterance| must not start with
+// whitespaces, and the caller might need to pre-trim the |utterance| before
+// calling this method.
+std::vector<uint16_t> FindTextBreaks(const std::u16string& utterance,
                                      const int length_limit);
 
 // Generate a response when encountering errors (e.g., server error, unexpected
diff --git a/chrome/browser/ash/enhanced_network_tts/enhanced_network_tts_utils_unittest.cc b/chrome/browser/ash/enhanced_network_tts/enhanced_network_tts_utils_unittest.cc
index 4ece57a..8ce45dc 100644
--- a/chrome/browser/ash/enhanced_network_tts/enhanced_network_tts_utils_unittest.cc
+++ b/chrome/browser/ash/enhanced_network_tts/enhanced_network_tts_utils_unittest.cc
@@ -75,23 +75,23 @@
 }
 
 TEST_F(EnhancedNetworkTtsUtilsTest, FindTextBreaks) {
-  std::string utterance = "";
+  std::u16string utterance = u"";
   int length_limit = 10;
   std::vector<uint16_t> expected_output = {};
   EXPECT_EQ(FindTextBreaks(utterance, length_limit), expected_output);
 
-  utterance = "utterance is shorter than length_limit";
+  utterance = u"utterance is shorter than length_limit";
   length_limit = 1000;
   expected_output = {37};
   EXPECT_EQ(FindTextBreaks(utterance, length_limit), expected_output);
 
-  utterance = "limit is 1";
+  utterance = u"limit is 1";
   length_limit = 1;
   expected_output = {1, 2, 3, 4, 5, 6, 7, 8, 9};
   EXPECT_EQ(FindTextBreaks(utterance, length_limit), expected_output);
 
-  // Index ref:012345678901234
-  utterance = "Sent 1! Sent 2!";
+  // Index ref: 012345678901234
+  utterance = u"Sent 1! Sent 2!";
   length_limit = 4;
   // 3 = word end of "Sent"
   // 7 = first sentence end
@@ -100,8 +100,8 @@
   expected_output = {3, 7, 11, 14};
   EXPECT_EQ(FindTextBreaks(utterance, length_limit), expected_output);
 
-  // Index ref:01234567890123456789012
-  utterance = "Sent 1! Sent 2. Sent 3!";
+  // Index ref: 01234567890123456789012
+  utterance = u"Sent 1! Sent 2. Sent 3!";
   length_limit = 8;
   // 7 = first sentence end
   // 15 = second sentence end
@@ -109,8 +109,8 @@
   expected_output = {7, 15, 22};
   EXPECT_EQ(FindTextBreaks(utterance, length_limit), expected_output);
 
-  // Index ref:01234567890123456
-  utterance = "Sent 1! Sent two!";
+  // Index ref: 01234567890123456
+  utterance = u"Sent 1! Sent two!";
   length_limit = 3;
   // 2 = over length limit at char 'n'
   // 5 = word end of "1"
diff --git a/chrome/browser/ash/file_system_provider/operations/read_file_unittest.cc b/chrome/browser/ash/file_system_provider/operations/read_file_unittest.cc
index 23fe0f7d..4920fed8 100644
--- a/chrome/browser/ash/file_system_provider/operations/read_file_unittest.cc
+++ b/chrome/browser/ash/file_system_provider/operations/read_file_unittest.cc
@@ -7,6 +7,7 @@
 #include <memory>
 #include <string>
 #include <utility>
+#include <vector>
 
 #include "base/bind.h"
 #include "base/containers/span.h"
@@ -164,15 +165,15 @@
   const bool has_more = false;
   const int execution_time = 0;
 
-  base::Value values_as_list(base::Value::Type::LIST);
-  values_as_list.Append(kFileSystemId);
-  values_as_list.Append(kRequestId);
-  values_as_list.Append(base::Value(base::as_bytes(base::make_span(data))));
-  values_as_list.Append(has_more);
-  values_as_list.Append(execution_time);
+  std::vector<base::Value> values_as_list;
+  values_as_list.emplace_back(kFileSystemId);
+  values_as_list.emplace_back(kRequestId);
+  values_as_list.emplace_back(
+      base::Value(base::as_bytes(base::make_span(data))));
+  values_as_list.emplace_back(has_more);
+  values_as_list.emplace_back(execution_time);
 
-  std::unique_ptr<Params> params(
-      Params::Create(base::Value::AsListValue(std::move(values_as_list))));
+  std::unique_ptr<Params> params(Params::Create(std::move(values_as_list)));
   ASSERT_TRUE(params.get());
   std::unique_ptr<RequestValue> request_value(
       RequestValue::CreateForReadFileSuccess(std::move(params)));
diff --git a/chrome/browser/ash/file_system_provider/provided_file_system_unittest.cc b/chrome/browser/ash/file_system_provider/provided_file_system_unittest.cc
index 8e4fd98..0639e83f 100644
--- a/chrome/browser/ash/file_system_provider/provided_file_system_unittest.cc
+++ b/chrome/browser/ash/file_system_provider/provided_file_system_unittest.cc
@@ -93,7 +93,7 @@
 
       using extensions::api::file_system_provider_internal::
           OperationRequestedSuccess::Params;
-      std::unique_ptr<Params> params(Params::Create(value_as_list));
+      std::unique_ptr<Params> params(Params::Create(value_as_list.GetList()));
       ASSERT_TRUE(params.get());
       file_system_->GetRequestManager()->FulfillRequest(
           request_id,
diff --git a/chrome/browser/ash/full_restore/full_restore_service.cc b/chrome/browser/ash/full_restore/full_restore_service.cc
index 55cecd5..d5b1787 100644
--- a/chrome/browser/ash/full_restore/full_restore_service.cc
+++ b/chrome/browser/ash/full_restore/full_restore_service.cc
@@ -197,9 +197,6 @@
   if (notification_ != nullptr && !is_shut_down_) {
     NotificationDisplayService::GetForProfile(profile_)->Close(
         NotificationHandler::Type::TRANSIENT, notification_->id());
-
-    if (accelerator_controller_observer_.IsObserving())
-      accelerator_controller_observer_.Reset();
   }
 
   if (allow_save) {
@@ -275,21 +272,6 @@
   ::full_restore::FullRestoreSaveHandler::GetInstance()->SetShutDown();
 }
 
-void FullRestoreService::OnActionPerformed(AcceleratorAction action) {
-  switch (action) {
-    case NEW_INCOGNITO_WINDOW:
-    case NEW_TAB:
-    case NEW_WINDOW:
-    case OPEN_CROSH:
-    case OPEN_DIAGNOSTICS:
-    case RESTORE_TAB:
-      MaybeCloseNotification();
-      return;
-    default:
-      return;
-  }
-}
-
 void FullRestoreService::SetAppLaunchHanlderForTesting(
     std::unique_ptr<FullRestoreAppLaunchHandler> app_launch_handler) {
   app_launch_handler_ = std::move(app_launch_handler);
@@ -303,12 +285,6 @@
   if (!ShouldShowNotification())
     return;
 
-  auto* accelerator_controller = ash::AcceleratorController::Get();
-  if (accelerator_controller) {
-    DCHECK(!accelerator_controller_observer_.IsObserving());
-    accelerator_controller_observer_.Observe(accelerator_controller);
-  }
-
   message_center::RichNotificationData notification_data;
 
   message_center::ButtonInfo restore_button(
diff --git a/chrome/browser/ash/full_restore/full_restore_service.h b/chrome/browser/ash/full_restore/full_restore_service.h
index 808a3ec..851b45c7 100644
--- a/chrome/browser/ash/full_restore/full_restore_service.h
+++ b/chrome/browser/ash/full_restore/full_restore_service.h
@@ -7,9 +7,7 @@
 
 #include <memory>
 
-#include "ash/public/cpp/accelerators.h"
 #include "base/memory/weak_ptr.h"
-#include "base/scoped_observation.h"
 #include "components/keyed_service/core/keyed_service.h"
 #include "components/prefs/pref_change_registrar.h"
 #include "content/public/browser/notification_observer.h"
@@ -57,8 +55,7 @@
 // interfaces to restore the app launchings and app windows.
 class FullRestoreService : public KeyedService,
                            public message_center::NotificationObserver,
-                           public content::NotificationObserver,
-                           public ash::AcceleratorController::Observer {
+                           public content::NotificationObserver {
  public:
   static FullRestoreService* GetForProfile(Profile* profile);
   static void MaybeCloseNotification(Profile* profile);
@@ -89,9 +86,6 @@
                const content::NotificationSource& source,
                const content::NotificationDetails& details) override;
 
-  // ash::AcceleratorController::Observer:
-  void OnActionPerformed(AcceleratorAction action) override;
-
   FullRestoreAppLaunchHandler* app_launch_handler() {
     return app_launch_handler_.get();
   }
@@ -160,10 +154,6 @@
 
   content::NotificationRegistrar notification_registrar_;
 
-  base::ScopedObservation<ash::AcceleratorController,
-                          ash::AcceleratorController::Observer>
-      accelerator_controller_observer_{this};
-
   base::WeakPtrFactory<FullRestoreService> weak_ptr_factory_{this};
 };
 
diff --git a/chrome/browser/ash/login/screens/error_screen.cc b/chrome/browser/ash/login/screens/error_screen.cc
index cefa0b3..38ac42f 100644
--- a/chrome/browser/ash/login/screens/error_screen.cc
+++ b/chrome/browser/ash/login/screens/error_screen.cc
@@ -39,7 +39,6 @@
 #include "chromeos/network/portal_detector/network_portal_detector_strategy.h"
 #include "components/session_manager/core/session_manager.h"
 #include "components/web_modal/web_contents_modal_dialog_manager.h"
-#include "content/public/browser/notification_service.h"
 #include "third_party/cros_system_api/dbus/service_constants.h"
 #include "ui/gfx/native_widget_types.h"
 
@@ -192,10 +191,7 @@
 
 void ErrorScreen::DoShow() {
   LOG(WARNING) << "Network error screen message is shown";
-  content::NotificationService::current()->Notify(
-      chrome::NOTIFICATION_LOGIN_NETWORK_ERROR_SHOWN,
-      content::NotificationService::AllSources(),
-      content::NotificationService::NoDetails());
+  session_manager::SessionManager::Get()->NotifyNetworkErrorScreenShown();
   network_portal_detector::GetInstance()->SetStrategy(
       PortalDetectorStrategy::STRATEGY_ID_ERROR_SCREEN);
 }
diff --git a/chrome/browser/ash/login/ui/login_display_host_webui.cc b/chrome/browser/ash/login/ui/login_display_host_webui.cc
index c531c98..3fe58ce 100644
--- a/chrome/browser/ash/login/ui/login_display_host_webui.cc
+++ b/chrome/browser/ash/login/ui/login_display_host_webui.cc
@@ -454,8 +454,8 @@
   // these notifications.
   registrar_.Add(this, chrome::NOTIFICATION_LOGIN_OR_LOCK_WEBUI_VISIBLE,
                  content::NotificationService::AllSources());
-  registrar_.Add(this, chrome::NOTIFICATION_LOGIN_NETWORK_ERROR_SHOWN,
-                 content::NotificationService::AllSources());
+
+  session_observation_.Observe(session_manager::SessionManager::Get());
 
   audio::SoundsManager* manager = audio::SoundsManager::Get();
   ui::ResourceBundle& bundle = ui::ResourceBundle::GetSharedInstance();
@@ -694,14 +694,12 @@
     const content::NotificationDetails& details) {
   LoginDisplayHostCommon::Observe(type, source, details);
 
-  if (chrome::NOTIFICATION_LOGIN_OR_LOCK_WEBUI_VISIBLE == type ||
-      chrome::NOTIFICATION_LOGIN_NETWORK_ERROR_SHOWN == type) {
+  if (chrome::NOTIFICATION_LOGIN_OR_LOCK_WEBUI_VISIBLE == type) {
     VLOG(1) << "Login WebUI >> WEBUI_VISIBLE";
     ShowWebUI();
     registrar_.Remove(this, chrome::NOTIFICATION_LOGIN_OR_LOCK_WEBUI_VISIBLE,
                       content::NotificationService::AllSources());
-    registrar_.Remove(this, chrome::NOTIFICATION_LOGIN_NETWORK_ERROR_SHOWN,
-                      content::NotificationService::AllSources());
+    session_observation_.Reset();
   }
 }
 
@@ -1076,6 +1074,14 @@
   observers_.RemoveObserver(observer);
 }
 
+void LoginDisplayHostWebUI::OnNetworkErrorScreenShown() {
+  VLOG(1) << "Login WebUI >> WEBUI_VISIBLE";
+  ShowWebUI();
+  registrar_.Remove(this, chrome::NOTIFICATION_LOGIN_OR_LOCK_WEBUI_VISIBLE,
+                    content::NotificationService::AllSources());
+  session_observation_.Reset();
+}
+
 void LoginDisplayHostWebUI::PlayStartupSoundIfPossible() {
   if (!need_to_play_startup_sound_ || oobe_startup_sound_played_)
     return;
diff --git a/chrome/browser/ash/login/ui/login_display_host_webui.h b/chrome/browser/ash/login/ui/login_display_host_webui.h
index f7f7bcc..53f62ba 100644
--- a/chrome/browser/ash/login/ui/login_display_host_webui.h
+++ b/chrome/browser/ash/login/ui/login_display_host_webui.h
@@ -15,6 +15,7 @@
 #include "base/macros.h"
 #include "base/memory/weak_ptr.h"
 #include "base/observer_list.h"
+#include "base/scoped_observation.h"
 #include "base/timer/elapsed_timer.h"
 #include "chrome/browser/ash/login/existing_user_controller.h"
 #include "chrome/browser/ash/login/oobe_configuration.h"
@@ -23,6 +24,8 @@
 #include "chrome/browser/ash/login/wizard_controller.h"
 #include "chrome/browser/ui/ash/multi_user/multi_user_window_manager_helper.h"
 #include "chromeos/dbus/session_manager/session_manager_client.h"
+#include "components/session_manager/core/session_manager.h"
+#include "components/session_manager/core/session_manager_observer.h"
 #include "content/public/browser/notification_observer.h"
 #include "content/public/browser/notification_registrar.h"
 #include "content/public/browser/web_contents_observer.h"
@@ -46,6 +49,7 @@
 // WebUI signals ready (via NOTIFICATION_LOGIN_OR_LOCK_WEBUI_VISIBLE) or there
 // is a network error (via NOTIFICATION_LOGIN_NETWORK_ERROR_SHOWN).
 class LoginDisplayHostWebUI : public LoginDisplayHostCommon,
+                              public session_manager::SessionManagerObserver,
                               public content::WebContentsObserver,
                               public chromeos::SessionManagerClient::Observer,
                               public CrasAudioHandler::AudioObserver,
@@ -93,6 +97,9 @@
   void AddObserver(LoginDisplayHost::Observer* observer) override;
   void RemoveObserver(LoginDisplayHost::Observer* observer) override;
 
+  // session_manager::SessionManagerObserver:
+  void OnNetworkErrorScreenShown() override;
+
   // Trace id for ShowLoginWebUI event (since there exists at most one login
   // WebUI at a time).
   static const char kShowLoginWebUIid[];
@@ -270,6 +277,10 @@
   // Measures OOBE WebUI load time.
   absl::optional<base::ElapsedTimer> oobe_load_timer_;
 
+  base::ScopedObservation<session_manager::SessionManager,
+                          session_manager::SessionManagerObserver>
+      session_observation_{this};
+
   display::ScopedDisplayObserver display_observer_{this};
 
   base::ObserverList<LoginDisplayHost::Observer> observers_;
diff --git a/chrome/browser/ash/login/ui/webui_login_view.cc b/chrome/browser/ash/login/ui/webui_login_view.cc
index 0ee0772..a8f85648 100644
--- a/chrome/browser/ash/login/ui/webui_login_view.cc
+++ b/chrome/browser/ash/login/ui/webui_login_view.cc
@@ -38,7 +38,6 @@
 #include "chromeos/network/network_state_handler.h"
 #include "components/content_settings/core/common/content_settings_pattern.h"
 #include "components/password_manager/core/browser/password_manager.h"
-#include "components/session_manager/core/session_manager.h"
 #include "components/web_modal/web_contents_modal_dialog_manager.h"
 #include "content/public/browser/notification_service.h"
 #include "content/public/browser/render_frame_host.h"
@@ -96,11 +95,11 @@
 
   registrar_.Add(this, chrome::NOTIFICATION_LOGIN_OR_LOCK_WEBUI_VISIBLE,
                  content::NotificationService::AllSources());
-  registrar_.Add(this, chrome::NOTIFICATION_LOGIN_NETWORK_ERROR_SHOWN,
-                 content::NotificationService::AllSources());
   registrar_.Add(this, chrome::NOTIFICATION_APP_TERMINATING,
                  content::NotificationService::AllSources());
 
+  session_observation_.Observe(session_manager::SessionManager::Get());
+
   for (size_t i = 0; i < kLoginAcceleratorDataLength; ++i) {
     ui::Accelerator accelerator(kLoginAcceleratorData[i].keycode,
                                 kLoginAcceleratorData[i].modifiers);
@@ -305,13 +304,11 @@
                              const content::NotificationSource& source,
                              const content::NotificationDetails& details) {
   switch (type) {
-    case chrome::NOTIFICATION_LOGIN_OR_LOCK_WEBUI_VISIBLE:
-    case chrome::NOTIFICATION_LOGIN_NETWORK_ERROR_SHOWN: {
+    case chrome::NOTIFICATION_LOGIN_OR_LOCK_WEBUI_VISIBLE: {
       OnLoginPromptVisible();
       registrar_.Remove(this, chrome::NOTIFICATION_LOGIN_OR_LOCK_WEBUI_VISIBLE,
                         content::NotificationService::AllSources());
-      registrar_.Remove(this, chrome::NOTIFICATION_LOGIN_NETWORK_ERROR_SHOWN,
-                        content::NotificationService::AllSources());
+      session_observation_.Reset();
       break;
     }
     case chrome::NOTIFICATION_APP_TERMINATING: {
@@ -329,6 +326,13 @@
   }
 }
 
+void WebUILoginView::OnNetworkErrorScreenShown() {
+  OnLoginPromptVisible();
+  registrar_.Remove(this, chrome::NOTIFICATION_LOGIN_OR_LOCK_WEBUI_VISIBLE,
+                    content::NotificationService::AllSources());
+  session_observation_.Reset();
+}
+
 ////////////////////////////////////////////////////////////////////////////////
 // ChromeKeyboardControllerClient::Observer
 
diff --git a/chrome/browser/ash/login/ui/webui_login_view.h b/chrome/browser/ash/login/ui/webui_login_view.h
index 8b85b900..2bc27ba 100644
--- a/chrome/browser/ash/login/ui/webui_login_view.h
+++ b/chrome/browser/ash/login/ui/webui_login_view.h
@@ -13,8 +13,11 @@
 #include "base/macros.h"
 #include "base/memory/ref_counted.h"
 #include "base/observer_list.h"
+#include "base/scoped_observation.h"
 #include "chrome/browser/ui/ash/keyboard/chrome_keyboard_controller_client.h"
 #include "chrome/browser/ui/chrome_web_modal_dialog_manager_delegate.h"
+#include "components/session_manager/core/session_manager.h"
+#include "components/session_manager/core/session_manager_observer.h"
 // TODO(https://crbug.com/1164001): use forward declaration.
 #include "chrome/browser/ui/webui/chromeos/login/oobe_ui.h"
 #include "components/web_modal/web_contents_modal_dialog_host.h"
@@ -46,6 +49,7 @@
                        public ChromeKeyboardControllerClient::Observer,
                        public content::WebContentsDelegate,
                        public content::NotificationObserver,
+                       public session_manager::SessionManagerObserver,
                        public ChromeWebModalDialogManagerDelegate,
                        public web_modal::WebContentsModalDialogHost,
                        public SystemTrayObserver {
@@ -133,6 +137,9 @@
                const content::NotificationSource& source,
                const content::NotificationDetails& details) override;
 
+  // session_manager::SessionManagerObserver:
+  void OnNetworkErrorScreenShown() override;
+
  private:
   // Map type for the accelerator-to-identifier map.
   typedef std::map<ui::Accelerator, LoginAcceleratorAction> AccelMap;
@@ -169,6 +176,9 @@
 
   content::NotificationRegistrar registrar_;
 
+  base::ScopedObservation<session_manager::SessionManager,
+                          session_manager::SessionManagerObserver>
+      session_observation_{this};
   // WebView configuration options.
   const WebViewSettings settings_;
 
diff --git a/chrome/browser/browser_resources.grd b/chrome/browser/browser_resources.grd
index 8f4c3a3..e7c06a1 100644
--- a/chrome/browser/browser_resources.grd
+++ b/chrome/browser/browser_resources.grd
@@ -350,7 +350,6 @@
         <include name="IDR_ADD_SUPERVISION_NETWORK_UNAVAILABLE_SVG" file="resources\chromeos\add_supervision\images\network_unavailable.svg" type="BINDATA" />
         <include name="IDR_ADD_SUPERVISION_UI_JS" file="${root_gen_dir}\chrome\browser\resources\chromeos\add_supervision\add_supervision_ui.js" use_base_dir="false" type="chrome_html" />
         <include name="IDR_ADD_SUPERVISION_API_SERVER_JS" file="resources\chromeos\add_supervision\add_supervision_api_server.js" type="BINDATA" />
-        <include name="IDR_ADD_SUPERVISION_POST_MESSAGE_API_JS" file="resources\chromeos\add_supervision\post_message_api.js" type="BINDATA" />
         <include name="IDR_ADD_SUPERVISION_MOJOM_LITE_JS" file="${root_gen_dir}\chrome\browser\ui\webui\chromeos\add_supervision\add_supervision.mojom-lite.js" use_base_dir="false" type="BINDATA" />
       </if>
       <if expr="chromeos">
diff --git a/chrome/browser/cart/cart_discount_fetcher.cc b/chrome/browser/cart/cart_discount_fetcher.cc
index c0e2136..4b99dd2 100644
--- a/chrome/browser/cart/cart_discount_fetcher.cc
+++ b/chrome/browser/cart/cart_discount_fetcher.cc
@@ -430,6 +430,12 @@
   const base::Value* is_tester_value = value->FindKey("externalTester");
   if (is_tester_value && is_tester_value->is_bool()) {
     is_tester = is_tester_value->GetBool();
+  } else {
+    const base::Value* is_internal_tester_value =
+        value->FindKey("internalTester");
+    if (is_internal_tester_value && is_internal_tester_value->is_bool()) {
+      is_tester = is_internal_tester_value->GetBool();
+    }
   }
 
   std::move(callback).Run(std::move(cart_discount_map), is_tester);
diff --git a/chrome/browser/chrome_notification_types.h b/chrome/browser/chrome_notification_types.h
index dbcaa4ce..361775c3 100644
--- a/chrome/browser/chrome_notification_types.h
+++ b/chrome/browser/chrome_notification_types.h
@@ -94,11 +94,6 @@
   // Misc --------------------------------------------------------------------
 
 #if BUILDFLAG(IS_CHROMEOS_ASH)
-  // Sent when a network error message is displayed on the WebUI login screen.
-  // First paint event of this fires NOTIFICATION_LOGIN_OR_LOCK_WEBUI_VISIBLE.
-  // TODO(https://crbug.com/1174791): Remove.
-  NOTIFICATION_LOGIN_NETWORK_ERROR_SHOWN,
-
   // Sent when the specific part of login/lock WebUI is considered to be
   // visible. That moment is tracked as the first paint event after one of the:
   // NOTIFICATION_LOGIN_NETWORK_ERROR_SHOWN
diff --git a/chrome/browser/chromeos/android_sms/fcm_connection_establisher.h b/chrome/browser/chromeos/android_sms/fcm_connection_establisher.h
index 40fe8ac..aa21b4fa 100644
--- a/chrome/browser/chromeos/android_sms/fcm_connection_establisher.h
+++ b/chrome/browser/chromeos/android_sms/fcm_connection_establisher.h
@@ -8,6 +8,7 @@
 #include <memory>
 #include <string>
 
+#include "base/containers/queue.h"
 #include "base/memory/weak_ptr.h"
 #include "base/timer/timer.h"
 #include "chrome/browser/chromeos/android_sms/connection_establisher.h"
diff --git a/chrome/browser/chromeos/extensions/file_manager/private_api_thumbnail.cc b/chrome/browser/chromeos/extensions/file_manager/private_api_thumbnail.cc
index b197c40..0d8893b 100644
--- a/chrome/browser/chromeos/extensions/file_manager/private_api_thumbnail.cc
+++ b/chrome/browser/chromeos/extensions/file_manager/private_api_thumbnail.cc
@@ -20,6 +20,7 @@
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/common/extensions/api/file_manager_private.h"
 #include "chrome/common/extensions/api/file_manager_private_internal.h"
+#include "chrome/services/printing/public/mojom/printing_service.mojom.h"
 #include "components/signin/public/identity_manager/consent_level.h"
 #include "content/public/browser/browser_task_traits.h"
 #include "content/public/browser/browser_thread.h"
diff --git a/chrome/browser/chromeos/phonehub/browser_tabs_metadata_fetcher_impl_unittest.cc b/chrome/browser/chromeos/phonehub/browser_tabs_metadata_fetcher_impl_unittest.cc
index 79f25d49..2e2e6f2 100644
--- a/chrome/browser/chromeos/phonehub/browser_tabs_metadata_fetcher_impl_unittest.cc
+++ b/chrome/browser/chromeos/phonehub/browser_tabs_metadata_fetcher_impl_unittest.cc
@@ -4,6 +4,8 @@
 
 #include "chrome/browser/chromeos/phonehub/browser_tabs_metadata_fetcher_impl.h"
 
+#include <deque>
+
 #include "base/strings/utf_string_conversions.h"
 #include "chrome/common/webui_url_constants.h"
 #include "components/favicon/core/history_ui_favicon_request_handler.h"
diff --git a/chrome/browser/client_hints/client_hints_browsertest.cc b/chrome/browser/client_hints/client_hints_browsertest.cc
index c9ec1187..4d80136 100644
--- a/chrome/browser/client_hints/client_hints_browsertest.cc
+++ b/chrome/browser/client_hints/client_hints_browsertest.cc
@@ -2402,10 +2402,18 @@
   absl::optional<std::string> ch_lang_ GUARDED_BY(ch_lang_lock_);
 };
 
+#if defined(OS_CHROMEOS)
+// Flaky on the linux-chromeos-chrome try bot: http://crbug.com/1222742
+#define MAYBE_CriticalClientHintInRequestHeader \
+  DISABLED_CriticalClientHintInRequestHeader
+#else
+#define MAYBE_CriticalClientHintInRequestHeader \
+  CriticalClientHintInRequestHeader
+#endif
 // Verify that setting Critical-CH in the response header causes the request to
 // be resent with the client hint included.
 IN_PROC_BROWSER_TEST_F(CriticalClientHintsBrowserTest,
-                       CriticalClientHintInRequestHeader) {
+                       MAYBE_CriticalClientHintInRequestHeader) {
   blink::UserAgentMetadata ua = embedder_support::GetUserAgentMetadata();
   // On the first navigation request, the client hints in the Critical-CH
   // should be set on the request header.
@@ -2725,8 +2733,16 @@
       /*critical_ch_ua_reduced_expected=*/false);
 }
 
+#if defined(OS_CHROMEOS)
+// Flaky on the linux-chromeos-chrome try bot: http://crbug.com/1222742
+#define MAYBE_CriticalChUaReducedWithValidOriginTrialToken \
+  DISABLED_CriticalChUaReducedWithValidOriginTrialToken
+#else
+#define MAYBE_CriticalChUaReducedWithValidOriginTrialToken \
+  CriticalChUaReducedWithValidOriginTrialToken
+#endif
 IN_PROC_BROWSER_TEST_F(UaReducedOriginTrialBrowserTest,
-                       CriticalChUaReducedWithValidOriginTrialToken) {
+                       MAYBE_CriticalChUaReducedWithValidOriginTrialToken) {
   // The initial navigation also contains the Critical-CH header, so the
   // Sec-CH-UA-Reduced header should be set after the first navigation.
   NavigateTwiceAndCheckHeader(
diff --git a/chrome/browser/continuous_search/BUILD.gn b/chrome/browser/continuous_search/BUILD.gn
index bb9d13c5..e94b396a 100644
--- a/chrome/browser/continuous_search/BUILD.gn
+++ b/chrome/browser/continuous_search/BUILD.gn
@@ -39,6 +39,7 @@
     "//services/network/public/mojom:url_loader_base_java",
     "//third_party/androidx:androidx_annotation_annotation_java",
     "//third_party/androidx:androidx_coordinatorlayout_coordinatorlayout_java",
+    "//third_party/androidx:androidx_core_core_java",
     "//third_party/androidx:androidx_recyclerview_recyclerview_java",
     "//ui/android:ui_full_java",
     "//url:gurl_java",
diff --git a/chrome/browser/continuous_search/android/java/res/layout/continuous_search_list_item.xml b/chrome/browser/continuous_search/android/java/res/layout/continuous_search_list_item.xml
index ac37cd63..ecfd197 100644
--- a/chrome/browser/continuous_search/android/java/res/layout/continuous_search_list_item.xml
+++ b/chrome/browser/continuous_search/android/java/res/layout/continuous_search_list_item.xml
@@ -8,5 +8,4 @@
     android:id="@+id/csn_chip"
     android:layout_width="wrap_content"
     android:layout_height="wrap_content"
-    android:gravity="center"
-    app:extendLateralPadding="true" />
\ No newline at end of file
+    android:gravity="center" />
\ No newline at end of file
diff --git a/chrome/browser/continuous_search/android/java/res/layout/continuous_search_list_provider.xml b/chrome/browser/continuous_search/android/java/res/layout/continuous_search_list_provider.xml
index 28086f8..8861469 100644
--- a/chrome/browser/continuous_search/android/java/res/layout/continuous_search_list_provider.xml
+++ b/chrome/browser/continuous_search/android/java/res/layout/continuous_search_list_provider.xml
@@ -9,8 +9,8 @@
         android:id="@+id/continuous_search_provider_label"
         android:layout_width="wrap_content"
         android:layout_height="match_parent"
-        android:paddingStart="@dimen/csn_chip_list_side_padding"
-        android:paddingEnd="@dimen/csn_chip_list_side_padding"
+        android:paddingStart="@dimen/csn_chip_provider_button_padding"
+        android:paddingEnd="@dimen/csn_chip_provider_button_padding"
         android:maxLines="2"
         android:gravity="center_vertical"
         android:textAppearance="@style/TextAppearance.TextMedium.Secondary"
diff --git a/chrome/browser/continuous_search/android/java/res/values/dimens.xml b/chrome/browser/continuous_search/android/java/res/values/dimens.xml
index ef7bf8f..6545bab 100644
--- a/chrome/browser/continuous_search/android/java/res/values/dimens.xml
+++ b/chrome/browser/continuous_search/android/java/res/values/dimens.xml
@@ -4,12 +4,13 @@
      found in the LICENSE file. -->
 
 <resources xmlns:tools="http://schemas.android.com/tools">
-    <dimen name="csn_chip_vertical_padding">5dp</dimen>
-    <dimen name="csn_chip_side_padding">@dimen/chip_element_leading_padding</dimen>
-    <dimen name="csn_chip_text_max_width">200dp</dimen>
+    <dimen name="csn_double_row_chip_vertical_padding">16dp</dimen>
+    <dimen name="csn_double_row_chip_side_padding">16dp</dimen>
+    <dimen name='csn_single_row_chip_side_padding'>4dp</dimen>
+    <dimen name="csn_chip_text_max_width">180dp</dimen>
 
     <dimen name="csn_chip_list_chip_spacing">4dp</dimen>
-    <dimen name="csn_chip_list_side_padding">8dp</dimen>
 
-    <dimen name="csn_chip_dismissal_button_padding">6dp</dimen>
+    <dimen name="csn_chip_provider_button_padding">12dp</dimen>
+    <dimen name="csn_chip_dismissal_button_padding">16dp</dimen>
 </resources>
diff --git a/chrome/browser/continuous_search/android/java/src/org/chromium/chrome/browser/continuous_search/ContinuousSearchChipView.java b/chrome/browser/continuous_search/android/java/src/org/chromium/chrome/browser/continuous_search/ContinuousSearchChipView.java
index af2ce69..c4bdf22f 100644
--- a/chrome/browser/continuous_search/android/java/src/org/chromium/chrome/browser/continuous_search/ContinuousSearchChipView.java
+++ b/chrome/browser/continuous_search/android/java/src/org/chromium/chrome/browser/continuous_search/ContinuousSearchChipView.java
@@ -12,6 +12,7 @@
 import android.widget.TextView;
 
 import androidx.annotation.Px;
+import androidx.core.view.ViewCompat;
 
 import org.chromium.ui.widget.ChipView;
 
@@ -24,6 +25,10 @@
     public ContinuousSearchChipView(Context context, AttributeSet attrs) {
         super(context, attrs);
         mIsTwoLineChipView = false;
+        // One-row chip padding modification.
+        int padding =
+                getResources().getDimensionPixelOffset(R.dimen.csn_single_row_chip_side_padding);
+        ViewCompat.setPaddingRelative(this, padding, 0, padding, 0);
     }
 
     /**
@@ -41,14 +46,7 @@
                 ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT));
         layout.setOrientation(VERTICAL);
 
-        // Modify chip  to accommodate vertically stacked TextViews
-        @Px
-        int sidePadding = getResources().getDimensionPixelSize(R.dimen.csn_chip_side_padding);
-        @Px
-        int verticalPadding =
-                getResources().getDimensionPixelSize(R.dimen.csn_chip_vertical_padding);
-        setPaddingRelative(sidePadding, getPaddingTop(), sidePadding, getPaddingBottom());
-        layout.setPaddingRelative(0, verticalPadding, 0, verticalPadding);
+        // Modify TextViews to have them vertically stacked.
         setupTextView(primaryText);
         setupTextView(secondaryText);
 
@@ -59,6 +57,16 @@
         layout.addView(primaryText);
         layout.addView(secondaryText);
         addView(layout);
+
+        // Adjust chip paddings
+        @Px
+        int sidePadding =
+                getResources().getDimensionPixelSize(R.dimen.csn_double_row_chip_side_padding);
+        @Px
+        int verticalPadding =
+                getResources().getDimensionPixelSize(R.dimen.csn_double_row_chip_vertical_padding);
+        ViewCompat.setPaddingRelative(
+                this, sidePadding, verticalPadding, sidePadding, verticalPadding);
         mIsTwoLineChipView = true;
     }
 
diff --git a/chrome/browser/continuous_search/android/java/src/org/chromium/chrome/browser/continuous_search/ContinuousSearchListCoordinator.java b/chrome/browser/continuous_search/android/java/src/org/chromium/chrome/browser/continuous_search/ContinuousSearchListCoordinator.java
index dec5e3e..9b75954 100644
--- a/chrome/browser/continuous_search/android/java/src/org/chromium/chrome/browser/continuous_search/ContinuousSearchListCoordinator.java
+++ b/chrome/browser/continuous_search/android/java/src/org/chromium/chrome/browser/continuous_search/ContinuousSearchListCoordinator.java
@@ -167,13 +167,10 @@
 
     private static class SpaceItemDecoration extends ItemDecoration {
         private final int mChipSpacingPx;
-        private final int mSidePaddingPx;
 
         public SpaceItemDecoration(Resources resources) {
             mChipSpacingPx =
                     (int) resources.getDimensionPixelSize(R.dimen.csn_chip_list_chip_spacing);
-            mSidePaddingPx =
-                    (int) resources.getDimensionPixelSize(R.dimen.csn_chip_list_side_padding);
         }
 
         @Override
@@ -182,10 +179,10 @@
             boolean isFirst = position == 0;
             boolean isLast = position == parent.getAdapter().getItemCount() - 1;
 
-            // Provider icon/text already has the required padding so the first item doesn't need to
-            // have any.
+            // Border chip paddings are provided by the provider icon and the dismiss button so no
+            // need to add any paddings here.
             outRect.left = isFirst ? 0 : mChipSpacingPx;
-            outRect.right = isLast ? mSidePaddingPx : mChipSpacingPx;
+            outRect.right = isLast ? 0 : mChipSpacingPx;
         }
     }
 }
diff --git a/chrome/browser/dependency_injection/BUILD.gn b/chrome/browser/dependency_injection/BUILD.gn
new file mode 100644
index 0000000..01f35779
--- /dev/null
+++ b/chrome/browser/dependency_injection/BUILD.gn
@@ -0,0 +1,16 @@
+# Copyright 2021 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import("//build/config/android/rules.gni")
+
+android_library("java") {
+  sources = [ "android/java/src/org/chromium/chrome/browser/dependency_injection/ActivityScope.java" ]
+  deps = [
+    "//base:base_java",
+    "//third_party/android_deps:dagger_java",
+    "//third_party/android_deps:javax_inject_javax_inject_java",
+    "//third_party/androidx:androidx_annotation_annotation_java",
+    "//third_party/androidx:androidx_core_core_java",
+  ]
+}
diff --git a/chrome/browser/dependency_injection/DIR_METADATA b/chrome/browser/dependency_injection/DIR_METADATA
new file mode 100644
index 0000000..7b9b438
--- /dev/null
+++ b/chrome/browser/dependency_injection/DIR_METADATA
@@ -0,0 +1,4 @@
+monorail {
+  component: "Internals>Core"
+}
+os: ANDROID
diff --git a/chrome/browser/dependency_injection/OWNERS b/chrome/browser/dependency_injection/OWNERS
new file mode 100644
index 0000000..e31d7a8
--- /dev/null
+++ b/chrome/browser/dependency_injection/OWNERS
@@ -0,0 +1 @@
+file://chrome/android/java/src/org/chromium/chrome/browser/dependency_injection/OWNERS
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/dependency_injection/ActivityScope.java b/chrome/browser/dependency_injection/android/java/src/org/chromium/chrome/browser/dependency_injection/ActivityScope.java
similarity index 100%
rename from chrome/android/java/src/org/chromium/chrome/browser/dependency_injection/ActivityScope.java
rename to chrome/browser/dependency_injection/android/java/src/org/chromium/chrome/browser/dependency_injection/ActivityScope.java
diff --git a/chrome/browser/enterprise/connectors/file_system/box_api_call_test_helper.cc b/chrome/browser/enterprise/connectors/file_system/box_api_call_test_helper.cc
index 0aed593..46b08aa 100644
--- a/chrome/browser/enterprise/connectors/file_system/box_api_call_test_helper.cc
+++ b/chrome/browser/enterprise/connectors/file_system/box_api_call_test_helper.cc
@@ -71,6 +71,8 @@
 // intentionally distinct from kFileSystemBoxFindFolderResponseFolderId above
 // to identify where the test flow gets the folder_id from.
 const char kFileSystemBoxFolderIdInPref[] = "1337";
+const char kFileSystemBoxFolderIdInPrefUrl[] =
+    "https://app.box.com/folder/1337";
 
 const char kFileSystemBoxFindFolderResponseEmptyEntriesList[] = R"({
     "entries": [
@@ -294,7 +296,4 @@
 const char kFileSystemBoxUploadResponseFileUrl[] =
     "https://app.box.com/file/314159";
 
-const char kFileSystemBoxUploadResponseFolderUrl[] =
-    "https://app.box.com/folder/1337";
-
 }  // namespace enterprise_connectors
diff --git a/chrome/browser/enterprise/connectors/file_system/box_api_call_test_helper.h b/chrome/browser/enterprise/connectors/file_system/box_api_call_test_helper.h
index 13ee3a7..ed5b58c 100644
--- a/chrome/browser/enterprise/connectors/file_system/box_api_call_test_helper.h
+++ b/chrome/browser/enterprise/connectors/file_system/box_api_call_test_helper.h
@@ -48,6 +48,8 @@
 
 // Saved folder id extracted from the kFileSystemUploadFolderIdPref pref.
 extern const char kFileSystemBoxFolderIdInPref[];
+// Expected folder url for the uploaded file with kFileSystemBoxFolderIdInPref.
+extern const char kFileSystemBoxFolderIdInPrefUrl[];
 
 // For Box Chunked Uploads /////////////////////////////////////////////////////
 
@@ -79,8 +81,7 @@
 // Expected file id/url extracted from above.
 extern const char kFileSystemBoxUploadResponseFileId[];
 extern const char kFileSystemBoxUploadResponseFileUrl[];
-// Expected folder url for the uploaded file with kFileSystemBoxFolderIdInPref.
-extern const char kFileSystemBoxUploadResponseFolderUrl[];
+
 }  // namespace enterprise_connectors
 
 #endif  // CHROME_BROWSER_ENTERPRISE_CONNECTORS_FILE_SYSTEM_BOX_API_CALL_TEST_HELPER_H_
diff --git a/chrome/browser/enterprise/connectors/file_system/box_uploader.cc b/chrome/browser/enterprise/connectors/file_system/box_uploader.cc
index 9e1bac0..2e5a750 100644
--- a/chrome/browser/enterprise/connectors/file_system/box_uploader.cc
+++ b/chrome/browser/enterprise/connectors/file_system/box_uploader.cc
@@ -145,6 +145,7 @@
   authentication_retry_callback_ = std::move(authen_retry_callback);
   progress_update_cb_ = std::move(progress_update_cb);
   upload_complete_cb_ = std::move(upload_complete_cb);
+  SendProgressUpdate();
   SetCurrentApiCall(GetFolderId().empty() ? MakeFindUpstreamFolderApiCall()
                                           : MakePreflightCheckApiCall());
 }
diff --git a/chrome/browser/enterprise/connectors/file_system/box_uploader_test_helper.cc b/chrome/browser/enterprise/connectors/file_system/box_uploader_test_helper.cc
index aeddd93..271e8d3 100644
--- a/chrome/browser/enterprise/connectors/file_system/box_uploader_test_helper.cc
+++ b/chrome/browser/enterprise/connectors/file_system/box_uploader_test_helper.cc
@@ -156,6 +156,7 @@
     const download::DownloadItemRenameProgressUpdate& update) {
   ++progress_update_cb_called_;
   file_name_reported_back_ = update.target_file_name;
+  reroute_info_reported_back_ = update.reroute_info;
 }
 
 void BoxUploaderTestBase::OnUploaderFinished(
diff --git a/chrome/browser/enterprise/connectors/file_system/box_uploader_test_helper.h b/chrome/browser/enterprise/connectors/file_system/box_uploader_test_helper.h
index e28e862..96d9cd5 100644
--- a/chrome/browser/enterprise/connectors/file_system/box_uploader_test_helper.h
+++ b/chrome/browser/enterprise/connectors/file_system/box_uploader_test_helper.h
@@ -26,6 +26,8 @@
 
 class BoxUploader;
 
+using State = download::DownloadItem::DownloadState;
+
 class BoxUploaderTestBase : public testing::Test {
  public:
   explicit BoxUploaderTestBase(base::FilePath::StringPieceType file_name =
@@ -35,8 +37,6 @@
   base::FilePath GetFilePath() const;
 
  protected:
-  using State = download::DownloadItem::DownloadState;
-
   virtual void CreateTemporaryFile();
   void CreateTemporaryFileWithContent(std::string content);
   void InitFolderIdInPrefs(std::string folder_id);
@@ -87,6 +87,7 @@
   download::DownloadInterruptReason reason_{
       download::DOWNLOAD_INTERRUPT_REASON_NONE};
   base::FilePath file_name_reported_back_;
+  DownloadItemRerouteInfo reroute_info_reported_back_;
 
  private:
   content::BrowserTaskEnvironment task_environment_;
diff --git a/chrome/browser/enterprise/connectors/file_system/box_uploader_unittest.cc b/chrome/browser/enterprise/connectors/file_system/box_uploader_unittest.cc
index 1738f4eb..3c850049 100644
--- a/chrome/browser/enterprise/connectors/file_system/box_uploader_unittest.cc
+++ b/chrome/browser/enterprise/connectors/file_system/box_uploader_unittest.cc
@@ -91,7 +91,7 @@
   ASSERT_EQ(uploader_->GetUploadedFileUrl(),
             GURL(kFileSystemBoxUploadResponseFileUrl));
   ASSERT_EQ(uploader_->GetDestinationFolderUrl(),
-            GURL(kFileSystemBoxUploadResponseFolderUrl));
+            GURL(kFileSystemBoxFolderIdInPrefUrl));
 
   ASSERT_FALSE(authentication_retry_);
   EXPECT_FALSE(download_thread_cb_called_);
@@ -154,8 +154,9 @@
     // deleted as part of error handling. Otherwise, upload API call flow got
     // InterceptedPreUpload(), so file was not deleted.
     EXPECT_EQ(upload_initiated_, base::PathExists(GetFilePath()));
-    // Only 1 update in StartUpload() when PreflightCheck succeeds.
-    EXPECT_EQ(progress_update_cb_called_, 1);
+    // 1 update in Init(), and 1 update in StartUpload() when PreflightCheck
+    // succeeds.
+    EXPECT_EQ(progress_update_cb_called_, 2);
   }
 
   void InterceptedPreUpload() {
@@ -214,7 +215,7 @@
 
   // Should be retrying authentication, no report via callback yet.
   ASSERT_EQ(authentication_retry_, 1);
-  ASSERT_EQ(progress_update_cb_called_, 0);
+  ASSERT_EQ(progress_update_cb_called_, 1);  // 1 in Init().
   EXPECT_FALSE(upload_initiated_);
   EXPECT_FALSE(download_thread_cb_called_);
   EXPECT_EQ(uploader_->GetFolderIdForTesting(), "");
@@ -498,7 +499,7 @@
 
   // Need to retry authentication, so no report via callback yet.
   ASSERT_EQ(authentication_retry_, 1);
-  ASSERT_EQ(progress_update_cb_called_, 0);
+  ASSERT_EQ(progress_update_cb_called_, 1);  // 1 in Init().
   ASSERT_FALSE(upload_initiated_);
   EXPECT_FALSE(download_thread_cb_called_);
   EXPECT_FALSE(upload_success_);
@@ -582,9 +583,9 @@
     // If upload was not initiated due to some error, file should've been
     // deleted as part of error handling.
     EXPECT_FALSE(base::PathExists(GetFilePath()));
-    // 1 update in StartUpload() when PreflightCheck succeeds, and 1 update in
-    // OnApiCallFlowDone().
-    EXPECT_EQ(progress_update_cb_called_, 2);
+    // 1 update in Init(), 1 update in StartUpload() when PreflightCheck
+    // succeeds, and 1 update in OnApiCallFlowDone().
+    EXPECT_EQ(progress_update_cb_called_, 3);
   }
   std::unique_ptr<BoxUploaderForNetErrorTest> uploader_;
 };
@@ -701,56 +702,107 @@
   ASSERT_FALSE(upload_success_);
 }
 
-TEST_F(BoxUploader_FileDeleteTest, LoadFromReroutedInfo_InProgress) {
-  test_item_.SetState(State::IN_PROGRESS);
+DownloadItemRerouteInfo MakeDownloadItemRerouteInfo(
+    std::string folder_id = std::string(),
+    std::string file_id = std::string()) {
+  DownloadItemRerouteInfo info;
+  info.set_service_provider(BoxUploader::kServiceProvider);
+  info.mutable_box();  // Set the oneof to be Box.
+  if (!folder_id.empty())
+    info.mutable_box()->set_folder_id(folder_id);
+  if (!file_id.empty())
+    info.mutable_box()->set_file_id(file_id);
+  return info;
+}
+
+class BoxUploader_FromRerouteInfoTestBase : public BoxUploaderTestBase {
+ public:
+  BoxUploader_FromRerouteInfoTestBase()
+      : BoxUploaderTestBase(
+            FILE_PATH_LITERAL("box_uploader_from_reroute_info_test.txt")) {}
+
+ protected:
+  void TearDown() override {
+    EXPECT_EQ(authentication_retry_, 0);
+    EXPECT_EQ(progress_update_cb_called_ > 0, download_thread_cb_called_);
+    EXPECT_FALSE(test_item_.GetFileExternallyRemoved());
+    // Ended with no local temporary file.
+    ASSERT_FALSE(base::PathExists(GetFilePath()));
+  }
+
+  std::unique_ptr<BoxUploader> uploader_;
+};
+
+class BoxUploader_IncompleteItemFromRerouteInfoTest
+    : public BoxUploader_FromRerouteInfoTestBase,
+      public testing::WithParamInterface<State> {};
+
+TEST_P(BoxUploader_IncompleteItemFromRerouteInfoTest, RetryUpload) {
+  // Skip COMPLETE to allow Range() to generate all other enum values.
+  if (GetParam() == State::COMPLETE)
+    return SUCCEED();
+
   CreateTemporaryFile();
 
-  DownloadItemRerouteInfo rerouted_info;
-  rerouted_info.set_service_provider(BoxUploader::kServiceProvider);
-  rerouted_info.mutable_box();  // set the oneof to be box;
-  test_item_.SetRerouteInfo(rerouted_info);
+  test_item_.SetState(GetParam());
+  DownloadItemRerouteInfo reroute_info = MakeDownloadItemRerouteInfo();
+  test_item_.SetRerouteInfo(reroute_info);
+  uploader_ = BoxUploader::Create(&test_item_);
+  ASSERT_TRUE(uploader_);
 
-  // Recreate uploader to load rerouted info.
-  uploader_ = std::make_unique<BoxUploaderForFileDeleteTest>(&test_item_);
+  // Assume there is already a folder_id stored in prefs, preflight check
+  // passes, and upload request succeeds.
+  InitFolderIdInPrefs(kFileSystemBoxFolderIdInPref);
+  AddFetchResult(kFileSystemBoxPreflightCheckUrl, net::HTTP_OK);
+  AddFetchResult(kFileSystemBoxDirectUploadUrl, net::HTTP_CREATED,
+                 kFileSystemBoxUploadResponseBody);
+
+  // Try uploading.
   InitUploader(uploader_.get());
   InitQuitClosure();
   uploader_->TryTask(url_factory_, "test_token");
   RunWithQuitClosure();
 
-  ASSERT_TRUE(uploader_);
-  ASSERT_EQ(uploader_->GetUploadedFileUrl(),
-            GURL(kFileSystemBoxUploadResponseFileUrl));
-  // TODO(https://crbug.com/1215847) Update to set folder id and check folder
-  // link too.
-
-  ASSERT_FALSE(authentication_retry_);
-  EXPECT_TRUE(download_thread_cb_called_);
-  EXPECT_TRUE(progress_update_cb_called_);
-  ASSERT_TRUE(upload_success_);
-  EXPECT_FALSE(base::PathExists(GetFilePath()));  // File deleted.
-}
-
-TEST_F(BoxUploader_FileDeleteTest, LoadFromReroutedInfo_Complete) {
-  test_item_.SetState(State::COMPLETE);
-
-  DownloadItemRerouteInfo rerouted_info;
-  rerouted_info.set_service_provider(BoxUploader::kServiceProvider);
-  rerouted_info.mutable_box()->set_file_id(kFileSystemBoxUploadResponseFileId);
-  rerouted_info.mutable_box()->set_folder_id(kFileSystemBoxFolderIdInPref);
-  test_item_.SetRerouteInfo(rerouted_info);
-
-  // Recreate uploader to load rerouted info.
-  uploader_ = std::make_unique<BoxUploaderForFileDeleteTest>(&test_item_);
-  ASSERT_TRUE(uploader_);
+  ASSERT_EQ(reroute_info_reported_back_.box().file_id(),
+            kFileSystemBoxUploadResponseFileId);
+  ASSERT_EQ(reroute_info_reported_back_.box().folder_id(),
+            kFileSystemBoxFolderIdInPref);
   ASSERT_EQ(uploader_->GetUploadedFileUrl(),
             GURL(kFileSystemBoxUploadResponseFileUrl));
   ASSERT_EQ(uploader_->GetDestinationFolderUrl(),
-            GURL(kFileSystemBoxUploadResponseFolderUrl));
+            GURL(kFileSystemBoxFolderIdInPrefUrl));
 
   ASSERT_FALSE(authentication_retry_);
+  EXPECT_TRUE(download_thread_cb_called_);
+  EXPECT_GE(progress_update_cb_called_, 2);
+  ASSERT_TRUE(upload_success_);
+}
+
+INSTANTIATE_TEST_CASE_P(,
+                        BoxUploader_IncompleteItemFromRerouteInfoTest,
+                        testing::Range(State::IN_PROGRESS,
+                                       State::MAX_DOWNLOAD_STATE));
+
+class BoxUploader_CompletedItemFromRerouteInfoTest
+    : public BoxUploader_FromRerouteInfoTestBase {};
+
+TEST_F(BoxUploader_CompletedItemFromRerouteInfoTest, Normal) {
+  test_item_.SetState(State::COMPLETE);
+  const std::string folder_id = "357321";
+  const std::string file_id = "13576123";
+  DownloadItemRerouteInfo reroute_info =
+      MakeDownloadItemRerouteInfo(folder_id, file_id);
+  test_item_.SetRerouteInfo(reroute_info);
+
+  uploader_ = BoxUploader::Create(&test_item_);
+  ASSERT_TRUE(uploader_);
+  ASSERT_EQ(uploader_->GetUploadedFileUrl(),
+            BoxApiCallFlow::MakeUrlToShowFile(file_id));
+  ASSERT_EQ(uploader_->GetDestinationFolderUrl(),
+            BoxApiCallFlow::MakeUrlToShowFolder(folder_id));
+
   EXPECT_FALSE(download_thread_cb_called_);  // No upload call was made.
   EXPECT_FALSE(progress_update_cb_called_);
-  EXPECT_FALSE(base::PathExists(GetFilePath()));  // File not created.
 }
 
 ////////////////////////////////////////////////////////////////////////////////
@@ -798,7 +850,7 @@
   EXPECT_EQ(uploader_->GetUploadedFileUrl(),
             kFileSystemBoxUploadResponseFileUrl);
   EXPECT_EQ(uploader_->GetDestinationFolderUrl(),
-            kFileSystemBoxUploadResponseFolderUrl);
+            kFileSystemBoxFolderIdInPrefUrl);
 }
 
 TEST_F(BoxDirectUploaderTest, FileReadFailure) {
@@ -813,7 +865,7 @@
   EXPECT_FALSE(upload_success_);
   EXPECT_TRUE(uploader_->GetUploadedFileUrl().is_empty());
   EXPECT_EQ(uploader_->GetDestinationFolderUrl(),
-            kFileSystemBoxUploadResponseFolderUrl);
+            kFileSystemBoxFolderIdInPrefUrl);
   ASSERT_REASON_EQ(FILE_FAILED, reason_);
 }
 
diff --git a/chrome/browser/extensions/BUILD.gn b/chrome/browser/extensions/BUILD.gn
index 3c3358a..004ceba7 100644
--- a/chrome/browser/extensions/BUILD.gn
+++ b/chrome/browser/extensions/BUILD.gn
@@ -1268,8 +1268,6 @@
   }
 
   if (enable_basic_printing && (is_win || is_mac || is_linux || is_chromeos)) {
-    # Needed by files which include //chrome/browser/service_sandbox_type.h.
-    # Inclusion of print_backend_service_manager.h depends on this.
     # TODO(crbug.com/1213762)  Replace this with more accurate dependency
     # once //chrome/browser/printing build is able to be cleanly targeted.
     deps += [ "//chrome/services/printing/public/mojom" ]
diff --git a/chrome/browser/extensions/api/cookies/cookies_unittest.cc b/chrome/browser/extensions/api/cookies/cookies_unittest.cc
index 1c31973..ff2856a8e3 100644
--- a/chrome/browser/extensions/api/cookies/cookies_unittest.cc
+++ b/chrome/browser/extensions/api/cookies/cookies_unittest.cc
@@ -9,6 +9,7 @@
 
 #include <memory>
 #include <utility>
+#include <vector>
 
 #include "base/cxx17_backports.h"
 #include "base/values.h"
@@ -173,10 +174,10 @@
 
   for (size_t i = 0; i < base::size(tests); ++i) {
     // Build up the Params struct.
-    base::ListValue args;
-    auto dict = std::make_unique<base::DictionaryValue>();
-    dict->SetString(keys::kDomainKey, std::string(tests[i].filter));
-    args.Set(0, std::move(dict));
+    std::vector<base::Value> args;
+    base::Value dict(base::Value::Type::DICTIONARY);
+    dict.SetStringKey(keys::kDomainKey, std::string(tests[i].filter));
+    args.emplace_back(std::move(dict));
     std::unique_ptr<GetAll::Params> params(GetAll::Params::Create(args));
 
     cookies_helpers::MatchFilter filter(&params->details);
diff --git a/chrome/browser/extensions/api/debugger/debugger_apitest.cc b/chrome/browser/extensions/api/debugger/debugger_apitest.cc
index 7aaffd3d..432de4bc8 100644
--- a/chrome/browser/extensions/api/debugger/debugger_apitest.cc
+++ b/chrome/browser/extensions/api/debugger/debugger_apitest.cc
@@ -117,13 +117,13 @@
   std::unique_ptr<base::Value> value(
       extension_function_test_utils::RunFunctionAndReturnSingleResult(
           get_targets_function.get(), "[]", browser()));
-  base::ListValue* targets = nullptr;
-  EXPECT_TRUE(value->GetAsList(&targets));
+  EXPECT_TRUE(value->is_list());
+  const base::ListValue& targets = base::Value::AsListValue(*value);
 
   std::string debugger_target_id;
-  for (size_t i = 0; i < targets->GetSize(); ++i) {
-    base::DictionaryValue* target_dict = nullptr;
-    EXPECT_TRUE(targets->GetDictionary(i, &target_dict));
+  for (size_t i = 0; i < targets.GetSize(); ++i) {
+    const base::DictionaryValue* target_dict = nullptr;
+    EXPECT_TRUE(targets.GetDictionary(i, &target_dict));
     int id = -1;
     if (target_dict->GetInteger("tabId", &id) && id == tab_id) {
       EXPECT_TRUE(target_dict->GetString("id", &debugger_target_id));
diff --git a/chrome/browser/extensions/api/declarative_content/declarative_content_css_condition_tracker.cc b/chrome/browser/extensions/api/declarative_content/declarative_content_css_condition_tracker.cc
index d8dee11..df9327e 100644
--- a/chrome/browser/extensions/api/declarative_content/declarative_content_css_condition_tracker.cc
+++ b/chrome/browser/extensions/api/declarative_content/declarative_content_css_condition_tracker.cc
@@ -43,16 +43,14 @@
                                        const base::Value& value,
                                        std::string* error) {
   std::vector<std::string> css_rules;
-  const base::ListValue* css_rules_value = nullptr;
-  if (value.GetAsList(&css_rules_value)) {
-    for (size_t i = 0; i < css_rules_value->GetSize(); ++i) {
-      std::string css_rule;
-      if (!css_rules_value->GetString(i, &css_rule)) {
+  if (value.is_list()) {
+    for (const base::Value& css_rule_value : value.GetList()) {
+      if (!css_rule_value.is_string()) {
         *error = base::StringPrintf(kCssInvalidTypeOfParameter,
                                     declarative_content_constants::kCss);
         return nullptr;
       }
-      css_rules.push_back(css_rule);
+      css_rules.push_back(css_rule_value.GetString());
     }
   } else {
     *error = base::StringPrintf(kCssInvalidTypeOfParameter,
diff --git a/chrome/browser/extensions/api/downloads/downloads_api_browsertest.cc b/chrome/browser/extensions/api/downloads/downloads_api_browsertest.cc
index ca243f02..35dbb319 100644
--- a/chrome/browser/extensions/api/downloads/downloads_api_browsertest.cc
+++ b/chrome/browser/extensions/api/downloads/downloads_api_browsertest.cc
@@ -71,6 +71,7 @@
 #include "storage/browser/file_system/file_system_context.h"
 #include "storage/browser/file_system/file_system_operation_runner.h"
 #include "storage/browser/file_system/file_system_url.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
 #include "ui/base/page_transition_types.h"
 #include "url/origin.h"
 
@@ -1181,9 +1182,8 @@
   std::unique_ptr<base::Value> result(
       RunFunctionAndReturnResult(new DownloadsSearchFunction(), "[{}]"));
   ASSERT_TRUE(result.get());
-  base::ListValue* result_list = NULL;
-  ASSERT_TRUE(result->GetAsList(&result_list));
-  ASSERT_EQ(1UL, result_list->GetSize());
+  ASSERT_TRUE(result->is_list());
+  ASSERT_EQ(1UL, result->GetList().size());
 }
 
 // Test that file existence check should be performed after search.
@@ -1201,9 +1201,9 @@
   std::unique_ptr<base::Value> result(
       RunFunctionAndReturnResult(new DownloadsSearchFunction(), "[{}]"));
   ASSERT_TRUE(result.get());
-  base::ListValue* result_list = nullptr;
-  ASSERT_TRUE(result->GetAsList(&result_list));
-  ASSERT_EQ(1UL, result_list->GetSize());
+  ASSERT_TRUE(result->is_list());
+  ASSERT_EQ(1UL, result->GetList().size());
+
   // Check file removal update will eventually come. WaitForEvent() will
   // immediately return if the file is already removed.
   content::DownloadUpdatedObserver(
@@ -1248,14 +1248,13 @@
   std::unique_ptr<base::Value> result(RunFunctionAndReturnResult(
       new DownloadsSearchFunction(), "[{\"filenameRegex\": \"foobar\"}]"));
   ASSERT_TRUE(result.get());
-  base::ListValue* result_list = NULL;
-  ASSERT_TRUE(result->GetAsList(&result_list));
-  ASSERT_EQ(1UL, result_list->GetSize());
-  base::DictionaryValue* item_value = NULL;
-  ASSERT_TRUE(result_list->GetDictionary(0, &item_value));
-  int item_id = -1;
-  ASSERT_TRUE(item_value->GetInteger("id", &item_id));
-  ASSERT_EQ(all_downloads[0]->GetId(), static_cast<uint32_t>(item_id));
+  ASSERT_TRUE(result->is_list());
+  ASSERT_EQ(1UL, result->GetList().size());
+  const base::Value& item_value = result->GetList()[0];
+  ASSERT_TRUE(item_value.is_dict());
+  absl::optional<int> item_id = item_value.FindIntKey("id");
+  ASSERT_TRUE(item_id);
+  ASSERT_EQ(all_downloads[0]->GetId(), static_cast<uint32_t>(*item_id));
 }
 
 // Test the |id| parameter for search().
@@ -1269,14 +1268,13 @@
       new DownloadsSearchFunction(),
       base::StringPrintf("[{\"id\": %u}]", items[0]->GetId())));
   ASSERT_TRUE(result.get());
-  base::ListValue* result_list = NULL;
-  ASSERT_TRUE(result->GetAsList(&result_list));
-  ASSERT_EQ(1UL, result_list->GetSize());
-  base::DictionaryValue* item_value = NULL;
-  ASSERT_TRUE(result_list->GetDictionary(0, &item_value));
-  int item_id = -1;
-  ASSERT_TRUE(item_value->GetInteger("id", &item_id));
-  ASSERT_EQ(items[0]->GetId(), static_cast<uint32_t>(item_id));
+  ASSERT_TRUE(result->is_list());
+  ASSERT_EQ(1UL, result->GetList().size());
+  const base::Value& item_value = result->GetList()[0];
+  ASSERT_TRUE(item_value.is_dict());
+  absl::optional<int> item_id = item_value.FindIntKey("id");
+  ASSERT_TRUE(item_id);
+  ASSERT_EQ(items[0]->GetId(), static_cast<uint32_t>(*item_id));
 }
 
 // Test specifying both the |id| and |filename| parameters for search().
@@ -1291,9 +1289,8 @@
       RunFunctionAndReturnResult(new DownloadsSearchFunction(),
                                  "[{\"id\": 0, \"filename\": \"foobar\"}]"));
   ASSERT_TRUE(result.get());
-  base::ListValue* result_list = NULL;
-  ASSERT_TRUE(result->GetAsList(&result_list));
-  ASSERT_EQ(0UL, result_list->GetSize());
+  ASSERT_TRUE(result->is_list());
+  ASSERT_EQ(0UL, result->GetList().size());
 }
 
 // Test a single |orderBy| parameter for search().
@@ -1311,19 +1308,19 @@
   std::unique_ptr<base::Value> result(RunFunctionAndReturnResult(
       new DownloadsSearchFunction(), "[{\"orderBy\": [\"filename\"]}]"));
   ASSERT_TRUE(result.get());
-  base::ListValue* result_list = NULL;
-  ASSERT_TRUE(result->GetAsList(&result_list));
-  ASSERT_EQ(2UL, result_list->GetSize());
-  base::DictionaryValue* item0_value = NULL;
-  base::DictionaryValue* item1_value = NULL;
-  ASSERT_TRUE(result_list->GetDictionary(0, &item0_value));
-  ASSERT_TRUE(result_list->GetDictionary(1, &item1_value));
-  std::string item0_name, item1_name;
-  ASSERT_TRUE(item0_value->GetString("filename", &item0_name));
-  ASSERT_TRUE(item1_value->GetString("filename", &item1_name));
+  ASSERT_TRUE(result->is_list());
+  ASSERT_EQ(2UL, result->GetList().size());
+  const base::Value& item0_value = result->GetList()[0];
+  const base::Value& item1_value = result->GetList()[1];
+  ASSERT_TRUE(item0_value.is_dict());
+  ASSERT_TRUE(item1_value.is_dict());
+  const std::string* item0_name = item0_value.FindStringKey("filename");
+  const std::string* item1_name = item1_value.FindStringKey("filename");
+  ASSERT_TRUE(item0_name);
+  ASSERT_TRUE(item1_name);
   ASSERT_GT(items[0]->GetTargetFilePath().value(),
             items[1]->GetTargetFilePath().value());
-  ASSERT_LT(item0_name, item1_name);
+  ASSERT_LT(*item0_name, *item1_name);
 }
 
 // Test specifying an empty |orderBy| parameter for search().
@@ -1341,16 +1338,16 @@
   std::unique_ptr<base::Value> result(RunFunctionAndReturnResult(
       new DownloadsSearchFunction(), "[{\"orderBy\": []}]"));
   ASSERT_TRUE(result.get());
-  base::ListValue* result_list = NULL;
-  ASSERT_TRUE(result->GetAsList(&result_list));
-  ASSERT_EQ(2UL, result_list->GetSize());
-  base::DictionaryValue* item0_value = NULL;
-  base::DictionaryValue* item1_value = NULL;
-  ASSERT_TRUE(result_list->GetDictionary(0, &item0_value));
-  ASSERT_TRUE(result_list->GetDictionary(1, &item1_value));
-  std::string item0_name, item1_name;
-  ASSERT_TRUE(item0_value->GetString("filename", &item0_name));
-  ASSERT_TRUE(item1_value->GetString("filename", &item1_name));
+  ASSERT_TRUE(result->is_list());
+  ASSERT_EQ(2UL, result->GetList().size());
+  const base::Value& item0_value = result->GetList()[0];
+  const base::Value& item1_value = result->GetList()[1];
+  ASSERT_TRUE(item0_value.is_dict());
+  ASSERT_TRUE(item1_value.is_dict());
+  const std::string* item0_name = item0_value.FindStringKey("filename");
+  const std::string* item1_name = item1_value.FindStringKey("filename");
+  ASSERT_TRUE(item0_name);
+  ASSERT_TRUE(item1_name);
   ASSERT_GT(items[0]->GetTargetFilePath().value(),
             items[1]->GetTargetFilePath().value());
   // The order of results when orderBy is empty is unspecified. When there are
@@ -1375,9 +1372,8 @@
   std::unique_ptr<base::Value> result(RunFunctionAndReturnResult(
       new DownloadsSearchFunction(), "[{\"danger\": \"content\"}]"));
   ASSERT_TRUE(result.get());
-  base::ListValue* result_list = NULL;
-  ASSERT_TRUE(result->GetAsList(&result_list));
-  ASSERT_EQ(1UL, result_list->GetSize());
+  ASSERT_TRUE(result->is_list());
+  ASSERT_EQ(1UL, result->GetList().size());
 }
 
 // Test the |state| option for search().
@@ -1392,9 +1388,8 @@
   std::unique_ptr<base::Value> result(RunFunctionAndReturnResult(
       new DownloadsSearchFunction(), "[{\"state\": \"in_progress\"}]"));
   ASSERT_TRUE(result.get());
-  base::ListValue* result_list = NULL;
-  ASSERT_TRUE(result->GetAsList(&result_list));
-  ASSERT_EQ(1UL, result_list->GetSize());
+  ASSERT_TRUE(result->is_list());
+  ASSERT_EQ(1UL, result->GetList().size());
 }
 
 // Test the |limit| option for search().
@@ -1407,9 +1402,8 @@
   std::unique_ptr<base::Value> result(RunFunctionAndReturnResult(
       new DownloadsSearchFunction(), "[{\"limit\": 1}]"));
   ASSERT_TRUE(result.get());
-  base::ListValue* result_list = NULL;
-  ASSERT_TRUE(result->GetAsList(&result_list));
-  ASSERT_EQ(1UL, result_list->GetSize());
+  ASSERT_TRUE(result->is_list());
+  ASSERT_EQ(1UL, result->GetList().size());
 }
 
 // Test invalid search parameters.
@@ -1452,14 +1446,13 @@
                                  "\"orderBy\": [\"filename\"], "
                                  "\"limit\": 1}]"));
   ASSERT_TRUE(result.get());
-  base::ListValue* result_list = NULL;
-  ASSERT_TRUE(result->GetAsList(&result_list));
-  ASSERT_EQ(1UL, result_list->GetSize());
-  base::DictionaryValue* item_value = NULL;
-  ASSERT_TRUE(result_list->GetDictionary(0, &item_value));
-  std::string item_name;
-  ASSERT_TRUE(item_value->GetString("filename", &item_name));
-  ASSERT_EQ(items[2]->GetTargetFilePath().AsUTF8Unsafe(), item_name);
+  ASSERT_TRUE(result->is_list());
+  ASSERT_EQ(1UL, result->GetList().size());
+  const base::Value& item_value = result->GetList()[0];
+  ASSERT_TRUE(item_value.is_dict());
+  const std::string* item_name = item_value.FindStringKey("filename");
+  ASSERT_TRUE(item_name);
+  ASSERT_EQ(items[2]->GetTargetFilePath().AsUTF8Unsafe(), *item_name);
 }
 
 // Test that incognito downloads are only visible in incognito contexts, and
@@ -1470,13 +1463,7 @@
     DownloadExtensionTest,
     DownloadExtensionTest_SearchPauseResumeCancelGetFileIconIncognito) {
   std::unique_ptr<base::Value> result_value;
-  base::ListValue* result_list = NULL;
-  base::DictionaryValue* result_dict = NULL;
-  std::string filename;
-  bool is_incognito = false;
   std::string error;
-  std::string on_item_arg;
-  std::string off_item_arg;
   std::string result_string;
 
   // Set up one on-record item and one off-record item.
@@ -1486,12 +1473,12 @@
   GoOffTheRecord();
   DownloadItem* off_item = CreateFirstSlowTestDownload();
   ASSERT_TRUE(off_item);
-  off_item_arg = DownloadItemIdAsArgList(off_item);
+  const std::string off_item_arg = DownloadItemIdAsArgList(off_item);
 
   GoOnTheRecord();
   DownloadItem* on_item = CreateSecondSlowTestDownload();
   ASSERT_TRUE(on_item);
-  on_item_arg = DownloadItemIdAsArgList(on_item);
+  const std::string on_item_arg = DownloadItemIdAsArgList(on_item);
   ASSERT_TRUE(on_item->GetTargetFilePath() != off_item->GetTargetFilePath());
 
   // Extensions running in the incognito window should have access to both
@@ -1500,20 +1487,30 @@
   result_value.reset(RunFunctionAndReturnResult(
       new DownloadsSearchFunction(), "[{}]"));
   ASSERT_TRUE(result_value.get());
-  ASSERT_TRUE(result_value->GetAsList(&result_list));
-  ASSERT_EQ(2UL, result_list->GetSize());
-  ASSERT_TRUE(result_list->GetDictionary(0, &result_dict));
-  ASSERT_TRUE(result_dict->GetString("filename", &filename));
-  ASSERT_TRUE(result_dict->GetBoolean("incognito", &is_incognito));
-  EXPECT_TRUE(on_item->GetTargetFilePath() ==
-              base::FilePath::FromUTF8Unsafe(filename));
-  EXPECT_FALSE(is_incognito);
-  ASSERT_TRUE(result_list->GetDictionary(1, &result_dict));
-  ASSERT_TRUE(result_dict->GetString("filename", &filename));
-  ASSERT_TRUE(result_dict->GetBoolean("incognito", &is_incognito));
-  EXPECT_TRUE(off_item->GetTargetFilePath() ==
-              base::FilePath::FromUTF8Unsafe(filename));
-  EXPECT_TRUE(is_incognito);
+  ASSERT_TRUE(result_value->is_list());
+  ASSERT_EQ(2UL, result_value->GetList().size());
+  {
+    const base::Value& result_dict = result_value->GetList()[0];
+    ASSERT_TRUE(result_dict.is_dict());
+    const std::string* filename = result_dict.FindStringKey("filename");
+    ASSERT_TRUE(filename);
+    absl::optional<bool> is_incognito = result_dict.FindBoolKey("incognito");
+    ASSERT_TRUE(is_incognito.has_value());
+    EXPECT_TRUE(on_item->GetTargetFilePath() ==
+                base::FilePath::FromUTF8Unsafe(*filename));
+    EXPECT_FALSE(is_incognito.value());
+  }
+  {
+    const base::Value& result_dict = result_value->GetList()[1];
+    ASSERT_TRUE(result_dict.is_dict());
+    const std::string* filename = result_dict.FindStringKey("filename");
+    ASSERT_TRUE(filename);
+    absl::optional<bool> is_incognito = result_dict.FindBoolKey("incognito");
+    ASSERT_TRUE(is_incognito.has_value());
+    EXPECT_TRUE(off_item->GetTargetFilePath() ==
+                base::FilePath::FromUTF8Unsafe(*filename));
+    EXPECT_TRUE(is_incognito.value());
+  }
 
   // Extensions running in the on-record window should have access only to the
   // on-record item.
@@ -1521,14 +1518,19 @@
   result_value.reset(RunFunctionAndReturnResult(
       new DownloadsSearchFunction(), "[{}]"));
   ASSERT_TRUE(result_value.get());
-  ASSERT_TRUE(result_value->GetAsList(&result_list));
-  ASSERT_EQ(1UL, result_list->GetSize());
-  ASSERT_TRUE(result_list->GetDictionary(0, &result_dict));
-  ASSERT_TRUE(result_dict->GetString("filename", &filename));
-  EXPECT_TRUE(on_item->GetTargetFilePath() ==
-              base::FilePath::FromUTF8Unsafe(filename));
-  ASSERT_TRUE(result_dict->GetBoolean("incognito", &is_incognito));
-  EXPECT_FALSE(is_incognito);
+  ASSERT_TRUE(result_value->is_list());
+  ASSERT_EQ(1UL, result_value->GetList().size());
+  {
+    const base::Value& result_dict = result_value->GetList()[0];
+    ASSERT_TRUE(result_dict.is_dict());
+    const std::string* filename = result_dict.FindStringKey("filename");
+    ASSERT_TRUE(filename);
+    EXPECT_TRUE(on_item->GetTargetFilePath() ==
+                base::FilePath::FromUTF8Unsafe(*filename));
+    absl::optional<bool> is_incognito = result_dict.FindBoolKey("incognito");
+    ASSERT_TRUE(is_incognito.has_value());
+    EXPECT_FALSE(is_incognito.value());
+  }
 
   // Pausing/Resuming the off-record item while on the record should return an
   // error. Cancelling "non-existent" downloads is not an error.
diff --git a/chrome/browser/extensions/api/font_settings/font_settings_api.cc b/chrome/browser/extensions/api/font_settings/font_settings_api.cc
index 60d10f006..afba024 100644
--- a/chrome/browser/extensions/api/font_settings/font_settings_api.cc
+++ b/chrome/browser/extensions/api/font_settings/font_settings_api.cc
@@ -298,23 +298,23 @@
 FontSettingsGetFontListFunction::CopyFontsToResult(base::ListValue* fonts) {
   std::unique_ptr<base::ListValue> result(new base::ListValue());
   for (const auto& entry : fonts->GetList()) {
-    const base::ListValue* font_list_value;
-    if (!entry.GetAsList(&font_list_value)) {
+    if (!entry.is_list()) {
       NOTREACHED();
       return Error("");
     }
+    const base::Value::ConstListView font_list_value = entry.GetList();
 
-    std::string name;
-    if (!font_list_value->GetString(0, &name)) {
+    if (font_list_value.size() < 2 || !font_list_value[0].is_string()) {
       NOTREACHED();
       return Error("");
     }
+    const std::string& name = font_list_value[0].GetString();
 
-    std::string localized_name;
-    if (!font_list_value->GetString(1, &localized_name)) {
+    if (!font_list_value[1].is_string()) {
       NOTREACHED();
       return Error("");
     }
+    const std::string& localized_name = font_list_value[1].GetString();
 
     std::unique_ptr<base::DictionaryValue> font_name(
         new base::DictionaryValue());
diff --git a/chrome/browser/extensions/api/identity/identity_apitest.cc b/chrome/browser/extensions/api/identity/identity_apitest.cc
index f4ebc716..2416294f 100644
--- a/chrome/browser/extensions/api/identity/identity_apitest.cc
+++ b/chrome/browser/extensions/api/identity/identity_apitest.cc
@@ -79,6 +79,7 @@
 #include "services/network/test/test_url_loader_factory.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
 #include "url/gurl.h"
 
 #if BUILDFLAG(IS_CHROMEOS_ASH)
@@ -139,10 +140,10 @@
                         content::BrowserContext* browser_context) {
     response_delegate_ =
         std::make_unique<api_test_utils::SendResponseHelper>(function);
-    std::unique_ptr<base::ListValue> parsed_args(utils::ParseList(args));
-    ASSERT_TRUE(parsed_args.get())
+    absl::optional<base::Value> parsed_args(utils::ParseList(args));
+    ASSERT_TRUE(parsed_args)
         << "Could not parse extension function arguments: " << args;
-    function->SetArgs(base::Value::FromUniquePtrValue(std::move(parsed_args)));
+    function->SetArgs(std::move(*parsed_args));
 
     if (!function->extension()) {
       scoped_refptr<const Extension> empty_extension(
diff --git a/chrome/browser/extensions/api/management/management_api_browsertest.cc b/chrome/browser/extensions/api/management/management_api_browsertest.cc
index 7cd11d6..d48ef5f 100644
--- a/chrome/browser/extensions/api/management/management_api_browsertest.cc
+++ b/chrome/browser/extensions/api/management/management_api_browsertest.cc
@@ -268,9 +268,8 @@
   std::unique_ptr<base::Value> result(
       test_utils::RunFunctionAndReturnSingleResult(function.get(), "[]",
                                                    browser()));
-  base::ListValue* list;
-  ASSERT_TRUE(result->GetAsList(&list));
-  EXPECT_EQ(1U, list->GetSize());
+  ASSERT_TRUE(result->is_list());
+  EXPECT_EQ(1U, result->GetList().size());
 
   // And it should continue to do so even after it crashes.
   ASSERT_TRUE(CrashEnabledExtension(extension->id()));
@@ -278,8 +277,8 @@
   function = new ManagementGetAllFunction();
   result.reset(test_utils::RunFunctionAndReturnSingleResult(function.get(),
                                                             "[]", browser()));
-  ASSERT_TRUE(result->GetAsList(&list));
-  EXPECT_EQ(1U, list->GetSize());
+  ASSERT_TRUE(result->is_list());
+  EXPECT_EQ(1U, result->GetList().size());
 }
 
 class ExtensionManagementApiEscalationTest :
diff --git a/chrome/browser/extensions/api/omnibox/omnibox_unittest.cc b/chrome/browser/extensions/api/omnibox/omnibox_unittest.cc
index c4cf4d1f..b239bf2 100644
--- a/chrome/browser/extensions/api/omnibox/omnibox_unittest.cc
+++ b/chrome/browser/extensions/api/omnibox/omnibox_unittest.cc
@@ -77,7 +77,7 @@
   styles_expected.push_back(ACMatchClassification(9, kNone));
 
   std::unique_ptr<SendSuggestions::Params> params(
-      SendSuggestions::Params::Create(*list));
+      SendSuggestions::Params::Create(list->GetList()));
   EXPECT_TRUE(params);
   ASSERT_FALSE(params->suggest_results.empty());
   CompareClassification(styles_expected, StyleTypesToACMatchClassifications(
@@ -109,7 +109,7 @@
           .Build();
 
   std::unique_ptr<SendSuggestions::Params> swapped_params(
-      SendSuggestions::Params::Create(*swap_list));
+      SendSuggestions::Params::Create(swap_list->GetList()));
   EXPECT_TRUE(swapped_params);
   ASSERT_FALSE(swapped_params->suggest_results.empty());
   CompareClassification(
@@ -173,7 +173,7 @@
   styles_expected.push_back(ACMatchClassification(9, kMatch | kDim));
 
   std::unique_ptr<SendSuggestions::Params> params(
-      SendSuggestions::Params::Create(*list));
+      SendSuggestions::Params::Create(list->GetList()));
   EXPECT_TRUE(params);
   ASSERT_FALSE(params->suggest_results.empty());
   CompareClassification(styles_expected, StyleTypesToACMatchClassifications(
@@ -221,7 +221,7 @@
           .Build();
 
   std::unique_ptr<SendSuggestions::Params> moved_params(
-      SendSuggestions::Params::Create(*moved_list));
+      SendSuggestions::Params::Create(moved_list->GetList()));
   EXPECT_TRUE(moved_params);
   ASSERT_FALSE(moved_params->suggest_results.empty());
   CompareClassification(styles_expected, StyleTypesToACMatchClassifications(
@@ -280,7 +280,7 @@
   styles_expected.push_back(ACMatchClassification(5, kNone));
 
   std::unique_ptr<SendSuggestions::Params> params(
-      SendSuggestions::Params::Create(*list));
+      SendSuggestions::Params::Create(list->GetList()));
   EXPECT_TRUE(params);
   ASSERT_FALSE(params->suggest_results.empty());
   CompareClassification(styles_expected, StyleTypesToACMatchClassifications(
@@ -332,7 +332,7 @@
           .Build();
 
   std::unique_ptr<SetDefaultSuggestion::Params> params(
-      SetDefaultSuggestion::Params::Create(*list));
+      SetDefaultSuggestion::Params::Create(list->GetList()));
   EXPECT_TRUE(params);
 }
 
diff --git a/chrome/browser/extensions/api/printing/print_job_submitter.cc b/chrome/browser/extensions/api/printing/print_job_submitter.cc
index f266659..039644d3 100644
--- a/chrome/browser/extensions/api/printing/print_job_submitter.cc
+++ b/chrome/browser/extensions/api/printing/print_job_submitter.cc
@@ -22,6 +22,7 @@
 #include "chrome/browser/ui/native_window_tracker.h"
 #include "chrome/common/pref_names.h"
 #include "chrome/services/printing/public/mojom/pdf_flattener.mojom.h"
+#include "chrome/services/printing/public/mojom/printing_service.mojom.h"
 #include "chromeos/crosapi/mojom/local_printer.mojom.h"
 #include "chromeos/printing/printer_configuration.h"
 #include "components/prefs/pref_service.h"
diff --git a/chrome/browser/extensions/api/printing/printing_api_handler_unittest.cc b/chrome/browser/extensions/api/printing/printing_api_handler_unittest.cc
index 9c3169e..4cfbc2f 100644
--- a/chrome/browser/extensions/api/printing/printing_api_handler_unittest.cc
+++ b/chrome/browser/extensions/api/printing/printing_api_handler_unittest.cc
@@ -7,6 +7,7 @@
 #include <memory>
 #include <string>
 #include <utility>
+#include <vector>
 
 #include "base/containers/span.h"
 #include "base/json/json_reader.h"
@@ -172,8 +173,8 @@
   request.job.content_type = content_type;
   request.document_blob_uuid = std::move(document_blob_uuid);
 
-  base::ListValue args;
-  args.Set(0, request.ToValue());
+  std::vector<base::Value> args;
+  args.emplace_back(base::Value::FromUniquePtrValue(request.ToValue()));
   return api::printing::SubmitJob::Params::Create(args);
 }
 
diff --git a/chrome/browser/extensions/api/tabs/tabs_api_unittest.cc b/chrome/browser/extensions/api/tabs/tabs_api_unittest.cc
index df436342..0f6c66cc 100644
--- a/chrome/browser/extensions/api/tabs/tabs_api_unittest.cc
+++ b/chrome/browser/extensions/api/tabs/tabs_api_unittest.cc
@@ -431,10 +431,10 @@
 
   scoped_refptr<TabsUpdateFunction> function = new TabsUpdateFunction();
   function->set_extension(extension.get());
-  std::unique_ptr<base::ListValue> args(
+  function->SetArgs(
       extension_function_test_utils::ParseList(
-          base::StringPrintf(R"([%d, {"url":"http://example.com"}])", tab_id)));
-  function->SetArgs(base::Value::FromUniquePtrValue(std::move(args)));
+          base::StringPrintf(R"([%d, {"url":"http://example.com"}])", tab_id))
+          .value());
   api_test_utils::SendResponseHelper response_helper(function.get());
   function->RunWithValidation()->Execute();
 
diff --git a/chrome/browser/extensions/api/top_sites/top_sites_apitest.cc b/chrome/browser/extensions/api/top_sites/top_sites_apitest.cc
index b885e0b..c672b319 100644
--- a/chrome/browser/extensions/api/top_sites/top_sites_apitest.cc
+++ b/chrome/browser/extensions/api/top_sites/top_sites_apitest.cc
@@ -79,9 +79,8 @@
 
   std::unique_ptr<base::Value> result(utils::RunFunctionAndReturnSingleResult(
       get_top_sites_function.get(), "[]", browser()));
-  base::ListValue* list;
-  ASSERT_TRUE(result->GetAsList(&list));
-  EXPECT_GE(list->GetSize(), top_sites_prepopulated_pages_size());
+  ASSERT_TRUE(result->is_list());
+  EXPECT_GE(result->GetList().size(), top_sites_prepopulated_pages_size());
 }
 
 }  // namespace extensions
diff --git a/chrome/browser/extensions/api/webrtc_audio_private/webrtc_audio_private_browsertest.cc b/chrome/browser/extensions/api/webrtc_audio_private/webrtc_audio_private_browsertest.cc
index 9111dde..1a504d1 100644
--- a/chrome/browser/extensions/api/webrtc_audio_private/webrtc_audio_private_browsertest.cc
+++ b/chrome/browser/extensions/api/webrtc_audio_private/webrtc_audio_private_browsertest.cc
@@ -123,14 +123,13 @@
     params->Append(std::move(request_info));
   }
 
-  std::unique_ptr<base::Value> InvokeGetSinks(base::ListValue** sink_list) {
+  std::unique_ptr<base::Value> InvokeGetSinks() {
     scoped_refptr<WebrtcAudioPrivateGetSinksFunction> function =
         new WebrtcAudioPrivateGetSinksFunction();
     function->set_source_url(source_url_);
 
     std::unique_ptr<base::Value> result(
         RunFunctionAndReturnSingleResult(function.get(), "[]", browser()));
-    result->GetAsList(sink_list);
     return result;
   }
 
@@ -143,23 +142,22 @@
   AudioDeviceDescriptions devices;
   GetAudioDeviceDescriptions(false, &devices);
 
-  base::ListValue* sink_list = NULL;
-  std::unique_ptr<base::Value> result = InvokeGetSinks(&sink_list);
+  std::unique_ptr<base::Value> result = InvokeGetSinks();
+  const base::ListValue& sink_list = base::Value::AsListValue(*result);
 
   std::string result_string;
   JSONWriter::Write(*result, &result_string);
   VLOG(2) << result_string;
 
-  EXPECT_EQ(devices.size(), sink_list->GetSize());
+  EXPECT_EQ(devices.size(), sink_list.GetSize());
 
   // Iterate through both lists in lockstep and compare. The order
   // should be identical.
   size_t ix = 0;
   AudioDeviceDescriptions::const_iterator it = devices.begin();
-  for (; ix < sink_list->GetSize() && it != devices.end();
-       ++ix, ++it) {
-    base::DictionaryValue* dict = NULL;
-    sink_list->GetDictionary(ix, &dict);
+  for (; ix < sink_list.GetSize() && it != devices.end(); ++ix, ++it) {
+    const base::DictionaryValue* dict = NULL;
+    sink_list.GetDictionary(ix, &dict);
     std::string sink_id;
     dict->GetString("sinkId", &sink_id);
 
diff --git a/chrome/browser/extensions/extension_api_unittest.cc b/chrome/browser/extensions/extension_api_unittest.cc
index 1867985..ba8e3a3fd 100644
--- a/chrome/browser/extensions/extension_api_unittest.cc
+++ b/chrome/browser/extensions/extension_api_unittest.cc
@@ -53,15 +53,13 @@
     ExtensionFunction* function,
     const std::string& args) {
   base::Value* value = RunFunctionAndReturnValue(function, args).release();
-  base::ListValue* list = NULL;
 
-  if (value && !value->GetAsList(&list))
+  if (value && !value->is_list())
     delete value;
 
-  // We expect to either have successfuly retrieved a list from the value,
-  // or the value to have been NULL.
-  EXPECT_TRUE(list);
-  return std::unique_ptr<base::ListValue>(list);
+  // We expect to have successfully retrieved a list from the value.
+  EXPECT_TRUE(value);
+  return base::ListValue::From(std::unique_ptr<base::Value>(value));
 }
 
 std::string ExtensionApiUnittest::RunFunctionAndReturnError(
diff --git a/chrome/browser/extensions/extension_function_test_utils.cc b/chrome/browser/extensions/extension_function_test_utils.cc
index 2ec364aa..d2572965 100644
--- a/chrome/browser/extensions/extension_function_test_utils.cc
+++ b/chrome/browser/extensions/extension_function_test_utils.cc
@@ -21,6 +21,7 @@
 #include "extensions/browser/extension_function_dispatcher.h"
 #include "extensions/common/extension.h"
 #include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
 
 using content::WebContents;
 using extensions::Extension;
@@ -50,16 +51,15 @@
 
 namespace extension_function_test_utils {
 
-base::ListValue* ParseList(const std::string& data) {
-  std::unique_ptr<base::Value> result = base::JSONReader::ReadDeprecated(data);
+absl::optional<base::Value> ParseList(const std::string& data) {
+  absl::optional<base::Value> result = base::JSONReader::Read(data);
   if (!result) {
     ADD_FAILURE() << "Failed to parse: " << data;
-    return nullptr;
+    return absl::nullopt;
   }
-  base::ListValue* list = NULL;
-  result->GetAsList(&list);
-  ignore_result(result.release());
-  return list;
+  if (!result->is_list())
+    return absl::nullopt;
+  return result;
 }
 
 base::DictionaryValue* ToDictionary(base::Value* val) {
@@ -134,9 +134,12 @@
                  const std::string& args,
                  Browser* browser,
                  extensions::api_test_utils::RunFunctionFlags flags) {
-  std::unique_ptr<base::ListValue> parsed_args(ParseList(args));
-  EXPECT_TRUE(parsed_args.get())
+  absl::optional<base::Value> maybe_parsed_args(ParseList(args));
+  EXPECT_TRUE(maybe_parsed_args)
       << "Could not parse extension function arguments: " << args;
+  std::unique_ptr<base::ListValue> parsed_args(base::ListValue::From(
+      base::Value::ToUniquePtrValue(std::move(*maybe_parsed_args))));
+
   return RunFunction(function, std::move(parsed_args), browser, flags);
 }
 
diff --git a/chrome/browser/extensions/extension_function_test_utils.h b/chrome/browser/extensions/extension_function_test_utils.h
index b1c76c47..3f9aa92f 100644
--- a/chrome/browser/extensions/extension_function_test_utils.h
+++ b/chrome/browser/extensions/extension_function_test_utils.h
@@ -8,8 +8,10 @@
 #include <string>
 
 #include "base/memory/ref_counted.h"
+#include "base/values.h"
 #include "extensions/browser/api_test_utils.h"
 #include "extensions/common/manifest.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
 
 class Browser;
 class ExtensionFunction;
@@ -26,8 +28,8 @@
 // extensions/browser/api_test_utils.h.
 namespace extension_function_test_utils {
 
-// Parse JSON and return as a ListValue, or null if invalid.
-base::ListValue* ParseList(const std::string& data);
+// Parse JSON and return as a list Value, or nullopt if invalid.
+absl::optional<base::Value> ParseList(const std::string& data);
 
 // If |val| is a dictionary, return it as one, otherwise NULL.
 base::DictionaryValue* ToDictionary(base::Value* val);
diff --git a/chrome/browser/extensions/extension_web_ui.cc b/chrome/browser/extensions/extension_web_ui.cc
index cb2e6b4b..75095fb 100644
--- a/chrome/browser/extensions/extension_web_ui.cc
+++ b/chrome/browser/extensions/extension_web_ui.cc
@@ -461,11 +461,10 @@
   // chrome://bookmarks/#1 for display in the omnibox.
   for (base::DictionaryValue::Iterator dict_iter(*overrides);
        !dict_iter.IsAtEnd(); dict_iter.Advance()) {
-    const base::ListValue* url_list = nullptr;
-    if (!dict_iter.value().GetAsList(&url_list))
+    if (!dict_iter.value().is_list())
       continue;
 
-    for (const auto& list_iter : url_list->GetList()) {
+    for (const auto& list_iter : dict_iter.value().GetList()) {
       const std::string* override = nullptr;
       if (list_iter.is_dict())
         override = list_iter.FindStringKey(kEntry);
diff --git a/chrome/browser/extensions/menu_manager.cc b/chrome/browser/extensions/menu_manager.cc
index 3241ff3..ad93682 100644
--- a/chrome/browser/extensions/menu_manager.cc
+++ b/chrome/browser/extensions/menu_manager.cc
@@ -73,16 +73,15 @@
                                        base::Value* value) {
   MenuItem::OwnedList items;
 
-  base::ListValue* list = nullptr;
-  if (!value || !value->GetAsList(&list))
+  if (!value || !value->is_list())
     return items;
 
-  for (size_t i = 0; i < list->GetSize(); ++i) {
-    base::DictionaryValue* dict = nullptr;
-    if (!list->GetDictionary(i, &dict))
+  for (const base::Value& elem : value->GetList()) {
+    if (!elem.is_dict())
       continue;
+    const base::DictionaryValue& dict = base::Value::AsDictionaryValue(elem);
     std::unique_ptr<MenuItem> item =
-        MenuItem::Populate(extension_id, *dict, nullptr);
+        MenuItem::Populate(extension_id, dict, nullptr);
     if (!item)
       continue;
     items.push_back(std::move(item));
diff --git a/chrome/browser/extensions/policy_handlers.cc b/chrome/browser/extensions/policy_handlers.cc
index 5ab665f..73269a3 100644
--- a/chrome/browser/extensions/policy_handlers.cc
+++ b/chrome/browser/extensions/policy_handlers.cc
@@ -108,15 +108,14 @@
   if (!policy_value)
     return true;
 
-  const base::ListValue* policy_list_value = nullptr;
-  if (!policy_value->GetAsList(&policy_list_value)) {
+  if (!policy_value->is_list()) {
     // This should have been caught in CheckPolicySettings.
     NOTREACHED();
     return false;
   }
 
   int index = -1;
-  for (const auto& entry : policy_list_value->GetList()) {
+  for (const auto& entry : policy_value->GetList()) {
     ++index;
     if (!entry.is_string()) {
       if (errors) {
@@ -179,15 +178,14 @@
   if (!value)
     return true;
 
-  const base::ListValue* list_value = NULL;
-  if (!value->GetAsList(&list_value)) {
+  if (!value->is_list()) {
     NOTREACHED();
     return false;
   }
 
   // Check that the list contains valid URLPattern strings only.
   int index = 0;
-  for (const auto& entry : list_value->GetList()) {
+  for (const auto& entry : value->GetList()) {
     if (!entry.is_string()) {
       errors->AddError(policy_name(), index, IDS_POLICY_TYPE_ERROR,
                        base::Value::GetTypeName(base::Value::Type::STRING));
diff --git a/chrome/browser/external_protocol/external_protocol_handler.cc b/chrome/browser/external_protocol/external_protocol_handler.cc
index cee19d84..34eaaae 100644
--- a/chrome/browser/external_protocol/external_protocol_handler.cc
+++ b/chrome/browser/external_protocol/external_protocol_handler.cc
@@ -68,6 +68,7 @@
     "hcp",
     "ie.http",
     "javascript",
+    "mk",
     "ms-help",
     "nntp",
     "res",
diff --git a/chrome/browser/external_protocol/external_protocol_handler_unittest.cc b/chrome/browser/external_protocol/external_protocol_handler_unittest.cc
index 9b60d89f..d356d73 100644
--- a/chrome/browser/external_protocol/external_protocol_handler_unittest.cc
+++ b/chrome/browser/external_protocol/external_protocol_handler_unittest.cc
@@ -345,6 +345,10 @@
   block_state = ExternalProtocolHandler::GetBlockState("ie.http", nullptr,
                                                        profile_.get());
   EXPECT_EQ(ExternalProtocolHandler::BLOCK, block_state);
+  EXPECT_EQ("mk", GURL("mk:@FooBar:ie.http:res://foo.bar/baz").scheme());
+  block_state =
+      ExternalProtocolHandler::GetBlockState("mk", nullptr, profile_.get());
+  EXPECT_EQ(ExternalProtocolHandler::BLOCK, block_state);
   EXPECT_TRUE(
       profile_->GetPrefs()
           ->GetDictionary(prefs::kProtocolHandlerPerOriginAllowedProtocols)
diff --git a/chrome/browser/flag-metadata.json b/chrome/browser/flag-metadata.json
index b1c1643..69235ac6 100644
--- a/chrome/browser/flag-metadata.json
+++ b/chrome/browser/flag-metadata.json
@@ -1171,8 +1171,13 @@
   },
    {
     "name": "document-transition",
-    "owners": [ "vmpstr", "chrishtr" ],
-    "expiry_milestone" : 94
+    "owners": [ "khushalsagar", "vmpstr", "chrishtr" ],
+    "expiry_milestone" : 98
+  },
+   {
+    "name": "document-transition-slowdown-factor",
+    "owners": [ "khushalsagar", "vmpstr", "chrishtr" ],
+    "expiry_milestone" : 98
   },
   {
     "name": "double-buffer-compositing",
@@ -4417,6 +4422,11 @@
     "expiry_milestone": 100
   },
   {
+    "name": "persistent-quota-is-temporary-quota",
+    "owners": [ "mek", "cfredric" ],
+    "expiry_milestone": 100
+  },
+  {
     "name": "photo-picker-video-support",
     "owners": [ "finnur" ],
     "expiry_milestone": 92
diff --git a/chrome/browser/flag_descriptions.cc b/chrome/browser/flag_descriptions.cc
index 2d92a0a..8f16c48 100644
--- a/chrome/browser/flag_descriptions.cc
+++ b/chrome/browser/flag_descriptions.cc
@@ -614,6 +614,12 @@
 const char kDocumentTransitionDescription[] =
     "Controls the availability of the documentTransition JavaScript API.";
 
+const char kDocumentTransitionSlowdownFactorName[] =
+    "documentTransition API Duration Control";
+const char kDocumentTransitionSlowdownFactorDescription[] =
+    "Slows down animations triggered by documentTransition JavaScript API for "
+    "debugging.";
+
 const char kEnableAudioFocusEnforcementName[] = "Audio Focus Enforcement";
 const char kEnableAudioFocusEnforcementDescription[] =
     "Enables enforcement of a single media session having audio focus at "
@@ -2032,6 +2038,12 @@
     "prompts. Requires chrome://flags/#quiet-notification-prompts to be "
     "enabled.";
 
+const char kPersistentQuotaIsTemporaryQuotaName[] =
+    "window.PERSISTENT is temporary quota.";
+const char kPersistentQuotaIsTemporaryQuotaDescription[] =
+    "Causes the window.PERSISTENT quota type to have the same semantics as "
+    "window.TEMPORARY.";
+
 const char kPlaybackSpeedButtonName[] = "Playback Speed Button";
 const char kPlaybackSpeedButtonDescription[] =
     "Enable the playback speed button on the media controls.";
diff --git a/chrome/browser/flag_descriptions.h b/chrome/browser/flag_descriptions.h
index cd82848..b50f698 100644
--- a/chrome/browser/flag_descriptions.h
+++ b/chrome/browser/flag_descriptions.h
@@ -370,6 +370,9 @@
 extern const char kDocumentTransitionName[];
 extern const char kDocumentTransitionDescription[];
 
+extern const char kDocumentTransitionSlowdownFactorName[];
+extern const char kDocumentTransitionSlowdownFactorDescription[];
+
 extern const char kEnableAudioFocusEnforcementName[];
 extern const char kEnableAudioFocusEnforcementDescription[];
 
@@ -1157,6 +1160,9 @@
 extern const char kPermissionQuietChipName[];
 extern const char kPermissionQuietChipDescription[];
 
+extern const char kPersistentQuotaIsTemporaryQuotaName[];
+extern const char kPersistentQuotaIsTemporaryQuotaDescription[];
+
 extern const char kPlaybackSpeedButtonName[];
 extern const char kPlaybackSpeedButtonDescription[];
 
diff --git a/chrome/browser/incognito/BUILD.gn b/chrome/browser/incognito/BUILD.gn
new file mode 100644
index 0000000..583cbbd9
--- /dev/null
+++ b/chrome/browser/incognito/BUILD.gn
@@ -0,0 +1,33 @@
+# Copyright 2021 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import("//build/config/android/rules.gni")
+
+android_library("java") {
+  sources = [
+    "android/java/src/org/chromium/chrome/browser/incognito/IncognitoCctProfileManager.java",
+    "android/java/src/org/chromium/chrome/browser/incognito/IncognitoStartup.java",
+    "android/java/src/org/chromium/chrome/browser/incognito/IncognitoTabPersistence.java",
+    "android/java/src/org/chromium/chrome/browser/incognito/IncognitoUtils.java",
+  ]
+  deps = [
+    "//base:base_java",
+    "//chrome/browser/dependency_injection:java",
+    "//chrome/browser/profiles/android:java",
+    "//chrome/browser/tab:java",
+    "//chrome/browser/tabmodel:java",
+    "//chrome/browser/tabpersistence:java",
+    "//chrome/browser/util:java",
+    "//third_party/android_deps:dagger_java",
+    "//third_party/android_deps:javax_inject_javax_inject_java",
+    "//third_party/androidx:androidx_annotation_annotation_java",
+    "//ui/android:ui_full_java",
+    "//url:gurl_java",
+  ]
+  annotation_processor_deps = [ "//base/android/jni_generator:jni_processor" ]
+}
+
+generate_jni("jni_headers") {
+  sources = [ "android/java/src/org/chromium/chrome/browser/incognito/IncognitoUtils.java" ]
+}
diff --git a/chrome/browser/incognito/DIR_METADATA b/chrome/browser/incognito/DIR_METADATA
new file mode 100644
index 0000000..7d2fe23
--- /dev/null
+++ b/chrome/browser/incognito/DIR_METADATA
@@ -0,0 +1,5 @@
+monorail {
+  component: "Privacy>Incognito"
+}
+team_email: "chrome-incognito@google.com"
+os: ANDROID
diff --git a/chrome/browser/incognito/OWNERS b/chrome/browser/incognito/OWNERS
new file mode 100644
index 0000000..cab471dc
--- /dev/null
+++ b/chrome/browser/incognito/OWNERS
@@ -0,0 +1,3 @@
+rhalavati@chromium.org
+roagarwal@chromium.org
+sideyilmaz@chromium.org
diff --git a/chrome/browser/profiles/incognito_utils_android.cc b/chrome/browser/incognito/android/incognito_utils_android.cc
similarity index 94%
rename from chrome/browser/profiles/incognito_utils_android.cc
rename to chrome/browser/incognito/android/incognito_utils_android.cc
index ee69be1..203519c 100644
--- a/chrome/browser/profiles/incognito_utils_android.cc
+++ b/chrome/browser/incognito/android/incognito_utils_android.cc
@@ -4,7 +4,7 @@
 
 #include <jni.h>
 
-#include "chrome/android/chrome_jni_headers/IncognitoUtils_jni.h"
+#include "chrome/browser/incognito/jni_headers/IncognitoUtils_jni.h"
 #include "chrome/browser/prefs/incognito_mode_prefs.h"
 #include "chrome/browser/profiles/profile_manager.h"
 #include "chrome/common/pref_names.h"
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/incognito/IncognitoCctProfileManager.java b/chrome/browser/incognito/android/java/src/org/chromium/chrome/browser/incognito/IncognitoCctProfileManager.java
similarity index 100%
rename from chrome/android/java/src/org/chromium/chrome/browser/incognito/IncognitoCctProfileManager.java
rename to chrome/browser/incognito/android/java/src/org/chromium/chrome/browser/incognito/IncognitoCctProfileManager.java
diff --git a/chrome/browser/incognito/android/java/src/org/chromium/chrome/browser/incognito/IncognitoStartup.java b/chrome/browser/incognito/android/java/src/org/chromium/chrome/browser/incognito/IncognitoStartup.java
new file mode 100644
index 0000000..22eda94e
--- /dev/null
+++ b/chrome/browser/incognito/android/java/src/org/chromium/chrome/browser/incognito/IncognitoStartup.java
@@ -0,0 +1,83 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.browser.incognito;
+
+import android.annotation.SuppressLint;
+import android.app.Activity;
+import android.app.ActivityManager.RecentTaskInfo;
+
+import org.chromium.base.ApplicationStatus;
+import org.chromium.base.ContextUtils;
+import org.chromium.base.supplier.ObservableSupplier;
+import org.chromium.chrome.browser.cookies.CookiesFetcher;
+import org.chromium.chrome.browser.profiles.Profile;
+import org.chromium.chrome.browser.tabmodel.IncognitoTabHostUtils;
+import org.chromium.chrome.browser.tabmodel.TabModelSelector;
+import org.chromium.chrome.browser.util.AndroidTaskUtils;
+
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * Operations that need to be executed on startup for incognito mode.
+ */
+public class IncognitoStartup {
+    public static void onResumeWithNative(
+            ObservableSupplier<TabModelSelector> tabModelSelectorSupplier,
+            Set<String> componentNames) {
+        if (shouldDestroyIncognitoProfileOnStartup(
+                    tabModelSelectorSupplier.get().getCurrentModel().isIncognito(),
+                    componentNames)) {
+            Profile.getLastUsedRegularProfile()
+                    .getPrimaryOTRProfile(/*createIfNeeded=*/true)
+                    .destroyWhenAppropriate();
+        } else {
+            CookiesFetcher.restoreCookies();
+        }
+    }
+
+    /**
+     * Determine whether the incognito profile needs to be destroyed as part of startup.  This is
+     * only needed on L+ when it is possible to swipe away tasks from Android recents without
+     * killing the process.  When this occurs, the normal incognito profile shutdown does not
+     * happen, which can leave behind incognito cookies from an existing session.
+     */
+    @SuppressLint("NewApi")
+    private static boolean shouldDestroyIncognitoProfileOnStartup(
+            boolean selectedTabModelIsIncognito, Set<String> componentNames) {
+        if (!Profile.getLastUsedRegularProfile().hasPrimaryOTRProfile()) {
+            return false;
+        }
+
+        Set<RecentTaskInfo> matchingTaskInfos =
+                AndroidTaskUtils.getRecentTaskInfosMatchingComponentNames(
+                        ContextUtils.getApplicationContext(), componentNames);
+        Set<Integer> tabbedModeTaskIds = new HashSet<>();
+        for (RecentTaskInfo info : matchingTaskInfos) {
+            tabbedModeTaskIds.add(info.id);
+        }
+
+        if (tabbedModeTaskIds.size() == 0) {
+            return true;
+        }
+
+        for (Activity activity : ApplicationStatus.getRunningActivities()) {
+            tabbedModeTaskIds.remove(activity.getTaskId());
+        }
+
+        // If all tabbed mode tasks listed in Android recents are alive, check to see if
+        // any incognito tabs exist and the current tab model isn't incognito. If so, we should
+        // destroy the incognito profile; otherwise it's not safe to do so yet.
+        if (tabbedModeTaskIds.size() == 0) {
+            return !(IncognitoTabHostUtils.doIncognitoTabsExist() || selectedTabModelIsIncognito);
+        }
+
+        // In this case, we have tabbed mode activities listed in recents that do not have an
+        // active running activity associated with them.  We can not accurately get an incognito
+        // tab count as we do not know if any incognito tabs are associated with the yet unrestored
+        // tabbed mode.  Thus we do not proactively destroy the incognito profile.
+        return false;
+    }
+}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/incognito/IncognitoTabPersistence.java b/chrome/browser/incognito/android/java/src/org/chromium/chrome/browser/incognito/IncognitoTabPersistence.java
similarity index 100%
rename from chrome/android/java/src/org/chromium/chrome/browser/incognito/IncognitoTabPersistence.java
rename to chrome/browser/incognito/android/java/src/org/chromium/chrome/browser/incognito/IncognitoTabPersistence.java
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/incognito/IncognitoUtils.java b/chrome/browser/incognito/android/java/src/org/chromium/chrome/browser/incognito/IncognitoUtils.java
similarity index 66%
rename from chrome/android/java/src/org/chromium/chrome/browser/incognito/IncognitoUtils.java
rename to chrome/browser/incognito/android/java/src/org/chromium/chrome/browser/incognito/IncognitoUtils.java
index aa9a060..73c9c3b9b 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/incognito/IncognitoUtils.java
+++ b/chrome/browser/incognito/android/java/src/org/chromium/chrome/browser/incognito/IncognitoUtils.java
@@ -4,26 +4,15 @@
 
 package org.chromium.chrome.browser.incognito;
 
-import android.annotation.SuppressLint;
-import android.app.Activity;
-import android.app.ActivityManager.RecentTaskInfo;
-
 import androidx.annotation.Nullable;
 import androidx.annotation.VisibleForTesting;
 
-import org.chromium.base.ApplicationStatus;
-import org.chromium.base.ContextUtils;
 import org.chromium.base.annotations.NativeMethods;
 import org.chromium.chrome.browser.profiles.OTRProfileID;
 import org.chromium.chrome.browser.profiles.Profile;
 import org.chromium.chrome.browser.profiles.ProfileKey;
-import org.chromium.chrome.browser.tabmodel.IncognitoTabHostUtils;
-import org.chromium.chrome.browser.util.AndroidTaskUtils;
 import org.chromium.ui.base.WindowAndroid;
 
-import java.util.HashSet;
-import java.util.Set;
-
 /**
  * Utilities for working with incognito tabs spread across multiple activities.
  */
@@ -33,49 +22,6 @@
     private IncognitoUtils() {}
 
     /**
-     * Determine whether the incognito profile needs to be destroyed as part of startup.  This is
-     * only needed on L+ when it is possible to swipe away tasks from Android recents without
-     * killing the process.  When this occurs, the normal incognito profile shutdown does not
-     * happen, which can leave behind incognito cookies from an existing session.
-     */
-    @SuppressLint("NewApi")
-    public static boolean shouldDestroyIncognitoProfileOnStartup(
-            boolean selectedTabModelIsIncognito, Set<String> componentNames) {
-        if (!Profile.getLastUsedRegularProfile().hasPrimaryOTRProfile()) {
-            return false;
-        }
-
-        Set<RecentTaskInfo> matchingTaskInfos =
-                AndroidTaskUtils.getRecentTaskInfosMatchingComponentNames(
-                        ContextUtils.getApplicationContext(), componentNames);
-        Set<Integer> tabbedModeTaskIds = new HashSet<>();
-        for (RecentTaskInfo info : matchingTaskInfos) {
-            tabbedModeTaskIds.add(info.id);
-        }
-
-        if (tabbedModeTaskIds.size() == 0) {
-            return true;
-        }
-
-        for (Activity activity : ApplicationStatus.getRunningActivities()) {
-            tabbedModeTaskIds.remove(activity.getTaskId());
-        }
-
-        // If all tabbed mode tasks listed in Android recents are alive, check to see if
-        // any incognito tabs exist and the current tab model isn't incognito. If so, we should
-        // destroy the incognito profile; otherwise it's not safe to do so yet.
-        if (tabbedModeTaskIds.size() == 0) {
-            return !(IncognitoTabHostUtils.doIncognitoTabsExist() || selectedTabModelIsIncognito);
-        }
-
-        // In this case, we have tabbed mode activities listed in recents that do not have an
-        // active running activity associated with them.  We can not accurately get an incognito
-        // tab count as we do not know if any incognito tabs are associated with the yet unrestored
-        // tabbed mode.  Thus we do not proactively destroy the incognito profile.
-        return false;
-    }
-
-    /**
      * @return true if incognito mode is enabled.
      */
     public static boolean isIncognitoModeEnabled() {
diff --git a/chrome/browser/lifetime/application_lifetime.cc b/chrome/browser/lifetime/application_lifetime.cc
index f7dd53aa..4fd1bec 100644
--- a/chrome/browser/lifetime/application_lifetime.cc
+++ b/chrome/browser/lifetime/application_lifetime.cc
@@ -316,7 +316,14 @@
 }
 
 #if !defined(OS_ANDROID)
+namespace {
+
+bool g_relaunch_ignore_unload_handlers_called = false;
+
+}  // namespace
+
 void RelaunchIgnoreUnloadHandlers() {
+  g_relaunch_ignore_unload_handlers_called = true;
 #if BUILDFLAG(IS_CHROMEOS_ASH)
   chromeos::PowerManagerClient::Get()->RequestRestart(
       power_manager::REQUEST_RESTART_OTHER, "Chrome relaunch");
@@ -324,6 +331,11 @@
 #endif
   AttemptRestartInternal(IgnoreUnloadHandlers(true));
 }
+
+bool DidCallRelaunchIgnoreUnloadHandlers() {
+  return g_relaunch_ignore_unload_handlers_called;
+}
+
 #endif
 
 void AttemptExit() {
diff --git a/chrome/browser/lifetime/application_lifetime.h b/chrome/browser/lifetime/application_lifetime.h
index 66f2d81..13f4a7f 100644
--- a/chrome/browser/lifetime/application_lifetime.h
+++ b/chrome/browser/lifetime/application_lifetime.h
@@ -41,6 +41,8 @@
 // that all user prompts (e.g., beforeunload handlers and confirmation to abort
 // in-progress downloads) are bypassed.
 void RelaunchIgnoreUnloadHandlers();
+// TODO(https://crbug.com/1227426): for debugging.
+bool DidCallRelaunchIgnoreUnloadHandlers();
 #endif
 
 // Attempt to exit by closing all browsers.  This is equivalent to
diff --git a/chrome/browser/lifetime/browser_shutdown.cc b/chrome/browser/lifetime/browser_shutdown.cc
index 083ee5be..a97092c 100644
--- a/chrome/browser/lifetime/browser_shutdown.cc
+++ b/chrome/browser/lifetime/browser_shutdown.cc
@@ -13,6 +13,7 @@
 #include "base/bind.h"
 #include "base/clang_profiling_buildflags.h"
 #include "base/command_line.h"
+#include "base/debug/dump_without_crashing.h"
 #include "base/files/file_path.h"
 #include "base/files/file_util.h"
 #include "base/metrics/histogram_functions.h"
@@ -462,6 +463,11 @@
   PrefService* pref_service = g_browser_process->local_state();
   if (pref_service) {
 #if !defined(OS_ANDROID)
+    // TODO(https://crbug.com/1227426): for debugging.
+    if (pref_service->GetBoolean(prefs::kWasRestarted) &&
+        chrome::DidCallRelaunchIgnoreUnloadHandlers()) {
+      base::debug::DumpWithoutCrashing();
+    }
     pref_service->ClearPref(prefs::kWasRestarted);
 #endif  // !defined(OS_ANDROID)
     pref_service->ClearPref(prefs::kRestartLastSessionOnShutdown);
diff --git a/chrome/browser/media/cast_mirroring_service_host.cc b/chrome/browser/media/cast_mirroring_service_host.cc
index 0998b005..71b8d59d 100644
--- a/chrome/browser/media/cast_mirroring_service_host.cc
+++ b/chrome/browser/media/cast_mirroring_service_host.cc
@@ -20,9 +20,9 @@
 #include "chrome/browser/media/webrtc/media_capture_devices_dispatcher.h"
 #include "chrome/browser/media/webrtc/media_stream_capture_indicator.h"
 #include "chrome/browser/net/system_network_context_manager.h"
-#include "chrome/browser/service_sandbox_type.h"
 #include "components/mirroring/browser/single_client_video_capture_host.h"
 #include "components/mirroring/mojom/cast_message_channel.mojom.h"
+#include "components/mirroring/mojom/mirroring_service.mojom.h"
 #include "components/mirroring/mojom/session_observer.mojom.h"
 #include "components/mirroring/mojom/session_parameters.mojom.h"
 #include "content/public/browser/audio_service.h"
diff --git a/chrome/browser/metrics/cloned_install_client_id_reset_browsertest.cc b/chrome/browser/metrics/cloned_install_client_id_reset_browsertest.cc
index 81f6cb1..40c4c1b 100644
--- a/chrome/browser/metrics/cloned_install_client_id_reset_browsertest.cc
+++ b/chrome/browser/metrics/cloned_install_client_id_reset_browsertest.cc
@@ -2,20 +2,21 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include "base/path_service.h"
+#include "base/test/task_environment.h"
 #include "build/build_config.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/metrics/chrome_metrics_service_accessor.h"
+#include "chrome/common/chrome_paths.h"
 #include "chrome/installer/util/google_update_settings.h"
 #include "chrome/test/base/in_process_browser_test.h"
 #include "components/metrics/metrics_pref_names.h"
 #include "components/metrics/metrics_service.h"
 #include "components/metrics/metrics_state_manager.h"
 #include "components/metrics_services_manager/metrics_services_manager.h"
+#include "components/prefs/json_pref_store.h"
 #include "content/public/test/browser_test.h"
-
-#if defined(OS_WIN)
-#include "base/test/test_reg_util_win.h"
-#endif  // defined(OS_WIN)
+#include "content/public/test/test_launcher.h"
 
 namespace {
 const char kInitialClientId[] = "11111111-2222-aaaa-bbbb-cccccccccccc";
@@ -26,6 +27,36 @@
   ClonedInstallClientIdResetBrowserTest() = default;
   ~ClonedInstallClientIdResetBrowserTest() override = default;
 
+  bool SetUpUserDataDirectory() override {
+    if (!InProcessBrowserTest::SetUpUserDataDirectory())
+      return false;
+
+    // Changing in user's data directory should only be done once before
+    // PRE_TestClonedInstallClientIdReset.
+    if (!content::IsPreTest())
+      return true;
+
+    base::ScopedAllowBlockingForTesting allow_blocking;
+    base::FilePath user_dir;
+    CHECK(base::PathService::Get(chrome::DIR_USER_DATA, &user_dir));
+
+    // Create a local-state file with what we want the browser to use. This
+    // has to be done here because there is no hook between when the browser
+    // is initialized and the metrics-client acts on the pref values. The
+    // "Local State" directory is hard-coded because the FILE_LOCAL_STATE
+    // path is not yet defined at this point.
+    base::test::TaskEnvironment task_env;
+    auto state = base::MakeRefCounted<JsonPrefStore>(
+        user_dir.Append(FILE_PATH_LITERAL("Local State")));
+
+    // Set up the initial client id for (before)
+    // PRE_TestClonedInstallClientIdReset.
+    state->SetValue(metrics::prefs::kMetricsClientID,
+                    std::make_unique<base::Value>(kInitialClientId), 0);
+
+    return true;
+  }
+
   void SetUp() override {
     // Make metrics reporting work same as in Chrome branded builds, for test
     // consistency between Chromium and Chrome builds.
@@ -34,16 +65,6 @@
     ChromeMetricsServiceAccessor::SetMetricsAndCrashReportingForTesting(
         &metrics_enabled_);
 
-// On windows, the registry is used for client info backups.
-#if defined(OS_WIN)
-    ASSERT_NO_FATAL_FAILURE(
-        registry_override_.OverrideRegistry(HKEY_CURRENT_USER));
-#endif  // defined(OS_WIN)
-
-    metrics::ClientInfo client_info;
-    client_info.client_id = kInitialClientId;
-    GoogleUpdateSettings::StoreMetricsClientInfo(client_info);
-
     InProcessBrowserTest::SetUp();
   }
 
@@ -55,10 +76,6 @@
 
  private:
   bool metrics_enabled_ = true;
-
-#if defined(OS_WIN)
-  registry_util::RegistryOverrideManager registry_override_;
-#endif  // defined(OS_WIN)
 };
 
 IN_PROC_BROWSER_TEST_F(ClonedInstallClientIdResetBrowserTest,
@@ -67,16 +84,8 @@
   EXPECT_EQ(kInitialClientId, metrics_service()->GetClientId());
 }
 
-// Test is flaky on Mac (https://crbug.com/1175077).
-// And on Linux (https://crbug.com/1227760).
-#if defined(OS_MAC) || defined(OS_LINUX)
-#define MAYBE_TestClonedInstallClientIdReset \
-  DISABLED_TestClonedInstallClientIdReset
-#else
-#define MAYBE_TestClonedInstallClientIdReset TestClonedInstallClientIdReset
-#endif
 IN_PROC_BROWSER_TEST_F(ClonedInstallClientIdResetBrowserTest,
-                       MAYBE_TestClonedInstallClientIdReset) {
+                       TestClonedInstallClientIdReset) {
   EXPECT_NE(kInitialClientId, metrics_service()->GetClientId());
   EXPECT_FALSE(local_state()->GetBoolean(metrics::prefs::kMetricsResetIds));
 }
diff --git a/chrome/browser/policy/configuration_policy_handler_list_factory.cc b/chrome/browser/policy/configuration_policy_handler_list_factory.cc
index 2f2c148..2151966 100644
--- a/chrome/browser/policy/configuration_policy_handler_list_factory.cc
+++ b/chrome/browser/policy/configuration_policy_handler_list_factory.cc
@@ -75,13 +75,13 @@
 #include "components/policy/core/browser/configuration_policy_handler.h"
 #include "components/policy/core/browser/configuration_policy_handler_list.h"
 #include "components/policy/core/browser/configuration_policy_handler_parameters.h"
-#include "components/policy/core/browser/proxy_policy_handler.h"
 #include "components/policy/core/browser/url_blocklist_policy_handler.h"
 #include "components/policy/core/common/policy_details.h"
 #include "components/policy/core/common/policy_map.h"
 #include "components/policy/core/common/policy_pref_names.h"
 #include "components/policy/core/common/schema.h"
 #include "components/policy/policy_constants.h"
+#include "components/proxy_config/proxy_policy_handler.h"
 #include "components/safe_browsing/core/common/safe_browsing_policy_handler.h"
 #include "components/safe_browsing/core/common/safe_browsing_prefs.h"
 #include "components/search_engines/default_search_policy_handler.h"
@@ -1551,7 +1551,7 @@
       std::make_unique<bookmarks::ManagedBookmarksPolicyHandler>(
           chrome_schema));
   handlers->AddHandler(std::make_unique<HomepageLocationPolicyHandler>());
-  handlers->AddHandler(std::make_unique<ProxyPolicyHandler>());
+  handlers->AddHandler(std::make_unique<proxy_config::ProxyPolicyHandler>());
   handlers->AddHandler(std::make_unique<SecureDnsPolicyHandler>());
 #if !defined(OS_ANDROID)
   handlers->AddHandler(std::make_unique<SimpleSchemaValidatingPolicyHandler>(
diff --git a/chrome/browser/policy/profile_policy_connector_unittest.cc b/chrome/browser/policy/profile_policy_connector_unittest.cc
index d3d6e751..4e76c833 100644
--- a/chrome/browser/policy/profile_policy_connector_unittest.cc
+++ b/chrome/browser/policy/profile_policy_connector_unittest.cc
@@ -16,7 +16,6 @@
 #include "components/account_id/account_id.h"
 #include "components/autofill/core/common/autofill_prefs.h"
 #include "components/policy/core/browser/browser_policy_connector.h"
-#include "components/policy/core/browser/proxy_policy_handler.h"
 #include "components/policy/core/common/cloud/cloud_policy_constants.h"
 #include "components/policy/core/common/cloud/cloud_policy_manager.h"
 #include "components/policy/core/common/cloud/mock_cloud_policy_store.h"
diff --git a/chrome/browser/policy/test/proxy_policies_browsertest.cc b/chrome/browser/policy/test/proxy_policies_browsertest.cc
index 11ab2af..b6cbb55a 100644
--- a/chrome/browser/policy/test/proxy_policies_browsertest.cc
+++ b/chrome/browser/policy/test/proxy_policies_browsertest.cc
@@ -15,10 +15,60 @@
 #include "components/policy/core/common/policy_service.h"
 #include "components/policy/core/common/policy_types.h"
 #include "components/policy/policy_constants.h"
+#include "components/prefs/pref_service.h"
+#include "components/proxy_config/proxy_config_dictionary.h"
+#include "components/proxy_config/proxy_config_pref_names.h"
+#include "components/proxy_config/proxy_prefs.h"
 #include "content/public/test/browser_test.h"
 
 namespace policy {
 
+namespace {
+
+// Verify that all the proxy prefs are set to the specified expected values.
+void VerifyProxyPrefs(PrefService* prefs,
+                      const std::string& expected_proxy_server,
+                      const std::string& expected_proxy_pac_url,
+                      absl::optional<bool> expected_proxy_pac_mandatory,
+                      const std::string& expected_proxy_bypass_list,
+                      const ProxyPrefs::ProxyMode& expected_proxy_mode) {
+  const base::Value* value = prefs->Get(proxy_config::prefs::kProxy);
+  ASSERT_TRUE(value);
+  ASSERT_TRUE(value->is_dict());
+  ProxyConfigDictionary dict(value->Clone());
+  std::string s;
+  bool b;
+  if (expected_proxy_server.empty()) {
+    EXPECT_FALSE(dict.GetProxyServer(&s));
+  } else {
+    ASSERT_TRUE(dict.GetProxyServer(&s));
+    EXPECT_EQ(expected_proxy_server, s);
+  }
+  if (expected_proxy_pac_url.empty()) {
+    EXPECT_FALSE(dict.GetPacUrl(&s));
+  } else {
+    ASSERT_TRUE(dict.GetPacUrl(&s));
+    EXPECT_EQ(expected_proxy_pac_url, s);
+  }
+  if (!expected_proxy_pac_mandatory) {
+    EXPECT_FALSE(dict.GetPacMandatory(&b));
+  } else {
+    ASSERT_TRUE(dict.GetPacMandatory(&b));
+    EXPECT_EQ(*expected_proxy_pac_mandatory, b);
+  }
+  if (expected_proxy_bypass_list.empty()) {
+    EXPECT_FALSE(dict.GetBypassList(&s));
+  } else {
+    ASSERT_TRUE(dict.GetBypassList(&s));
+    EXPECT_EQ(expected_proxy_bypass_list, s);
+  }
+  ProxyPrefs::ProxyMode mode;
+  ASSERT_TRUE(dict.GetMode(&mode));
+  EXPECT_EQ(expected_proxy_mode, mode);
+}
+
+}  // namespace
+
 IN_PROC_BROWSER_TEST_F(PolicyTest, SeparateProxyPoliciesMerging) {
   // Add an individual proxy policy value.
   PolicyMap policies;
@@ -26,30 +76,12 @@
                POLICY_SOURCE_CLOUD, base::Value(3), nullptr);
   UpdateProviderPolicy(policies);
 
-  // It should be removed and replaced with a dictionary.
-  PolicyMap expected;
-  base::Value expected_value(base::Value::Type::DICTIONARY);
-  expected_value.SetIntKey(key::kProxyServerMode, 3);
-  expected.Set(key::kProxySettings, POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
-               POLICY_SOURCE_CLOUD, std::move(expected_value), nullptr);
-#if defined(OS_CHROMEOS)
-  SetEnterpriseUsersDefaults(&expected);
-#endif
-
-  // Check both the browser and the profile.
-  const PolicyMap& actual_from_browser =
-      g_browser_process->browser_policy_connector()
-          ->GetPolicyService()
-          ->GetPolicies(PolicyNamespace(POLICY_DOMAIN_CHROME, std::string()));
-
-  EXPECT_TRUE(expected.Equals(actual_from_browser));
-  const PolicyMap& actual_from_profile =
-      browser()
-          ->profile()
-          ->GetProfilePolicyConnector()
-          ->policy_service()
-          ->GetPolicies(PolicyNamespace(POLICY_DOMAIN_CHROME, std::string()));
-  EXPECT_TRUE(expected.Equals(actual_from_profile));
+  VerifyProxyPrefs(g_browser_process->local_state(), std::string(),
+                   std::string(), absl::nullopt, std::string(),
+                   ProxyPrefs::MODE_SYSTEM);
+  VerifyProxyPrefs(browser()->profile()->GetPrefs(), std::string(),
+                   std::string(), absl::nullopt, std::string(),
+                   ProxyPrefs::MODE_SYSTEM);
 }
 
 }  // namespace policy
diff --git a/chrome/browser/printing/pdf_nup_converter_client.cc b/chrome/browser/printing/pdf_nup_converter_client.cc
index 275e664..06d90ac4 100644
--- a/chrome/browser/printing/pdf_nup_converter_client.cc
+++ b/chrome/browser/printing/pdf_nup_converter_client.cc
@@ -9,6 +9,7 @@
 #include "base/bind.h"
 #include "chrome/browser/printing/printing_service.h"
 #include "chrome/services/printing/public/mojom/pdf_nup_converter.mojom.h"
+#include "chrome/services/printing/public/mojom/printing_service.mojom.h"
 
 namespace printing {
 
diff --git a/chrome/browser/printing/pdf_to_emf_converter.cc b/chrome/browser/printing/pdf_to_emf_converter.cc
index cb76164..be19d700 100644
--- a/chrome/browser/printing/pdf_to_emf_converter.cc
+++ b/chrome/browser/printing/pdf_to_emf_converter.cc
@@ -27,6 +27,7 @@
 #include "base/threading/thread_task_runner_handle.h"
 #include "chrome/browser/printing/printing_service.h"
 #include "chrome/services/printing/public/mojom/pdf_to_emf_converter.mojom.h"
+#include "chrome/services/printing/public/mojom/printing_service.mojom.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/child_process_data.h"
 #include "mojo/public/cpp/bindings/pending_receiver.h"
diff --git a/chrome/browser/printing/printing_service.cc b/chrome/browser/printing/printing_service.cc
index 2b73b110..986568e 100644
--- a/chrome/browser/printing/printing_service.cc
+++ b/chrome/browser/printing/printing_service.cc
@@ -5,8 +5,8 @@
 #include "chrome/browser/printing/printing_service.h"
 
 #include "base/no_destructor.h"
-#include "chrome/browser/service_sandbox_type.h"
 #include "chrome/grit/generated_resources.h"
+#include "chrome/services/printing/public/mojom/printing_service.mojom.h"
 #include "content/public/browser/service_process_host.h"
 
 const mojo::Remote<printing::mojom::PrintingService>& GetPrintingService() {
diff --git a/chrome/browser/printing/printing_service.h b/chrome/browser/printing/printing_service.h
index 59529e75..7e367ea 100644
--- a/chrome/browser/printing/printing_service.h
+++ b/chrome/browser/printing/printing_service.h
@@ -5,7 +5,7 @@
 #ifndef CHROME_BROWSER_PRINTING_PRINTING_SERVICE_H_
 #define CHROME_BROWSER_PRINTING_PRINTING_SERVICE_H_
 
-#include "chrome/services/printing/public/mojom/printing_service.mojom.h"
+#include "chrome/services/printing/public/mojom/printing_service.mojom-forward.h"
 #include "mojo/public/cpp/bindings/remote.h"
 
 // Acquires a remote handle to the sandboxed Printing Service
diff --git a/chrome/browser/printing/pwg_raster_converter.cc b/chrome/browser/printing/pwg_raster_converter.cc
index b01eaefb..8ed000ad 100644
--- a/chrome/browser/printing/pwg_raster_converter.cc
+++ b/chrome/browser/printing/pwg_raster_converter.cc
@@ -18,6 +18,7 @@
 #include "base/notreached.h"
 #include "chrome/browser/printing/printing_service.h"
 #include "chrome/services/printing/public/mojom/pdf_to_pwg_raster_converter.mojom.h"
+#include "chrome/services/printing/public/mojom/printing_service.mojom.h"
 #include "components/cloud_devices/common/cloud_device_description.h"
 #include "components/cloud_devices/common/printer_description.h"
 #include "content/public/browser/browser_thread.h"
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 f9237e83..b4d0202f 100644
--- a/chrome/browser/renderer_context_menu/render_view_context_menu.cc
+++ b/chrome/browser/renderer_context_menu/render_view_context_menu.cc
@@ -2896,12 +2896,15 @@
           source_web_contents_)) {
     return false;
   }
+  std::string source_lang =
+      chrome_translate_client->GetLanguageState().source_language();
   // Note that we intentionally enable the menu even if the source and
   // target languages are identical.  This is to give a way to user to
   // translate a page that might contains text fragments in a different
   // language.
   return ((params_.edit_flags & ContextMenuDataEditFlags::kCanTranslate) !=
           0) &&
+         !source_lang.empty() &&  // Did we receive the page language yet?
          // Disable on the Instant Extended NTP.
          !search::IsInstantNTP(embedder_web_contents_);
 }
@@ -3438,7 +3441,11 @@
   if (!chrome_translate_client)
     return;
 
-  chrome_translate_client->ManualTranslateWhenReady();
+  translate::TranslateManager* manager =
+      chrome_translate_client->GetTranslateManager();
+  DCHECK(manager);
+  manager->ShowTranslateUI(/*auto_translate=*/true,
+                           /*triggered_from_menu=*/true);
 }
 
 void RenderViewContextMenu::ExecLanguageSettings(int event_flags) {
diff --git a/chrome/browser/resources/chromeos/add_supervision/BUILD.gn b/chrome/browser/resources/chromeos/add_supervision/BUILD.gn
index 51ca7b9..bff720e 100644
--- a/chrome/browser/resources/chromeos/add_supervision/BUILD.gn
+++ b/chrome/browser/resources/chromeos/add_supervision/BUILD.gn
@@ -10,14 +10,14 @@
   deps = [
     ":add_supervision_api_server",
     ":add_supervision_ui",
-    ":post_message_api",
+    "//ui/webui/resources/js:post_message_api_server.m",
   ]
 }
 
 js_library("add_supervision_api_server") {
   deps = [
-    ":post_message_api",
     "//chrome/browser/ui/webui/chromeos/add_supervision:mojo_bindings_js_library_for_compile",
+    "//ui/webui/resources/js:post_message_api_server.m",
   ]
 }
 
@@ -33,9 +33,6 @@
   ]
 }
 
-js_library("post_message_api") {
-}
-
 html_to_js("web_components") {
   js_files = [ "add_supervision_ui.js" ]
 }
diff --git a/chrome/browser/resources/chromeos/add_supervision/add_supervision_api_server.js b/chrome/browser/resources/chromeos/add_supervision/add_supervision_api_server.js
index f12e53e..d86f3e9 100644
--- a/chrome/browser/resources/chromeos/add_supervision/add_supervision_api_server.js
+++ b/chrome/browser/resources/chromeos/add_supervision/add_supervision_api_server.js
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import {PostMessageAPIServer} from './post_message_api.js';
+import {PostMessageAPIServer} from 'chrome://resources/js/post_message_api_server.m.js';
 
 /**
  * The methods to expose to the client.
diff --git a/chrome/browser/resources/chromeos/add_supervision/post_message_api.js b/chrome/browser/resources/chromeos/add_supervision/post_message_api.js
deleted file mode 100644
index c3dbbdc..0000000
--- a/chrome/browser/resources/chromeos/add_supervision/post_message_api.js
+++ /dev/null
@@ -1,369 +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.
-
-/**
- *  Initialization retry wait in milliseconds (subject to exponential backoff).
- */
-const INITIALIZATION_ATTEMPT_RETRY_WAIT_MS = 100;
-
-/**
- * Maximum number of initialization attempts before resetting the
- * initialization attempt cycle.  With exponential backoff, this works out
- * a maximum wait of 25 seconds on the 8th attempt before restarting.
- */
-const MAX_INITIALIZATION_ATTEMPTS = 8;
-
-/**
- * Class that provides the functionality for talking to a client
- * over the PostMessageAPI.  This should be subclassed and the
- * methods provided in methodList should be implemented as methods
- * of the subclass.
- */
-export class PostMessageAPIServer {
-  constructor(clientElement, methodList, targetURL, messageOriginURLFilter) {
-    /**
-     * The Window type element to which this server will listen for messages,
-     * probably a <webview>, but also could be an <iframe> or a browser window
-     * object.
-     * @private @const {!Element}
-     */
-    this.clientElement_ = clientElement;
-
-    /**
-     * The guest URL embedded in the element above. Used for message targeting.
-     * This should be same as the URL loaded in the clientElement, i.e. the
-     * "src" attribute of a <webview>.
-     * @private @const {!URL}
-     */
-    this.targetURL_ = new URL(targetURL);
-
-    /**
-     * Incoming messages received from origin URLs without this prefix
-     * will not be accepted. This should be used to restrict the API access
-     * to the intended guest content.
-     * @private @const {!URL}
-     */
-    this.messageOriginURLFilter_ = new URL(messageOriginURLFilter);
-
-    /**
-     * Map that stores references to the methods implemented by the API.
-     * @private {!Map<string, function(!Array):?>}
-     */
-    this.apiFns_ = new Map();
-
-    /**
-     *  The ID of the timeout set before checking whether initialization has
-     * taken place yet.
-     * @private {number}
-     */
-    this.initialization_timeout_id_ = 0;
-
-    /**
-     * Indicates how many attempts have been made to initialize the channel.
-     * @private {number}
-     */
-    this.numInitializationAttempts_ = 0;
-
-    /**
-     * Indicates whether the communication channel between this server and the
-     * WebView has been established.
-     * @private {boolean}
-     */
-    this.isInitialized_ = false;
-
-    // Wait for content to load before attempting to initializing the
-    // message listener.
-    this.clientElement_.addEventListener('contentload', () => {
-      this.numInitializationAttempts_ = 0;
-      this.isInitialized_ = false;
-      this.initialize();
-    });
-
-    // Listen for events.
-    window.addEventListener('message', (event) => {
-      this.onMessage_(event);
-    });
-  }
-
-  /**
-   * Registers the specified method name with the specified
-   * function.
-   *
-   * @param {!string} methodName name of the method to register.
-   * @param {!function(!Array):?} method The function to associate with the
-   *     name.
-   */
-  registerMethod(methodName, method) {
-    this.apiFns_.set(methodName, method);
-  }
-
-  /**
-   * Send initialization message to client element.
-   */
-  initialize() {
-    if (this.isInitialized_ ||
-        !this.originMatchesFilter(this.clientElement_.src)) {
-      return;
-    }
-
-    if (this.numInitializationAttempts_ < MAX_INITIALIZATION_ATTEMPTS) {
-      // Tell the embedded webviews whose src matches our origin to initialize
-      // by sending it a message, which will include a handle for it to use to
-      // send messages back.
-      console.log(
-          'Sending init message to guest content,  attempt # :' +
-          this.numInitializationAttempts_);
-
-      this.clientElement_.contentWindow.postMessage(
-          'init', this.targetURL_.toString());
-
-      // Set timeout to check if initialization message has been received using
-      // exponential backoff.
-      this.initialization_timeout_id_ = setTimeout(
-          () => {
-            // If the timeout id is non-zero, that indicates that initialization
-            // hasn't succeeded yet, so  try to initialize again.
-            this.initialize();
-          },
-          INITIALIZATION_ATTEMPT_RETRY_WAIT_MS *
-              (2 ** this.numInitializationAttempts_));
-
-      this.numInitializationAttempts_++;
-    } else {
-      // Exponential backoff has maxed out. Show error page if present.
-      this.onInitializationError(this.clientElement_.src);
-    }
-  }
-
-  /**
-   *  Virtual method to be overridden by implementations of this class to notify
-   * them that we were unable to initialize communication channel with the
-   * `clientElement_`.
-   *
-   * @param {!string} origin The origin URL that was not able to initialize
-   *     communication.
-   */
-  onInitializationError(origin) {}
-
-  /**
-   * Determines if the specified origin matches the origin filter.
-   * @param {!string} origin The origin URL to match with the filter.
-   * @return {boolean}  whether the specified origin matches the filter.
-   */
-  originMatchesFilter(origin) {
-    const originURL = new URL(origin);
-
-    // We allow the pathname portion of the URL to be a prefix filter,
-    // to permit for different paths communicating with this server.
-    return originURL.protocol == this.messageOriginURLFilter_.protocol &&
-        originURL.host == this.messageOriginURLFilter_.host &&
-        originURL.pathname.startsWith(this.messageOriginURLFilter_.pathname);
-  }
-
-  /**
-   * Handles postMessage events from the client.
-   * @private
-   * @param {Event} event  The postMessage event to handle.
-   */
-  onMessage_(event) {
-    if (!this.originMatchesFilter(event.origin)) {
-      console.log('Message received from unauthorized origin: ' + event.origin);
-      return;
-    }
-
-    if (event.data === 'init') {
-      if (this.initialization_timeout_id_) {
-        // Cancel the current init timeout, and signal to the initialization
-        // polling process that we have received an init message from the guest
-        // content, so it doesn't reschedule the timer.
-        clearTimeout(this.initialization_timeout_id_);
-        this.initialization_timeout_id_ = 0;
-      }
-
-      this.isInitialized_ = true;
-      return;
-    }
-    // If we have gotten this far, we have received a message from a trusted
-    // origin, and we should try to process it.  We can't gate this on whether
-    // the channel is initialized, because we can receive events out of order,
-    // and method calls can be received before the init event. Essentially, we
-    // should treat the channel as being potentially as soon as we send 'init'
-    // to the guest content.
-    const methodId = event.data.methodId;
-    const fn = event.data.fn;
-    const args = event.data.args || [];
-
-    if (!this.apiFns_.has(fn)) {
-      console.error('Unknown function requested: ' + fn);
-      return;
-    }
-
-    const sendMessage = (methodId, result) => {
-      this.clientElement_.contentWindow.postMessage(
-          {
-            methodId: methodId,
-            result: result,
-          },
-          this.targetURL_.toString());
-    };
-
-    // Some methods return a promise and some don't. If we have a promise,
-    // we resolve it first, otherwise we send the result directly (e.g., for
-    // void functions we send 'undefined').
-    const result = this.apiFns_.get(fn)(args);
-    if (result instanceof Promise) {
-      result.then((result) => sendMessage(methodId, result));
-    } else {
-      sendMessage(methodId, result);
-    }
-  }
-}
-
-/**
- * Class that provides the functionality for talking to a PostMessageAPIServer
- * over the postMessage API.  This should be subclassed and the methods in the
- * server that the client needs to access should be provided in methodList.
- */
-export class PostMessageAPIClient {
-  /**
-   * @param {!Array<string>} methodList The list of methods accessible via the
-   *     client.
-   * @param {!string} serverOriginURLFilter  Only messages from this origin
-   *     will be accepted.
-   */
-  constructor(methodList, serverOriginURLFilter) {
-    /**
-     * @private @const {!string} Filter to use to validate
-     * the origin of received messages.  The origin of messages
-     * must exactly match this value.
-     */
-    this.serverOriginURLFilter_ = serverOriginURLFilter;
-
-    /**
-     * The parent window.
-     * @private {Window}
-     */
-    this.parentWindow_ = null;
-    /*
-     * @private {number}
-     */
-    this.nextMethodId_ = 0;
-    /**
-     * Map of methods awaiting a response.
-     * @private {!Map}
-     */
-    this.methodsAwaitingResponse_ = new Map;
-    /**
-     * Function property that tracks whether client has
-     * been initialized by the server.
-     * @private {Function}
-     */
-    this.boundOnInitialize_ = null;
-
-    // Generate the client-callable methods.
-    this.generateAPIMethods_(methodList);
-  }
-
-  /*
-   * Generates methods for the specified method names.
-   * @private
-   * @param {!Array<string>} methods  The names of the methods.
-   */
-  generateAPIMethods_(methods) {
-    methods.forEach((method) => {
-      Object.getPrototypeOf(this)[method] = function(args) {
-        return this.callApiFn_(method, args);
-      };
-    });
-  }
-
-  initialize() {
-    this.boundOnInitialize_ = this.onInitialize_.bind(this);
-    // Wait for an init message from the server.
-    window.addEventListener('message', this.boundOnInitialize_);
-  }
-
-  //
-  // Private implementation:
-  //
-
-  /**
-   * Handles initialization event sent from the server to establish
-   * communication.
-   * @private
-   * @param {!Event} event  An event received when the initialization message is
-   *     sent from the server.
-   */
-  onInitialize_(event) {
-    if (!this.originMatchesFilter(event.origin)) {
-      console.error(
-          'Initialization event received from non-authorized origin: ' +
-          event.origin);
-      return;
-    }
-    this.parentWindow_ = event.source;
-    window.removeEventListener('message', this.boundOnInitialize_);
-    this.boundOnInitialize_ = null;
-    window.addEventListener('message', this.onMessage_.bind(this));
-  }
-
-  /**
-   * Determine if the specified server origin URL matches the origin filter.
-   * @param {!string} origin The origin URL to match with the filter.
-   * @return {boolean}  whether the specified origin matches the filter.
-   */
-  originMatchesFilter(origin) {
-    return origin == this.serverOriginURLFilter_;
-  }
-
-  /**
-   * Handles postMessage events sent from the server.
-   * @param {Event} event  An event received from the server via the postMessage
-   *     API.
-   */
-  onMessage_(event) {
-    if (!this.originMatchesFilter(event.origin)) {
-      console.error(
-          'Message received from non-authorized origin: ' + event.origin);
-      return;
-    }
-    if (event.source != this.parentWindow_) {
-      console.error('discarding event whose source is not the parent window');
-      return;
-    }
-    if (!this.methodsAwaitingResponse_.has(event.data.methodId)) {
-      console.error('discarding event method is not waiting for a response');
-      return;
-    }
-    let method = this.methodsAwaitingResponse_.get(event.data.methodId);
-    this.methodsAwaitingResponse_.delete(event.data.methodId);
-    method(event.data.result);
-  }
-
-  /**
-   * Converts a function call with arguments into a postMessage event
-   * and sends it to the server via the postMessage API.
-   * @param {string} fn  The function to call.
-   * @param {!Array<Object>} args The arguments to pass to the function.
-   * @return {!Promise} A promise capturing the executing of the function.
-   */
-  callApiFn_(fn, args) {
-    let newMethodId = this.nextMethodId_++;
-    let promise = new Promise((resolve, reject) => {
-      if (!this.parentWindow_) {
-        reject('No parent window defined');
-      }
-      this.parentWindow_.postMessage(
-          {
-            methodId: newMethodId,
-            fn: fn,
-            args: args,
-          },
-          '*');
-
-      this.methodsAwaitingResponse_.set(newMethodId, resolve);
-    });
-    return promise;
-  }
-}
diff --git a/chrome/browser/resources/chromeos/edu_coexistence/BUILD.gn b/chrome/browser/resources/chromeos/edu_coexistence/BUILD.gn
index 9eda9cc..6f9193a6 100644
--- a/chrome/browser/resources/chromeos/edu_coexistence/BUILD.gn
+++ b/chrome/browser/resources/chromeos/edu_coexistence/BUILD.gn
@@ -88,8 +88,8 @@
 js_library("edu_coexistence_controller") {
   deps = [
     ":edu_coexistence_browser_proxy",
-    "//chrome/browser/resources/chromeos/add_supervision:post_message_api",
     "//chrome/browser/resources/gaia_auth_host:authenticator.m",
+    "//ui/webui/resources/js:post_message_api_server.m",
   ]
   externs_list = [
     "$externs_path/chrome_extensions.js",
diff --git a/chrome/browser/resources/chromeos/edu_coexistence/edu_coexistence_controller.js b/chrome/browser/resources/chromeos/edu_coexistence/edu_coexistence_controller.js
index 9301f09..122f1c2 100644
--- a/chrome/browser/resources/chromeos/edu_coexistence/edu_coexistence_controller.js
+++ b/chrome/browser/resources/chromeos/edu_coexistence/edu_coexistence_controller.js
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import {PostMessageAPIServer} from '../../chromeos/add_supervision/post_message_api.js';
+import {PostMessageAPIServer} from 'chrome://resources/js/post_message_api_server.m.js';
 import {AuthCompletedCredentials, Authenticator, AuthParams} from '../../gaia_auth_host/authenticator.m.js';
 import {EduCoexistenceBrowserProxyImpl} from './edu_coexistence_browser_proxy.js';
 
diff --git a/chrome/browser/resources/settings/chromeos/os_bluetooth_page/BUILD.gn b/chrome/browser/resources/settings/chromeos/os_bluetooth_page/BUILD.gn
index 93d6eeb..02ef447 100644
--- a/chrome/browser/resources/settings/chromeos/os_bluetooth_page/BUILD.gn
+++ b/chrome/browser/resources/settings/chromeos/os_bluetooth_page/BUILD.gn
@@ -65,6 +65,7 @@
     "//third_party/polymer/v3_0/components-chromium/polymer:polymer_bundled",
     "//ui/webui/resources/cr_components/chromeos/bluetooth:bluetooth_utils",
     "//ui/webui/resources/js:i18n_behavior.m",
+    "//ui/webui/resources/js/cr/ui:focus_row_behavior.m",
   ]
 }
 
diff --git a/chrome/browser/resources/settings/chromeos/os_bluetooth_page/os_bluetooth_device_detail_subpage.html b/chrome/browser/resources/settings/chromeos/os_bluetooth_page/os_bluetooth_device_detail_subpage.html
index 97f9d18..f472b2a 100644
--- a/chrome/browser/resources/settings/chromeos/os_bluetooth_page/os_bluetooth_device_detail_subpage.html
+++ b/chrome/browser/resources/settings/chromeos/os_bluetooth_page/os_bluetooth_device_detail_subpage.html
@@ -3,8 +3,9 @@
     color: var(--google-green-500);
   }
 
-  #bluetoothDeviceName {
-    padding-inline-start: 0;
+  .bluetooth-middle {
+    align-items: center;
+    flex: auto;
   }
 
   .cancel-button {
@@ -32,8 +33,9 @@
       </template>
     </div>
   </div>
-  <div id="deviceNameSettings" class="settings-box ">
-    <div id="bluetoothDeviceName" class="middle settings-box-text no-padding"
+  <div id="deviceNameSettings" class="settings-box">
+    <div id="bluetoothDeviceName"
+        class="bluetooth-middle settings-box-text no-padding"
         aria-hidden="true">
       $i18n{bluetoothDeviceDetailName}
       <div class="secondary" id="bluetoothDeviceNameLabel">
@@ -46,9 +48,22 @@
       $i18n{bluetoothDeviceDetailChangeDeviceName}
     </cr-button>
   </div>
-  <div id="changeDeviceSettings" class="settings-box" aria-hidden="true">
-    <!-- TODO(crbug.com/1010321): Add device settings string -->
-  </div>
+  <template is="dom-if"
+      if="[[shouldShowChangeMouseDeviceSettings_(device_.*)]]" restamp>
+    <cr-link-row
+        id="changeMouseSettings"
+        class="hr settings-box"
+        label="$i18n{bluetoothDeviceDetailChangeDeviceSettingsMouse}">
+    </cr-link-row>
+  </template>
+  <template is="dom-if"
+      if="[[shouldShowChangeKeyboardDeviceSettings_(device_.*)]]" restamp>
+    <cr-link-row
+        id="changeKeyboardSettings"
+        class="hr settings-box"
+        label="$i18n{bluetoothDeviceDetailChangeDeviceSettingsKeyboard}">
+    </cr-link-row>
+  </template>
 </div>
 
 <template is="dom-if" if="[[shouldShowChangeDeviceNameDialog_]]" restamp>
diff --git a/chrome/browser/resources/settings/chromeos/os_bluetooth_page/os_bluetooth_device_detail_subpage.js b/chrome/browser/resources/settings/chromeos/os_bluetooth_page/os_bluetooth_device_detail_subpage.js
index b39666e..543b633 100644
--- a/chrome/browser/resources/settings/chromeos/os_bluetooth_page/os_bluetooth_device_detail_subpage.js
+++ b/chrome/browser/resources/settings/chromeos/os_bluetooth_page/os_bluetooth_device_detail_subpage.js
@@ -199,6 +199,29 @@
   onCloseChangeDeviceNameDialog_() {
     this.shouldShowChangeDeviceNameDialog_ = false;
   }
+
+  /**
+   * @return {boolean}
+   * @private
+   */
+  shouldShowChangeMouseDeviceSettings_() {
+    if (!this.device_) {
+      return false;
+    }
+    return this.device_.deviceProperties.deviceType === mojom.DeviceType.kMouse;
+  }
+
+  /**
+   * @return {boolean}
+   * @private
+   */
+  shouldShowChangeKeyboardDeviceSettings_() {
+    if (!this.device_) {
+      return false;
+    }
+    return this.device_.deviceProperties.deviceType ===
+        mojom.DeviceType.kKeyboard;
+  }
 }
 
 customElements.define(
diff --git a/chrome/browser/resources/settings/chromeos/os_bluetooth_page/os_bluetooth_summary.html b/chrome/browser/resources/settings/chromeos/os_bluetooth_page/os_bluetooth_summary.html
index 47eebe9..9e5472a 100644
--- a/chrome/browser/resources/settings/chromeos/os_bluetooth_page/os_bluetooth_summary.html
+++ b/chrome/browser/resources/settings/chromeos/os_bluetooth_page/os_bluetooth_summary.html
@@ -53,4 +53,3 @@
     </cr-icon-button>
   </div>
 </template>
-
diff --git a/chrome/browser/resources/settings/chromeos/os_bluetooth_page/os_paired_bluetooth_list.html b/chrome/browser/resources/settings/chromeos/os_bluetooth_page/os_paired_bluetooth_list.html
index e83e7e5..7052a8c 100644
--- a/chrome/browser/resources/settings/chromeos/os_bluetooth_page/os_paired_bluetooth_list.html
+++ b/chrome/browser/resources/settings/chromeos/os_bluetooth_page/os_paired_bluetooth_list.html
@@ -11,10 +11,12 @@
     no-bottom-scroll-border>
   <iron-list items="[[devices]]" scroll-target="container" preserve-focus>
     <template>
-      <!-- TODO(crbug.com/1010321): Fix focus behavior. -->
       <os-settings-paired-bluetooth-list-item
           device="[[item]]"
-          tabindex="[[tabIndex]]">
+          tabindex$="[[tabIndex]]"
+          iron-list-tab-index="[[tabIndex]]"
+          item-index="[[index]]"
+          list-size="[[devices.length]]">
       </os-settings-paired-bluetooth-list-item>
     </template>
   </iron-list>
diff --git a/chrome/browser/resources/settings/chromeos/os_bluetooth_page/os_paired_bluetooth_list_item.html b/chrome/browser/resources/settings/chromeos/os_bluetooth_page/os_paired_bluetooth_list_item.html
index 89d768f..3bda49d 100644
--- a/chrome/browser/resources/settings/chromeos/os_bluetooth_page/os_paired_bluetooth_list_item.html
+++ b/chrome/browser/resources/settings/chromeos/os_bluetooth_page/os_paired_bluetooth_list_item.html
@@ -21,12 +21,12 @@
   }
 </style>
 <div focus-row-container>
-  <!-- TODO(crbug.com/1010321): Add a11y label. -->
   <div class="list-item"
       focus-row-control
       focus-type="rowWrapper"
       role="button"
-      selectable>
+      selectable
+      aria-label="[[getAriaLabel_(device, itemIndex, listSize)]]">
     <iron-icon id="deviceTypeIcon"
         icon="os-settings:[[getDeviceTypeIcon_(device)]]">
     </iron-icon>
@@ -45,9 +45,11 @@
     </div>
     <div>
       <!-- TODO(crbug.com/1010321): Add click listener. -->
-      <cr-icon-button class="subpage-arrow"
+      <cr-icon-button id="subpageButton"
+          class="subpage-arrow"
           focus-row-control
-          focus-type="subpageButton">
+          focus-type="subpageButton"
+          aria-label="[[getSubpageButtonA11yLabel_(device)]]">
       </cr-icon-button>
     </div>
   </div>
diff --git a/chrome/browser/resources/settings/chromeos/os_bluetooth_page/os_paired_bluetooth_list_item.js b/chrome/browser/resources/settings/chromeos/os_bluetooth_page/os_paired_bluetooth_list_item.js
index 4252e27..01f2224b 100644
--- a/chrome/browser/resources/settings/chromeos/os_bluetooth_page/os_paired_bluetooth_list_item.js
+++ b/chrome/browser/resources/settings/chromeos/os_bluetooth_page/os_paired_bluetooth_list_item.js
@@ -16,6 +16,7 @@
 import {html, mixinBehaviors, PolymerElement} from '//resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 import {getDeviceName} from 'chrome://resources/cr_components/chromeos/bluetooth/bluetooth_utils.js';
 import {assert} from 'chrome://resources/js/assert.m.js';
+import {FocusRowBehavior, FocusRowBehaviorInterface} from 'chrome://resources/js/cr/ui/focus_row_behavior.m.js';
 
 /**
  * The threshold percentage where any battery percentage lower is considered
@@ -30,7 +31,7 @@
  * the maximum battery percentage in the range (inclusive).
  * @type {Array<Array<number>>}
  */
-const batteryIconsRanges = [
+const BATTERY_ICONS_RANGES = [
   [0, 7], [8, 14], [15, 21], [22, 28], [29, 35], [36, 42], [43, 49], [50, 56],
   [57, 63], [64, 70], [71, 77], [78, 85], [86, 92], [93, 100]
 ];
@@ -39,9 +40,10 @@
  * @constructor
  * @extends {PolymerElement}
  * @implements {I18nBehaviorInterface}
+ * @implements {FocusRowBehaviorInterface}
  */
 const SettingsPairedBluetoothListItemElementBase =
-    mixinBehaviors([I18nBehavior], PolymerElement);
+    mixinBehaviors([I18nBehavior, FocusRowBehavior], PolymerElement);
 
 /** @polymer */
 class SettingsPairedBluetoothListItemElement extends
@@ -63,6 +65,15 @@
         type: Object,
       },
 
+      /** The index of this item in its parent list, used for its a11y label. */
+      itemIndex: Number,
+
+      /**
+       * The total number of elements in this item's parent list, used for its
+       * a11y label.
+       */
+      listSize: Number,
+
       /** @private {boolean} */
       isBatteryPercentageAvailable_: {
         type: Boolean,
@@ -201,7 +212,7 @@
 
     // Range should always find a value because isBatteryPercentageAvailable_
     // ensures batteryPercentage is within bounds.
-    const range = batteryIconsRanges.find(range => {
+    const range = BATTERY_ICONS_RANGES.find(range => {
       return range[0] <= batteryPercentage && batteryPercentage <= range[1];
     });
     assert(
@@ -209,6 +220,87 @@
 
     return 'os-settings:battery-' + range[0] + '-' + range[1];
   }
+
+  /**
+   * @param {!chromeos.bluetoothConfig.mojom.PairedBluetoothDeviceProperties}
+   *     device
+   * @return {string}
+   * @private
+   */
+  getAriaLabel_(device) {
+    const deviceName = this.getDeviceName_(device);
+    const deviceType = chromeos.bluetoothConfig.mojom.DeviceType;
+    let stringName;
+    switch (device.deviceProperties.deviceType) {
+      case deviceType.kComputer:
+        stringName = this.isBatteryPercentageAvailable_ ?
+            'bluetoothPairedDeviceItemA11yLabelTypeComputerWithBatteryInfo' :
+            'bluetoothPairedDeviceItemA11yLabelTypeComputer';
+        break;
+      case deviceType.kPhone:
+        stringName = this.isBatteryPercentageAvailable_ ?
+            'bluetoothPairedDeviceItemA11yLabelTypePhoneWithBatteryInfo' :
+            'bluetoothPairedDeviceItemA11yLabelTypePhone';
+        break;
+      case deviceType.kHeadset:
+        stringName = this.isBatteryPercentageAvailable_ ?
+            'bluetoothPairedDeviceItemA11yLabelTypeHeadsetWithBatteryInfo' :
+            'bluetoothPairedDeviceItemA11yLabelTypeHeadset';
+        break;
+      case deviceType.kVideoCamera:
+        stringName = this.isBatteryPercentageAvailable_ ?
+            'bluetoothPairedDeviceItemA11yLabelTypeVideoCameraWithBatteryInfo' :
+            'bluetoothPairedDeviceItemA11yLabelTypeVideoCamera';
+        break;
+      case deviceType.kGameController:
+        stringName = this.isBatteryPercentageAvailable_ ?
+            'bluetoothPairedDeviceItemA11yLabelTypeGameControllerWithBatteryInfo' :
+            'bluetoothPairedDeviceItemA11yLabelTypeGameController';
+        break;
+      case deviceType.kKeyboard:
+        stringName = this.isBatteryPercentageAvailable_ ?
+            'bluetoothPairedDeviceItemA11yLabelTypeKeyboardWithBatteryInfo' :
+            'bluetoothPairedDeviceItemA11yLabelTypeKeyboard';
+        break;
+      case deviceType.kMouse:
+        stringName = this.isBatteryPercentageAvailable_ ?
+            'bluetoothPairedDeviceItemA11yLabelTypeMouseWithBatteryInfo' :
+            'bluetoothPairedDeviceItemA11yLabelTypeMouse';
+        break;
+      case deviceType.kTablet:
+        stringName = this.isBatteryPercentageAvailable_ ?
+            'bluetoothPairedDeviceItemA11yLabelTypeTabletWithBatteryInfo' :
+            'bluetoothPairedDeviceItemA11yLabelTypeTablet';
+        break;
+      default:
+        stringName = this.isBatteryPercentageAvailable_ ?
+            'bluetoothPairedDeviceItemA11yLabelTypeUnknownWithBatteryInfo' :
+            'bluetoothPairedDeviceItemA11yLabelTypeUnknown';
+    }
+
+    if (!this.isBatteryPercentageAvailable_) {
+      return this.i18n(
+          stringName, this.itemIndex + 1, this.listSize, deviceName);
+    }
+
+    const batteryPercentage = this.getBatteryPercentage_(device);
+    assert(batteryPercentage !== undefined);
+    return this.i18n(
+        stringName, this.itemIndex + 1, this.listSize, deviceName,
+        batteryPercentage);
+  }
+
+  /**
+   * @param {!chromeos.bluetoothConfig.mojom.PairedBluetoothDeviceProperties}
+   *     device
+   * @return {string}
+   * @private
+   */
+  getSubpageButtonA11yLabel_(device) {
+    const deviceName = this.getDeviceName_(device);
+    return this.i18n(
+        'bluetoothPairedDeviceItemSubpageButtonA11yLabel', deviceName);
+  }
 }
 
 customElements.define(
diff --git a/chrome/browser/safe_browsing/android/java/src/org/chromium/chrome/browser/safe_browsing/settings/NoProtectionConfirmationDialog.java b/chrome/browser/safe_browsing/android/java/src/org/chromium/chrome/browser/safe_browsing/settings/NoProtectionConfirmationDialog.java
index a2faec5..85b6b93 100644
--- a/chrome/browser/safe_browsing/android/java/src/org/chromium/chrome/browser/safe_browsing/settings/NoProtectionConfirmationDialog.java
+++ b/chrome/browser/safe_browsing/android/java/src/org/chromium/chrome/browser/safe_browsing/settings/NoProtectionConfirmationDialog.java
@@ -43,8 +43,9 @@
                         .with(ModalDialogProperties.CONTROLLER, makeController())
                         .with(ModalDialogProperties.TITLE, resources,
                                 R.string.safe_browsing_no_protection_confirmation_dialog_title)
-                        .with(ModalDialogProperties.MESSAGE, resources,
-                                R.string.safe_browsing_no_protection_confirmation_dialog_message)
+                        .with(ModalDialogProperties.MESSAGE,
+                                resources.getString(
+                                        R.string.safe_browsing_no_protection_confirmation_dialog_message))
                         .with(ModalDialogProperties.POSITIVE_BUTTON_TEXT, resources,
                                 R.string.safe_browsing_no_protection_confirmation_dialog_confirm)
                         .with(ModalDialogProperties.PRIMARY_BUTTON_FILLED, true)
diff --git a/chrome/browser/service_sandbox_type.h b/chrome/browser/service_sandbox_type.h
index 4b72e57f..1c3bbdf 100644
--- a/chrome/browser/service_sandbox_type.h
+++ b/chrome/browser/service_sandbox_type.h
@@ -108,41 +108,6 @@
 }
 #endif  // !defined(OS_ANDROID)
 
-// mirroring::mojom::MirroringService
-namespace mirroring {
-namespace mojom {
-class MirroringService;
-}
-}  // namespace mirroring
-template <>
-inline sandbox::policy::SandboxType
-content::GetServiceSandboxType<mirroring::mojom::MirroringService>() {
-#if defined(OS_MAC)
-  return sandbox::policy::SandboxType::kMirroring;
-#else
-  return sandbox::policy::SandboxType::kUtility;
-#endif  // OS_MAC
-}
-
-// printing::mojom::PrintingService
-#if BUILDFLAG(ENABLE_PRINT_PREVIEW)
-namespace printing {
-namespace mojom {
-class PrintingService;
-}
-}  // namespace printing
-
-template <>
-inline sandbox::policy::SandboxType
-content::GetServiceSandboxType<printing::mojom::PrintingService>() {
-#if defined(OS_WIN)
-  return sandbox::policy::SandboxType::kPdfConversion;
-#else
-  return sandbox::policy::SandboxType::kUtility;
-#endif
-}
-#endif  // BUILDFLAG(ENABLE_PRINT_PREVIEW)
-
 // printing::mojom::PrintBackendService
 #if (defined(OS_WIN) || defined(OS_MAC) || defined(OS_LINUX) || \
      defined(OS_CHROMEOS)) &&                                   \
diff --git a/chrome/browser/share/share_ranking.cc b/chrome/browser/share/share_ranking.cc
index 7f2df68..087af67f 100644
--- a/chrome/browser/share/share_ranking.cc
+++ b/chrome/browser/share/share_ranking.cc
@@ -358,6 +358,8 @@
 
   if (fix_more)
     computed_display_ranking[logical_fold] = kMoreTarget;
+  else
+    computed_display_ranking.push_back(kMoreTarget);
 
   *persisted_ranking = new_ranking;
   *display_ranking = computed_display_ranking;
@@ -374,6 +376,8 @@
     DCHECK(AtMostOneSlotChanged(old_ranking, *persisted_ranking, logical_fold));
     DCHECK(NoEmptySlots(*display_ranking, logical_fold));
 
+    DCHECK(RankingContains(*display_ranking, kMoreTarget));
+
     DCHECK_GE(persisted_ranking->size(), fold);
   }
 #endif  // DCHECK_IS_ON()
diff --git a/chrome/browser/share/share_ranking_unittest.cc b/chrome/browser/share/share_ranking_unittest.cc
index 59502db2..47d416f2 100644
--- a/chrome/browser/share/share_ranking_unittest.cc
+++ b/chrome/browser/share/share_ranking_unittest.cc
@@ -61,7 +61,8 @@
 
 // The "easy case": the existing usage counts are the same as the current
 // ranking, and every app in the ranking is available, so the new ranking and
-// the displayed ranking should both be the same as the current ranking.
+// the displayed ranking should both be the same as the current ranking, modulo
+// the addition of the More target.
 TEST(ShareRankingStaticTest, CountsMatchOldRanking) {
   std::map<std::string, int> history = {
       {"bar", 2},
@@ -74,9 +75,11 @@
 
   ShareRanking::ComputeRanking(history, history, current, {"foo", "bar", "baz"},
                                3, false, &displayed, &persisted);
-  ASSERT_EQ(displayed.size(), current.size());
+
+  ShareRanking::Ranking expected_displayed{"foo", "bar", "baz", "$more"};
+  ASSERT_EQ(displayed.size(), expected_displayed.size());
   ASSERT_EQ(persisted.size(), current.size());
-  EXPECT_EQ(displayed, current);
+  EXPECT_EQ(displayed, expected_displayed);
   EXPECT_EQ(persisted, current);
 }
 
@@ -95,10 +98,7 @@
                                {"foo", "quxx", "baz", "blit"}, 4, false,
                                &displayed, &persisted);
   ShareRanking::Ranking expected_displayed{
-      "foo",
-      "blit",
-      "baz",
-      "quxx",
+      "foo", "blit", "baz", "quxx", "$more",
   };
   EXPECT_EQ(displayed, expected_displayed);
   EXPECT_EQ(persisted, current);
@@ -114,10 +114,7 @@
                                {"foo", "bar", "quxx", "baz", "blit"}, 4, false,
                                &displayed, &persisted);
   ShareRanking::Ranking expected_displayed{
-      "foo",
-      "bar",
-      "baz",
-      "blit",
+      "foo", "bar", "baz", "blit", "$more",
   };
   ShareRanking::Ranking expected_persisted{"foo", "bar", "baz", "blit", "quxx"};
   EXPECT_EQ(displayed, expected_displayed);
@@ -133,12 +130,8 @@
   ShareRanking::ComputeRanking({}, history, current,
                                {"foo", "bar", "quxx", "baz", "blit"}, 4, false,
                                &displayed, &persisted);
-  ShareRanking::Ranking expected_displayed{
-      "foo",
-      "bar",
-      "baz",
-      "blit",
-  };
+  ShareRanking::Ranking expected_displayed{"foo", "bar", "baz", "blit",
+                                           "$more"};
   ShareRanking::Ranking expected_persisted{"foo", "bar", "baz", "blit", "quxx"};
   EXPECT_EQ(displayed, expected_displayed);
   EXPECT_EQ(persisted, expected_persisted);
@@ -210,7 +203,7 @@
 
   // Note that since "abc" isn't available on the system, it won't appear in the
   // displayed ranking, but the persisted ranking should still get updated.
-  ShareRanking::Ranking expected_displayed{"foo", "bar", "baz"};
+  ShareRanking::Ranking expected_displayed{"foo", "bar", "baz", "$more"};
   ShareRanking::Ranking expected_persisted{"foo", "bar", "abc", "baz"};
 
   EXPECT_EQ(displayed, expected_displayed);
diff --git a/chrome/browser/translate/chrome_translate_client.cc b/chrome/browser/translate/chrome_translate_client.cc
index 63d81e15..ba68be6 100644
--- a/chrome/browser/translate/chrome_translate_client.cc
+++ b/chrome/browser/translate/chrome_translate_client.cc
@@ -300,7 +300,6 @@
 int ChromeTranslateClient::GetInfobarIconID() const {
   return IDR_ANDROID_INFOBAR_TRANSLATE;
 }
-#endif
 
 void ChromeTranslateClient::ManualTranslateWhenReady() {
   if (GetLanguageState().source_language().empty()) {
@@ -311,6 +310,7 @@
                              /*triggered_from_menu=*/true);
   }
 }
+#endif
 
 void ChromeTranslateClient::SetPredefinedTargetLanguage(
     const std::string& translate_language_code) {
@@ -360,11 +360,13 @@
     GetTranslateManager()->NotifyLanguageDetected(details);
   }
 
+#if defined(OS_ANDROID)
+  // See ChromeTranslateClient::ManualTranslateOnReady
   if (manual_translate_on_ready_) {
-    GetTranslateManager()->ShowTranslateUI(/*auto_translate=*/true,
-                                           /*triggered_from_menu=*/true);
+    GetTranslateManager()->ShowTranslateUI(/*auto_translate=*/true);
     manual_translate_on_ready_ = false;
   }
+#endif
 }
 
 // The bubble is implemented only on the desktop platforms.
diff --git a/chrome/browser/translate/chrome_translate_client.h b/chrome/browser/translate/chrome_translate_client.h
index 0b69cef..307b23b 100644
--- a/chrome/browser/translate/chrome_translate_client.h
+++ b/chrome/browser/translate/chrome_translate_client.h
@@ -92,12 +92,11 @@
       std::unique_ptr<translate::TranslateInfoBarDelegate> delegate)
       const override;
   int GetInfobarIconID() const override;
-#endif
 
   // Trigger a manual translation when the necessary state (e.g. source
   // language) is ready.
   void ManualTranslateWhenReady();
-
+#endif
   void SetPredefinedTargetLanguage(const std::string& translate_language_code);
 
   bool ShowTranslateUI(translate::TranslateStep step,
@@ -145,9 +144,11 @@
       per_frame_translate_driver_;
   std::unique_ptr<translate::TranslateManager> translate_manager_;
 
+#if defined(OS_ANDROID)
   // Whether to trigger a manual translation when ready.
   // See ChromeTranslateClient::ManualTranslateOnReady
   bool manual_translate_on_ready_ = false;
+#endif
 
   WEB_CONTENTS_USER_DATA_KEY_DECL();
 
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 f92d547a3..ad34128 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,4 +295,6 @@
     /* package */ void setUrlActionContainerVisibility(int visibility) {
         mUrlActionContainer.setVisibility(visibility);
     }
+
+    public void notifyVoiceRecognitionCanceled() {}
 }
diff --git a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/LocationBarMediator.java b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/LocationBarMediator.java
index 4b9f0a0..a142e8e 100644
--- a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/LocationBarMediator.java
+++ b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/LocationBarMediator.java
@@ -1253,6 +1253,11 @@
         setUrlBarFocus(/*shouldBeFocused=*/false, /*pastedText=*/null, OmniboxFocusReason.UNFOCUS);
     }
 
+    @Override
+    public void notifyVoiceRecognitionCanceled() {
+        mLocationBarLayout.notifyVoiceRecognitionCanceled();
+    }
+
     // AssistantVoiceSearchService.Observer implementation.
 
     @Override
diff --git a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/AutocompleteMediator.java b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/AutocompleteMediator.java
index b220a7f..0f7cc6a 100644
--- a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/AutocompleteMediator.java
+++ b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/AutocompleteMediator.java
@@ -540,7 +540,7 @@
                 new PropertyModel.Builder(ModalDialogProperties.ALL_KEYS)
                         .with(ModalDialogProperties.CONTROLLER, dialogController)
                         .with(ModalDialogProperties.TITLE, suggestion.getDisplayText())
-                        .with(ModalDialogProperties.MESSAGE, resources, dialogMessageId)
+                        .with(ModalDialogProperties.MESSAGE, resources.getString(dialogMessageId))
                         .with(ModalDialogProperties.POSITIVE_BUTTON_TEXT, resources, R.string.ok)
                         .with(ModalDialogProperties.NEGATIVE_BUTTON_TEXT, resources,
                                 R.string.cancel)
diff --git a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/voice/VoiceRecognitionHandler.java b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/voice/VoiceRecognitionHandler.java
index 3826b94..cbec1a13 100644
--- a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/voice/VoiceRecognitionHandler.java
+++ b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/voice/VoiceRecognitionHandler.java
@@ -272,6 +272,9 @@
 
         /** Clears omnibox focus, used to display the dialog when the keyboard is shown. */
         void clearOmniboxFocus();
+
+        /** Notifies the delegate that voice recognition could not complete. */
+        void notifyVoiceRecognitionCanceled();
     }
 
     /** Interface for observers interested in updates to the voice state. */
@@ -466,10 +469,12 @@
             mCallbackComplete = true;
             if (resultCode == Activity.RESULT_CANCELED) {
                 recordVoiceSearchDismissedEvent(mSource, mTarget);
+                mDelegate.notifyVoiceRecognitionCanceled();
                 return;
             }
             if (resultCode != Activity.RESULT_OK || data.getExtras() == null) {
                 recordVoiceSearchFailureEvent(mSource, mTarget);
+                mDelegate.notifyVoiceRecognitionCanceled();
                 return;
             }
 
@@ -730,9 +735,16 @@
         startTrackingQueryDuration();
 
         WindowAndroid windowAndroid = mDelegate.getWindowAndroid();
-        if (windowAndroid == null) return;
+        if (windowAndroid == null) {
+            mDelegate.notifyVoiceRecognitionCanceled();
+            return;
+        }
+
         Activity activity = windowAndroid.getActivity().get();
-        if (activity == null) return;
+        if (activity == null) {
+            mDelegate.notifyVoiceRecognitionCanceled();
+            return;
+        }
 
         if (FeatureList.isInitialized()
                 && ChromeFeatureList.isEnabled(
@@ -744,6 +756,7 @@
             @Nullable
             PrefService prefService = getPrefService();
             if (prefService == null || !prefService.getBoolean(Pref.AUDIO_CAPTURE_ALLOWED)) {
+                mDelegate.notifyVoiceRecognitionCanceled();
                 return;
             }
         }
@@ -789,6 +802,7 @@
         PermissionCallback callback = (permissions, grantResults) -> {
             if (grantResults.length != 1) {
                 recordAudioPermissionStateEvent(AudioPermissionState.DENIED_CAN_ASK_AGAIN);
+                mDelegate.notifyVoiceRecognitionCanceled();
                 return;
             }
 
@@ -799,8 +813,10 @@
             } else if (!windowAndroid.canRequestPermission(Manifest.permission.RECORD_AUDIO)) {
                 recordAudioPermissionStateEvent(AudioPermissionState.DENIED_CANNOT_ASK_AGAIN);
                 notifyVoiceAvailabilityImpacted();
+                mDelegate.notifyVoiceRecognitionCanceled();
             } else {
                 recordAudioPermissionStateEvent(AudioPermissionState.DENIED_CAN_ASK_AGAIN);
+                mDelegate.notifyVoiceRecognitionCanceled();
             }
         };
         windowAndroid.requestPermissions(new String[] {Manifest.permission.RECORD_AUDIO}, callback);
@@ -813,7 +829,10 @@
             Activity activity, WindowAndroid windowAndroid, @VoiceInteractionSource int source) {
         // Check if we need to request audio permissions. If we don't, then trigger a permissions
         // prompt will appear and startVoiceRecognition will be called again.
-        if (!ensureAudioPermissionGranted(activity, windowAndroid, source)) return false;
+        if (!ensureAudioPermissionGranted(activity, windowAndroid, source)) {
+            mDelegate.notifyVoiceRecognitionCanceled();
+            return false;
+        }
 
         Intent intent = new Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH);
         intent.putExtra(
diff --git a/chrome/browser/ui/ash/holding_space/holding_space_downloads_delegate.cc b/chrome/browser/ui/ash/holding_space/holding_space_downloads_delegate.cc
index b4862ab..758789e 100644
--- a/chrome/browser/ui/ash/holding_space/holding_space_downloads_delegate.cc
+++ b/chrome/browser/ui/ash/holding_space/holding_space_downloads_delegate.cc
@@ -4,8 +4,6 @@
 
 #include "chrome/browser/ui/ash/holding_space/holding_space_downloads_delegate.h"
 
-#include <vector>
-
 #include "ash/constants/ash_features.h"
 #include "ash/public/cpp/holding_space/holding_space_constants.h"
 #include "ash/public/cpp/holding_space/holding_space_progress.h"
@@ -19,9 +17,11 @@
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/ash/holding_space/holding_space_util.h"
 #include "components/download/public/common/download_item.h"
+#include "components/download/public/common/simple_download_manager.h"
 #include "components/vector_icons/vector_icons.h"
 #include "content/public/browser/browser_context.h"
 #include "content/public/browser/download_item_utils.h"
+#include "content/public/browser/download_manager.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/base/text/bytes_formatting.h"
@@ -32,8 +32,6 @@
 
 namespace {
 
-content::DownloadManager* download_manager_for_testing = nullptr;
-
 // Helpers ---------------------------------------------------------------------
 
 // Returns a mojo download state converted from the specified item `state`.
@@ -75,7 +73,9 @@
   mojo_download_item->is_mixed_content = item->IsMixedContent();
   mojo_download_item->has_is_mixed_content = true;
 
-  // NOTE: `browser_context` may be `nullptr` in tests.
+  // NOTE: `browser_context` may be `nullptr` in tests. We assume in this case
+  // that the item is not from an incognito profile; tests exercising incognito
+  // behavior should make sure `browser_context` is not `nullptr`.
   auto* browser_context = content::DownloadItemUtils::GetBrowserContext(item);
   mojo_download_item->is_from_incognito_profile =
       browser_context
@@ -110,12 +110,6 @@
              : nullptr;
 }
 
-// Returns the download manager to use for the given `profile`.
-content::DownloadManager* GetDownloadManager(Profile* profile) {
-  return download_manager_for_testing ? download_manager_for_testing
-                                      : profile->GetDownloadManager();
-}
-
 // Returns whether the specified `mojo_download_item` is complete.
 bool IsComplete(const crosapi::mojom::DownloadItem* mojo_download_item) {
   return mojo_download_item->state == crosapi::mojom::DownloadState::kComplete;
@@ -163,9 +157,11 @@
 
   InProgressDownload(Type type,
                      HoldingSpaceDownloadsDelegate* delegate,
+                     content::DownloadManager* manager,
                      crosapi::mojom::DownloadItemPtr mojo_download_item)
       : type_(type),
         delegate_(delegate),
+        manager_(manager),
         mojo_download_item_(std::move(mojo_download_item)) {
     DCHECK(IsInProgress(mojo_download_item_.get()));
   }
@@ -345,6 +341,10 @@
     return secondary_text;
   }
 
+  // Returns the download manager that originated this download. NOTE: This will
+  // be `nullptr` for Lacros downloads.
+  const content::DownloadManager* manager() const { return manager_; }
+
  protected:
   // Updates the `mojo_download_item_` associated with this in-progress
   // download, notifying `delegate_` of the change in state. Note that invoking
@@ -387,6 +387,7 @@
  private:
   const Type type_;
   HoldingSpaceDownloadsDelegate* const delegate_;  // NOTE: Owns `this`.
+  content::DownloadManager* const manager_;
   crosapi::mojom::DownloadItemPtr mojo_download_item_;
 
   // The in-progress holding space item associated with this in-progress
@@ -409,9 +410,11 @@
       public download::DownloadItem::Observer {
  public:
   InProgressAshDownload(HoldingSpaceDownloadsDelegate* delegate,
+                        content::DownloadManager* manager,
                         download::DownloadItem* download_item)
       : InProgressDownload(Type::kAsh,
                            delegate,
+                           manager,
                            ConvertToMojoDownloadItem(download_item)),
         download_item_(download_item) {
     download_item_observation_.Observe(download_item);
@@ -463,6 +466,7 @@
                            crosapi::mojom::DownloadItemPtr mojo_download_item)
       : InProgressDownload(Type::kLacros,
                            delegate,
+                           /*manager=*/nullptr,
                            std::move(mojo_download_item)) {
     auto* const download_controller_ash = GetDownloadControllerAsh();
     if (download_controller_ash)
@@ -535,12 +539,6 @@
     download_controller_ash->RemoveObserver(this);
 }
 
-// static
-void HoldingSpaceDownloadsDelegate::SetDownloadManagerForTesting(
-    content::DownloadManager* download_manager) {
-  download_manager_for_testing = download_manager;
-}
-
 void HoldingSpaceDownloadsDelegate::Cancel(const HoldingSpaceItem* item) {
   DCHECK(HoldingSpaceItem::IsDownload(item->type()));
   for (const auto& in_progress_download : in_progress_downloads_) {
@@ -595,10 +593,7 @@
   }
 
   // Ash Chrome downloads.
-  auto* const download_manager = GetDownloadManager(profile());
-  download_manager_observation_.Observe(download_manager);
-  if (download_manager->IsManagerInitialized())
-    OnManagerInitialized();
+  download_notifier_.AddProfile(profile());
 
   // Lacros Chrome downloads.
   auto* const download_controller_ash = GetDownloadControllerAsh();
@@ -619,6 +614,54 @@
   });
 }
 
+void HoldingSpaceDownloadsDelegate::OnManagerInitialized(
+    content::DownloadManager* manager) {
+  DCHECK(!is_restoring_persistence());
+  download::SimpleDownloadManager::DownloadVector downloads;
+  manager->GetAllDownloads(&downloads);
+  for (auto* download : downloads)
+    OnDownloadCreated(manager, download);
+}
+
+void HoldingSpaceDownloadsDelegate::OnManagerGoingDown(
+    content::DownloadManager* manager) {
+  // Collect all downloads associated with `manager`. These downloads will be
+  // removed from `in_progress_downloads_` on failure, so they cannot be failed
+  // in a loop that iterates over `in_progress_downloads_`.
+  std::set<InProgressDownload*> downloads_to_remove;
+  for (const auto& in_progress_download : in_progress_downloads_) {
+    if (in_progress_download->manager() == manager)
+      downloads_to_remove.insert(in_progress_download.get());
+  }
+
+  // Fail all of `manager`'s downloads.
+  for (InProgressDownload* in_progress_download : downloads_to_remove)
+    OnDownloadFailed(in_progress_download);
+}
+
+void HoldingSpaceDownloadsDelegate::OnDownloadCreated(
+    content::DownloadManager* manager,
+    download::DownloadItem* download_item) {
+  DCHECK(!is_restoring_persistence());
+  if (IsInProgress(download_item)) {
+    in_progress_downloads_.emplace(
+        std::make_unique<InProgressAshDownload>(this, manager, download_item));
+  }
+}
+
+// Override to avoid hiding `OnDownloadUpdated(InProgressDownload*, bool)`.
+void HoldingSpaceDownloadsDelegate::OnDownloadUpdated(
+    content::DownloadManager* manager,
+    download::DownloadItem* download_item) {
+  // TODO(crbug.com/1240644): Create an `InProgressDownload` here if one does
+  // not already exist, e.g., because the download was interrupted and resumed.
+}
+
+bool HoldingSpaceDownloadsDelegate::ShouldObserveProfile(Profile* profile) {
+  return !profile->IsIncognitoProfile() ||
+         features::IsHoldingSpaceIncognitoProfileIntegrationEnabled();
+}
+
 void HoldingSpaceDownloadsDelegate::OnArcDownloadAdded(
     const base::FilePath& relative_path,
     const std::string& owner_package_name) {
@@ -646,46 +689,6 @@
   service()->AddDownload(HoldingSpaceItem::Type::kArcDownload, path);
 }
 
-void HoldingSpaceDownloadsDelegate::OnManagerInitialized() {
-  if (is_restoring_persistence())
-    return;
-
-  content::DownloadManager* download_manager = GetDownloadManager(profile());
-  DCHECK(download_manager->IsManagerInitialized());
-
-  download::SimpleDownloadManager::DownloadVector downloads;
-  download_manager->GetAllDownloads(&downloads);
-
-  for (download::DownloadItem* download_item : downloads) {
-    if (IsInProgress(download_item)) {
-      in_progress_downloads_.emplace(
-          std::make_unique<InProgressAshDownload>(this, download_item));
-    }
-  }
-}
-
-void HoldingSpaceDownloadsDelegate::ManagerGoingDown(
-    content::DownloadManager* manager) {
-  download_manager_observation_.Reset();
-  base::EraseIf(in_progress_downloads_, [](const auto& in_progress_download) {
-    return in_progress_download->GetType() == InProgressDownload::Type::kAsh;
-  });
-}
-
-void HoldingSpaceDownloadsDelegate::OnDownloadCreated(
-    content::DownloadManager* manager,
-    download::DownloadItem* download_item) {
-  // Ignore `OnDownloadCreated()` events prior to `manager` initialization. For
-  // those events we create any objects necessary in `OnManagerInitialized()`.
-  if (is_restoring_persistence() || !manager->IsManagerInitialized())
-    return;
-
-  if (IsInProgress(download_item)) {
-    in_progress_downloads_.emplace(
-        std::make_unique<InProgressAshDownload>(this, download_item));
-  }
-}
-
 void HoldingSpaceDownloadsDelegate::OnLacrosDownloadCreated(
     const crosapi::mojom::DownloadItem& mojo_download_item) {
   if (mojo_download_item.is_from_incognito_profile &&
diff --git a/chrome/browser/ui/ash/holding_space/holding_space_downloads_delegate.h b/chrome/browser/ui/ash/holding_space/holding_space_downloads_delegate.h
index d34bb73..9757c90 100644
--- a/chrome/browser/ui/ash/holding_space/holding_space_downloads_delegate.h
+++ b/chrome/browser/ui/ash/holding_space/holding_space_downloads_delegate.h
@@ -8,28 +8,33 @@
 #include <memory>
 #include <set>
 #include <string>
+#include <vector>
 
 #include "base/containers/unique_ptr_adapters.h"
 #include "base/scoped_observation.h"
 #include "chrome/browser/ash/crosapi/download_controller_ash.h"
+#include "chrome/browser/download/notification/multi_profile_download_notifier.h"
 #include "chrome/browser/ui/ash/holding_space/holding_space_keyed_service_delegate.h"
 #include "chromeos/crosapi/mojom/download_controller.mojom-forward.h"
 #include "components/arc/intent_helper/arc_intent_helper_bridge.h"
 #include "components/arc/intent_helper/arc_intent_helper_observer.h"
-#include "content/public/browser/download_manager.h"
 
 namespace base {
 class FilePath;
 }  // namespace base
 
+namespace content {
+class DownloadManager;
+}  // namespace content
+
 namespace ash {
 
 // A delegate of `HoldingSpaceKeyedService` tasked with monitoring the status of
 // of downloads on its behalf.
 class HoldingSpaceDownloadsDelegate
     : public HoldingSpaceKeyedServiceDelegate,
+      public MultiProfileDownloadNotifier::Client,
       public arc::ArcIntentHelperObserver,
-      public content::DownloadManager::Observer,
       public crosapi::DownloadControllerAsh::DownloadControllerObserver {
  public:
   HoldingSpaceDownloadsDelegate(HoldingSpaceKeyedService* service,
@@ -39,11 +44,6 @@
       const HoldingSpaceDownloadsDelegate&) = delete;
   ~HoldingSpaceDownloadsDelegate() override;
 
-  // Sets the `content::DownloadManager` to be used for testing.
-  // NOTE: This method must be called prior to delegate initialization.
-  static void SetDownloadManagerForTesting(
-      content::DownloadManager* download_manager);
-
   // Attempts to cancel/pause/resume the download underlying the given `item`.
   void Cancel(const HoldingSpaceItem* item);
   void Pause(const HoldingSpaceItem* item);
@@ -63,16 +63,19 @@
   void OnHoldingSpaceItemsRemoved(
       const std::vector<const HoldingSpaceItem*>& items) override;
 
+  // MultiProfileDownloadNotifier::Client:
+  void OnManagerInitialized(content::DownloadManager* manager) override;
+  void OnManagerGoingDown(content::DownloadManager* manager) override;
+  void OnDownloadCreated(content::DownloadManager* manager,
+                         download::DownloadItem* item) override;
+  void OnDownloadUpdated(content::DownloadManager* manager,
+                         download::DownloadItem* item) override;
+  bool ShouldObserveProfile(Profile* profile) override;
+
   // arc::ArcIntentHelperObserver:
   void OnArcDownloadAdded(const base::FilePath& relative_path,
                           const std::string& owner_package_name) override;
 
-  // content::DownloadManager::Observer:
-  void OnManagerInitialized() override;
-  void ManagerGoingDown(content::DownloadManager* manager) override;
-  void OnDownloadCreated(content::DownloadManager* manager,
-                         download::DownloadItem* download_item) override;
-
   // crosapi::DownloadControllerAsh::DownloadControllerObserver:
   void OnLacrosDownloadCreated(
       const crosapi::mojom::DownloadItem& mojo_download_item) override;
@@ -124,9 +127,13 @@
                           arc::ArcIntentHelperObserver>
       arc_intent_helper_observation_{this};
 
-  base::ScopedObservation<content::DownloadManager,
-                          content::DownloadManager::Observer>
-      download_manager_observation_{this};
+  // Notifies this delegate of download events created for the profile
+  // associated with this delegate's service. If the incognito profile
+  // integration feature is enabled, the delegate is also notified of download
+  // events created for incognito profiles spawned from the service's main
+  // profile.
+  MultiProfileDownloadNotifier download_notifier_{
+      this, /*wait_for_manager_initialization=*/true};
 
   base::WeakPtrFactory<HoldingSpaceDownloadsDelegate> weak_factory_{this};
 };
diff --git a/chrome/browser/ui/ash/holding_space/holding_space_keyed_service_unittest.cc b/chrome/browser/ui/ash/holding_space/holding_space_keyed_service_unittest.cc
index 0ab3831..691d4f1 100644
--- a/chrome/browser/ui/ash/holding_space/holding_space_keyed_service_unittest.cc
+++ b/chrome/browser/ui/ash/holding_space/holding_space_keyed_service_unittest.cc
@@ -282,6 +282,11 @@
     observers_.RemoveObserver(observer);
   }
 
+  void Shutdown() override {
+    for (auto& observer : observers_)
+      observer.ManagerGoingDown(this);
+  }
+
   void NotifyDownloadCreated(download::DownloadItem* item) {
     for (auto& observer : observers_)
       observer.OnDownloadCreated(this, item);
@@ -314,7 +319,6 @@
     // Needed by `file_manager::VolumeManager`.
     chromeos::disks::DiskMountManager::InitializeForTesting(
         new file_manager::FakeDiskMountManager);
-    SetUpDownloadManager();
     BrowserWithTestWindowTest::SetUp();
     holding_space_util::SetNowForTesting(absl::nullopt);
   }
@@ -322,7 +326,6 @@
   void TearDown() override {
     BrowserWithTestWindowTest::TearDown();
     chromeos::disks::DiskMountManager::Shutdown();
-    HoldingSpaceDownloadsDelegate::SetDownloadManagerForTesting(nullptr);
   }
 
   TestingProfile* CreateProfile() override {
@@ -335,13 +338,15 @@
     GetSessionControllerClient()->AddUserSession(kPrimaryProfileName);
     GetSessionControllerClient()->SwitchActiveUser(account_id);
 
-    return profile_manager()->CreateTestingProfile(
+    TestingProfile* profile = profile_manager()->CreateTestingProfile(
         kPrimaryProfileName,
         /*testing_factories=*/{
             {arc::ArcIntentHelperBridge::GetFactory(),
              base::BindRepeating(&BuildArcIntentHelperBridge)},
             {file_manager::VolumeManagerFactory::GetInstance(),
              base::BindRepeating(&BuildVolumeManager)}});
+    SetUpDownloadManager(profile);
+    return profile;
   }
 
   TestingProfile* CreateSecondaryProfile(
@@ -350,7 +355,7 @@
     const AccountId account_id(AccountId::FromUserEmail(kSecondaryProfileName));
     fake_user_manager_->AddUser(account_id);
     fake_user_manager_->LoginUser(account_id);
-    return profile_manager()->CreateTestingProfile(
+    TestingProfile* profile = profile_manager()->CreateTestingProfile(
         kSecondaryProfileName, std::move(prefs), u"Test profile",
         1 /*avatar_id*/, std::string() /*supervised_user_id*/,
         /*testing_factories=*/
@@ -358,6 +363,8 @@
           base::BindRepeating(&BuildArcIntentHelperBridge)},
          {file_manager::VolumeManagerFactory::GetInstance(),
           base::BindRepeating(&BuildVolumeManager)}});
+    SetUpDownloadManager(profile);
+    return profile;
   }
 
   using PopulatePrefStoreCallback = base::OnceCallback<void(TestingPrefStore*)>;
@@ -437,9 +444,11 @@
     return result;
   }
 
-  // Creates and returns a fake download item with the specified `state`,
-  // `file_path`, `target_file_path`, `received_bytes`, and `total_bytes`.
+  // Creates and returns a fake download item for `profile` with the specified
+  // `state`, `file_path`, `target_file_path`, `received_bytes`, and
+  // `total_bytes`.
   std::unique_ptr<content::FakeDownloadItem> CreateFakeDownloadItem(
+      Profile* profile,
       download::DownloadItem::DownloadState state,
       const base::FilePath& file_path,
       const base::FilePath& target_file_path,
@@ -453,29 +462,26 @@
     fake_download_item->SetTotalBytes(total_bytes);
 
     // Notify observers of the created download.
-    download_manager()->NotifyDownloadCreated(fake_download_item.get());
+    download_managers_[profile]->NotifyDownloadCreated(
+        fake_download_item.get());
     return fake_download_item;
   }
 
-  MockDownloadManager* download_manager() { return &download_manager_; }
-
- private:
-  void SetUpDownloadManager() {
-    // The `content::DownloadManager` needs to be set prior to initialization
-    // of the `HoldingSpaceDownloadsDelegate`. This must happen before the
-    // `HoldingSpaceKeyedService` is created for the profile under test.
-    MockDownloadManager* mock_download_manager = download_manager();
-    HoldingSpaceDownloadsDelegate::SetDownloadManagerForTesting(
-        mock_download_manager);
-
-    // Spoof initialization of the `mock_download_manager`.
-    ON_CALL(*mock_download_manager, IsManagerInitialized)
+ protected:
+  // Creates a `MockDownloadManager` for `profile` to use.
+  void SetUpDownloadManager(Profile* profile) {
+    auto manager = std::make_unique<testing::NiceMock<MockDownloadManager>>();
+    ON_CALL(*manager, IsManagerInitialized)
         .WillByDefault(testing::Return(true));
+    download_managers_[profile] = manager.get();
+    profile->SetDownloadManagerForTesting(std::move(manager));
   }
 
+ private:
   FakeChromeUserManager* fake_user_manager_;
   user_manager::ScopedUserManager user_manager_enabler_;
-  testing::NiceMock<MockDownloadManager> download_manager_;
+  std::map<Profile*, testing::NiceMock<MockDownloadManager>*>
+      download_managers_;
   arc::ArcServiceManager arc_service_manager_;
 };
 
@@ -1579,7 +1585,7 @@
 
 // Verifies that screenshots restored from persistence are not older than
 // kMaxFileAge.
-TEST_F(HoldingSpaceKeyedServiceTest, RemoveOlderFilesFromPersistance) {
+TEST_F(HoldingSpaceKeyedServiceTest, RemoveOlderFilesFromPersistence) {
   // Create file system mount point.
   std::unique_ptr<ScopedTestMountPoint> downloads_mount =
       ScopedTestMountPoint::CreateAndMountDownloads(GetProfile());
@@ -1702,7 +1708,7 @@
 
   // Create a fake in-progress download item and cache a function to update it.
   std::unique_ptr<content::FakeDownloadItem> fake_download_item =
-      CreateFakeDownloadItem(current_state, current_path,
+      CreateFakeDownloadItem(profile, current_state, current_path,
                              /*target_file_path=*/base::FilePath(),
                              current_received_bytes, current_total_bytes);
   auto UpdateFakeDownloadItem = [&]() {
@@ -1768,8 +1774,9 @@
 
   // Create a fake download item and cache a function to update it.
   std::unique_ptr<content::FakeDownloadItem> fake_download_item =
-      CreateFakeDownloadItem(current_state, current_path, current_target_path,
-                             current_received_bytes, current_total_bytes);
+      CreateFakeDownloadItem(profile, current_state, current_path,
+                             current_target_path, current_received_bytes,
+                             current_total_bytes);
   auto UpdateFakeDownloadItem = [&]() {
     fake_download_item->SetDummyFilePath(current_path);
     fake_download_item->SetReceivedBytes(current_received_bytes);
@@ -1867,9 +1874,9 @@
   current_path = base::FilePath();
   current_target_path = base::FilePath();
   current_received_bytes = 0;
-  fake_download_item =
-      CreateFakeDownloadItem(current_state, current_path, current_target_path,
-                             current_received_bytes, current_total_bytes);
+  fake_download_item = CreateFakeDownloadItem(
+      profile, current_state, current_path, current_target_path,
+      current_received_bytes, current_total_bytes);
 
   // Verify that no holding space item has been created since the download does
   // not yet have file path set.
@@ -2214,7 +2221,8 @@
 }
 
 // Base class for tests of print-to-PDF integration. Parameterized by whether
-// tests should use an incognito browser.
+// tests should use an incognito browser and whether the holding space incognito
+// profile feature is enabled.
 class HoldingSpaceKeyedServicePrintToPdfIntegrationTest
     : public HoldingSpaceKeyedServiceTest,
       public testing::WithParamInterface<
@@ -2323,4 +2331,180 @@
   EXPECT_EQ(model->items()[0]->file_path(), file_path);
 }
 
+// Base class for tests of incognito profile integration. Parameterized by
+// whether the holding space incognito profile feature is enabled.
+class HoldingSpaceKeyedServiceIncognitoDownloadsTest
+    : public HoldingSpaceKeyedServiceTest,
+      public testing::WithParamInterface<
+          bool /* incognito_downloads_enabled */> {
+ public:
+  HoldingSpaceKeyedServiceIncognitoDownloadsTest() {
+    scoped_feature_list_.InitWithFeatureState(
+        features::kHoldingSpaceIncognitoProfileIntegration,
+        IncognitoDownloadsEnabled());
+  }
+
+  // HoldingSpaceKeyedServiceTest:
+  TestingProfile* CreateProfile() override {
+    TestingProfile* profile = HoldingSpaceKeyedServiceTest::CreateProfile();
+
+    // Construct an incognito profile from the primary profile.
+    TestingProfile::Builder incognito_profile_builder;
+    incognito_profile_builder.SetProfileName(profile->GetProfileUserName());
+    incognito_profile_ = incognito_profile_builder.BuildIncognito(profile);
+    EXPECT_TRUE(incognito_profile_);
+    EXPECT_TRUE(incognito_profile_->IsIncognitoProfile());
+    SetUpDownloadManager(incognito_profile_);
+    EXPECT_NE(incognito_profile_->GetDownloadManager(),
+              profile->GetDownloadManager());
+
+    return profile;
+  }
+
+  // Returns true if the test should run with the incognito profile feature
+  // enabled, false otherwise.
+  bool IncognitoDownloadsEnabled() const { return GetParam(); }
+
+  // Returns the incognito profile spawned from the test's main profile.
+  TestingProfile* incognito_profile() { return incognito_profile_; }
+
+ private:
+  base::test::ScopedFeatureList scoped_feature_list_;
+  TestingProfile* incognito_profile_ = nullptr;
+};
+
+INSTANTIATE_TEST_SUITE_P(All,
+                         HoldingSpaceKeyedServiceIncognitoDownloadsTest,
+                         /*incognito_downloads_enabled=*/::testing::Bool());
+
+TEST_P(HoldingSpaceKeyedServiceIncognitoDownloadsTest, AddDownloadItem) {
+  TestingProfile* profile = GetProfile();
+  HoldingSpaceModelAttachedWaiter(profile).Wait();
+
+  // Create a test downloads mount point.
+  std::unique_ptr<ScopedTestMountPoint> downloads_mount =
+      ScopedTestMountPoint::CreateAndMountDownloads(profile);
+  ASSERT_TRUE(downloads_mount->IsValid());
+
+  // Cache current state, file path, received bytes, and total bytes.
+  auto current_state = download::DownloadItem::IN_PROGRESS;
+  base::FilePath current_path;
+  int64_t current_received_bytes = 0;
+  int64_t current_total_bytes = 100;
+
+  // Create a fake in-progress download item for the incognito profile and cache
+  // a function to update it.
+  std::unique_ptr<content::FakeDownloadItem> fake_download_item =
+      CreateFakeDownloadItem(incognito_profile(), current_state, current_path,
+                             /*target_file_path=*/base::FilePath(),
+                             current_received_bytes, current_total_bytes);
+  auto UpdateFakeDownloadItem = [&]() {
+    fake_download_item->SetDummyFilePath(current_path);
+    fake_download_item->SetReceivedBytes(current_received_bytes);
+    fake_download_item->SetState(current_state);
+    fake_download_item->SetTotalBytes(current_total_bytes);
+    fake_download_item->NotifyDownloadUpdated();
+  };
+
+  // Verify holding space is empty.
+  HoldingSpaceModel* const model = HoldingSpaceController::Get()->model();
+  ASSERT_EQ(0u, model->items().size());
+
+  // Update the file path for the download.
+  current_path = downloads_mount->CreateFile(base::FilePath("tmp/temp_path"));
+  UpdateFakeDownloadItem();
+
+  // Verify holding space is empty.
+  ASSERT_EQ(0u, model->items().size());
+
+  // Complete the download.
+  current_state = download::DownloadItem::COMPLETE;
+  current_path = downloads_mount->CreateFile(base::FilePath("tmp/final_path"));
+  current_received_bytes = current_total_bytes;
+  UpdateFakeDownloadItem();
+
+  // Verify that a holding space item is created if and only if the incognito
+  // profile feature is enabled.
+  if (!IncognitoDownloadsEnabled()) {
+    ASSERT_EQ(model->items().size(), 0u);
+    return;
+  }
+
+  ASSERT_EQ(1u, model->items().size());
+  HoldingSpaceItem* download_item = model->items()[0].get();
+  EXPECT_EQ(download_item->type(), HoldingSpaceItem::Type::kDownload);
+  EXPECT_EQ(download_item->file_path(), current_path);
+  EXPECT_TRUE(download_item->progress().IsComplete());
+}
+
+TEST_P(HoldingSpaceKeyedServiceIncognitoDownloadsTest,
+       AddInProgressDownloadItem) {
+  base::test::ScopedFeatureList scoped_feature_list;
+  scoped_feature_list.InitAndEnableFeature(
+      features::kHoldingSpaceInProgressDownloadsIntegration);
+
+  TestingProfile* profile = GetProfile();
+  HoldingSpaceModelAttachedWaiter(profile).Wait();
+
+  // Verify the holding space model is empty.
+  HoldingSpaceModel* const model = HoldingSpaceController::Get()->model();
+  ASSERT_TRUE(model);
+  EXPECT_EQ(model->items().size(), 0u);
+
+  // Create a test downloads mount point.
+  std::unique_ptr<ScopedTestMountPoint> downloads_mount =
+      ScopedTestMountPoint::CreateAndMountDownloads(profile);
+  ASSERT_TRUE(downloads_mount->IsValid());
+
+  // Cache current state, file paths, received bytes, and total bytes.
+  auto current_state = download::DownloadItem::IN_PROGRESS;
+  base::FilePath current_path;
+  base::FilePath current_target_path;
+  int64_t current_received_bytes = 0;
+  int64_t current_total_bytes = 100;
+  bool current_is_dangerous = false;
+
+  // Create a fake download item and cache a function to update it.
+  std::unique_ptr<content::FakeDownloadItem> fake_download_item =
+      CreateFakeDownloadItem(incognito_profile(), current_state, current_path,
+                             current_target_path, current_received_bytes,
+                             current_total_bytes);
+  auto UpdateFakeDownloadItem = [&]() {
+    fake_download_item->SetDummyFilePath(current_path);
+    fake_download_item->SetReceivedBytes(current_received_bytes);
+    fake_download_item->SetState(current_state);
+    fake_download_item->SetTargetFilePath(current_target_path);
+    fake_download_item->SetTotalBytes(current_total_bytes);
+    fake_download_item->SetIsDangerous(current_is_dangerous);
+    fake_download_item->NotifyDownloadUpdated();
+  };
+
+  // Verify that no holding space item has been created since the download does
+  // not yet have file path set.
+  EXPECT_EQ(model->items().size(), 0u);
+
+  // Update the file paths for the download.
+  current_path = downloads_mount->CreateFile(base::FilePath("foo.crdownload"));
+  current_target_path = downloads_mount->CreateFile(base::FilePath("foo.png"));
+  UpdateFakeDownloadItem();
+
+  // Verify that a holding space item is created if and only if the incognito
+  // profile feature is enabled.
+  if (!IncognitoDownloadsEnabled()) {
+    ASSERT_EQ(model->items().size(), 0u);
+    return;
+  }
+
+  ASSERT_EQ(1u, model->items().size());
+  HoldingSpaceItem* download_item = model->items()[0].get();
+  EXPECT_EQ(download_item->type(), HoldingSpaceItem::Type::kDownload);
+  EXPECT_EQ(download_item->file_path(), current_path);
+  EXPECT_FALSE(download_item->progress().IsComplete());
+
+  // Verify that destroying a profile with an in-progress download destroys
+  // the holding space item.
+  profile->DestroyOffTheRecordProfile(incognito_profile());
+  ASSERT_EQ(0u, model->items().size());
+}
+
 }  // namespace ash
diff --git a/chrome/browser/ui/ash/holding_space/holding_space_ui_browsertest.cc b/chrome/browser/ui/ash/holding_space/holding_space_ui_browsertest.cc
index 2d86e97..9ca918a 100644
--- a/chrome/browser/ui/ash/holding_space/holding_space_ui_browsertest.cc
+++ b/chrome/browser/ui/ash/holding_space/holding_space_ui_browsertest.cc
@@ -1359,6 +1359,7 @@
     HoldingSpaceKeyedServiceFactory::SetTestingFactory(base::NullCallback());
   }
 
+  // HoldingSpaceUiBrowserTest:
   void SetUpOnMainThread() override {
     HoldingSpaceUiBrowserTest::SetUpOnMainThread();
 
diff --git a/chrome/browser/ui/global_media_controls/cast_media_session_controller.cc b/chrome/browser/ui/global_media_controls/cast_media_session_controller.cc
index 6ac80f6..79d7050f 100644
--- a/chrome/browser/ui/global_media_controls/cast_media_session_controller.cc
+++ b/chrome/browser/ui/global_media_controls/cast_media_session_controller.cc
@@ -68,6 +68,7 @@
     case media_session::mojom::MediaSessionAction::kToggleCamera:
     case media_session::mojom::MediaSessionAction::kHangUp:
     case media_session::mojom::MediaSessionAction::kRaise:
+    case media_session::mojom::MediaSessionAction::kSetMute:
       NOTREACHED();
       return;
   }
diff --git a/chrome/browser/ui/global_media_controls/media_session_notification_item.cc b/chrome/browser/ui/global_media_controls/media_session_notification_item.cc
index d44c82f..e0e1d42 100644
--- a/chrome/browser/ui/global_media_controls/media_session_notification_item.cc
+++ b/chrome/browser/ui/global_media_controls/media_session_notification_item.cc
@@ -71,8 +71,10 @@
   MaybeUnfreeze();
   MaybeHideOrShowNotification();
 
-  if (view_ && !frozen_)
+  if (view_ && !frozen_) {
     view_->UpdateWithMediaSessionInfo(session_info_);
+    view_->UpdateWithMuteStatus(session_info_->muted);
+  }
 }
 
 void MediaSessionNotificationItem::MediaSessionMetadataChanged(
@@ -153,6 +155,7 @@
     view_->UpdateWithMediaSessionInfo(session_info_);
     view_->UpdateWithMediaMetadata(session_metadata_);
     view_->UpdateWithMediaActions(session_actions_);
+    view_->UpdateWithMuteStatus(session_info_->muted);
 
     if (session_position_.has_value())
       view_->UpdateWithMediaPosition(*session_position_);
@@ -196,6 +199,11 @@
   media_controller_remote_->Raise();
 }
 
+void MediaSessionNotificationItem::SetMute(bool mute) {
+  if (!frozen_)
+    media_controller_remote_->SetMute(mute);
+}
+
 void MediaSessionNotificationItem::SetController(
     mojo::Remote<media_session::mojom::MediaController> controller,
     media_session::mojom::MediaSessionInfoPtr session_info) {
@@ -310,6 +318,7 @@
     view_->UpdateWithMediaSessionInfo(session_info_);
     view_->UpdateWithMediaMetadata(session_metadata_);
     view_->UpdateWithMediaActions(session_actions_);
+    view_->UpdateWithMuteStatus(session_info_->muted);
 
     if (session_position_.has_value())
       view_->UpdateWithMediaPosition(*session_position_);
diff --git a/chrome/browser/ui/global_media_controls/media_session_notification_item.h b/chrome/browser/ui/global_media_controls/media_session_notification_item.h
index 6723ee4..10b59aa 100644
--- a/chrome/browser/ui/global_media_controls/media_session_notification_item.h
+++ b/chrome/browser/ui/global_media_controls/media_session_notification_item.h
@@ -92,7 +92,7 @@
   void Dismiss() override;
   media_message_center::SourceType SourceType() override;
   void SetVolume(float volume) override {}
-  void SetMute(bool mute) override {}
+  void SetMute(bool mute) override;
 
   // Calls |Raise()| on the underlying MediaSession, which will focus the
   // WebContents if the MediaSession is associated with one.
diff --git a/chrome/browser/ui/toolbar/test_toolbar_actions_bar_bubble_delegate.cc b/chrome/browser/ui/toolbar/test_toolbar_actions_bar_bubble_delegate.cc
index c4b1d4b..88e04a9 100644
--- a/chrome/browser/ui/toolbar/test_toolbar_actions_bar_bubble_delegate.cc
+++ b/chrome/browser/ui/toolbar/test_toolbar_actions_bar_bubble_delegate.cc
@@ -4,6 +4,7 @@
 
 #include "chrome/browser/ui/toolbar/test_toolbar_actions_bar_bubble_delegate.h"
 
+#include "base/callback.h"
 #include "base/check.h"
 #include "base/logging.h"
 #include "base/macros.h"
diff --git a/chrome/browser/ui/views/download/download_item_view.cc b/chrome/browser/ui/views/download/download_item_view.cc
index 6dec3a06..1e6682f1 100644
--- a/chrome/browser/ui/views/download/download_item_view.cc
+++ b/chrome/browser/ui/views/download/download_item_view.cc
@@ -912,8 +912,15 @@
 void DownloadItemView::UpdateAccessibleAlertAndAnimationsForNormalMode() {
   using State = download::DownloadItem::DownloadState;
   const State state = model_->GetState();
+  const std::u16string web_drive = model_->GetWebDriveName();
   if ((state == State::IN_PROGRESS) && !model_->IsPaused()) {
-    UpdateAccessibleAlert(GetInProgressAccessibleAlertText());
+    if (web_drive.empty()) {
+      UpdateAccessibleAlert(GetInProgressAccessibleAlertText());
+    } else {
+      announce_accessible_alert_soon_ = true;
+      UpdateAccessibleAlert(
+          l10n_util::GetStringFUTF16(IDS_DOWNLOAD_STATUS_UPLOADING, web_drive));
+    }
 
     if (!indeterminate_progress_timer_.IsRunning()) {
       indeterminate_progress_start_time_ = base::TimeTicks::Now();
@@ -944,8 +951,17 @@
         {State::COMPLETE, IDS_DOWNLOAD_COMPLETE_ACCESSIBLE_ALERT},
         {State::CANCELLED, IDS_DOWNLOAD_CANCELLED_ACCESSIBLE_ALERT},
     });
-    const std::u16string alert_text = l10n_util::GetStringFUTF16(
-        kMap.at(state), model_->GetFileNameToReportUser().LossyDisplayName());
+    const std::u16string alert_text =
+        (web_drive.empty() || state == State::CANCELLED)
+            ? l10n_util::GetStringFUTF16(
+                  kMap.at(state),
+                  model_->GetFileNameToReportUser().LossyDisplayName())
+            // When the file is rereouted to web drive and not cancelled, use
+            // regular string formulated in DownloadUIModel.
+            // TODO(https://crbug.com/1240372) Update with accessibility
+            // specific localized strings.
+            : model_->GetStatusText();
+
     announce_accessible_alert_soon_ = true;
     UpdateAccessibleAlert(alert_text);
   }
diff --git a/chrome/browser/ui/views/external_protocol_dialog.cc b/chrome/browser/ui/views/external_protocol_dialog.cc
index 264af90..cf4fc1b 100644
--- a/chrome/browser/ui/views/external_protocol_dialog.cc
+++ b/chrome/browser/ui/views/external_protocol_dialog.cc
@@ -75,7 +75,7 @@
     const GURL& url,
     const std::u16string& program_name,
     const absl::optional<url::Origin>& initiating_origin)
-    : content::WebContentsObserver(web_contents),
+    : web_contents_(web_contents->GetWeakPtr()),
       url_(url),
       program_name_(program_name),
       initiating_origin_(initiating_origin) {
@@ -156,7 +156,7 @@
   ExternalProtocolHandler::RecordHandleStateMetrics(
       remember, ExternalProtocolHandler::DONT_BLOCK);
 
-  if (!web_contents()) {
+  if (!web_contents_) {
     // Dialog outlasted the WebContents.
     return;
   }
@@ -164,14 +164,15 @@
   if (remember) {
     DCHECK(initiating_origin_);
     Profile* profile =
-        Profile::FromBrowserContext(web_contents()->GetBrowserContext());
+        Profile::FromBrowserContext(web_contents_->GetBrowserContext());
 
     ExternalProtocolHandler::SetBlockState(url_.scheme(), *initiating_origin_,
                                            ExternalProtocolHandler::DONT_BLOCK,
                                            profile);
   }
 
-  ExternalProtocolHandler::LaunchUrlWithoutSecurityCheck(url_, web_contents());
+  ExternalProtocolHandler::LaunchUrlWithoutSecurityCheck(url_,
+                                                         web_contents_.get());
 }
 
 views::View* ExternalProtocolDialog::GetContentsView() {
diff --git a/chrome/browser/ui/views/external_protocol_dialog.h b/chrome/browser/ui/views/external_protocol_dialog.h
index b2e0cf3f..c8e9996 100644
--- a/chrome/browser/ui/views/external_protocol_dialog.h
+++ b/chrome/browser/ui/views/external_protocol_dialog.h
@@ -6,8 +6,8 @@
 #define CHROME_BROWSER_UI_VIEWS_EXTERNAL_PROTOCOL_DIALOG_H_
 
 #include "base/macros.h"
+#include "base/memory/weak_ptr.h"
 #include "chrome/browser/profiles/profile.h"
-#include "content/public/browser/web_contents_observer.h"
 #include "ui/base/metadata/metadata_header_macros.h"
 #include "ui/views/window/dialog_delegate.h"
 #include "url/gurl.h"
@@ -25,8 +25,7 @@
 class MessageBoxView;
 }
 
-class ExternalProtocolDialog : public views::DialogDelegateView,
-                               public content::WebContentsObserver {
+class ExternalProtocolDialog : public views::DialogDelegateView {
  public:
   METADATA_HEADER(ExternalProtocolDialog);
   // Show by calling ExternalProtocolHandler::RunExternalProtocolDialog().
@@ -52,6 +51,8 @@
   void SetRememberSelectionCheckboxCheckedForTesting(bool checked);
   void OnDialogAccepted();
 
+  base::WeakPtr<content::WebContents> web_contents_;
+
   const GURL url_;
   const std::u16string program_name_;
   const absl::optional<url::Origin> initiating_origin_;
diff --git a/chrome/browser/ui/views/frame/opaque_browser_frame_view_browsertest.cc b/chrome/browser/ui/views/frame/opaque_browser_frame_view_browsertest.cc
index d787e397..f2925b8 100644
--- a/chrome/browser/ui/views/frame/opaque_browser_frame_view_browsertest.cc
+++ b/chrome/browser/ui/views/frame/opaque_browser_frame_view_browsertest.cc
@@ -551,8 +551,16 @@
   EXPECT_EQ(u"onresize", title_watcher2.WaitAndGetTitle());
 }
 
+// TODO(https://crbug.com/1234323): Very flaky on Wayland builder.
+#if defined(OS_LINUX)
+#define MAYBE_WindowControlsOverlayDraggableRegions \
+  DISABLED_WindowControlsOverlayDraggableRegions
+#else
+#define MAYBE_WindowControlsOverlayDraggableRegions \
+  WindowControlsOverlayDraggableRegions
+#endif  // defined(OS_LINUX)
 IN_PROC_BROWSER_TEST_F(WebAppOpaqueBrowserFrameViewWindowControlsOverlayTest,
-                       WindowControlsOverlayDraggableRegions) {
+                       MAYBE_WindowControlsOverlayDraggableRegions) {
   if (!InstallAndLaunchWebAppWithWindowControlsOverlay())
     return;
 
diff --git a/chrome/browser/ui/views/location_bar/location_bar_bubble_delegate_view.cc b/chrome/browser/ui/views/location_bar/location_bar_bubble_delegate_view.cc
index 01f1c517..d91e24b 100644
--- a/chrome/browser/ui/views/location_bar/location_bar_bubble_delegate_view.cc
+++ b/chrome/browser/ui/views/location_bar/location_bar_bubble_delegate_view.cc
@@ -122,16 +122,18 @@
   if (reason == USER_GESTURE) {
     GetWidget()->Show();
   } else {
-    GetWidget()->ShowInactive();
-
+// TODO(crbug.com/1186729): Reenable this announcement on Mac when the keyboard
+// shortcut to reach it gets a keybinding and is verified to work. This will
+// also require a string update with the new key binding on Mac.
+#if !defined(OS_MAC)
     if (allow_refocus_alert) {
-      // Since this widget is inactive (but shown), accessibility tools won't
-      // alert the user to its presence. Accessibility tools such as screen
-      // readers work by tracking system focus. Give users of these tools a hint
-      // description and alert them to the presence of this widget.
+      // Since this will show as inactive, add a description for how to get to
+      // it.
       GetWidget()->GetRootView()->GetViewAccessibility().OverrideDescription(
           l10n_util::GetStringUTF8(IDS_SHOW_BUBBLE_INACTIVE_DESCRIPTION));
     }
+#endif
+    GetWidget()->ShowInactive();
   }
 }
 
diff --git a/chrome/browser/ui/views/omnibox/omnibox_view_views.cc b/chrome/browser/ui/views/omnibox/omnibox_view_views.cc
index a36c248..e13ae10 100644
--- a/chrome/browser/ui/views/omnibox/omnibox_view_views.cc
+++ b/chrome/browser/ui/views/omnibox/omnibox_view_views.cc
@@ -203,7 +203,7 @@
 
 void OmniboxViewViews::Init() {
   set_controller(this);
-  SetTextInputType(ui::TEXT_INPUT_TYPE_URL);
+  SetTextInputType(GetPreferredTextInputType());
   GetRenderText()->SetElideBehavior(gfx::ELIDE_TAIL);
   GetRenderText()->set_symmetric_selection_visual_bounds(true);
   InstallPlaceholderText();
@@ -531,6 +531,13 @@
 
 void OmniboxViewViews::OnInputMethodChanged() {
 #if defined(OS_WIN)
+  // Update the input type with the input method on Windows for CJK.
+  SetTextInputType(GetPreferredTextInputType());
+#endif  // OS_WIN
+}
+
+ui::TextInputType OmniboxViewViews::GetPreferredTextInputType() const {
+#if defined(OS_WIN)
   // We'd like to set the text input type to TEXT_INPUT_TYPE_URL, because this
   // triggers URL-specific layout in software keyboards, e.g. adding top-level
   // "/" and ".com" keys for English.  However, this also causes IMEs to default
@@ -541,11 +548,10 @@
     ui::InputMethod* input_method =
         location_bar_view_->GetWidget()->GetInputMethod();
     if (input_method && input_method->IsInputLocaleCJK())
-      SetTextInputType(ui::TEXT_INPUT_TYPE_SEARCH);
-    else
-      SetTextInputType(ui::TEXT_INPUT_TYPE_URL);
+      return ui::TEXT_INPUT_TYPE_SEARCH;
   }
-#endif
+#endif  // OS_WIN
+  return ui::TEXT_INPUT_TYPE_URL;
 }
 
 void OmniboxViewViews::AddedToWidget() {
diff --git a/chrome/browser/ui/views/omnibox/omnibox_view_views.h b/chrome/browser/ui/views/omnibox/omnibox_view_views.h
index 206c174..05a7703 100644
--- a/chrome/browser/ui/views/omnibox/omnibox_view_views.h
+++ b/chrome/browser/ui/views/omnibox/omnibox_view_views.h
@@ -225,6 +225,9 @@
   void AnnounceFriendlySuggestionText();
 #endif
 
+  // Get the preferred text input type, this checks the IME locale on Windows.
+  ui::TextInputType GetPreferredTextInputType() const;
+
   // OmniboxView:
   void SetCaretPos(size_t caret_pos) override;
   void UpdatePopup() override;
diff --git a/chrome/browser/ui/views/omnibox/omnibox_view_views_browsertest.cc b/chrome/browser/ui/views/omnibox/omnibox_view_views_browsertest.cc
index 74112d7..cc9f1d40 100644
--- a/chrome/browser/ui/views/omnibox/omnibox_view_views_browsertest.cc
+++ b/chrome/browser/ui/views/omnibox/omnibox_view_views_browsertest.cc
@@ -881,6 +881,21 @@
   omnibox_view_views->OnInputMethodChanged();
   EXPECT_EQ(ui::TEXT_INPUT_TYPE_URL, omnibox_view_views->GetTextInputType());
 }
+
+IN_PROC_BROWSER_TEST_F(OmniboxViewViewsIMETest, TextInputTypeInitRespectsIME) {
+  OmniboxMockInputMethod* input_method = new OmniboxMockInputMethod();
+  ui::SetUpInputMethodForTesting(input_method);
+  input_method->SetInputLocaleCJK(/*is_cjk=*/true);
+  Browser* browser_2 = CreateBrowser(browser()->profile());
+  OmniboxView* view = nullptr;
+  ASSERT_NO_FATAL_FAILURE(GetOmniboxViewForBrowser(browser_2, &view));
+  OmniboxViewViews* omnibox_view_views = static_cast<OmniboxViewViews*>(view);
+  EXPECT_EQ(omnibox_view_views->GetWidget()->GetInputMethod(), input_method);
+  EXPECT_EQ(ui::TEXT_INPUT_TYPE_SEARCH, omnibox_view_views->GetTextInputType());
+  input_method->SetInputLocaleCJK(/*is_cjk=*/false);
+  omnibox_view_views->OnInputMethodChanged();
+  EXPECT_EQ(ui::TEXT_INPUT_TYPE_URL, omnibox_view_views->GetTextInputType());
+}
 #endif  // OS_WIN
 
 // ClickOnView(VIEW_ID_OMNIBOX) does not set focus to omnibox on Mac.
diff --git a/chrome/browser/ui/views/payments/payment_handler_modal_dialog_manager_delegate.cc b/chrome/browser/ui/views/payments/payment_handler_modal_dialog_manager_delegate.cc
index 53a6fdbf..cf23a23b 100644
--- a/chrome/browser/ui/views/payments/payment_handler_modal_dialog_manager_delegate.cc
+++ b/chrome/browser/ui/views/payments/payment_handler_modal_dialog_manager_delegate.cc
@@ -16,9 +16,10 @@
 PaymentHandlerModalDialogManagerDelegate::
     PaymentHandlerModalDialogManagerDelegate(
         content::WebContents* host_web_contents)
-    : content::WebContentsObserver(host_web_contents), web_contents_(nullptr) {
-  DCHECK(host_web_contents);
-}
+    : host_web_contents_(host_web_contents->GetWeakPtr()) {}
+
+PaymentHandlerModalDialogManagerDelegate::
+    ~PaymentHandlerModalDialogManagerDelegate() = default;
 
 void PaymentHandlerModalDialogManagerDelegate::SetWebContentsBlocked(
     content::WebContents* web_contents,
@@ -32,12 +33,12 @@
 
 web_modal::WebContentsModalDialogHost*
 PaymentHandlerModalDialogManagerDelegate::GetWebContentsModalDialogHost() {
-  if (!web_contents())
+  if (!host_web_contents_)
     return nullptr;
 
   auto* dialog_manager =
       static_cast<web_modal::WebContentsModalDialogManagerDelegate*>(
-          chrome::FindBrowserWithWebContents(web_contents()));
+          chrome::FindBrowserWithWebContents(host_web_contents_.get()));
   if (!dialog_manager)
     return nullptr;
 
diff --git a/chrome/browser/ui/views/payments/payment_handler_modal_dialog_manager_delegate.h b/chrome/browser/ui/views/payments/payment_handler_modal_dialog_manager_delegate.h
index 6733888..efaa2f9 100644
--- a/chrome/browser/ui/views/payments/payment_handler_modal_dialog_manager_delegate.h
+++ b/chrome/browser/ui/views/payments/payment_handler_modal_dialog_manager_delegate.h
@@ -6,8 +6,8 @@
 #define CHROME_BROWSER_UI_VIEWS_PAYMENTS_PAYMENT_HANDLER_MODAL_DIALOG_MANAGER_DELEGATE_H_
 
 #include "base/macros.h"
+#include "base/memory/weak_ptr.h"
 #include "components/web_modal/web_contents_modal_dialog_manager_delegate.h"
-#include "content/public/browser/web_contents_observer.h"
 
 namespace content {
 class WebContents;
@@ -25,13 +25,12 @@
 // spawned the payment sheet. Observes the WebContents that spawned the payment
 // handler.
 class PaymentHandlerModalDialogManagerDelegate
-    : public web_modal::WebContentsModalDialogManagerDelegate,
-      public content::WebContentsObserver {
+    : public web_modal::WebContentsModalDialogManagerDelegate {
  public:
   // |host| must not be null.
   explicit PaymentHandlerModalDialogManagerDelegate(
       content::WebContents* host_web_contents);
-  ~PaymentHandlerModalDialogManagerDelegate() override = default;
+  ~PaymentHandlerModalDialogManagerDelegate() override;
 
   // Sets the |web_contents| that is behind the modal dialogs managed by this
   // modal dialog manager. |web_contents| must not be null.
@@ -47,8 +46,11 @@
   bool IsWebContentsVisible(content::WebContents* web_contents) override;
 
  private:
+  // The WebContents hosting the dialog.
+  base::WeakPtr<content::WebContents> host_web_contents_;
+
   // A not-owned pointer to the WebContents behind the modal dialogs.
-  content::WebContents* web_contents_;
+  content::WebContents* web_contents_ = nullptr;
 
   DISALLOW_COPY_AND_ASSIGN(PaymentHandlerModalDialogManagerDelegate);
 };
diff --git a/chrome/browser/ui/views/translate/translate_bubble_view.cc b/chrome/browser/ui/views/translate/translate_bubble_view.cc
index cf46bda..022b9a5 100644
--- a/chrome/browser/ui/views/translate/translate_bubble_view.cc
+++ b/chrome/browser/ui/views/translate/translate_bubble_view.cc
@@ -222,10 +222,7 @@
     translate_bubble_view_->SwitchToErrorView(error_type);
   }
 
-  // |allow_refocus_alert| is set to false because translate bubble does not
-  // have an additional screen reader alert instructing the user to use a
-  // hotkey combination to focus the bubble.
-  view->ShowForReason(reason, false);
+  view->ShowForReason(reason);
   translate::ReportUiAction(translate::BUBBLE_SHOWN);
 
   ChromeTranslateClient::GetManagerFromWebContents(web_contents)
diff --git a/chrome/browser/ui/views/webauthn/authenticator_request_dialog_view.h b/chrome/browser/ui/views/webauthn/authenticator_request_dialog_view.h
index 8e24b13..76dc0ac1 100644
--- a/chrome/browser/ui/views/webauthn/authenticator_request_dialog_view.h
+++ b/chrome/browser/ui/views/webauthn/authenticator_request_dialog_view.h
@@ -80,6 +80,7 @@
   void OnStepTransition() override;
   void OnSheetModelChanged() override;
 
+  // content::WebContentsObserver:
   void OnVisibilityChanged(content::Visibility visibility) override;
 
  private:
diff --git a/chrome/browser/ui/webui/chromeos/add_supervision/add_supervision_ui.cc b/chrome/browser/ui/webui/chromeos/add_supervision/add_supervision_ui.cc
index 644ccb0..032228bd 100644
--- a/chrome/browser/ui/webui/chromeos/add_supervision/add_supervision_ui.cc
+++ b/chrome/browser/ui/webui/chromeos/add_supervision/add_supervision_ui.cc
@@ -204,8 +204,6 @@
   source->EnableReplaceI18nInJS();
 
   // Forward data to the WebUI.
-  source->AddResourcePath("post_message_api.js",
-                          IDR_ADD_SUPERVISION_POST_MESSAGE_API_JS);
   source->AddResourcePath("add_supervision_api_server.js",
                           IDR_ADD_SUPERVISION_API_SERVER_JS);
   source->AddResourcePath("add_supervision_ui.js", IDR_ADD_SUPERVISION_UI_JS);
diff --git a/chrome/browser/ui/webui/settings/chromeos/bluetooth_section.cc b/chrome/browser/ui/webui/settings/chromeos/bluetooth_section.cc
index b3cfbe7..d34125d 100644
--- a/chrome/browser/ui/webui/settings/chromeos/bluetooth_section.cc
+++ b/chrome/browser/ui/webui/settings/chromeos/bluetooth_section.cc
@@ -197,6 +197,10 @@
        IDS_SETTINGS_BLUETOOTH_CHANGE_DEVICE_NAME_DIALOG_CANCEL},
       {"bluetoothDeviceDetailChangeNameDialogDone",
        IDS_SETTINGS_BLUETOOTH_CHANGE_DEVICE_NAME_DIALOG_DONE},
+      {"bluetoothDeviceDetailChangeDeviceSettingsKeyboard",
+       IDS_SETTINGS_BLUETOOTH_DEVICE_DETAIL_CHANGE_DEVICE_SETTINGS_KEYBOARD},
+      {"bluetoothDeviceDetailChangeDeviceSettingsMouse",
+       IDS_SETTINGS_BLUETOOTH_DEVICE_DETAIL_CHANGE_DEVICE_SETTINGS_MOUSE},
       {"bluetoothDeviceDetailChangeDeviceName",
        IDS_SETTINGS_BLUETOOTH_DEVICE_DETAIL_CHANGE_DEVICE_NAME},
       {"bluetoothToggleA11yLabel",
@@ -221,6 +225,44 @@
       {"bluetoothSummaryPageOn", IDS_SETTINGS_BLUETOOTH_SUMMARY_PAGE_ON},
       {"bluetoothPairedDeviceItemBatteryPercentage",
        IDS_SETTINGS_BLUETOOTH_PAIRED_DEVICE_ITEM_BATTERY_PERCENTAGE},
+      {"bluetoothPairedDeviceItemA11yLabelTypeUnknown",
+       IDS_SETTINGS_BLUETOOTH_PAIRED_DEVICE_ITEM_A11Y_LABEL_TYPE_UNKNOWN},
+      {"bluetoothPairedDeviceItemA11yLabelTypeUnknownWithBatteryInfo",
+       IDS_SETTINGS_BLUETOOTH_PAIRED_DEVICE_ITEM_A11Y_LABEL_TYPE_UNKNOWN_WITH_BATTERY_INFO},
+      {"bluetoothPairedDeviceItemA11yLabelTypeComputer",
+       IDS_SETTINGS_BLUETOOTH_PAIRED_DEVICE_ITEM_A11Y_LABEL_TYPE_COMPUTER},
+      {"bluetoothPairedDeviceItemA11yLabelTypeComputerWithBatteryInfo",
+       IDS_SETTINGS_BLUETOOTH_PAIRED_DEVICE_ITEM_A11Y_LABEL_TYPE_COMPUTER_WITH_BATTERY_INFO},
+      {"bluetoothPairedDeviceItemA11yLabelTypePhone",
+       IDS_SETTINGS_BLUETOOTH_PAIRED_DEVICE_ITEM_A11Y_LABEL_TYPE_PHONE},
+      {"bluetoothPairedDeviceItemA11yLabelTypePhoneWithBatteryInfo",
+       IDS_SETTINGS_BLUETOOTH_PAIRED_DEVICE_ITEM_A11Y_LABEL_TYPE_PHONE_WITH_BATTERY_INFO},
+      {"bluetoothPairedDeviceItemA11yLabelTypeHeadset",
+       IDS_SETTINGS_BLUETOOTH_PAIRED_DEVICE_ITEM_A11Y_LABEL_TYPE_HEADSET},
+      {"bluetoothPairedDeviceItemA11yLabelTypeHeadsetWithBatteryInfo",
+       IDS_SETTINGS_BLUETOOTH_PAIRED_DEVICE_ITEM_A11Y_LABEL_TYPE_HEADSET_WITH_BATTERY_INFO},
+      {"bluetoothPairedDeviceItemA11yLabelTypeVideoCamera",
+       IDS_SETTINGS_BLUETOOTH_PAIRED_DEVICE_ITEM_A11Y_LABEL_TYPE_VIDEO_CAMERA},
+      {"bluetoothPairedDeviceItemA11yLabelTypeVideoCameraWithBatteryInfo",
+       IDS_SETTINGS_BLUETOOTH_PAIRED_DEVICE_ITEM_A11Y_LABEL_TYPE_VIDEO_CAMERA_WITH_BATTERY_INFO},
+      {"bluetoothPairedDeviceItemA11yLabelTypeGameController",
+       IDS_SETTINGS_BLUETOOTH_PAIRED_DEVICE_ITEM_A11Y_LABEL_TYPE_GAME_CONTROLLER},
+      {"bluetoothPairedDeviceItemA11yLabelTypeGameControllerWithBatteryInfo",
+       IDS_SETTINGS_BLUETOOTH_PAIRED_DEVICE_ITEM_A11Y_LABEL_TYPE_GAME_CONTROLLER_WITH_BATTERY_INFO},
+      {"bluetoothPairedDeviceItemA11yLabelTypeKeyboard",
+       IDS_SETTINGS_BLUETOOTH_PAIRED_DEVICE_ITEM_A11Y_LABEL_TYPE_KEYBOARD},
+      {"bluetoothPairedDeviceItemA11yLabelTypeKeyboardWithBatteryInfo",
+       IDS_SETTINGS_BLUETOOTH_PAIRED_DEVICE_ITEM_A11Y_LABEL_TYPE_KEYBOARD_WITH_BATTERY_INFO},
+      {"bluetoothPairedDeviceItemA11yLabelTypeMouse",
+       IDS_SETTINGS_BLUETOOTH_PAIRED_DEVICE_ITEM_A11Y_LABEL_TYPE_MOUSE},
+      {"bluetoothPairedDeviceItemA11yLabelTypeMouseWithBatteryInfo",
+       IDS_SETTINGS_BLUETOOTH_PAIRED_DEVICE_ITEM_A11Y_LABEL_TYPE_MOUSE_WITH_BATTERY_INFO},
+      {"bluetoothPairedDeviceItemA11yLabelTypeTablet",
+       IDS_SETTINGS_BLUETOOTH_PAIRED_DEVICE_ITEM_A11Y_LABEL_TYPE_TABLET},
+      {"bluetoothPairedDeviceItemA11yLabelTypeTabletWithBatteryInfo",
+       IDS_SETTINGS_BLUETOOTH_PAIRED_DEVICE_ITEM_A11Y_LABEL_TYPE_TABLET_WITH_BATTERY_INFO},
+      {"bluetoothPairedDeviceItemSubpageButtonA11yLabel",
+       IDS_SETTINGS_BLUETOOTH_PAIRED_DEVICE_ITEM_SUBPAGE_BUTTON_A11Y_LABEL},
       {"bluetoothPairDevicePageTitle",
        IDS_SETTINGS_BLUETOOTH_PAIR_DEVICE_TITLE},
       {"bluetoothRemove", IDS_SETTINGS_BLUETOOTH_REMOVE},
diff --git a/chrome/browser/ui/webui/settings/chromeos/os_settings_manager_factory.cc b/chrome/browser/ui/webui/settings/chromeos/os_settings_manager_factory.cc
index fd3d2f2e..e08f8acf 100644
--- a/chrome/browser/ui/webui/settings/chromeos/os_settings_manager_factory.cc
+++ b/chrome/browser/ui/webui/settings/chromeos/os_settings_manager_factory.cc
@@ -4,6 +4,7 @@
 
 #include "chrome/browser/ui/webui/settings/chromeos/os_settings_manager_factory.h"
 
+#include "chrome/browser/apps/app_service/app_service_proxy_chromeos.h"
 #include "chrome/browser/apps/app_service/app_service_proxy_factory.h"
 #include "chrome/browser/ash/kerberos/kerberos_credentials_manager_factory.h"
 #include "chrome/browser/ash/profiles/profile_helper.h"
diff --git a/chrome/browser/ui/webui/signin/inline_login_ui.cc b/chrome/browser/ui/webui/signin/inline_login_ui.cc
index 2dfff4b..63d9916a 100644
--- a/chrome/browser/ui/webui/signin/inline_login_ui.cc
+++ b/chrome/browser/ui/webui/signin/inline_login_ui.cc
@@ -158,8 +158,6 @@
     {"edu_coexistence_ui.js", IDR_EDU_COEXISTENCE_EDU_COEXISTENCE_UI_JS},
     {"edu_coexistence_controller.js",
      IDR_EDU_COEXISTENCE_EDU_COEXISTENCE_CONTROLLER_JS},
-    {"chromeos/add_supervision/post_message_api.js",
-     IDR_ADD_SUPERVISION_POST_MESSAGE_API_JS},
     {"edu_coexistence_browser_proxy.js",
      IDR_EDU_COEXISTENCE_EDU_COEXISTENCE_BROWSER_PROXY_JS},
     {"edu_coexistence_button.js",
@@ -237,6 +235,9 @@
                     chrome::GetOSSettingsUrl(
                         chromeos::settings::mojom::kMyAccountsSubpagePath)
                         .spec());
+
+  source->OverrideContentSecurityPolicy(
+      network::mojom::CSPDirectiveName::FrameSrc, "frame-src chrome://test/;");
 #endif
 
   return source;
diff --git a/chrome/browser/ui/webui/test_data_source.cc b/chrome/browser/ui/webui/test_data_source.cc
index cd0e7e7..7d1247bc 100644
--- a/chrome/browser/ui/webui/test_data_source.cc
+++ b/chrome/browser/ui/webui/test_data_source.cc
@@ -101,6 +101,10 @@
     return std::string();
   } else if (directive == network::mojom::CSPDirectiveName::FrameAncestors) {
     return "frame-ancestors chrome://* 'self';";
+  } else if (directive == network::mojom::CSPDirectiveName::FrameSrc) {
+    return "frame-src chrome://test/;";
+  } else if (directive == network::mojom::CSPDirectiveName::ChildSrc) {
+    return "child-src chrome://test/;";
   }
 
   return content::URLDataSource::GetContentSecurityPolicy(directive);
diff --git a/chrome/browser/web_applications/components/web_app_install_utils.cc b/chrome/browser/web_applications/components/web_app_install_utils.cc
index 41f756e..fa53bf6 100644
--- a/chrome/browser/web_applications/components/web_app_install_utils.cc
+++ b/chrome/browser/web_applications/components/web_app_install_utils.cc
@@ -388,7 +388,6 @@
     }
   }
 
-  // TODO(crbug.com/1152661): Consider eliminating duplicates.
   return web_app_info_icon_urls;
 }
 
diff --git a/chrome/chrome_cleaner/os/initializer.cc b/chrome/chrome_cleaner/os/initializer.cc
index 3d19dee..265b413 100644
--- a/chrome/chrome_cleaner/os/initializer.cc
+++ b/chrome/chrome_cleaner/os/initializer.cc
@@ -12,6 +12,7 @@
 #include "base/rand_util.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/synchronization/waitable_event.h"
+#include "base/time/time.h"
 #include "base/win/scoped_handle.h"
 #include "base/win/win_util.h"
 #include "chrome/chrome_cleaner/constants/chrome_cleaner_switches.h"
diff --git a/chrome/chrome_cleaner/settings/settings.h b/chrome/chrome_cleaner/settings/settings.h
index 33fcd48b..16577dc 100644
--- a/chrome/chrome_cleaner/settings/settings.h
+++ b/chrome/chrome_cleaner/settings/settings.h
@@ -12,6 +12,7 @@
 
 #include "base/command_line.h"
 #include "base/memory/singleton.h"
+#include "base/time/time.h"
 #include "chrome/chrome_cleaner/logging/proto/shared_data.pb.h"
 #include "chrome/chrome_cleaner/settings/settings_definitions.h"
 #include "components/chrome_cleaner/public/constants/constants.h"
diff --git a/chrome/credential_provider/gaiacp/gcp_utils.cc b/chrome/credential_provider/gaiacp/gcp_utils.cc
index 2eb1b169..4ce1ca6 100644
--- a/chrome/credential_provider/gaiacp/gcp_utils.cc
+++ b/chrome/credential_provider/gaiacp/gcp_utils.cc
@@ -37,6 +37,7 @@
 #include "base/macros.h"
 #include "base/no_destructor.h"
 #include "base/path_service.h"
+#include "base/strings/string_number_conversions_win.h"
 #include "base/strings/string_util.h"
 #include "base/strings/stringprintf.h"
 #include "base/strings/utf_string_conversions.h"
diff --git a/chrome/installer/setup/downgrade_cleanup_unittest.cc b/chrome/installer/setup/downgrade_cleanup_unittest.cc
index 9fb951f9..a7aa8b1b 100644
--- a/chrome/installer/setup/downgrade_cleanup_unittest.cc
+++ b/chrome/installer/setup/downgrade_cleanup_unittest.cc
@@ -5,6 +5,7 @@
 #include "chrome/installer/setup/downgrade_cleanup.h"
 
 #include "base/command_line.h"
+#include "base/notreached.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/test/multiprocess_test.h"
 #include "base/test/test_reg_util_win.h"
diff --git a/chrome/services/printing/public/mojom/BUILD.gn b/chrome/services/printing/public/mojom/BUILD.gn
index bae6a32..fe6901a 100644
--- a/chrome/services/printing/public/mojom/BUILD.gn
+++ b/chrome/services/printing/public/mojom/BUILD.gn
@@ -20,6 +20,7 @@
 
   public_deps = [
     "//mojo/public/mojom/base",
+    "//sandbox/policy/mojom",
     "//url/mojom:url_mojom_gurl",
   ]
 
diff --git a/chrome/services/printing/public/mojom/printing_service.mojom b/chrome/services/printing/public/mojom/printing_service.mojom
index dc16846..990346a6 100644
--- a/chrome/services/printing/public/mojom/printing_service.mojom
+++ b/chrome/services/printing/public/mojom/printing_service.mojom
@@ -6,6 +6,7 @@
 
 import "chrome/services/printing/public/mojom/pdf_nup_converter.mojom";
 import "chrome/services/printing/public/mojom/pdf_to_pwg_raster_converter.mojom";
+import "sandbox/policy/mojom/sandbox.mojom";
 
 [EnableIf=is_chromeos]
 import "chrome/services/printing/public/mojom/pdf_flattener.mojom";
@@ -15,8 +16,18 @@
 [EnableIf=is_win]
 import "chrome/services/printing/public/mojom/pdf_to_emf_converter.mojom";
 
+// The different sandbox on Windows allows the service to know it should
+// patch certain GDI functions prior to service startup.
+[EnableIf=is_win]
+const sandbox.mojom.Sandbox kPrintingServiceSandbox
+  = sandbox.mojom.Sandbox.kPdfConversion;
+[EnableIfNot=is_win]
+const sandbox.mojom.Sandbox kPrintingServiceSandbox
+  = sandbox.mojom.Sandbox.kUtility;
+
 // The main interface to Chrome's Printing Service, which performs various PDF
 // conversion operations in an isolated sandboxed process.
+[ServiceSandbox=kPrintingServiceSandbox]
 interface PrintingService {
   // Binds an interface that can be used to do Nup PDF conversion.
   BindPdfNupConverter(pending_receiver<PdfNupConverter> receiver);
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn
index 20679af..942fc32 100644
--- a/chrome/test/BUILD.gn
+++ b/chrome/test/BUILD.gn
@@ -9546,7 +9546,7 @@
   }
 }
 
-if (is_linux) {
+if (is_linux || is_win) {
   script_test("variations_smoke_tests") {
     run_under_python2 = true
 
diff --git a/chrome/test/chromedriver/chrome/status.cc b/chrome/test/chromedriver/chrome/status.cc
index 155432a..f878cdc 100644
--- a/chrome/test/chromedriver/chrome/status.cc
+++ b/chrome/test/chromedriver/chrome/status.cc
@@ -74,10 +74,6 @@
       return "target frame detached";
     case kElementClickIntercepted:
       return "element click intercepted";
-    case kNoSuchShadowRoot:
-      return "no such shadow root";
-    case kDetachedShadowRoot:
-      return "detached shadow root";
     default:
       return "<unknown>";
   }
diff --git a/chrome/test/chromedriver/chrome/status.h b/chrome/test/chromedriver/chrome/status.h
index e08d017..098746a9 100644
--- a/chrome/test/chromedriver/chrome/status.h
+++ b/chrome/test/chromedriver/chrome/status.h
@@ -34,8 +34,6 @@
   kInvalidArgument = 61,
   kNoSuchCookie = 62,
   kElementClickIntercepted = 64,
-  kNoSuchShadowRoot = 65,
-  kDetachedShadowRoot = 66,
   kUnsupportedOperation = 405,
   // Chrome-specific status codes.
   kChromeNotReachable = 100,
diff --git a/chrome/test/chromedriver/client/chromedriver.py b/chrome/test/chromedriver/client/chromedriver.py
index 9480307..935b59d 100644
--- a/chrome/test/chromedriver/client/chromedriver.py
+++ b/chrome/test/chromedriver/client/chromedriver.py
@@ -11,12 +11,10 @@
 import command_executor
 from command_executor import Command
 from webelement import WebElement
-from webshadowroot import WebShadowRoot
 from websocket_connection import WebSocketConnection
 
 ELEMENT_KEY_W3C = "element-6066-11e4-a52e-4f735466cecf"
 ELEMENT_KEY = "ELEMENT"
-SHADOW_KEY = "shadow-6066-11e4-a52e-4f735466cecf"
 MAX_RETRY_COUNT = 5
 
 class ChromeDriverException(Exception):
@@ -65,10 +63,6 @@
   pass
 class UnsupportedOperation(ChromeDriverException):
   pass
-class NoSuchShadowRoot(ChromeDriverException):
-  pass
-class DetachedShadowRoot(ChromeDriverException):
-  pass
 
 def _ExceptionForLegacyResponse(response):
   exception_class_map = {
@@ -123,8 +117,6 @@
     'invalid argument': InvalidArgument,
     'element not interactable': ElementNotInteractable,
     'unsupported operation': UnsupportedOperation,
-    'no such shadow root': NoSuchShadowRoot,
-    'detached shadow root': DetachedShadowRoot,
   }
 
   error = response['value']['error']
@@ -332,8 +324,6 @@
         return {ELEMENT_KEY_W3C: value._id}
       else:
         return {ELEMENT_KEY: value._id}
-    elif isinstance(value, WebShadowRoot):
-        return {SHADOW_KEY: value._id}
     elif isinstance(value, list):
       return list(self._WrapValue(item) for item in value)
     else:
@@ -346,9 +336,6 @@
           and isinstance(
             value[ELEMENT_KEY_W3C], basestring)):
         return WebElement(self, value[ELEMENT_KEY_W3C])
-      elif (len(value) == 1 and SHADOW_KEY in value
-            and isinstance(value[SHADOW_KEY], basestring)):
-        return WebShadowRoot(self, value[SHADOW_KEY])
       elif (len(value) == 1 and ELEMENT_KEY in value
             and isinstance(value[ELEMENT_KEY], basestring)):
         return WebElement(self, value[ELEMENT_KEY])
diff --git a/chrome/test/chromedriver/client/command_executor.py b/chrome/test/chromedriver/client/command_executor.py
index 5d8a3fd..f3e03fed 100644
--- a/chrome/test/chromedriver/client/command_executor.py
+++ b/chrome/test/chromedriver/client/command_executor.py
@@ -83,12 +83,6 @@
       _Method.GET, '/session/:sessionId/element/:id/computedlabel')
   GET_ELEMENT_COMPUTED_ROLE = (
       _Method.GET, '/session/:sessionId/element/:id/computedrole')
-  GET_ELEMENT_SHADOW_ROOT = (
-      _Method.GET, '/session/:sessionId/element/:id/shadow')
-  FIND_ELEMENT_FROM_SHADOW_ROOT = (
-      _Method.POST, '/session/:sessionId/shadow/:id/element')
-  FIND_ELEMENTS_FROM_SHADOW_ROOT = (
-      _Method.POST, '/session/:sessionId/shadow/:id/elements')
   ELEMENT_EQUALS = (
       _Method.GET, '/session/:sessionId/element/:id/equals/:other')
   GET_COOKIES = (_Method.GET, '/session/:sessionId/cookie')
diff --git a/chrome/test/chromedriver/client/webelement.py b/chrome/test/chromedriver/client/webelement.py
index 70cd829..422b226 100644
--- a/chrome/test/chromedriver/client/webelement.py
+++ b/chrome/test/chromedriver/client/webelement.py
@@ -14,7 +14,7 @@
   def _Execute(self, command, params=None):
     if params is None:
       params = {}
-    params['id'] = self._id
+    params['id'] = self._id;
     return self._chromedriver.ExecuteCommand(command, params)
 
   def FindElement(self, strategy, target):
@@ -25,9 +25,6 @@
     return self._Execute(
         Command.FIND_CHILD_ELEMENTS, {'using': strategy, 'value': target})
 
-  def GetElementShadowRoot(self):
-    return self._Execute(Command.GET_ELEMENT_SHADOW_ROOT)
-
   def GetText(self):
     return self._Execute(Command.GET_ELEMENT_TEXT)
 
diff --git a/chrome/test/chromedriver/client/webshadowroot.py b/chrome/test/chromedriver/client/webshadowroot.py
deleted file mode 100644
index a822b0e6..0000000
--- a/chrome/test/chromedriver/client/webshadowroot.py
+++ /dev/null
@@ -1,27 +0,0 @@
-# Copyright 2021 The Chromium Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-from command_executor import Command
-
-class WebShadowRoot(object):
-  """Represents an HTML shadow root"""
-  def __init__(self, chromedriver, id_):
-    self._chromedriver = chromedriver
-    self._id = id_
-
-  def _Execute(self, command, params=None):
-    if params is None:
-        params = {}
-    params['id'] = self._id
-    return self._chromedriver.ExecuteCommand(command, params)
-
-  def FindElement(self, strategy, target):
-    return self._Execute(
-      Command.FIND_ELEMENT_FROM_SHADOW_ROOT,
-      {'using': strategy, 'value': target})
-
-  def FindElements(self, strategy, target):
-    return self._Execute(
-      Command.FIND_ELEMENTS_FROM_SHADOW_ROOT,
-      {'using': strategy, 'value': target})
diff --git a/chrome/test/chromedriver/element_commands.cc b/chrome/test/chromedriver/element_commands.cc
index ddd523d..977bb10 100644
--- a/chrome/test/chromedriver/element_commands.cc
+++ b/chrome/test/chromedriver/element_commands.cc
@@ -201,62 +201,6 @@
       interval_ms, true, &element_id, session, web_view, params, value);
 }
 
-Status ExecuteFindChildElementFromShadowRoot(
-    int interval_ms,
-    Session* session,
-    WebView* web_view,
-    const std::string& shadow_root_id,
-    const base::DictionaryValue& params,
-    std::unique_ptr<base::Value>* value) {
-  return FindShadowElement(interval_ms, true, &shadow_root_id, session,
-                           web_view, params, value);
-}
-
-Status ExecuteFindChildElementsFromShadowRoot(
-    int interval_ms,
-    Session* session,
-    WebView* web_view,
-    const std::string& shadow_root_id,
-    const base::DictionaryValue& params,
-    std::unique_ptr<base::Value>* value) {
-  return FindShadowElement(interval_ms, false, &shadow_root_id, session,
-                           web_view, params, value);
-}
-
-Status ExecuteGetElementShadowRoot(Session* session,
-                                   WebView* web_view,
-                                   const std::string& element_id,
-                                   const base::DictionaryValue& params,
-                                   std::unique_ptr<base::Value>* value) {
-  Status status = CheckElement(element_id);
-
-  if (status.IsError())
-    return status;
-
-  base::ListValue args;
-  args.Append(CreateElement(element_id));
-
-  std::string currentFrameId = session->GetCurrentFrameId();
-
-  status = web_view->CallFunction(session->GetCurrentFrameId(),
-                                  "function(elem) { return elem.shadowRoot; }",
-                                  args, value);
-
-  if (status.IsError()) {
-    if (status.message().find("no such shadow root") != std::string::npos) {
-      return Status(kNoSuchShadowRoot);
-    }
-
-    return status;
-  }
-
-  if (value->get()->is_none()) {
-    return Status(kNoSuchShadowRoot);
-  }
-
-  return status;
-}
-
 Status ExecuteFindChildElements(int interval_ms,
                                 Session* session,
                                 WebView* web_view,
diff --git a/chrome/test/chromedriver/element_commands.h b/chrome/test/chromedriver/element_commands.h
index 7c51c4c9..d74c7a8 100644
--- a/chrome/test/chromedriver/element_commands.h
+++ b/chrome/test/chromedriver/element_commands.h
@@ -221,26 +221,4 @@
                                 const base::DictionaryValue& params,
                                 std::unique_ptr<base::Value>* value);
 
-Status ExecuteGetElementShadowRoot(Session* session,
-                                   WebView* web_view,
-                                   const std::string& element_id,
-                                   const base::DictionaryValue& params,
-                                   std::unique_ptr<base::Value>* value);
-
-Status ExecuteFindChildElementFromShadowRoot(
-    int interval_ms,
-    Session* session,
-    WebView* web_view,
-    const std::string& shadow_root_id,
-    const base::DictionaryValue& params,
-    std::unique_ptr<base::Value>* value);
-
-Status ExecuteFindChildElementsFromShadowRoot(
-    int interval_ms,
-    Session* session,
-    WebView* web_view,
-    const std::string& shadow_root_id,
-    const base::DictionaryValue& params,
-    std::unique_ptr<base::Value>* value);
-
 #endif  // CHROME_TEST_CHROMEDRIVER_ELEMENT_COMMANDS_H_
diff --git a/chrome/test/chromedriver/element_util.cc b/chrome/test/chromedriver/element_util.cc
index 0b18b8c2..d6d019c 100644
--- a/chrome/test/chromedriver/element_util.cc
+++ b/chrome/test/chromedriver/element_util.cc
@@ -28,7 +28,6 @@
 
 const char kElementKey[] = "ELEMENT";
 const char kElementKeyW3C[] = "element-6066-11e4-a52e-4f735466cecf";
-const char kShadowRootKey[] = "shadow-6066-11e4-a52e-4f735466cecf";
 
 bool ParseFromValue(base::Value* value, WebPoint* point) {
   if (!value->is_dict())
@@ -313,22 +312,11 @@
   return Status(kOk);
 }
 
-std::unique_ptr<base::DictionaryValue> CreateElementCommon(
-    const std::string& key,
-    const std::string& value) {
-  std::unique_ptr<base::DictionaryValue> element(new base::DictionaryValue());
-  element->SetString(key, value);
-  return element;
-}
-
 std::unique_ptr<base::DictionaryValue> CreateElement(
     const std::string& element_id) {
-  return CreateElementCommon(GetElementKey(), element_id);
-}
-
-std::unique_ptr<base::DictionaryValue> CreateShadowRoot(
-    const std::string& shadow_root_id) {
-  return CreateElementCommon(kShadowRootKey, shadow_root_id);
+  std::unique_ptr<base::DictionaryValue> element(new base::DictionaryValue());
+  element->SetString(GetElementKey(), element_id);
+  return element;
 }
 
 std::unique_ptr<base::DictionaryValue> CreateValueFrom(const WebPoint& point) {
@@ -338,14 +326,13 @@
   return dict;
 }
 
-Status FindElementCommon(int interval_ms,
-                         bool only_one,
-                         const std::string* root_element_id,
-                         Session* session,
-                         WebView* web_view,
-                         const base::DictionaryValue& params,
-                         std::unique_ptr<base::Value>* value,
-                         bool isShadowRoot) {
+Status FindElement(int interval_ms,
+                   bool only_one,
+                   const std::string* root_element_id,
+                   Session* session,
+                   WebView* web_view,
+                   const base::DictionaryValue& params,
+                   std::unique_ptr<base::Value>* value) {
   std::string strategy;
   if (!params.GetString("using", &strategy))
     return Status(kInvalidArgument, "'using' must be a string");
@@ -357,17 +344,6 @@
       strategy != "xpath")
     return Status(kInvalidArgument, "invalid locator");
 
-  /*
-   * Currently there is an opened discussion about if the
-   * following values has to be supported for a Shadow Root
-   * because the current implementation doesn't support them.
-   * We have them disabled for now.
-   * https://github.com/w3c/webdriver/issues/1610
-   */
-  if (isShadowRoot && (strategy == "tag name" || strategy == "xpath")) {
-    return Status(kInvalidArgument, "invalid locator");
-  }
-
   std::string target;
   if (!params.GetString("value", &target))
     return Status(kInvalidArgument, "'value' must be a string");
@@ -381,12 +357,8 @@
   locator->SetString(strategy, target);
   base::ListValue arguments;
   arguments.Append(std::move(locator));
-  if (root_element_id) {
-    if (isShadowRoot)
-      arguments.Append(CreateShadowRoot(*root_element_id));
-    else
-      arguments.Append(CreateElement(*root_element_id));
-  }
+  if (root_element_id)
+    arguments.Append(CreateElement(*root_element_id));
 
   base::TimeTicks start_time = base::TimeTicks::Now();
   int context_retry = 0;
@@ -394,7 +366,6 @@
     std::unique_ptr<base::Value> temp;
     Status status = web_view->CallFunction(
         session->GetCurrentFrameId(), script, arguments, &temp);
-
     // A "Cannot find context" error can occur due to transition from in-process
     // iFrame to OOPIF. Retry a couple of times.
     if (status.IsError() &&
@@ -430,28 +401,6 @@
   }
 }
 
-Status FindElement(int interval_ms,
-                   bool only_one,
-                   const std::string* root_element_id,
-                   Session* session,
-                   WebView* web_view,
-                   const base::DictionaryValue& params,
-                   std::unique_ptr<base::Value>* value) {
-  return FindElementCommon(interval_ms, only_one, root_element_id, session,
-                           web_view, params, value, false);
-}
-
-Status FindShadowElement(int interval_ms,
-                         bool only_one,
-                         const std::string* shadow_root_id,
-                         Session* session,
-                         WebView* web_view,
-                         const base::DictionaryValue& params,
-                         std::unique_ptr<base::Value>* value) {
-  return FindElementCommon(interval_ms, only_one, shadow_root_id, session,
-                           web_view, params, value, true);
-}
-
 Status GetActiveElement(Session* session,
                         WebView* web_view,
                         std::unique_ptr<base::Value>* value) {
diff --git a/chrome/test/chromedriver/element_util.h b/chrome/test/chromedriver/element_util.h
index 3d7a2b4..b66e56d 100644
--- a/chrome/test/chromedriver/element_util.h
+++ b/chrome/test/chromedriver/element_util.h
@@ -37,14 +37,6 @@
                    const base::DictionaryValue& params,
                    std::unique_ptr<base::Value>* value);
 
-Status FindShadowElement(int interval_ms,
-                         bool only_one,
-                         const std::string* shadow_root_id,
-                         Session* session,
-                         WebView* web_view,
-                         const base::DictionaryValue& params,
-                         std::unique_ptr<base::Value>* value);
-
 Status GetActiveElement(Session* session,
                         WebView* web_view,
                         std::unique_ptr<base::Value>* value);
diff --git a/chrome/test/chromedriver/js/call_function.js b/chrome/test/chromedriver/js/call_function.js
index ac516eb3..e2b41d7 100644
--- a/chrome/test/chromedriver/js/call_function.js
+++ b/chrome/test/chromedriver/js/call_function.js
@@ -9,8 +9,6 @@
 var StatusCode = {
   STALE_ELEMENT_REFERENCE: 10,
   JAVA_SCRIPT_ERROR: 17,
-  NO_SUCH_SHADOW_ROOT: 65,
-  DETACHED_SHADOW_ROOT: 66
 };
 
 /**
@@ -30,13 +28,6 @@
 var ELEMENT_KEY = 'ELEMENT';
 
 /**
- * Dictionary key to use for holding a shadow element ID.
- * @const
- * @type {string}
- */
- var SHADOW_ROOT_KEY = 'shadow-6066-11e4-a52e-4f735466cecf';
-
-/**
  * True if using W3C Element references.
  * @const
  * @type {boolean}
@@ -329,23 +320,11 @@
   if (isElement(item)) {
     const root = getNodeRootThroughAnyShadows(item);
     const cache = getPageCache(root, w3cEnabled);
-    if (!cache.isNodeReachable_(item)) {
-      if (item instanceof ShadowRoot)
-        throw newError('detached shadow root', StatusCode.DETACHED_SHADOW_ROOT);
+    if (!cache.isNodeReachable_(item))
       throw newError('stale element not found',
                      StatusCode.STALE_ELEMENT_REFERENCE);
-    }
     const ret = {};
-    let key = ELEMENT_KEY;
-    if (item instanceof ShadowRoot) {
-      if (!item.nodeType ||
-          item.nodeType !== item.DOCUMENT_FRAGMENT_NODE ||
-          !item.host) {
-        throw newError('no such shadow root', StatusCode.NO_SUCH_SHADOW_ROOT);
-      }
-      key = SHADOW_ROOT_KEY;
-    }
-    ret[key] = cache.storeItem(item);
+    ret[ELEMENT_KEY] = cache.storeItem(item);
     return ret;
   }
   if (isCollection(item))
@@ -382,22 +361,12 @@
       typeof item === 'string' ||
       typeof item === 'function')
     return item;
-  if (item.hasOwnProperty(ELEMENT_KEY) ||
-      item.hasOwnProperty(SHADOW_ROOT_KEY)) {
+  if (item.hasOwnProperty(ELEMENT_KEY)) {
     if (opt_cache === undefined || opt_cache === null) {
       const root = getNodeRootThroughAnyShadows(item);
       opt_cache = getPageCache(root, w3cEnabled);
     }
-    try {
-      return  opt_cache.retrieveItem(item[ELEMENT_KEY] ||
-                                     item[SHADOW_ROOT_KEY]);
-    } catch(err) {
-      if (err.message &&
-          err.message === 'element is not attached to the page document' &&
-          item.hasOwnProperty(SHADOW_ROOT_KEY))
-        throw newError('detached shadow root', StatusCode.DETACHED_SHADOW_ROOT);
-      throw err;
-    }
+    return  opt_cache.retrieveItem(item[ELEMENT_KEY]);
   }
   if (isCollection(item) || typeof item === 'object')
     return cloneWithAlgorithm(item, opt_seen, jsonDeserialize, opt_cache);
diff --git a/chrome/test/chromedriver/server/http_handler.cc b/chrome/test/chromedriver/server/http_handler.cc
index 67cbc6da..ac73f3f 100644
--- a/chrome/test/chromedriver/server/http_handler.cc
+++ b/chrome/test/chromedriver/server/http_handler.cc
@@ -264,20 +264,6 @@
           WrapToCommand("GetActiveElement",
                         base::BindRepeating(&ExecuteGetActiveElement))),
       CommandMapping(
-          kGet, "session/:sessionId/element/:id/shadow",
-          WrapToCommand("GetElementShadowRoot",
-                        base::BindRepeating(&ExecuteGetElementShadowRoot))),
-      CommandMapping(
-          kPost, "session/:sessionId/shadow/:id/element",
-          WrapToCommand(
-              "FindChildElementFromShadowRoot",
-              base::BindRepeating(&ExecuteFindChildElementFromShadowRoot, 50))),
-      CommandMapping(
-          kPost, "session/:sessionId/shadow/:id/elements",
-          WrapToCommand("FindChildElementsFromShadowRoot",
-                        base::BindRepeating(
-                            &ExecuteFindChildElementsFromShadowRoot, 50))),
-      CommandMapping(
           kPost, "session/:sessionId/element",
           WrapToCommand("FindElement",
                         base::BindRepeating(&ExecuteFindElement, 50))),
@@ -1296,14 +1282,6 @@
       response = std::make_unique<net::HttpServerResponseInfo>(
           net::HTTP_INTERNAL_SERVER_ERROR);
       break;
-    case kNoSuchShadowRoot:
-      response =
-          std::make_unique<net::HttpServerResponseInfo>(net::HTTP_NOT_FOUND);
-      break;
-    case kDetachedShadowRoot:
-      response =
-          std::make_unique<net::HttpServerResponseInfo>(net::HTTP_NOT_FOUND);
-      break;
 
     default:
       DCHECK(false);
diff --git a/chrome/test/chromedriver/test/run_py_tests.py b/chrome/test/chromedriver/test/run_py_tests.py
index 61c8b19..e8a2cef 100755
--- a/chrome/test/chromedriver/test/run_py_tests.py
+++ b/chrome/test/chromedriver/test/run_py_tests.py
@@ -52,7 +52,6 @@
 import chromedriver
 import websocket_connection
 import webelement
-import webshadowroot
 sys.path.remove(_CLIENT_DIR)
 
 sys.path.insert(1, _SERVER_DIR)
@@ -848,70 +847,6 @@
     self._driver.Load(self.GetHttpUrlForFile('/chromedriver/page_test.html'))
     self.assertTrue('Link to empty.html' in self._driver.GetPageSource())
 
-  def testGetElementShadowRoot(self):
-    self._driver.Load(
-      self.GetHttpUrlForFile('/chromedriver/get_element_shadow_root.html'))
-    element = self._driver.FindElement('tag name', 'custom-checkbox-element')
-    shadow = element.GetElementShadowRoot()
-    self.assertTrue(isinstance(shadow, webshadowroot.WebShadowRoot))
-
-  def testGetElementShadowRootNotExists(self):
-    self._driver.Load(
-      self.GetHttpUrlForFile('/chromedriver/get_element_shadow_root.html'))
-    element = self._driver.FindElement('tag name', 'div')
-    with self.assertRaises(chromedriver.NoSuchShadowRoot):
-      element.GetElementShadowRoot()
-
-  def testFindElementFromShadowRoot(self):
-    self._driver.Load(
-      self.GetHttpUrlForFile('/chromedriver/get_element_shadow_root.html'))
-    element = self._driver.FindElement('tag name', 'custom-checkbox-element')
-    shadow = element.GetElementShadowRoot()
-    self.assertTrue(isinstance(shadow, webshadowroot.WebShadowRoot))
-    elementInShadow = shadow.FindElement('css selector', 'input')
-    self.assertTrue(isinstance(elementInShadow, webelement.WebElement))
-
-  def testFindElementFromShadowRootInvalidArgs(self):
-    self._driver.Load(
-      self.GetHttpUrlForFile('/chromedriver/get_element_shadow_root.html'))
-    element = self._driver.FindElement('tag name', 'custom-checkbox-element')
-    shadow = element.GetElementShadowRoot()
-    self.assertTrue(isinstance(shadow, webshadowroot.WebShadowRoot))
-    with self.assertRaises(chromedriver.InvalidArgument):
-      shadow.FindElement('tag name', 'input')
-    with self.assertRaises(chromedriver.InvalidArgument):
-      shadow.FindElement('xpath', '//')
-
-  def testDetachedShadowRootError(self):
-    self._driver.Load(
-      self.GetHttpUrlForFile('/chromedriver/get_element_shadow_root.html'))
-    element = self._driver.FindElement('tag name', 'custom-checkbox-element')
-    shadow = element.GetElementShadowRoot()
-    self._driver.Refresh()
-    with self.assertRaises(chromedriver.DetachedShadowRoot):
-      shadow.FindElement('css selector', 'input')
-
-  def testFindElementsFromShadowRoot(self):
-    self._driver.Load(
-      self.GetHttpUrlForFile('/chromedriver/get_element_shadow_root.html'))
-    element = self._driver.FindElement('tag name', 'custom-checkbox-element')
-    shadow = element.GetElementShadowRoot()
-    self.assertTrue(isinstance(shadow, webshadowroot.WebShadowRoot))
-    elementsInShadow = shadow.FindElements('css selector', 'input')
-    self.assertTrue(isinstance(elementsInShadow, list))
-    self.assertTrue(2, len(elementsInShadow))
-
-  def testFindElementsFromShadowRootInvalidArgs(self):
-    self._driver.Load(
-      self.GetHttpUrlForFile('/chromedriver/get_element_shadow_root.html'))
-    element = self._driver.FindElement('tag name', 'custom-checkbox-element')
-    shadow = element.GetElementShadowRoot()
-    self.assertTrue(isinstance(shadow, webshadowroot.WebShadowRoot))
-    with self.assertRaises(chromedriver.InvalidArgument):
-      shadow.FindElements('tag name', 'input')
-    with self.assertRaises(chromedriver.InvalidArgument):
-      shadow.FindElements('xpath', '//')
-
   def testFindElement(self):
     self._driver.Load(self.GetHttpUrlForFile('/chromedriver/empty.html'))
     self._driver.ExecuteScript(
diff --git a/chrome/test/chromedriver/test/run_py_tests.pydeps b/chrome/test/chromedriver/test/run_py_tests.pydeps
index ae77f00..f34373f 100644
--- a/chrome/test/chromedriver/test/run_py_tests.pydeps
+++ b/chrome/test/chromedriver/test/run_py_tests.pydeps
@@ -83,7 +83,6 @@
 ../client/chromedriver.py
 ../client/command_executor.py
 ../client/webelement.py
-../client/webshadowroot.py
 ../client/websocket_connection.py
 ../server/server.py
 ../util.py
diff --git a/chrome/test/data/chromedriver/get_element_shadow_root.html b/chrome/test/data/chromedriver/get_element_shadow_root.html
deleted file mode 100644
index d103f88..0000000
--- a/chrome/test/data/chromedriver/get_element_shadow_root.html
+++ /dev/null
@@ -1,21 +0,0 @@
-<style>
-  custom-checkbox-element {
-    display: block;
-    width: 20px;
-    height: 20px;
-  }
-</style>
-<div></div>
-<custom-checkbox-element></custom-checkbox-element>
-<script>
-  customElements.define('custom-checkbox-element',
-    class extends HTMLElement {
-      constructor() {
-        super();
-        this.attachShadow({ mode: 'open' }).innerHTML = `
-          <div><input/><input type="button"/></div>
-          `;
-      }
-    }
-  );
-</script>
diff --git a/chrome/test/data/webui/BUILD.gn b/chrome/test/data/webui/BUILD.gn
index a55c077e..9209520 100644
--- a/chrome/test/data/webui/BUILD.gn
+++ b/chrome/test/data/webui/BUILD.gn
@@ -41,6 +41,8 @@
     deps = [
       ":modulize",
       "//chrome/browser/ui",
+      "//ui/webui/resources/js:post_message_api_client.m",
+      "//ui/webui/resources/js:post_message_api_server.m",
     ]
     data = [
       "$root_gen_dir/chrome/test/data/webui/fake_chrome_event.m.js",
@@ -164,6 +166,7 @@
         "chromeos/gaia_action_buttons/gaia_action_buttons_browsertest.js",
         "chromeos/internet_detail_dialog_browsertest.js",
         "cr_components/chromeos/cr_components_chromeos_v3_browsertest.js",
+        "post_message_api/post_message_api_browsertest.js",
         "set_time_dialog_browsertest.js",
         "settings/chromeos/a11y/v3_os_a11y_browsertest.js",
         "settings/chromeos/os_settings_v3_browsertest.js",
diff --git a/chrome/test/data/webui/chromeos/ash_common/navigation_view_panel_test.js b/chrome/test/data/webui/chromeos/ash_common/navigation_view_panel_test.js
index 8aa66b3..12357474 100644
--- a/chrome/test/data/webui/chromeos/ash_common/navigation_view_panel_test.js
+++ b/chrome/test/data/webui/chromeos/ash_common/navigation_view_panel_test.js
@@ -193,4 +193,19 @@
     assertFalse(!!viewElement.shadowRoot.querySelector(`#${id1}`));
     assertFalse(!!viewElement.shadowRoot.querySelector(`#${dummyPage2}`));
   });
+
+  test('toolBarVisible', async () => {
+    const dummyPage1 = 'dummy-page1';
+    const expectedTitle = 'title';
+    viewElement.title = expectedTitle;
+    viewElement.showToolBar = true;
+
+    await addNavigationSection('dummyPage1', dummyPage1);
+
+    assertFalse(viewElement.shadowRoot.querySelector(`#${dummyPage1}`).hidden);
+    // The title is only visible if the toolbar is stamped.
+    const pageToolbar = viewElement.shadowRoot.querySelector('page-toolbar');
+    const toolbarTitle = pageToolbar.$.title.textContent.trim();
+    assertEquals(expectedTitle, toolbarTitle);
+  });
 }
diff --git a/chrome/test/data/webui/post_message_api/iframe.html b/chrome/test/data/webui/post_message_api/iframe.html
new file mode 100644
index 0000000..7c5fa3a
--- /dev/null
+++ b/chrome/test/data/webui/post_message_api/iframe.html
@@ -0,0 +1,6 @@
+<html>
+<body>
+<script type="module" src="chrome://test/post_message_api/post_message_api_client_test.js"></script>
+HELLO WORLD.
+</body>
+</html>
diff --git a/chrome/test/data/webui/post_message_api/post_message_api_browsertest.js b/chrome/test/data/webui/post_message_api/post_message_api_browsertest.js
new file mode 100644
index 0000000..d58cd746
--- /dev/null
+++ b/chrome/test/data/webui/post_message_api/post_message_api_browsertest.js
@@ -0,0 +1,32 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Polymer BrowserTest fixture.
+GEN_INCLUDE(['//chrome/test/data/webui/polymer_browser_test_base.js']);
+
+GEN('#include "content/public/test/browser_test.h"');
+
+function PostMessageAPIModuleTest() {}
+
+PostMessageAPIModuleTest.prototype = {
+
+  __proto__: testing.Test.prototype,
+
+  /** @override */
+  browsePreload:
+      'chrome://chrome-signin/test_loader.html?module=post_message_api/post_message_api_test.js',
+
+  extraLibraries: [
+    '//third_party/mocha/mocha.js',
+    '//chrome/test/data/webui/mocha_adapter.js',
+  ],
+
+  isAsync: true,
+};
+
+// Test the postmessage api server and client by defining server methods, and
+// passing a series of postmessages.
+TEST_F('PostMessageAPIModuleTest', 'PostMessageCommTest', function() {
+  runMochaTest('PostMessageAPIModuleTest', 'PostMessageCommTest');
+});
diff --git a/chrome/test/data/webui/post_message_api/post_message_api_client_test.js b/chrome/test/data/webui/post_message_api/post_message_api_client_test.js
new file mode 100644
index 0000000..79d20e04
--- /dev/null
+++ b/chrome/test/data/webui/post_message_api/post_message_api_client_test.js
@@ -0,0 +1,76 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import {PostMessageAPIClient} from 'chrome://resources/js/post_message_api_client.m.js';
+
+const METHODS = ['setX', 'increment', 'decrement', 'finalize'];
+const ServerOriginURLFilter = 'chrome://chrome-signin/';
+
+class TestPostMessageAPIClient extends PostMessageAPIClient {
+  constructor() {
+    super(METHODS, ServerOriginURLFilter);
+  }
+
+  setX(x) {
+    return this.callApiFn('setX', x);
+  }
+
+  increment(y) {
+    return this.callApiFn('increment', y);
+  }
+
+  decrement(z) {
+    return this.callApiFn('decrement', z);
+  }
+
+  finalize(success) {
+    return this.callApiFn('finalize', success);
+  }
+
+  /**
+   * Exercise the methods in the METHOD list above, which are defined in the
+   * TestPostMessageAPIServer class.
+   * @override
+   */
+  onInitialized() {
+    // Iinitializes the "x" value in the test PostMessageAPIServer and ensures
+    // that it has been set via the callback.
+    this.setX(1).then((result) => {
+      if (result !== 1) {
+        this.finalize(false);
+        return;
+      }
+
+      // Increments the "x" value in the test PostMessageAPIServer and ensures
+      // that the value has been updated.
+      this.increment(5).then((result) => {
+        if (result !== 6) {
+          this.finalize(false);
+          return;
+        }
+
+        // Decrements the "x" value in the test PostMessageAPIServer and ensures
+        // that the value has been updated.
+        this.decrement(1).then((result) => {
+          if (result !== 5) {
+            this.finalize(false);
+            return;
+          }
+
+          // By this time, multiple requests have been successfully sent and
+          // received between the test PostMessageAPIServer and
+          // PostMessageAPIClient. Notify the server that the test is
+          // successfully completed.
+          this.finalize(true);
+        });
+      });
+    });
+  }
+}
+
+document.addEventListener('DOMContentLoaded', function() {
+  // Construct the PostMessageAPIClient so that it can run the tests.
+  const post_message_listener =
+      new TestPostMessageAPIClient(METHODS, ServerOriginURLFilter);
+});
diff --git a/chrome/test/data/webui/post_message_api/post_message_api_test.js b/chrome/test/data/webui/post_message_api/post_message_api_test.js
new file mode 100644
index 0000000..068f513
--- /dev/null
+++ b/chrome/test/data/webui/post_message_api/post_message_api_test.js
@@ -0,0 +1,99 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import {PostMessageAPIServer} from 'chrome://resources/js/post_message_api_server.m.js';
+
+const METHODS = ['setX', 'increment', 'decrement', 'finalize'];
+const TARGET_URL = 'chrome://test/post_message_api/iframe.html';
+const TARGET_ORIGIN = 'chrome://test/';
+
+class TestPostMessageAPIServer extends PostMessageAPIServer {
+  constructor(iframeElement) {
+    super(iframeElement, METHODS, TARGET_URL, TARGET_ORIGIN);
+    /**
+     * Whether the test was successful or not.
+     * {boolean}
+     */
+    this.success = false;
+
+    /**
+     * The value that is being communicated between the server and client.
+     * {integer}
+     */
+    this.x = -1;
+
+    /**
+     * The promise to be resolved when test finishes successfully.
+     * {Promise<boolean>}
+     */
+    this.promise_resolve = null;
+
+    this.registerMethod('setX', this.setX.bind(this));
+    this.registerMethod('increment', this.increment.bind(this));
+    this.registerMethod('decrement', this.decrement.bind(this));
+    this.registerMethod('finalize', this.finalize.bind(this));
+  }
+
+  /**
+   * @param {int} x
+   */
+  setX(x) {
+    this.x = x;
+    return this.x;
+  }
+
+  /**
+   * @param {int} y
+   */
+  increment(y) {
+    this.x = this.x + y;
+    return this.x;
+  }
+  /**
+   * @param {int} y
+   */
+  decrement(y) {
+    this.x = this.x - y;
+    return this.x;
+  }
+  /**
+   * @param {boolean} success
+   */
+  finalize(success) {
+    this.success = success;
+    if (this.promise_resolve) {
+      this.promise_resolve(this.success);
+    }
+  }
+
+  /**
+   * Returns a promise which when resolved will tell whether the test passed or
+   * not.
+   * @return {Promise<boolean>}
+   */
+  getTestFinalized() {
+    const promise = new Promise((resolve, reject) => {
+      this.promise_resolve = resolve;
+    });
+
+    if (this.success) {
+      this.promise_resolve(this.success);
+    }
+    return promise;
+  }
+}
+
+suite('PostMessageAPIModuleTest', function() {
+  suiteSetup(function() {
+    this.innerFrame = document.createElement('iframe');
+    this.innerFrame.src = TARGET_URL;
+    document.body.appendChild(this.innerFrame);
+  });
+
+  test('PostMessageCommTest', async function() {
+    var server = new TestPostMessageAPIServer(this.innerFrame);
+    let success = await server.getTestFinalized();
+    assertTrue(success);
+  });
+});
diff --git a/chrome/test/data/webui/settings/chromeos/fake_bluetooth_config.js b/chrome/test/data/webui/settings/chromeos/fake_bluetooth_config.js
index 8bcc4b6..8983e12 100644
--- a/chrome/test/data/webui/settings/chromeos/fake_bluetooth_config.js
+++ b/chrome/test/data/webui/settings/chromeos/fake_bluetooth_config.js
@@ -16,18 +16,21 @@
  * @param {string} publicName
  * @param {boolean} connected
  * @param {string|undefined} nickname
- * @param {!chromeos.bluetoothConfig.mojom.AudioOutputCapability}
+ * @param {?chromeos.bluetoothConfig.mojom.AudioOutputCapability}
  *     audioCapability
+ * @param {?chromeos.bluetoothConfig.mojom.DeviceType}
+ *     deviceType
  * @return {!chromeos.bluetoothConfig.mojom.PairedBluetoothDeviceProperties}
  */
 export function createDefaultBluetoothDevice(
     id, publicName, connected, nickname = undefined,
-    audioCapability = mojom.AudioOutputCapability.kNotCapableOfAudio) {
+    audioCapability = mojom.AudioOutputCapability.kNotCapableOfAudio,
+    deviceType = mojom.DeviceType.kUnknown) {
   return {
     deviceProperties: {
       id: id,
       publicName: stringToMojoString16(publicName),
-      deviceType: mojom.DeviceType.kUnknown,
+      deviceType: deviceType,
       audioCapability: audioCapability,
       connectionState: connected ? mojom.DeviceConnectionState.kConnected :
                                    mojom.DeviceConnectionState.kNotConnected,
diff --git a/chrome/test/data/webui/settings/chromeos/os_bluetooth_device_detail_subpage_tests.js b/chrome/test/data/webui/settings/chromeos/os_bluetooth_device_detail_subpage_tests.js
index 2400d2c..669e0ac 100644
--- a/chrome/test/data/webui/settings/chromeos/os_bluetooth_device_detail_subpage_tests.js
+++ b/chrome/test/data/webui/settings/chromeos/os_bluetooth_device_detail_subpage_tests.js
@@ -89,6 +89,43 @@
     assertEquals('device1', bluetoothDeviceDetailPage.parentNode.pageTitle);
   });
 
+  test('Show change settings row', async function() {
+    init();
+    bluetoothConfig.setBluetoothEnabledState(/*enabled=*/ true);
+
+    const getChangeMouseSettings = () =>
+        bluetoothDeviceDetailPage.$$('#changeMouseSettings');
+    const getChangeKeyboardSettings = () =>
+        bluetoothDeviceDetailPage.$$('#changeKeyboardSettings');
+
+    const device1 = createDefaultBluetoothDevice(
+        /*id=*/ '12//345&6789',
+        /*publicName=*/ 'BeatsX',
+        /*connected=*/ true,
+        /*nickname=*/ 'device1',
+        /*audioCapability=*/ mojom.AudioOutputCapability.kCapableOfAudioOutput,
+        /*deviceType=*/ mojom.DeviceType.kMouse);
+
+    bluetoothConfig.appendToPairedDeviceList([device1]);
+    await flushAsync();
+
+    assertFalse(!!getChangeMouseSettings());
+    assertFalse(!!getChangeKeyboardSettings());
+
+    const params = new URLSearchParams();
+    params.append('id', '12//345&6789');
+    settings.Router.getInstance().navigateTo(
+        settings.routes.BLUETOOTH_DEVICE_DETAIL, params);
+
+    await flushAsync();
+    assertTrue(!!getChangeMouseSettings());
+    assertFalse(!!getChangeKeyboardSettings());
+    assertEquals(
+        bluetoothDeviceDetailPage.i18n(
+            'bluetoothDeviceDetailChangeDeviceSettingsMouse'),
+        getChangeMouseSettings().label);
+  });
+
   test('Device becomes unavailable while viewing page.', async function() {
     init();
     bluetoothConfig.setBluetoothEnabledState(/*enabled=*/ true);
@@ -146,7 +183,8 @@
         /*publicName=*/ 'BeatsX',
         /*connected=*/ true,
         /*nickname=*/ 'device1',
-        /*audioCapability=*/ mojom.AudioOutputCapability.kCapableOfAudioOutput);
+        /*audioCapability=*/ mojom.AudioOutputCapability.kCapableOfAudioOutput,
+        /*deviceType=*/ mojom.DeviceType.kHeadset);
 
     bluetoothConfig.appendToPairedDeviceList([device1]);
     await flushAsync();
diff --git a/chrome/test/data/webui/settings/chromeos/os_paired_bluetooth_list_item_tests.js b/chrome/test/data/webui/settings/chromeos/os_paired_bluetooth_list_item_tests.js
index 8a712b5..c5225c5d 100644
--- a/chrome/test/data/webui/settings/chromeos/os_paired_bluetooth_list_item_tests.js
+++ b/chrome/test/data/webui/settings/chromeos/os_paired_bluetooth_list_item_tests.js
@@ -50,51 +50,86 @@
         'os-settings:battery-' + batteryIconRange);
   }
 
-  test('Device name, type and battery percentage', async function() {
-    // Device with no nickname, battery info and unknown device type.
-    const publicName = 'BeatsX';
-    const device = createDefaultBluetoothDevice(
-        /*id=*/ '123456789', /*publicName=*/ publicName, /*connected=*/ true);
-    pairedBluetoothListItem.device = device;
-    await flushAsync();
+  test(
+      'Device name, type, battery percentage and a11y labels',
+      async function() {
+        // Device with no nickname, battery info and unknown device type.
+        const publicName = 'BeatsX';
+        const device = createDefaultBluetoothDevice(
+            /*id=*/ '123456789', /*publicName=*/ publicName,
+            /*connected=*/ true);
+        pairedBluetoothListItem.device = device;
 
-    const getDeviceName = () => {
-      return pairedBluetoothListItem.$.deviceName;
-    };
-    const getBatteryPercentage = () => {
-      return pairedBluetoothListItem.shadowRoot.querySelector(
-          '#batteryPercentage');
-    };
-    const getDeviceTypeIcon = () => {
-      return pairedBluetoothListItem.$.deviceTypeIcon;
-    };
-    assertTrue(!!getDeviceName());
-    assertEquals(getDeviceName().innerText, publicName);
-    assertFalse(!!getBatteryPercentage());
-    assertTrue(!!getDeviceTypeIcon());
-    assertEquals(getDeviceTypeIcon().icon, 'os-settings:bluetooth');
+        const itemIndex = 3;
+        const listSize = 15;
+        pairedBluetoothListItem.itemIndex = itemIndex;
+        pairedBluetoothListItem.listSize = listSize;
+        await flushAsync();
 
-    // Set device nickname, type and battery info.
-    const nickname = 'nickname';
-    device.nickname = nickname;
-    device.deviceProperties.deviceType =
-        chromeos.bluetoothConfig.mojom.DeviceType.kComputer;
-    const batteryPercentage = 60;
-    device.deviceProperties.batteryInfo = {
-      defaultProperties: {batteryPercentage: batteryPercentage}
-    };
-    pairedBluetoothListItem.device = {...device};
-    await flushAsync();
+        const getDeviceName = () => {
+          return pairedBluetoothListItem.$.deviceName;
+        };
+        const getBatteryPercentage = () => {
+          return pairedBluetoothListItem.shadowRoot.querySelector(
+              '#batteryPercentage');
+        };
+        const getDeviceTypeIcon = () => {
+          return pairedBluetoothListItem.$.deviceTypeIcon;
+        };
+        const getItemA11yLabel = () => {
+          return pairedBluetoothListItem.shadowRoot.querySelector('.list-item')
+              .ariaLabel;
+        };
+        const getSubpageButtonA11yLabel = () => {
+          return pairedBluetoothListItem.$.subpageButton.ariaLabel;
+        };
+        assertTrue(!!getDeviceName());
+        assertEquals(getDeviceName().innerText, publicName);
+        assertFalse(!!getBatteryPercentage());
+        assertTrue(!!getDeviceTypeIcon());
+        assertEquals(getDeviceTypeIcon().icon, 'os-settings:bluetooth');
+        assertEquals(
+            getItemA11yLabel(),
+            pairedBluetoothListItem.i18n(
+                'bluetoothPairedDeviceItemA11yLabelTypeUnknown', itemIndex + 1,
+                listSize, publicName));
+        assertEquals(
+            getSubpageButtonA11yLabel(),
+            pairedBluetoothListItem.i18n(
+                'bluetoothPairedDeviceItemSubpageButtonA11yLabel', publicName));
 
-    assertTrue(!!getDeviceName());
-    assertEquals(getDeviceName().innerText, nickname);
-    assertTrue(!!getBatteryPercentage());
-    assertEquals(
-        getBatteryPercentage().innerText.trim(),
-        pairedBluetoothListItem.i18n(
-            'bluetoothPairedDeviceItemBatteryPercentage', batteryPercentage));
-    assertEquals(getDeviceTypeIcon().icon, 'os-settings:bluetooth-computer');
-  });
+        // Set device nickname, type and battery info.
+        const nickname = 'nickname';
+        device.nickname = nickname;
+        device.deviceProperties.deviceType =
+            chromeos.bluetoothConfig.mojom.DeviceType.kComputer;
+        const batteryPercentage = 60;
+        device.deviceProperties.batteryInfo = {
+          defaultProperties: {batteryPercentage: batteryPercentage}
+        };
+        pairedBluetoothListItem.device = {...device};
+        await flushAsync();
+
+        assertTrue(!!getDeviceName());
+        assertEquals(getDeviceName().innerText, nickname);
+        assertTrue(!!getBatteryPercentage());
+        assertEquals(
+            getBatteryPercentage().innerText.trim(),
+            pairedBluetoothListItem.i18n(
+                'bluetoothPairedDeviceItemBatteryPercentage',
+                batteryPercentage));
+        assertEquals(
+            getDeviceTypeIcon().icon, 'os-settings:bluetooth-computer');
+        assertEquals(
+            getItemA11yLabel(),
+            pairedBluetoothListItem.i18n(
+                'bluetoothPairedDeviceItemA11yLabelTypeComputerWithBatteryInfo',
+                itemIndex + 1, listSize, nickname, batteryPercentage));
+        assertEquals(
+            getSubpageButtonA11yLabel(),
+            pairedBluetoothListItem.i18n(
+                'bluetoothPairedDeviceItemSubpageButtonA11yLabel', nickname));
+      });
 
   test('Battery icon and color', async function() {
     const device = createDefaultBluetoothDevice(
diff --git a/chrome/updater/activity_impl_win.cc b/chrome/updater/activity_impl_win.cc
index 3ecc1780..37d415e 100644
--- a/chrome/updater/activity_impl_win.cc
+++ b/chrome/updater/activity_impl_win.cc
@@ -26,7 +26,7 @@
 constexpr wchar_t kDidRun[] = L"dr";
 
 std::wstring GetAppClientStateKey(const std::string& id) {
-  return base::ASCIIToWide(base::StrCat({CLIENT_STATE_KEY, id}));
+  return base::StrCat({CLIENT_STATE_KEY, base::ASCIIToWide(id)});
 }
 
 bool GetActiveBitUnderKey(HKEY rootkey, const std::wstring& key_name) {
diff --git a/chrome/updater/activity_impl_win_unittest.cc b/chrome/updater/activity_impl_win_unittest.cc
index 7559cd1..5c65200 100644
--- a/chrome/updater/activity_impl_win_unittest.cc
+++ b/chrome/updater/activity_impl_win_unittest.cc
@@ -4,8 +4,6 @@
 
 #include "chrome/updater/activity_impl.h"
 
-#include <tchar.h>
-
 #include <string>
 #include <tuple>
 
@@ -52,7 +50,7 @@
 constexpr wchar_t kDidRun[] = L"dr";
 constexpr char kAppId[] = "{6ACB7D4D-E5BA-48b0-85FE-A4051500A1BD}";
 constexpr wchar_t kClientStateKeyPath[] =
-    _T(CLIENT_STATE_KEY) L"{6ACB7D4D-E5BA-48b0-85FE-A4051500A1BD}";
+    CLIENT_STATE_KEY L"{6ACB7D4D-E5BA-48b0-85FE-A4051500A1BD}";
 
 DWORD WriteActiveBitAsString(base::win::RegKey& key, bool value) {
   return key.WriteValue(kDidRun, value ? L"1" : L"0");
diff --git a/chrome/updater/test/integration_tests_impl.cc b/chrome/updater/test/integration_tests_impl.cc
index 43ab3af..4df5377 100644
--- a/chrome/updater/test/integration_tests_impl.cc
+++ b/chrome/updater/test/integration_tests_impl.cc
@@ -21,6 +21,7 @@
 #include "base/process/process.h"
 #include "base/run_loop.h"
 #include "base/strings/strcat.h"
+#include "base/synchronization/waitable_event.h"
 #include "base/task/post_task.h"
 #include "base/task/task_traits.h"
 #include "base/task/thread_pool.h"
diff --git a/chrome/updater/test/integration_tests_win.cc b/chrome/updater/test/integration_tests_win.cc
index 24fd2622..cb8c9c7 100644
--- a/chrome/updater/test/integration_tests_win.cc
+++ b/chrome/updater/test/integration_tests_win.cc
@@ -83,7 +83,7 @@
 }
 
 std::wstring GetAppClientStateKey(const std::string& id) {
-  return base::ASCIIToWide(base::StrCat({CLIENT_STATE_KEY, id}));
+  return base::StrCat({CLIENT_STATE_KEY, base::ASCIIToWide(id)});
 }
 
 bool RegKeyExists(HKEY root, REGSAM regsam, const std::wstring& path) {
@@ -139,8 +139,7 @@
 
     ::CloseServiceHandle(service);
   }
-  base::win::RegKey(HKEY_LOCAL_MACHINE, base::ASCIIToWide(UPDATER_KEY).c_str(),
-                    KEY_WRITE)
+  base::win::RegKey(HKEY_LOCAL_MACHINE, UPDATER_KEY, KEY_WRITE)
       .DeleteValue(service_name);
 
   ::CloseServiceHandle(scm);
@@ -151,8 +150,8 @@
 void Clean(UpdaterScope scope) {
   const HKEY root =
       scope == UpdaterScope::kSystem ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER;
-  for (const char* key : {CLIENT_STATE_KEY, CLIENTS_KEY, UPDATER_KEY}) {
-    EXPECT_TRUE(DeleteRegKey(root, KEY_WOW64_32KEY, base::ASCIIToWide(key)));
+  for (const wchar_t* key : {CLIENT_STATE_KEY, CLIENTS_KEY, UPDATER_KEY}) {
+    EXPECT_TRUE(DeleteRegKey(root, KEY_WOW64_32KEY, key));
   }
   for (const wchar_t* key : {kRegKeyCompanyCloudManagement,
                              kRegKeyCompanyEnrollment, UPDATER_POLICIES_KEY}) {
@@ -212,16 +211,15 @@
   ::CloseServiceHandle(scm);
 
   return is_service_gone &&
-         !base::win::RegKey(HKEY_LOCAL_MACHINE,
-                            base::ASCIIToWide(UPDATER_KEY).c_str(), KEY_READ)
+         !base::win::RegKey(HKEY_LOCAL_MACHINE, UPDATER_KEY, KEY_READ)
               .HasValue(service_name);
 }
 
 void ExpectClean(UpdaterScope scope) {
   const HKEY root =
       scope == UpdaterScope::kSystem ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER;
-  for (const char* key : {CLIENT_STATE_KEY, CLIENTS_KEY, UPDATER_KEY}) {
-    EXPECT_FALSE(RegKeyExists(root, KEY_WOW64_32KEY, base::ASCIIToWide(key)));
+  for (const wchar_t* key : {CLIENT_STATE_KEY, CLIENTS_KEY, UPDATER_KEY}) {
+    EXPECT_FALSE(RegKeyExists(root, KEY_WOW64_32KEY, key));
   }
   for (const wchar_t* key : {kRegKeyCompanyCloudManagement,
                              kRegKeyCompanyEnrollment, UPDATER_POLICIES_KEY}) {
diff --git a/chrome/updater/win/installer_api.cc b/chrome/updater/win/installer_api.cc
index 7051c2e..f9dd983 100644
--- a/chrome/updater/win/installer_api.cc
+++ b/chrome/updater/win/installer_api.cc
@@ -35,8 +35,7 @@
   if (!base::UTF8ToWide(app_id.c_str(), app_id.size(), &subkey))
     return absl::nullopt;
   regsam = regsam | KEY_WOW64_32KEY;
-  base::win::RegKey key(HKEY_CURRENT_USER,
-                        base::ASCIIToWide(CLIENT_STATE_KEY).c_str(), regsam);
+  base::win::RegKey key(HKEY_CURRENT_USER, CLIENT_STATE_KEY, regsam);
   if (key.OpenKey(subkey.c_str(), regsam) != ERROR_SUCCESS)
     return absl::nullopt;
   return key;
@@ -52,8 +51,7 @@
   if (!base::UTF8ToWide(app_id.c_str(), app_id.size(), &subkey))
     return absl::nullopt;
   regsam = regsam | KEY_WOW64_32KEY;
-  base::win::RegKey key(HKEY_CURRENT_USER,
-                        base::ASCIIToWide(CLIENT_STATE_KEY).c_str(), regsam);
+  base::win::RegKey key(HKEY_CURRENT_USER, CLIENT_STATE_KEY, regsam);
   if (key.CreateKey(subkey.c_str(), regsam) != ERROR_SUCCESS)
     return absl::nullopt;
   return key;
@@ -70,8 +68,7 @@
   if (!base::UTF8ToWide(app_id.c_str(), app_id.size(), &subkey))
     return false;
   constexpr REGSAM kRegSam = KEY_WRITE | KEY_WOW64_32KEY;
-  base::win::RegKey key(HKEY_CURRENT_USER,
-                        base::ASCIIToWide(CLIENT_STATE_KEY).c_str(), kRegSam);
+  base::win::RegKey key(HKEY_CURRENT_USER, CLIENT_STATE_KEY, kRegSam);
   return key.DeleteKey(subkey.c_str()) == ERROR_SUCCESS;
 }
 
diff --git a/chrome/updater/win/setup/setup_util.cc b/chrome/updater/win/setup/setup_util.cc
index 73a438d..1fc19745 100644
--- a/chrome/updater/win/setup/setup_util.cc
+++ b/chrome/updater/win/setup/setup_util.cc
@@ -220,7 +220,7 @@
   list->AddWorkItem(new installer::InstallServiceWorkItem(
       GetServiceName(internal_service).c_str(),
       GetServiceDisplayName(internal_service).c_str(), com_service_command,
-      base::ASCIIToWide(UPDATER_KEY),
+      UPDATER_KEY,
       internal_service ? GetSideBySideServers(UpdaterScope::kSystem)
                        : GetActiveServers(UpdaterScope::kSystem),
       {}));
diff --git a/chrome/updater/win/setup/uninstall.cc b/chrome/updater/win/setup/uninstall.cc
index 79d181d..c545543 100644
--- a/chrome/updater/win/setup/uninstall.cc
+++ b/chrome/updater/win/setup/uninstall.cc
@@ -17,7 +17,6 @@
 #include "base/process/launch.h"
 #include "base/process/process.h"
 #include "base/strings/stringprintf.h"
-#include "base/strings/utf_string_conversions.h"
 #include "base/win/scoped_com_initializer.h"
 #include "chrome/installer/util/install_service_work_item.h"
 #include "chrome/installer/util/install_util.h"
@@ -61,7 +60,7 @@
   for (const bool is_internal_service : {true, false}) {
     const std::wstring service_name = GetServiceName(is_internal_service);
     if (!installer::InstallServiceWorkItem::DeleteService(
-            service_name.c_str(), base::ASCIIToWide(UPDATER_KEY), {}, {})) {
+            service_name.c_str(), UPDATER_KEY, {}, {})) {
       LOG(WARNING) << "DeleteService [" << service_name << "] failed.";
     }
   }
@@ -139,7 +138,7 @@
   updater::UnregisterWakeTask();
 
   std::unique_ptr<WorkItemList> uninstall_list(WorkItem::CreateWorkItemList());
-  uninstall_list->AddDeleteRegKeyWorkItem(key, base::ASCIIToWide(UPDATER_KEY),
+  uninstall_list->AddDeleteRegKeyWorkItem(key, UPDATER_KEY,
                                           WorkItem::kWow64Default);
   if (!uninstall_list->Do()) {
     LOG(ERROR) << "Failed to delete the registry keys.";
diff --git a/chrome/updater/win/win_constants.h b/chrome/updater/win/win_constants.h
index 295b2d6..a92fe19 100644
--- a/chrome/updater/win/win_constants.h
+++ b/chrome/updater/win/win_constants.h
@@ -19,13 +19,13 @@
 extern const wchar_t kPrefsAccessMutex[];
 
 // Registry keys and value names.
-#define COMPANY_KEY "Software\\" COMPANY_SHORTNAME_STRING "\\"
+#define COMPANY_KEY L"Software\\" COMPANY_SHORTNAME_STRING L"\\"
 
 // Use |Update| instead of PRODUCT_FULLNAME_STRING for the registry key name
 // to be backward compatible with Google Update / Omaha.
-#define UPDATER_KEY COMPANY_KEY "Update\\"
-#define CLIENTS_KEY UPDATER_KEY "Clients\\"
-#define CLIENT_STATE_KEY UPDATER_KEY "ClientState\\"
+#define UPDATER_KEY COMPANY_KEY L"Update\\"
+#define CLIENTS_KEY UPDATER_KEY L"Clients\\"
+#define CLIENT_STATE_KEY UPDATER_KEY L"ClientState\\"
 
 #define COMPANY_POLICIES_KEY \
   L"Software\\Policies\\" COMPANY_SHORTNAME_STRING L"\\"
diff --git a/chrome/updater/win/win_util.cc b/chrome/updater/win/win_util.cc
index 5fbc7c5..1a134b0 100644
--- a/chrome/updater/win/win_util.cc
+++ b/chrome/updater/win/win_util.cc
@@ -412,11 +412,11 @@
 }
 
 std::wstring GetRegistryKeyClientsUpdater() {
-  return base::ASCIIToWide(base::StrCat({CLIENTS_KEY, kUpdaterAppId}));
+  return base::StrCat({CLIENTS_KEY, base::ASCIIToWide(kUpdaterAppId)});
 }
 
 std::wstring GetRegistryKeyClientStateUpdater() {
-  return base::ASCIIToWide(base::StrCat({CLIENT_STATE_KEY, kUpdaterAppId}));
+  return base::StrCat({CLIENT_STATE_KEY, base::ASCIIToWide(kUpdaterAppId)});
 }
 
 int GetDownloadProgress(int64_t downloaded_bytes, int64_t total_bytes) {
diff --git a/chromecast/browser/cast_media_blocker_unittest.cc b/chromecast/browser/cast_media_blocker_unittest.cc
index 5f5b33fe..47e6fc9 100644
--- a/chromecast/browser/cast_media_blocker_unittest.cc
+++ b/chromecast/browser/cast_media_blocker_unittest.cc
@@ -61,6 +61,7 @@
   MOCK_METHOD0(ToggleCamera, void());
   MOCK_METHOD0(HangUp, void());
   MOCK_METHOD0(Raise, void());
+  MOCK_METHOD1(SetMute, void(bool));
 
  private:
   DISALLOW_COPY_AND_ASSIGN(MockMediaSession);
diff --git a/chromeos/components/multidevice/logging/log_buffer.cc b/chromeos/components/multidevice/logging/log_buffer.cc
index 5d5f93f..1896380 100644
--- a/chromeos/components/multidevice/logging/log_buffer.cc
+++ b/chromeos/components/multidevice/logging/log_buffer.cc
@@ -4,7 +4,7 @@
 
 #include "chromeos/components/multidevice/logging/log_buffer.h"
 
-#include "base/lazy_instance.h"
+#include "base/no_destructor.h"
 
 namespace chromeos {
 
@@ -15,9 +15,6 @@
 // The maximum number of logs that can be stored in the buffer.
 const size_t kMaxBufferSize = 1000;
 
-// The global instance returned by LogBuffer::GetInstance().
-base::LazyInstance<LogBuffer>::Leaky g_log_buffer = LAZY_INSTANCE_INITIALIZER;
-
 }  // namespace
 
 LogBuffer::LogMessage::LogMessage(const std::string& text,
@@ -33,7 +30,8 @@
 
 // static
 LogBuffer* LogBuffer::GetInstance() {
-  return &g_log_buffer.Get();
+  static base::NoDestructor<LogBuffer> log_buffer;
+  return log_buffer.get();
 }
 
 void LogBuffer::AddObserver(Observer* observer) {
diff --git a/chromeos/components/personalization_app/resources/common/styles.js b/chromeos/components/personalization_app/resources/common/styles.js
index b5c2c79c..4098920 100644
--- a/chromeos/components/personalization_app/resources/common/styles.js
+++ b/chromeos/components/personalization_app/resources/common/styles.js
@@ -200,6 +200,7 @@
     }
     .photo-empty .photo-text-container > p {
       color: var(--cros-button-label-color-secondary);
+      text-shadow: none;
     }
     .photo-gradient-mask {
       border-radius: 12px;
diff --git a/chromeos/metrics/login_event_recorder.cc b/chromeos/metrics/login_event_recorder.cc
index 0ad641e1..13a7d95 100644
--- a/chromeos/metrics/login_event_recorder.cc
+++ b/chromeos/metrics/login_event_recorder.cc
@@ -14,6 +14,7 @@
 #include "base/logging.h"
 #include "base/metrics/histogram_macros.h"
 #include "base/sequenced_task_runner.h"
+#include "base/strings/string_number_conversions.h"
 #include "base/strings/stringprintf.h"
 #include "base/system/sys_info.h"
 #include "base/task/current_thread.h"
diff --git a/chromeos/profiles/orderfile.newest.txt b/chromeos/profiles/orderfile.newest.txt
index 8e096538..5afd77d 100644
--- a/chromeos/profiles/orderfile.newest.txt
+++ b/chromeos/profiles/orderfile.newest.txt
@@ -1 +1 @@
-chromeos-chrome-orderfile-field-93-4577.22-1628502876-benchmark-93.0.4577.47-r1.orderfile.xz
+chromeos-chrome-orderfile-field-93-4577.36-1629108064-benchmark-93.0.4577.48-r1.orderfile.xz
diff --git a/chromeos/services/assistant/media_host_unittest.cc b/chromeos/services/assistant/media_host_unittest.cc
index ae4b071..fe3eab59 100644
--- a/chromeos/services/assistant/media_host_unittest.cc
+++ b/chromeos/services/assistant/media_host_unittest.cc
@@ -100,6 +100,7 @@
   MOCK_METHOD(void, ToggleCamera, ());
   MOCK_METHOD(void, HangUp, ());
   MOCK_METHOD(void, Raise, ());
+  MOCK_METHOD(void, SetMute, (bool mute));
   void AddObserver(
       mojo::PendingRemote<media_session::mojom::MediaControllerObserver> remote)
       override {
diff --git a/chromeos/services/assistant/media_session/assistant_media_session.h b/chromeos/services/assistant/media_session/assistant_media_session.h
index 6025d21..954f2f37 100644
--- a/chromeos/services/assistant/media_session/assistant_media_session.h
+++ b/chromeos/services/assistant/media_session/assistant_media_session.h
@@ -58,6 +58,7 @@
   void ToggleCamera() override {}
   void HangUp() override {}
   void Raise() override {}
+  void SetMute(bool mute) override {}
 
   // Requests/abandons audio focus to the AudioFocusManager.
   void RequestAudioFocus(media_session::mojom::AudioFocusType audio_focus_type);
diff --git a/components/arc/session/arc_container_client_adapter.cc b/components/arc/session/arc_container_client_adapter.cc
index 1981642..27015fff 100644
--- a/components/arc/session/arc_container_client_adapter.cc
+++ b/components/arc/session/arc_container_client_adapter.cc
@@ -16,7 +16,6 @@
 #include "chromeos/dbus/dbus_method_call_status.h"
 #include "chromeos/dbus/login_manager/arc.pb.h"
 #include "chromeos/dbus/session_manager/session_manager_client.h"
-#include "components/arc/arc_util.h"
 #include "components/arc/session/arc_session.h"
 
 namespace arc {
@@ -125,6 +124,7 @@
     request.set_disable_media_store_maintenance(
         params.disable_media_store_maintenance);
     request.set_disable_download_provider(params.disable_download_provider);
+    request.set_disable_ureadahead(params.disable_ureadahead);
     request.set_arc_generate_pai(params.arc_generate_play_auto_install);
 
     switch (params.usap_profile) {
@@ -137,10 +137,6 @@
         break;
     }
 
-    // TODO(b:196390269): Enable handling after arc.proto is uprev-ed.
-    if (IsUreadaheadDisabled())
-      LOG(ERROR) << "Disabling ureadahead is not supported in container yet.";
-
     chromeos::SessionManagerClient::Get()->StartArcMiniContainer(
         request, std::move(callback));
   }
diff --git a/components/arc/session/arc_container_client_adapter_unittest.cc b/components/arc/session/arc_container_client_adapter_unittest.cc
index 5195ff1..7ddbeea 100644
--- a/components/arc/session/arc_container_client_adapter_unittest.cc
+++ b/components/arc/session/arc_container_client_adapter_unittest.cc
@@ -154,6 +154,27 @@
   EXPECT_TRUE(request.disable_download_provider());
 }
 
+TEST_F(ArcContainerClientAdapterTest, StartArc_UreadaheadByDefault) {
+  StartParams start_params;
+  client_adapter()->StartMiniArc(std::move(start_params),
+                                 base::BindOnce(&OnMiniInstanceStarted));
+  const auto& request = chromeos::FakeSessionManagerClient::Get()
+                            ->last_start_arc_mini_container_request();
+  EXPECT_TRUE(request.has_disable_ureadahead());
+  EXPECT_FALSE(request.disable_ureadahead());
+}
+
+TEST_F(ArcContainerClientAdapterTest, StartArc_DisableUreadahead) {
+  StartParams start_params;
+  start_params.disable_ureadahead = true;
+  client_adapter()->StartMiniArc(std::move(start_params),
+                                 base::BindOnce(&OnMiniInstanceStarted));
+  const auto& request = chromeos::FakeSessionManagerClient::Get()
+                            ->last_start_arc_mini_container_request();
+  EXPECT_TRUE(request.has_disable_ureadahead());
+  EXPECT_TRUE(request.disable_ureadahead());
+}
+
 struct DalvikMemoryProfileTestParam {
   // Requested profile.
   StartParams::DalvikMemoryProfile profile;
diff --git a/components/autofill/android/BUILD.gn b/components/autofill/android/BUILD.gn
index a8dd599..054c13c5 100644
--- a/components/autofill/android/BUILD.gn
+++ b/components/autofill/android/BUILD.gn
@@ -36,6 +36,13 @@
   ]
 }
 
+java_cpp_features("java_features_srcjar") {
+  visibility = [ ":*" ]
+  sources = [ "//components/autofill/core/common/autofill_features.cc" ]
+  template =
+      "//components/autofill/android/java_templates/AutofillFeatures.java.tmpl"
+}
+
 android_library("full_autofill_java") {
   deps = [
     ":autofill_java_resources",
@@ -59,6 +66,11 @@
   resources_package = "org.chromium.components.autofill"
 }
 
+# A minimal library used to expose autofill features to webview.
+android_library("autofill_features_java") {
+  srcjar_deps = [ ":java_features_srcjar" ]
+}
+
 # A library containing the minimal deps for payments, so that ui_java_resources
 # doesn't have to be pulled in.
 android_library("payments_autofill_java") {
diff --git a/components/autofill/android/java_templates/AutofillFeatures.java.tmpl b/components/autofill/android/java_templates/AutofillFeatures.java.tmpl
new file mode 100644
index 0000000..cddfb80
--- /dev/null
+++ b/components/autofill/android/java_templates/AutofillFeatures.java.tmpl
@@ -0,0 +1,16 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.components.autofill;
+
+/**
+ * Contains features that are specific to the autofill component.
+ */
+public final class AutofillFeatures {{
+
+{NATIVE_FEATURES}
+
+    // Prevents instantiation.
+    private AutofillFeatures() {{}}
+}}
diff --git a/components/autofill/core/browser/payments/credit_card_access_manager_unittest.cc b/components/autofill/core/browser/payments/credit_card_access_manager_unittest.cc
index 9001db7f..0083aa0 100644
--- a/components/autofill/core/browser/payments/credit_card_access_manager_unittest.cc
+++ b/components/autofill/core/browser/payments/credit_card_access_manager_unittest.cc
@@ -75,6 +75,7 @@
 #endif
 
 using base::ASCIIToUTF16;
+using testing::NiceMock;
 
 namespace autofill {
 namespace {
@@ -174,7 +175,7 @@
                                 /*is_off_the_record=*/false);
     personal_data_manager_.SetPrefService(autofill_client_.GetPrefs());
     autocomplete_history_manager_ =
-        std::make_unique<MockAutocompleteHistoryManager>();
+        std::make_unique<NiceMock<MockAutocompleteHistoryManager>>();
 
     accessor_ = std::make_unique<TestAccessor>();
     autofill_driver_ = std::make_unique<TestAutofillDriver>();
@@ -1035,11 +1036,11 @@
       histogram_tester.ExpectTotalCount(
           "Autofill.BetterAuth.UserPerceivedLatencyOnCardSelection.OptedIn."
           "Duration",
-          int(user_is_opted_in));
+          static_cast<int>(user_is_opted_in));
       histogram_tester.ExpectTotalCount(
           "Autofill.BetterAuth.UserPerceivedLatencyOnCardSelection.OptedIn."
           "TimedOutCvcFallback",
-          int(user_is_opted_in));
+          static_cast<int>(user_is_opted_in));
     }
 
     {
diff --git a/components/autofill/core/browser/payments/credit_card_save_manager_unittest.cc b/components/autofill/core/browser/payments/credit_card_save_manager_unittest.cc
index 4531089..2f9aae25 100644
--- a/components/autofill/core/browser/payments/credit_card_save_manager_unittest.cc
+++ b/components/autofill/core/browser/payments/credit_card_save_manager_unittest.cc
@@ -62,6 +62,7 @@
 using base::ASCIIToUTF16;
 using testing::_;
 using testing::AtLeast;
+using testing::NiceMock;
 using testing::Return;
 using testing::SaveArg;
 using testing::UnorderedElementsAre;
@@ -345,7 +346,7 @@
   std::unique_ptr<TestBrowserAutofillManager> browser_autofill_manager_;
   scoped_refptr<AutofillWebDataService> database_;
   MockPersonalDataManager personal_data_;
-  MockAutocompleteHistoryManager autocomplete_history_manager_;
+  NiceMock<MockAutocompleteHistoryManager> autocomplete_history_manager_;
   syncer::TestSyncService sync_service_;
   // Ends up getting owned (and destroyed) by TestFormDataImporter:
   TestCreditCardSaveManager* credit_card_save_manager_;
diff --git a/components/autofill/core/browser/payments/full_card_request_unittest.cc b/components/autofill/core/browser/payments/full_card_request_unittest.cc
index 248743f..3824e0cb 100644
--- a/components/autofill/core/browser/payments/full_card_request_unittest.cc
+++ b/components/autofill/core/browser/payments/full_card_request_unittest.cc
@@ -5,7 +5,6 @@
 #include "components/autofill/core/browser/payments/full_card_request.h"
 
 #include "base/command_line.h"
-#include "base/macros.h"
 #include "base/memory/weak_ptr.h"
 #include "base/strings/stringprintf.h"
 #include "base/test/scoped_feature_list.h"
@@ -34,6 +33,7 @@
 namespace payments {
 
 using testing::_;
+using testing::NiceMock;
 
 // The consumer of the full card request API.
 class MockResultDelegate : public FullCardRequest::ResultDelegate,
@@ -114,7 +114,10 @@
         "sync-url", "https://google.com");
   }
 
-  ~FullCardRequestTest() override {}
+  FullCardRequestTest(const FullCardRequestTest&) = delete;
+  FullCardRequestTest& operator=(const FullCardRequestTest&) = delete;
+
+  ~FullCardRequestTest() override = default;
 
   MockPersonalDataManager* personal_data() { return &personal_data_; }
 
@@ -155,7 +158,7 @@
   base::test::SingleThreadTaskEnvironment task_environment_;
   variations::ScopedVariationsIdsProvider scoped_variations_ids_provider_{
       variations::VariationsIdsProvider::Mode::kUseSignedInState};
-  MockPersonalDataManager personal_data_;
+  NiceMock<MockPersonalDataManager> personal_data_;
   MockResultDelegate result_delegate_;
   MockUIDelegate ui_delegate_;
   TestAutofillClient autofill_client_;
@@ -164,8 +167,6 @@
   scoped_refptr<network::SharedURLLoaderFactory> test_shared_loader_factory_;
   std::unique_ptr<PaymentsClient> payments_client_;
   std::unique_ptr<FullCardRequest> request_;
-
-  DISALLOW_COPY_AND_ASSIGN(FullCardRequestTest);
 };
 
 // Matches the |arg| credit card to the given |record_type| and |card_number|.
diff --git a/components/autofill/core/browser/payments/local_card_migration_manager_unittest.cc b/components/autofill/core/browser/payments/local_card_migration_manager_unittest.cc
index 5bf60a7..623d861 100644
--- a/components/autofill/core/browser/payments/local_card_migration_manager_unittest.cc
+++ b/components/autofill/core/browser/payments/local_card_migration_manager_unittest.cc
@@ -58,6 +58,7 @@
 
 using base::ASCIIToUTF16;
 using testing::_;
+using testing::NiceMock;
 
 namespace autofill {
 
@@ -320,7 +321,7 @@
   std::unique_ptr<TestAutofillDriver> autofill_driver_;
   std::unique_ptr<TestBrowserAutofillManager> browser_autofill_manager_;
   TestPersonalDataManager personal_data_;
-  MockAutocompleteHistoryManager autocomplete_history_manager_;
+  NiceMock<MockAutocompleteHistoryManager> autocomplete_history_manager_;
   syncer::TestSyncService sync_service_;
   base::test::ScopedFeatureList scoped_feature_list_;
   // Ends up getting owned (and destroyed) by TestAutofillClient:
diff --git a/components/autofill_assistant/browser/metrics.cc b/components/autofill_assistant/browser/metrics.cc
index a4c2f9a3..c50d788 100644
--- a/components/autofill_assistant/browser/metrics.cc
+++ b/components/autofill_assistant/browser/metrics.cc
@@ -223,4 +223,14 @@
   base::UmaHistogramEnumeration(kFeatureModuleInstallationEnumName, event);
 }
 
+// static
+void Metrics::RecordTriggerConditionEvaluationTime(
+    ukm::UkmRecorder* ukm_recorder,
+    ukm::SourceId source_id,
+    base::TimeDelta evaluation_time) {
+  ukm::builders::AutofillAssistant_Timing(source_id)
+      .SetTriggerConditionEvaluationMs(evaluation_time.InMilliseconds())
+      .Record(ukm_recorder);
+}
+
 }  // namespace autofill_assistant
diff --git a/components/autofill_assistant/browser/metrics.h b/components/autofill_assistant/browser/metrics.h
index 4c7c30c..bcd0b49 100644
--- a/components/autofill_assistant/browser/metrics.h
+++ b/components/autofill_assistant/browser/metrics.h
@@ -6,6 +6,7 @@
 #define COMPONENTS_AUTOFILL_ASSISTANT_BROWSER_METRICS_H_
 
 #include <ostream>
+#include "base/time/time.h"
 #include "components/autofill_assistant/browser/service.pb.h"
 #include "components/autofill_assistant/browser/startup_util.h"
 #include "services/metrics/public/cpp/ukm_recorder.h"
@@ -402,8 +403,13 @@
                                           InChromeTriggerAction event);
   static void RecordOnboardingResult(OnBoarding event);
   static void RecordFeatureModuleInstallation(FeatureModuleInstallation event);
+  static void RecordTriggerConditionEvaluationTime(
+      ukm::UkmRecorder* ukm_recorder,
+      ukm::SourceId source_id,
+      base::TimeDelta evaluation_time);
 
-  // Intended for debugging: writes string representation of |reason| to |out|.
+  // Intended for debugging: writes string representation of |reason| to
+  // |out|.
   friend std::ostream& operator<<(std::ostream& out,
                                   const DropOutReason& reason) {
 #ifdef NDEBUG
diff --git a/components/autofill_assistant/browser/trigger_scripts/trigger_script_coordinator.cc b/components/autofill_assistant/browser/trigger_scripts/trigger_script_coordinator.cc
index 6092b6e..da5a18b 100644
--- a/components/autofill_assistant/browser/trigger_scripts/trigger_script_coordinator.cc
+++ b/components/autofill_assistant/browser/trigger_scripts/trigger_script_coordinator.cc
@@ -467,7 +467,8 @@
       base::BindOnce(
           &TriggerScriptCoordinator::OnDynamicTriggerConditionsEvaluated,
           weak_ptr_factory_.GetWeakPtr(),
-          /* is_out_of_schedule = */ false));
+          /* is_out_of_schedule = */ false,
+          /* start_time = */ base::TimeTicks::Now()));
 }
 
 void TriggerScriptCoordinator::StopCheckingTriggerConditions() {
@@ -510,7 +511,8 @@
 }
 
 void TriggerScriptCoordinator::OnDynamicTriggerConditionsEvaluated(
-    bool is_out_of_schedule) {
+    bool is_out_of_schedule,
+    absl::optional<base::TimeTicks> start_time) {
   if (!web_contents_visible_ || !is_checking_trigger_conditions_) {
     return;
   }
@@ -520,6 +522,11 @@
     return;
   }
 
+  if (start_time.has_value()) {
+    Metrics::RecordTriggerConditionEvaluationTime(
+        ukm_recorder_, ukm_source_id_, base::TimeTicks::Now() - *start_time);
+  }
+
   VLOG(3) << "Evaluating trigger conditions...";
   std::vector<bool> evaluated_trigger_conditions;
   for (const auto& trigger_script : trigger_scripts_) {
@@ -593,7 +600,8 @@
 }
 
 void TriggerScriptCoordinator::RunOutOfScheduleTriggerConditionCheck() {
-  OnDynamicTriggerConditionsEvaluated(/* is_out_of_schedule = */ true);
+  OnDynamicTriggerConditionsEvaluated(/* is_out_of_schedule = */ true,
+                                      /* start_time = */ absl::nullopt);
 }
 
 void TriggerScriptCoordinator::RunCallback(
diff --git a/components/autofill_assistant/browser/trigger_scripts/trigger_script_coordinator.h b/components/autofill_assistant/browser/trigger_scripts/trigger_script_coordinator.h
index 65784bc..92c6bc5 100644
--- a/components/autofill_assistant/browser/trigger_scripts/trigger_script_coordinator.h
+++ b/components/autofill_assistant/browser/trigger_scripts/trigger_script_coordinator.h
@@ -136,7 +136,9 @@
   void ShowTriggerScript(int index);
   void HideTriggerScript();
   void CheckDynamicTriggerConditions();
-  void OnDynamicTriggerConditionsEvaluated(bool is_out_of_schedule);
+  void OnDynamicTriggerConditionsEvaluated(
+      bool is_out_of_schedule,
+      absl::optional<base::TimeTicks> start_time);
   void OnGetTriggerScripts(int http_status, const std::string& response);
   GURL GetCurrentURL() const;
   void OnEffectiveVisibilityChanged();
diff --git a/components/autofill_assistant/browser/trigger_scripts/trigger_script_coordinator_unittest.cc b/components/autofill_assistant/browser/trigger_scripts/trigger_script_coordinator_unittest.cc
index 59e65d3..ccd2cc6 100644
--- a/components/autofill_assistant/browser/trigger_scripts/trigger_script_coordinator_unittest.cc
+++ b/components/autofill_assistant/browser/trigger_scripts/trigger_script_coordinator_unittest.cc
@@ -42,6 +42,7 @@
 using ::testing::Return;
 using ::testing::UnorderedElementsAre;
 using ::testing::UnorderedElementsAreArray;
+using ::testing::WithArg;
 
 std::unique_ptr<base::test::ScopedFeatureList> CreateScopedFeatureList(
     bool dialog_onboarding) {
@@ -1415,4 +1416,43 @@
                      TriggerScriptProto::UNSPECIFIED_TRIGGER_UI_TYPE}}})));
 }
 
+TEST_F(TriggerScriptCoordinatorTest, RecordTriggerConditionEvaluationTime) {
+  GetTriggerScriptsResponseProto response;
+  response.set_trigger_condition_check_interval_ms(1000);
+  response.add_trigger_scripts();
+  std::string serialized_response;
+  response.SerializeToString(&serialized_response);
+
+  EXPECT_CALL(*mock_request_sender_, OnSendRequest(GURL(kFakeServerUrl), _, _))
+      .WillOnce(RunOnceCallback<2>(net::HTTP_OK, serialized_response));
+
+  EXPECT_CALL(*mock_dynamic_trigger_conditions_, OnUpdate)
+      .WillOnce(WithArg<1>([&](auto& callback) {
+        task_environment()->FastForwardBy(
+            base::TimeDelta::FromMilliseconds(700));
+        std::move(callback).Run();
+      }))
+      .WillOnce(WithArg<1>([&](auto& callback) {
+        task_environment()->FastForwardBy(
+            base::TimeDelta::FromMilliseconds(300));
+        std::move(callback).Run();
+      }));
+
+  // Start will immediately run the first trigger condition evaluation.
+  coordinator_->Start(GURL(kFakeDeepLink), std::make_unique<TriggerContext>(),
+                      mock_callback_.Get());
+
+  // Run the second scheduled trigger condition evaluation.
+  task_environment()->FastForwardBy(base::TimeDelta::FromSeconds(1));
+
+  // Run out-of-schedule trigger condition evaluation (should not be recorded).
+  coordinator_->OnKeyboardVisibilityChanged(true);
+
+  EXPECT_THAT(
+      GetUkmTriggerConditionEvaluationTime(ukm_recorder_),
+      ElementsAreArray(std::vector<ukm::TestUkmRecorder::HumanReadableUkmEntry>{
+          {navigation_ids_[0], {{kTriggerConditionTimingMs, 700}}},
+          {navigation_ids_[0], {{kTriggerConditionTimingMs, 300}}}}));
+}
+
 }  // namespace autofill_assistant
diff --git a/components/autofill_assistant/browser/ukm_test_util.cc b/components/autofill_assistant/browser/ukm_test_util.cc
index 411a9a90..108ef20 100644
--- a/components/autofill_assistant/browser/ukm_test_util.cc
+++ b/components/autofill_assistant/browser/ukm_test_util.cc
@@ -37,6 +37,13 @@
                                  {kInChromeTriggerAction});
 }
 
+std::vector<ukm::TestUkmRecorder::HumanReadableUkmEntry>
+GetUkmTriggerConditionEvaluationTime(
+    ukm::TestAutoSetUkmRecorder& ukm_recorder) {
+  return ukm_recorder.GetEntries(kAutofillAssistantTimingEntry,
+                                 {kTriggerConditionTimingMs});
+}
+
 std::vector<ukm::TestUkmRecorder::HumanReadableUkmEntry> ToHumanReadableMetrics(
     const std::vector<std::pair<ukm::SourceId, std::vector<UkmEnumVariant>>>&
         input) {
diff --git a/components/autofill_assistant/browser/ukm_test_util.h b/components/autofill_assistant/browser/ukm_test_util.h
index 5e21ee8..b19c5e95 100644
--- a/components/autofill_assistant/browser/ukm_test_util.h
+++ b/components/autofill_assistant/browser/ukm_test_util.h
@@ -27,6 +27,7 @@
 const char kTriggerScriptOnboardingEntry[] =
     "AutofillAssistant.LiteScriptOnboarding";
 const char kInChromeTriggeringEntry[] = "AutofillAssistant.InChromeTriggering";
+const char kAutofillAssistantTimingEntry[] = "AutofillAssistant.Timing";
 
 // The identifiers for all UKM metrics that we currently record/test.
 const char kTriggerUiType[] = "TriggerUIType";
@@ -35,6 +36,7 @@
 const char kTriggerScriptFinished[] = "LiteScriptFinished";
 const char kTriggerScriptOnboarding[] = "LiteScriptOnboarding";
 const char kInChromeTriggerAction[] = "InChromeTriggerAction";
+const char kTriggerConditionTimingMs[] = "TriggerConditionEvaluationMs";
 
 // Convenience accessors for UKM metrics.
 std::vector<ukm::TestUkmRecorder::HumanReadableUkmEntry>
@@ -47,6 +49,8 @@
 GetUkmTriggerScriptOnboarding(ukm::TestAutoSetUkmRecorder& ukm_recorder);
 std::vector<ukm::TestUkmRecorder::HumanReadableUkmEntry>
 GetUkmInChromeTriggering(ukm::TestAutoSetUkmRecorder& ukm_recorder);
+std::vector<ukm::TestUkmRecorder::HumanReadableUkmEntry>
+GetUkmTriggerConditionEvaluationTime(ukm::TestAutoSetUkmRecorder& ukm_recorder);
 
 // Variant containing all UKM enums that we currently record/test.
 // NOTE: When adding entries, remember to also modify kUkmEnumMetricNames!
diff --git a/components/browser_ui/modaldialog/android/java/src/org/chromium/components/browser_ui/modaldialog/ModalDialogView.java b/components/browser_ui/modaldialog/android/java/src/org/chromium/components/browser_ui/modaldialog/ModalDialogView.java
index d523482..b671f22a 100644
--- a/components/browser_ui/modaldialog/android/java/src/org/chromium/components/browser_ui/modaldialog/ModalDialogView.java
+++ b/components/browser_ui/modaldialog/android/java/src/org/chromium/components/browser_ui/modaldialog/ModalDialogView.java
@@ -8,6 +8,7 @@
 import android.graphics.drawable.Drawable;
 import android.os.Build;
 import android.text.TextUtils;
+import android.text.method.LinkMovementMethod;
 import android.util.AttributeSet;
 import android.view.MotionEvent;
 import android.view.View;
@@ -89,6 +90,7 @@
 
         mPositiveButton.setOnClickListener(this);
         mNegativeButton.setOnClickListener(this);
+        mMessageView.setMovementMethod(LinkMovementMethod.getInstance());
         updateContentVisibility();
         updateButtonVisibility();
 
@@ -247,7 +249,7 @@
     }
 
     /** @param message The message in the dialog content. */
-    void setMessage(String message) {
+    void setMessage(CharSequence message) {
         mMessageView.setText(message);
         updateContentVisibility();
     }
diff --git a/components/browser_ui/modaldialog/android/java/src/org/chromium/components/browser_ui/modaldialog/ModalDialogViewTest.java b/components/browser_ui/modaldialog/android/java/src/org/chromium/components/browser_ui/modaldialog/ModalDialogViewTest.java
index 0b0b703..9c79f57a6 100644
--- a/components/browser_ui/modaldialog/android/java/src/org/chromium/components/browser_ui/modaldialog/ModalDialogViewTest.java
+++ b/components/browser_ui/modaldialog/android/java/src/org/chromium/components/browser_ui/modaldialog/ModalDialogViewTest.java
@@ -21,6 +21,9 @@
 
 import android.app.Activity;
 import android.content.res.Resources;
+import android.text.Spannable;
+import android.text.SpannableStringBuilder;
+import android.text.style.ForegroundColorSpan;
 import android.view.ContextThemeWrapper;
 import android.view.LayoutInflater;
 import android.view.View;
@@ -199,8 +202,8 @@
     @Feature({"ModalDialog"})
     public void testMessage() {
         // Verify that the message set from builder is displayed.
-        PropertyModel model = createModel(
-                mModelBuilder.with(ModalDialogProperties.MESSAGE, sResources, R.string.more));
+        String msg = sResources.getString(R.string.more);
+        PropertyModel model = createModel(mModelBuilder.with(ModalDialogProperties.MESSAGE, msg));
         onView(withId(R.id.title_container)).check(matches(not(isDisplayed())));
         onView(withId(R.id.scrollable_title_container)).check(matches(not(isDisplayed())));
         onView(withId(R.id.modal_dialog_scroll_view)).check(matches(isDisplayed()));
@@ -212,6 +215,15 @@
         onView(withId(R.id.scrollable_title_container)).check(matches(not(isDisplayed())));
         onView(withId(R.id.modal_dialog_scroll_view)).check(matches(not(isDisplayed())));
         onView(withId(R.id.message)).check(matches(not(isDisplayed())));
+
+        // Use CharSequence for the message.
+        SpannableStringBuilder sb = new SpannableStringBuilder(msg);
+        sb.setSpan(new ForegroundColorSpan(0xffff0000), 0, 1, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
+        TestThreadUtils.runOnUiThreadBlocking(() -> model.set(ModalDialogProperties.MESSAGE, sb));
+        onView(withId(R.id.title_container)).check(matches(not(isDisplayed())));
+        onView(withId(R.id.scrollable_title_container)).check(matches(not(isDisplayed())));
+        onView(withId(R.id.modal_dialog_scroll_view)).check(matches(isDisplayed()));
+        onView(withId(R.id.message)).check(matches(allOf(isDisplayed(), withText(R.string.more))));
     }
 
     @Test
diff --git a/components/cast_streaming/browser/cast_message_port_impl.cc b/components/cast_streaming/browser/cast_message_port_impl.cc
index c9cbd3b7..ef443285 100644
--- a/components/cast_streaming/browser/cast_message_port_impl.cc
+++ b/components/cast_streaming/browser/cast_message_port_impl.cc
@@ -15,6 +15,42 @@
 
 namespace cast_streaming {
 
+namespace {
+
+const char kKeyMediaSessionId[] = "mediaSessionId";
+const char kKeyPlaybackRate[] = "playbackRate";
+const char kKeyPlayerState[] = "playerState";
+const char kKeyCurrentTime[] = "currentTime";
+const char kKeySupportedMediaCommands[] = "supportedMediaCommands";
+const char kKeyDisableStreamGrouping[] = "disableStreamGrouping";
+const char kKeyMedia[] = "media";
+const char kKeyContentId[] = "contentId";
+const char kKeyStreamType[] = "streamType";
+const char kKeyContentType[] = "contentType";
+const char kValuePlaying[] = "PLAYING";
+const char kValueLive[] = "LIVE";
+const char kValueVideoWebm[] = "video/webm";
+
+base::Value GetMediaCurrentStatusValue() {
+  base::Value media(base::Value::Type::DICTIONARY);
+  media.SetKey(kKeyContentId, base::Value(""));
+  media.SetKey(kKeyStreamType, base::Value(kValueLive));
+  media.SetKey(kKeyContentType, base::Value(kValueVideoWebm));
+
+  base::Value media_current_status(base::Value::Type::DICTIONARY);
+  media_current_status.SetKey(kKeyMediaSessionId, base::Value(0));
+  media_current_status.SetKey(kKeyPlaybackRate, base::Value(1.0));
+  media_current_status.SetKey(kKeyPlayerState, base::Value(kValuePlaying));
+  media_current_status.SetKey(kKeyCurrentTime, base::Value(0));
+  media_current_status.SetKey(kKeySupportedMediaCommands, base::Value(0));
+  media_current_status.SetKey(kKeyDisableStreamGrouping, base::Value(true));
+  media_current_status.SetKey(kKeyMedia, std::move(media));
+
+  return media_current_status;
+}
+
+}  // namespace
+
 CastMessagePortImpl::CastMessagePortImpl(
     std::unique_ptr<cast_api_bindings::MessagePort> message_port,
     base::OnceClosure on_close)
@@ -104,6 +140,59 @@
   PostMessage(sender_id, kInjectNamespace, json_message);
 }
 
+void CastMessagePortImpl::HandleMediaMessage(const std::string& sender_id,
+                                             const std::string& message) {
+  absl::optional<base::Value> value = base::JSONReader::Read(message);
+  if (!value) {
+    LOG(ERROR) << "Malformed message from sender " << sender_id
+               << ": not a json payload: " << message;
+    return;
+  }
+
+  if (!value->is_dict()) {
+    LOG(ERROR) << "Malformed message from sender " << sender_id
+               << ": non-dictionary json payload: " << message;
+    return;
+  }
+
+  const std::string* type = value->FindStringKey(kKeyType);
+  if (!type) {
+    LOG(ERROR) << "Malformed message from sender " << sender_id
+               << ": no message type: " << message;
+    return;
+  }
+
+  if (*type == kValueMediaPlay || *type == kValueMediaPause) {
+    // Not supported. Just ignore.
+    return;
+  }
+
+  if (*type != kValueMediaGetStatus) {
+    LOG(ERROR) << "Malformed message from sender " << sender_id
+               << ": unknown message type: " << *type;
+    return;
+  }
+
+  absl::optional<int> request_id = value->FindIntKey(kKeyRequestId);
+  if (!request_id.has_value()) {
+    LOG(ERROR) << "Malformed message from sender " << sender_id
+               << ": no request id: " << message;
+    return;
+  }
+
+  base::Value message_status_list(base::Value::Type::LIST);
+  message_status_list.Append(GetMediaCurrentStatusValue());
+
+  base::Value response_value(base::Value::Type::DICTIONARY);
+  response_value.SetKey(kKeyRequestId, base::Value(request_id.value()));
+  response_value.SetKey(kKeyType, base::Value(kValueMediaStatus));
+  response_value.SetKey(kKeyStatus, std::move(message_status_list));
+
+  std::string json_message;
+  CHECK(base::JSONWriter::Write(response_value, &json_message));
+  PostMessage(sender_id, kMediaNamespace, json_message);
+}
+
 void CastMessagePortImpl::PostMessage(const std::string& sender_id,
                                       const std::string& message_namespace,
                                       const std::string& message) {
@@ -151,6 +240,8 @@
     client_->OnMessage(sender_id, message_namespace, str_message);
   } else if (message_namespace == kInjectNamespace) {
     SendInjectResponse(sender_id, str_message);
+  } else if (message_namespace == kMediaNamespace) {
+    HandleMediaMessage(sender_id, str_message);
   } else if (message_namespace != kSystemNamespace) {
     // System messages are ignored, log messages from unknown namespaces.
     DVLOG(2) << "Unknown message from " << sender_id
diff --git a/components/cast_streaming/browser/cast_message_port_impl.h b/components/cast_streaming/browser/cast_message_port_impl.h
index da32d88..71c5ca16 100644
--- a/components/cast_streaming/browser/cast_message_port_impl.h
+++ b/components/cast_streaming/browser/cast_message_port_impl.h
@@ -44,6 +44,11 @@
   void SendInjectResponse(const std::string& sender_id,
                           const std::string& message);
 
+  // Handles messages from the media namespace. Ignores play/pause requests and
+  // sends the media status as continuously playing.
+  void HandleMediaMessage(const std::string& sender_id,
+                          const std::string& message);
+
   // cast_api_bindings::MessagePort::Receiver implementation.
   bool OnMessage(base::StringPiece message,
                  std::vector<std::unique_ptr<cast_api_bindings::MessagePort>>
diff --git a/components/cast_streaming/browser/cast_message_port_impl_unittest.cc b/components/cast_streaming/browser/cast_message_port_impl_unittest.cc
index c8c175b..b71d4159 100644
--- a/components/cast_streaming/browser/cast_message_port_impl_unittest.cc
+++ b/components/cast_streaming/browser/cast_message_port_impl_unittest.cc
@@ -209,4 +209,135 @@
   RunUntilCastChannelClosed();
 }
 
+// Tests the media status namespace is properly handled.
+TEST_F(CastMessagePortImplTest, MediaStatus) {
+  const int kRequestId = 42;
+  base::Value media_value(base::Value::Type::DICTIONARY);
+  media_value.SetKey(kKeyType, base::Value(kValueMediaGetStatus));
+  media_value.SetKey(kKeyRequestId, base::Value(kRequestId));
+  std::string media_message;
+  ASSERT_TRUE(base::JSONWriter::Write(media_value, &media_message));
+
+  sender_message_port_->PostMessage(
+      SerializeCastMessage(kSenderId, kMediaNamespace, media_message));
+  sender_message_port_receiver_.RunUntilMessageCountEqual(2u);
+  ASSERT_EQ(sender_message_port_receiver_.buffer().size(), 2u);
+
+  std::string sender_id;
+  std::string message_namespace;
+  std::string message;
+  ASSERT_TRUE(
+      DeserializeCastMessage(sender_message_port_receiver_.buffer().at(1).first,
+                             &sender_id, &message_namespace, &message));
+  EXPECT_EQ(sender_id, kSenderId);
+  EXPECT_EQ(message_namespace, kMediaNamespace);
+
+  absl::optional<base::Value> return_value = base::JSONReader::Read(message);
+  ASSERT_TRUE(return_value);
+  ASSERT_TRUE(return_value->is_dict());
+
+  const std::string* type_value = return_value->FindStringKey(kKeyType);
+  ASSERT_TRUE(type_value);
+  EXPECT_EQ(*type_value, kValueMediaStatus);
+
+  absl::optional<int> request_id_value =
+      return_value->FindIntKey(kKeyRequestId);
+  ASSERT_TRUE(request_id_value);
+  EXPECT_EQ(request_id_value.value(), kRequestId);
+
+  const base::Value* status_value = return_value->FindListKey(kKeyStatus);
+  ASSERT_TRUE(status_value);
+  EXPECT_EQ(status_value->GetList().size(), 1u);
+}
+
+// Checks sending invalid media messages results in no response.
+TEST_F(CastMessagePortImplTest, InvalidMediaMessages) {
+  const int kRequestId = 42;
+
+  {
+    // Send an invalid message value.
+    sender_message_port_->PostMessage(
+        SerializeCastMessage(kSenderId, kMediaNamespace, "not a json"));
+  }
+
+  {
+    // Send a non-dictionary value.
+    std::string media_message;
+    ASSERT_TRUE(
+        base::JSONWriter::Write(base::Value("string value"), &media_message));
+    sender_message_port_->PostMessage(
+        SerializeCastMessage(kSenderId, kMediaNamespace, media_message));
+  }
+
+  {
+    // Send a message with no type.
+    base::Value media_value(base::Value::Type::DICTIONARY);
+    media_value.SetKey(kKeyRequestId, base::Value(kRequestId));
+    std::string media_message;
+    ASSERT_TRUE(base::JSONWriter::Write(media_value, &media_message));
+    sender_message_port_->PostMessage(
+        SerializeCastMessage(kSenderId, kMediaNamespace, media_message));
+  }
+
+  {
+    // Send a PLAY message. This is not incorrect but should be ignored.
+    base::Value media_value(base::Value::Type::DICTIONARY);
+    media_value.SetKey(kKeyType, base::Value(kValueMediaPlay));
+    media_value.SetKey(kKeyRequestId, base::Value(kRequestId));
+    std::string media_message;
+    ASSERT_TRUE(base::JSONWriter::Write(media_value, &media_message));
+    sender_message_port_->PostMessage(
+        SerializeCastMessage(kSenderId, kMediaNamespace, media_message));
+  }
+
+  {
+    // Send a PAUSE message. This is not incorrect but should be ignored.
+    base::Value media_value(base::Value::Type::DICTIONARY);
+    media_value.SetKey(kKeyType, base::Value(kValueMediaPause));
+    media_value.SetKey(kKeyRequestId, base::Value(kRequestId));
+    std::string media_message;
+    ASSERT_TRUE(base::JSONWriter::Write(media_value, &media_message));
+    sender_message_port_->PostMessage(
+        SerializeCastMessage(kSenderId, kMediaNamespace, media_message));
+  }
+
+  {
+    // Send a message with an invalid type.
+    base::Value media_value(base::Value::Type::DICTIONARY);
+    media_value.SetKey(kKeyType, base::Value("INVALID_TYPE"));
+    media_value.SetKey(kKeyRequestId, base::Value(kRequestId));
+    std::string media_message;
+    ASSERT_TRUE(base::JSONWriter::Write(media_value, &media_message));
+    sender_message_port_->PostMessage(
+        SerializeCastMessage(kSenderId, kMediaNamespace, media_message));
+  }
+
+  {
+    // Send a GET_STATUS message with no request ID.
+    base::Value media_value(base::Value::Type::DICTIONARY);
+    media_value.SetKey(kKeyType, base::Value(kValueMediaGetStatus));
+    std::string media_message;
+    ASSERT_TRUE(base::JSONWriter::Write(media_value, &media_message));
+    sender_message_port_->PostMessage(
+        SerializeCastMessage(kSenderId, kMediaNamespace, media_message));
+  }
+
+  {
+    // Send a message with a non-integer request ID.
+    base::Value media_value(base::Value::Type::DICTIONARY);
+    media_value.SetKey(kKeyType, base::Value(kValueMediaGetStatus));
+    media_value.SetKey(kKeyRequestId, base::Value("not an integer"));
+    std::string media_message;
+    ASSERT_TRUE(base::JSONWriter::Write(media_value, &media_message));
+    sender_message_port_->PostMessage(
+        SerializeCastMessage(kSenderId, kMediaNamespace, media_message));
+  }
+
+  // Process all the things. There should be a single message (the original
+  // system message), since none of the other messages sent will trigger a
+  // response.
+  base::RunLoop().RunUntilIdle();
+  ASSERT_EQ(sender_message_port_receiver_.buffer().size(), 1u);
+}
+
 }  // namespace cast_streaming
diff --git a/components/cast_streaming/browser/message_serialization.cc b/components/cast_streaming/browser/message_serialization.cc
index f7b5550..10a7095 100644
--- a/components/cast_streaming/browser/message_serialization.cc
+++ b/components/cast_streaming/browser/message_serialization.cc
@@ -15,6 +15,7 @@
 const char kRemotingNamespace[] = "urn:x-cast:com.google.cast.remoting";
 const char kSystemNamespace[] = "urn:x-cast:com.google.cast.system";
 const char kInjectNamespace[] = "urn:x-cast:com.google.cast.inject";
+const char kMediaNamespace[] = "urn:x-cast:com.google.cast.media";
 
 const char kKeySenderId[] = "senderId";
 const char kKeyNamespace[] = "namespace";
@@ -22,11 +23,17 @@
 const char kKeyType[] = "type";
 const char kKeyRequestId[] = "requestId";
 const char kKeyCode[] = "code";
+const char kKeyStatus[] = "status";
 
 const char kValueSystemSenderId[] = "SystemSender";
 const char kValueWrapped[] = "WRAPPED";
 const char kValueError[] = "ERROR";
 const char kValueWrappedError[] = "WRAPPED_ERROR";
+const char kValueMediaPlay[] = "PLAY";
+const char kValueMediaPause[] = "PAUSE";
+const char kValueMediaGetStatus[] = "GET_STATUS";
+const char kValueMediaStatus[] = "MEDIA_STATUS";
+
 const char kValueInjectNotSupportedError[] =
     R"({"code":"NOT_SUPPORTED","type":"ERROR"})";
 
@@ -36,7 +43,8 @@
       "activeNamespaces": [
         "urn:x-cast:com.google.cast.webrtc",
         "urn:x-cast:com.google.cast.remoting",
-        "urn:x-cast:com.google.cast.inject"
+        "urn:x-cast:com.google.cast.inject",
+        "urn:x-cast:com.google.cast.media"
       ],
       "version": "2.0.0",
       "messagesVersion": "1.0"
diff --git a/components/cast_streaming/browser/message_serialization.h b/components/cast_streaming/browser/message_serialization.h
index 6b5dc6e..1f2673c 100644
--- a/components/cast_streaming/browser/message_serialization.h
+++ b/components/cast_streaming/browser/message_serialization.h
@@ -17,6 +17,7 @@
 extern const char kRemotingNamespace[];
 extern const char kSystemNamespace[];
 extern const char kInjectNamespace[];
+extern const char kMediaNamespace[];
 
 extern const char kKeySenderId[];
 extern const char kKeyNamespace[];
@@ -24,10 +25,16 @@
 extern const char kKeyType[];
 extern const char kKeyRequestId[];
 extern const char kKeyCode[];
+extern const char kKeyStatus[];
 
 extern const char kValueSystemSenderId[];
 extern const char kValueWrapped[];
 extern const char kValueError[];
+extern const char kValueMediaPlay[];
+extern const char kValueMediaPause[];
+extern const char kValueMediaGetStatus[];
+extern const char kValueMediaStatus[];
+
 extern const char kValueWrappedError[];
 extern const char kValueInjectNotSupportedError[];
 
diff --git a/components/content_settings/core/browser/content_settings_origin_identifier_value_map.cc b/components/content_settings/core/browser/content_settings_origin_identifier_value_map.cc
index a7d40da7..3ed4179 100644
--- a/components/content_settings/core/browser/content_settings_origin_identifier_value_map.cc
+++ b/components/content_settings/core/browser/content_settings_origin_identifier_value_map.cc
@@ -23,16 +23,14 @@
 // |resource_identifier| in the precedence order of the rules.
 class RuleIteratorImpl : public RuleIterator {
  public:
-  // |RuleIteratorImpl| takes the ownership of |auto_lock|.
   RuleIteratorImpl(
       const OriginIdentifierValueMap::Rules::const_iterator& current_rule,
       const OriginIdentifierValueMap::Rules::const_iterator& rule_end,
-      base::AutoLock* auto_lock)
+      std::unique_ptr<base::AutoLock> auto_lock)
       : current_rule_(current_rule),
         rule_end_(rule_end),
-        auto_lock_(auto_lock) {
-  }
-  ~RuleIteratorImpl() override {}
+        auto_lock_(std::move(auto_lock)) {}
+  ~RuleIteratorImpl() override = default;
 
   bool HasNext() const override { return (current_rule_ != rule_end_); }
 
@@ -71,9 +69,9 @@
          std::tie(other.primary_pattern, other.secondary_pattern);
 }
 
-OriginIdentifierValueMap::ValueEntry::ValueEntry() {}
+OriginIdentifierValueMap::ValueEntry::ValueEntry() = default;
 
-OriginIdentifierValueMap::ValueEntry::~ValueEntry() {}
+OriginIdentifierValueMap::ValueEntry::~ValueEntry() = default;
 
 std::unique_ptr<RuleIterator> OriginIdentifierValueMap::GetRuleIterator(
     ContentSettingsType content_type,
@@ -88,8 +86,8 @@
   auto it = entries_.find(content_type);
   if (it == entries_.end())
     return nullptr;
-  return std::unique_ptr<RuleIterator>(new RuleIteratorImpl(
-      it->second.begin(), it->second.end(), auto_lock.release()));
+  return std::make_unique<RuleIteratorImpl>(
+      it->second.begin(), it->second.end(), std::move(auto_lock));
 }
 
 size_t OriginIdentifierValueMap::size() const {
@@ -99,9 +97,9 @@
   return size;
 }
 
-OriginIdentifierValueMap::OriginIdentifierValueMap() {}
+OriginIdentifierValueMap::OriginIdentifierValueMap() = default;
 
-OriginIdentifierValueMap::~OriginIdentifierValueMap() {}
+OriginIdentifierValueMap::~OriginIdentifierValueMap() = default;
 
 const base::Value* OriginIdentifierValueMap::GetValue(
     const GURL& primary_url,
diff --git a/components/exo/extended_drag_source.cc b/components/exo/extended_drag_source.cc
index 9876c5b..ab8ec93 100644
--- a/components/exo/extended_drag_source.cc
+++ b/components/exo/extended_drag_source.cc
@@ -247,10 +247,12 @@
   auto move_source = drag_event_source_ == ui::mojom::DragEventSource::kTouch
                          ? ::wm::WINDOW_MOVE_SOURCE_TOUCH
                          : ::wm::WINDOW_MOVE_SOURCE_MOUSE;
+  // TODO(crbug.com/1167581): Experiment setting |update_gesture_target| back
+  // to true when capture is removed from drag and drop.
   toplevel_handler->AttemptToStartDrag(
       toplevel, pointer_location, HTCAPTION, move_source,
       ash::ToplevelWindowEventHandler::EndClosure(),
-      /*update_gesture_target=*/true, /*grab_capture=*/false);
+      /*update_gesture_target=*/false, /*grab_capture=*/false);
 }
 
 void ExtendedDragSource::OnDraggedWindowVisibilityChanging(bool visible) {
diff --git a/components/exo/extended_drag_source_unittest.cc b/components/exo/extended_drag_source_unittest.cc
index 89eef958..8a771f8 100644
--- a/components/exo/extended_drag_source_unittest.cc
+++ b/components/exo/extended_drag_source_unittest.cc
@@ -23,10 +23,13 @@
 #include "testing/gtest/include/gtest/gtest.h"
 #include "ui/aura/client/drag_drop_client.h"
 #include "ui/aura/client/drag_drop_delegate.h"
+#include "ui/aura/window_tree_host.h"
 #include "ui/base/dragdrop/drag_drop_types.h"
 #include "ui/base/dragdrop/mojom/drag_drop_types.mojom-shared.h"
 #include "ui/base/dragdrop/os_exchange_data.h"
+#include "ui/events/event_utils.h"
 #include "ui/events/test/event_generator.h"
+#include "ui/events/test/events_test_utils.h"
 #include "ui/gfx/geometry/point.h"
 #include "ui/gfx/geometry/size.h"
 #include "ui/gfx/geometry/vector2d.h"
@@ -34,6 +37,18 @@
 namespace exo {
 namespace {
 
+void DispatchGesture(ui::EventType gesture_type, gfx::Point location) {
+  ui::GestureEventDetails event_details(gesture_type);
+  ui::GestureEvent gesture_event(location.x(), location.y(), 0,
+                                 ui::EventTimeForNow(), event_details);
+  ui::EventSource* event_source =
+      ash::Shell::GetPrimaryRootWindow()->GetHost()->GetEventSource();
+  ui::EventSourceTestApi event_source_test(event_source);
+  ui::EventDispatchDetails details =
+      event_source_test.SendEventToSink(&gesture_event);
+  CHECK(!details.dispatcher_destroyed);
+}
+
 class TestDataSourceDelegate : public DataSourceDelegate {
  public:
   TestDataSourceDelegate() {}
@@ -256,6 +271,57 @@
   EXPECT_EQ(gfx::Point(140, 140), window->GetBoundsInScreen().origin());
 }
 
+TEST_F(ExtendedDragSourceTest, DragSurfaceNotMappedYetWithTouch) {
+  // Create and Map the drag origin surface
+  auto surface = std::make_unique<Surface>();
+  auto shell_surface = std::make_unique<ShellSurface>(surface.get());
+  auto buffer = CreateBuffer({32, 32});
+  surface->Attach(buffer.get());
+  surface->Commit();
+
+  // Start the DND + extended-drag session.
+  StartExtendedDragSession(shell_surface->GetWidget()->GetNativeWindow(),
+                           gfx::Point(0, 0), ui::DragDropTypes::DRAG_MOVE,
+                           ui::mojom::DragEventSource::kTouch);
+
+  // Create a new surface to emulate a "detachment" process.
+  auto detached_surface = std::make_unique<Surface>();
+  auto detached_shell_surface =
+      std::make_unique<ShellSurface>(detached_surface.get());
+
+  // Set |surface| as the dragged surface while it's still unmapped/invisible.
+  // This can be used to implement tab detaching in Chrome's tab drag use case,
+  // for example. Extended drag source will monitor surface mapping and it's
+  // expected to position it correctly using the provided drag offset here
+  // relative to the current pointer location.
+  extended_drag_source_->Drag(detached_surface.get(), gfx::Vector2d(10, 10));
+  EXPECT_FALSE(extended_drag_source_->GetDraggedWindowForTesting());
+  EXPECT_TRUE(extended_drag_source_->GetDragOffsetForTesting().has_value());
+  EXPECT_EQ(gfx::Vector2d(10, 10),
+            *extended_drag_source_->GetDragOffsetForTesting());
+
+  // Initiate the gesture sequence.
+  DispatchGesture(ui::ET_GESTURE_BEGIN, gfx::Point(10, 10));
+
+  // Map the |detached_surface|.
+  auto detached_buffer = CreateBuffer({50, 50});
+  detached_surface->Attach(detached_buffer.get());
+  detached_surface->Commit();
+
+  // Ensure the toplevel window for the dragged surface set above, is correctly
+  // detected, after it's mapped.
+  aura::Window* window = detached_shell_surface->GetWidget()->GetNativeWindow();
+  EXPECT_TRUE(extended_drag_source_->GetDraggedWindowForTesting());
+  EXPECT_EQ(window, extended_drag_source_->GetDraggedWindowForTesting());
+
+  // Verify that dragging it by 100,100, with drag offset 10,10 and current
+  // pointer location 50,50 will set the dragged window bounds as expected.
+  ui::test::EventGenerator generator(GetContext());
+  generator.set_current_screen_location(gfx::Point(100, 100));
+  generator.PressMoveAndReleaseTouchBy(50, 50);
+  EXPECT_EQ(gfx::Point(140, 140), window->GetBoundsInScreen().origin());
+}
+
 TEST_F(ExtendedDragSourceTest, DestroyDraggedSurfaceWhileDragging) {
   // Create and map a toplevel shell surface.
   gfx::Size buffer_size(32, 32);
diff --git a/components/feed/core/proto/v2/wire/chrome_client_info.proto b/components/feed/core/proto/v2/wire/chrome_client_info.proto
index 97edd3a..7b7939b 100644
--- a/components/feed/core/proto/v2/wire/chrome_client_info.proto
+++ b/components/feed/core/proto/v2/wire/chrome_client_info.proto
@@ -10,4 +10,5 @@
 
 message ChromeClientInfo {
   optional string session_id = 3;
+  optional bool start_surface = 4;
 }
diff --git a/components/feed/core/v2/api_test/feed_api_stream_unittest.cc b/components/feed/core/v2/api_test/feed_api_stream_unittest.cc
index 3664a6c0..2f37ae9d 100644
--- a/components/feed/core/v2/api_test/feed_api_stream_unittest.cc
+++ b/components/feed/core/v2/api_test/feed_api_stream_unittest.cc
@@ -1959,7 +1959,8 @@
                   .empty());
   EXPECT_FALSE(network_.query_request_sent->feed_request()
                    .client_info()
-                   .has_chrome_client_info());
+                   .chrome_client_info()
+                   .has_session_id());
   EXPECT_EQ(kSessionToken1, stream_->GetMetadata().session_id().token());
   const base::Time kSessionToken1ExpiryTime =
       feedstore::GetSessionIdExpiryTime(stream_->GetMetadata());
@@ -2074,7 +2075,8 @@
   ASSERT_EQ(1, network_.send_query_call_count);
   EXPECT_FALSE(network_.query_request_sent->feed_request()
                    .client_info()
-                   .has_chrome_client_info());
+                   .chrome_client_info()
+                   .has_session_id());
   EXPECT_EQ(kSessionToken1, stream_->GetMetadata().session_id().token());
 
   // (2) Reload the stream from the network:
@@ -2114,7 +2116,8 @@
   ASSERT_EQ(3, network_.send_query_call_count);
   EXPECT_FALSE(network_.query_request_sent->feed_request()
                    .client_info()
-                   .has_chrome_client_info());
+                   .chrome_client_info()
+                   .has_session_id());
   EXPECT_EQ(kSessionToken2, stream_->GetMetadata().session_id().token());
 }
 
@@ -2537,6 +2540,38 @@
   EXPECT_EQ("loading -> 2 slices", surface.DescribeUpdates());
 }
 
+TEST_F(FeedApiTest, StartSurface) {
+  CreateStream(/*wait_for_initialization=*/true, /*start_surface=*/true);
+  TestForYouSurface surface(stream_.get());
+  WaitForIdleTaskQueue();
+  response_translator_.InjectResponse(MakeTypicalRefreshModelState());
+  CallbackReceiver<bool> callback;
+  stream_->ManualRefresh(surface.GetStreamType(), callback.Bind());
+  WaitForIdleTaskQueue();
+
+  ASSERT_TRUE(network_.query_request_sent.has_value());
+  EXPECT_TRUE(network_.query_request_sent->feed_request()
+                  .client_info()
+                  .chrome_client_info()
+                  .start_surface());
+}
+
+TEST_F(FeedApiTest, NoStartSurface) {
+  CreateStream(/*wait_for_initialization=*/true, /*start_surface=*/false);
+  TestForYouSurface surface(stream_.get());
+  WaitForIdleTaskQueue();
+  response_translator_.InjectResponse(MakeTypicalRefreshModelState());
+  CallbackReceiver<bool> callback;
+  stream_->ManualRefresh(surface.GetStreamType(), callback.Bind());
+  WaitForIdleTaskQueue();
+
+  ASSERT_TRUE(network_.query_request_sent.has_value());
+  EXPECT_FALSE(network_.query_request_sent->feed_request()
+                   .client_info()
+                   .chrome_client_info()
+                   .start_surface());
+}
+
 TEST_F(FeedStreamTestForAllStreamTypes, ContentOrderIsGroupedByDefault) {
   response_translator_.InjectResponse(MakeTypicalInitialModelState());
   TestWebFeedSurface surface(stream_.get());
diff --git a/components/feed/core/v2/api_test/feed_api_test.cc b/components/feed/core/v2/api_test/feed_api_test.cc
index 8a1dab0f..4f23ae7 100644
--- a/components/feed/core/v2/api_test/feed_api_test.cc
+++ b/components/feed/core/v2/api_test/feed_api_test.cc
@@ -815,10 +815,12 @@
   prefetch_image_call_count_++;
 }
 
-void FeedApiTest::CreateStream(bool wait_for_initialization) {
+void FeedApiTest::CreateStream(bool wait_for_initialization,
+                               bool start_surface) {
   ChromeInfo chrome_info;
   chrome_info.channel = version_info::Channel::STABLE;
   chrome_info.version = base::Version({99, 1, 9911, 2});
+  chrome_info.start_surface = start_surface;
   stream_ = std::make_unique<FeedStream>(
       &refresh_scheduler_, metrics_reporter_.get(), this, &profile_prefs_,
       &network_, image_fetcher_.get(), store_.get(),
diff --git a/components/feed/core/v2/api_test/feed_api_test.h b/components/feed/core/v2/api_test/feed_api_test.h
index e6bc0ec..6c17c07 100644
--- a/components/feed/core/v2/api_test/feed_api_test.h
+++ b/components/feed/core/v2/api_test/feed_api_test.h
@@ -443,7 +443,8 @@
   // For tests.
 
   // Replace stream_.
-  void CreateStream(bool wait_for_initialization = true);
+  void CreateStream(bool wait_for_initialization = true,
+                    bool start_surface = false);
   std::unique_ptr<StreamModel> CreateStreamModel();
   bool IsTaskQueueIdle() const;
   void WaitForIdleTaskQueue();
diff --git a/components/feed/core/v2/proto_util.cc b/components/feed/core/v2/proto_util.cc
index 40882f7d..8214d394 100644
--- a/components/feed/core/v2/proto_util.cc
+++ b/components/feed/core/v2/proto_util.cc
@@ -258,6 +258,8 @@
         request_metadata.session_id);
   }
 
+  client_info.mutable_chrome_client_info()->set_start_surface(
+      request_metadata.chrome_info.start_surface);
   return client_info;
 }
 
diff --git a/components/feed/core/v2/proto_util_unittest.cc b/components/feed/core/v2/proto_util_unittest.cc
index 6770a895..940992b 100644
--- a/components/feed/core/v2/proto_util_unittest.cc
+++ b/components/feed/core/v2/proto_util_unittest.cc
@@ -48,6 +48,13 @@
   EXPECT_EQ("en-US", result.locale());
 }
 
+TEST(ProtoUtilTest, ClientInfoStartSurface) {
+  RequestMetadata request_metadata;
+  request_metadata.chrome_info.start_surface = true;
+  feedwire::ClientInfo result = CreateClientInfo(request_metadata);
+  EXPECT_TRUE(result.chrome_client_info().start_surface());
+}
+
 TEST(ProtoUtilTest, DefaultCapabilities) {
   feedwire::FeedRequest request =
       CreateFeedQueryRefreshRequest(kForYouStream,
diff --git a/components/feed/core/v2/public/types.h b/components/feed/core/v2/public/types.h
index 5e18416..ff5b0b15 100644
--- a/components/feed/core/v2/public/types.h
+++ b/components/feed/core/v2/public/types.h
@@ -26,10 +26,11 @@
   kRefreshWebFeed,
 };
 
-// Information about the Chrome build.
+// Information about the Chrome build and feature flags.
 struct ChromeInfo {
   version_info::Channel channel{};
   base::Version version;
+  bool start_surface;
 };
 // Device display metrics.
 struct DisplayMetrics {
diff --git a/components/flags_ui/flags_test_helpers.cc b/components/flags_ui/flags_test_helpers.cc
index f5b2ac1..ff11d5c 100644
--- a/components/flags_ui/flags_test_helpers.cc
+++ b/components/flags_ui/flags_test_helpers.cc
@@ -291,7 +291,8 @@
                              FlagFile::kFlagNeverExpire);
 }
 
-// TODO(ellyjones): Does this / should this run on iOS as well?
+// TODO(https://crbug.com/1241068): Call this from the iOS flags unittests once
+// flag expiration is supported there.
 void EnsureRecentUnexpireFlagsArePresent(
     const base::span<const flags_ui::FeatureEntry>& entries,
     int current_milestone) {
diff --git a/components/media_message_center/media_notification_view_impl.cc b/components/media_message_center/media_notification_view_impl.cc
index 9e949e6..56b9538 100644
--- a/components/media_message_center/media_notification_view_impl.cc
+++ b/components/media_message_center/media_notification_view_impl.cc
@@ -104,6 +104,7 @@
     case MediaSessionAction::kToggleCamera:
     case MediaSessionAction::kHangUp:
     case MediaSessionAction::kRaise:
+    case MediaSessionAction::kSetMute:
       NOTREACHED();
       break;
   }
diff --git a/components/media_message_center/media_notification_view_modern_impl.cc b/components/media_message_center/media_notification_view_modern_impl.cc
index 8beda3e..30f4b6a 100644
--- a/components/media_message_center/media_notification_view_modern_impl.cc
+++ b/components/media_message_center/media_notification_view_modern_impl.cc
@@ -111,6 +111,7 @@
     case MediaSessionAction::kToggleCamera:
     case MediaSessionAction::kHangUp:
     case MediaSessionAction::kRaise:
+    case MediaSessionAction::kSetMute:
       NOTREACHED();
       break;
   }
@@ -154,6 +155,7 @@
     case MediaSessionAction::kToggleCamera:
     case MediaSessionAction::kHangUp:
     case MediaSessionAction::kRaise:
+    case MediaSessionAction::kSetMute:
       NOTREACHED();
       break;
   }
@@ -439,20 +441,19 @@
       volume_slider->SetPreferredSize(kVolumeSliderSize);
       volume_slider_ =
           util_buttons_container->AddChildView(std::move(volume_slider));
-
-      auto mute_button =
-          std::make_unique<views::ToggleImageButton>(base::BindRepeating(
-              &MediaNotificationViewModernImpl::OnMuteButtonClicked,
-              base::Unretained(this)));
-      mute_button->SetPreferredSize(kMuteButtonSize);
-      mute_button->SetImageHorizontalAlignment(
-          views::ImageButton::HorizontalAlignment::ALIGN_CENTER);
-      mute_button->SetImageVerticalAlignment(
-          views::ImageButton::VerticalAlignment::ALIGN_MIDDLE);
-      mute_button_ =
-          util_buttons_container->AddChildView(std::move(mute_button));
     }
 
+    auto mute_button =
+        std::make_unique<views::ToggleImageButton>(base::BindRepeating(
+            &MediaNotificationViewModernImpl::OnMuteButtonClicked,
+            base::Unretained(this)));
+    mute_button->SetPreferredSize(kMuteButtonSize);
+    mute_button->SetImageHorizontalAlignment(
+        views::ImageButton::HorizontalAlignment::ALIGN_CENTER);
+    mute_button->SetImageVerticalAlignment(
+        views::ImageButton::VerticalAlignment::ALIGN_MIDDLE);
+    mute_button_ = util_buttons_container->AddChildView(std::move(mute_button));
+
     AddChildView(std::move(util_buttons_container));
   }
 
diff --git a/components/metrics/field_trials_provider_unittest.cc b/components/metrics/field_trials_provider_unittest.cc
index 9182c50..e379789 100644
--- a/components/metrics/field_trials_provider_unittest.cc
+++ b/components/metrics/field_trials_provider_unittest.cc
@@ -5,6 +5,7 @@
 #include "components/metrics/field_trials_provider.h"
 
 #include "base/cxx17_backports.h"
+#include "base/threading/platform_thread.h"
 #include "components/variations/active_field_trials.h"
 #include "components/variations/synthetic_trial_registry.h"
 #include "testing/gtest/include/gtest/gtest.h"
diff --git a/components/mirroring/mojom/BUILD.gn b/components/mirroring/mojom/BUILD.gn
index bcee9163..4b2ce67 100644
--- a/components/mirroring/mojom/BUILD.gn
+++ b/components/mirroring/mojom/BUILD.gn
@@ -34,6 +34,7 @@
     "//media/capture/mojom:video_capture",
     "//media/mojo/mojom",
     "//media/mojo/mojom:remoting",
+    "//sandbox/policy/mojom",
     "//services/network/public/mojom",
     "//services/viz/public/mojom",
     "//ui/gfx/geometry/mojom",
diff --git a/components/mirroring/mojom/mirroring_service.mojom b/components/mirroring/mojom/mirroring_service.mojom
index df85bc3..ae64d9a9 100644
--- a/components/mirroring/mojom/mirroring_service.mojom
+++ b/components/mirroring/mojom/mirroring_service.mojom
@@ -8,9 +8,17 @@
 import "components/mirroring/mojom/resource_provider.mojom";
 import "components/mirroring/mojom/session_observer.mojom";
 import "components/mirroring/mojom/session_parameters.mojom";
+import "sandbox/policy/mojom/sandbox.mojom";
 import "ui/gfx/geometry/mojom/geometry.mojom";
 
-// This interface is used to control a mirroring session.
+[EnableIf=is_mac]
+const sandbox.mojom.Sandbox kMirroringSandbox
+  = sandbox.mojom.Sandbox.kMirroring;
+[EnableIfNot=is_mac]
+const sandbox.mojom.Sandbox kMirroringSandbox = sandbox.mojom.Sandbox.kUtility;
+
+// This interface is used to control a Cast mirroring session.
+[ServiceSandbox=kMirroringSandbox]
 interface MirroringService {
   // Starts a mirroring session. |max_resolution| is the maximium video
   // capturing resolution. |observer| will get notifications about lifecycle
diff --git a/components/optimization_guide/core/optimization_guide_features.cc b/components/optimization_guide/core/optimization_guide_features.cc
index 1e4edfc..c56f3fa 100644
--- a/components/optimization_guide/core/optimization_guide_features.cc
+++ b/components/optimization_guide/core/optimization_guide_features.cc
@@ -25,8 +25,14 @@
 
 // Enables the syncing of the Optimization Hints component, which provides
 // hints for what optimizations can be applied on a page load.
-const base::Feature kOptimizationHints{"OptimizationHints",
-                                       base::FEATURE_ENABLED_BY_DEFAULT};
+const base::Feature kOptimizationHints {
+  "OptimizationHints",
+#if defined(OS_IOS)
+      base::FEATURE_DISABLED_BY_DEFAULT
+#else   // !defined(OS_IOS)
+      base::FEATURE_ENABLED_BY_DEFAULT
+#endif  // defined(OS_IOS)
+};
 
 // Feature flag that contains a feature param that specifies the field trials
 // that are allowed to be sent up to the Optimization Guide Server.
diff --git a/components/optimization_guide/core/optimization_hints_component_update_listener.h b/components/optimization_guide/core/optimization_hints_component_update_listener.h
index a4c5bda..314249f30 100644
--- a/components/optimization_guide/core/optimization_hints_component_update_listener.h
+++ b/components/optimization_guide/core/optimization_hints_component_update_listener.h
@@ -12,6 +12,8 @@
 #include "components/optimization_guide/core/optimization_hints_component_observer.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 
+class OptimizationGuideServiceTest;
+
 namespace optimization_guide {
 
 // Tracks the info for the current Optimization Hints component and notifies
@@ -51,6 +53,7 @@
 
   friend class base::NoDestructor<OptimizationHintsComponentUpdateListener>;
   friend class OptimizationHintsComponentUpdateListenerTest;
+  friend class ::OptimizationGuideServiceTest;
 
   void ResetStateForTesting();
 
diff --git a/components/paint_preview/player/player_compositor_delegate.h b/components/paint_preview/player/player_compositor_delegate.h
index a0c4ca6..6f6b7e5 100644
--- a/components/paint_preview/player/player_compositor_delegate.h
+++ b/components/paint_preview/player/player_compositor_delegate.h
@@ -8,6 +8,7 @@
 #include "base/callback.h"
 #include "base/cancelable_callback.h"
 #include "base/containers/flat_map.h"
+#include "base/containers/queue.h"
 #include "base/memory/memory_pressure_listener.h"
 #include "base/memory/weak_ptr.h"
 #include "base/unguessable_token.h"
diff --git a/components/permissions/android/java/src/org/chromium/components/permissions/PermissionDialogController.java b/components/permissions/android/java/src/org/chromium/components/permissions/PermissionDialogController.java
index afb2267..20af7d1c 100644
--- a/components/permissions/android/java/src/org/chromium/components/permissions/PermissionDialogController.java
+++ b/components/permissions/android/java/src/org/chromium/components/permissions/PermissionDialogController.java
@@ -232,8 +232,9 @@
                         .with(ModalDialogProperties.TITLE,
                                 context.getString(R.string.overlay_detected_dialog_title,
                                         BuildInfo.getInstance().hostPackageLabel))
-                        .with(ModalDialogProperties.MESSAGE, context.getResources(),
-                                R.string.overlay_detected_dialog_message)
+                        .with(ModalDialogProperties.MESSAGE,
+                                context.getResources().getString(
+                                        R.string.overlay_detected_dialog_message))
                         .with(ModalDialogProperties.POSITIVE_BUTTON_TEXT, context.getResources(),
                                 R.string.cancel)
                         .with(ModalDialogProperties.NEGATIVE_BUTTON_TEXT, context.getResources(),
diff --git a/components/policy/core/browser/BUILD.gn b/components/policy/core/browser/BUILD.gn
index 604dcbd..43414401 100644
--- a/components/policy/core/browser/BUILD.gn
+++ b/components/policy/core/browser/BUILD.gn
@@ -34,8 +34,6 @@
     "policy_conversions_client.h",
     "policy_error_map.cc",
     "policy_error_map.h",
-    "proxy_policy_handler.cc",
-    "proxy_policy_handler.h",
     "url_allowlist_policy_handler.cc",
     "url_allowlist_policy_handler.h",
     "url_blocklist_manager.cc",
@@ -86,7 +84,6 @@
 
   public_deps += [ "//components/policy/core/common:internal" ]
   deps += [
-    "//components/proxy_config",
     "//google_apis",
     "//net",
     "//third_party/icu",
@@ -138,7 +135,6 @@
     "configuration_policy_handler_unittest.cc",
     "configuration_policy_pref_store_unittest.cc",
     "policy_error_map_unittest.cc",
-    "proxy_policy_handler_unittest.cc",
     "url_allowlist_policy_handler_unittest.cc",
     "url_blocklist_manager_unittest.cc",
     "url_blocklist_policy_handler_unittest.cc",
@@ -150,7 +146,6 @@
     "//base",
     "//components/policy:generated",
     "//components/prefs:test_support",
-    "//components/proxy_config",
     "//components/url_formatter",
     "//google_apis",
     "//net",
diff --git a/components/policy/core/browser/DEPS b/components/policy/core/browser/DEPS
index 15fc70d9..0c52c59b 100644
--- a/components/policy/core/browser/DEPS
+++ b/components/policy/core/browser/DEPS
@@ -1,7 +1,6 @@
 include_rules = [
   "+components/google/core",
   "+components/pref_registry",
-  "+components/proxy_config",
   "+components/reporting",
   "+components/strings/grit/components_strings.h",
   "+components/url_formatter",
diff --git a/components/policy/core/common/policy_service_impl.cc b/components/policy/core/common/policy_service_impl.cc
index 8d95230d..c25412e3 100644
--- a/components/policy/core/common/policy_service_impl.cc
+++ b/components/policy/core/common/policy_service_impl.cc
@@ -39,48 +39,6 @@
 
 namespace {
 
-const char* kProxyPolicies[] = {
-    key::kProxyMode,   key::kProxyServerMode, key::kProxyServer,
-    key::kProxyPacUrl, kProxyPacMandatory,    key::kProxyBypassList,
-};
-
-// Maps the separate policies for proxy settings into a single Dictionary
-// policy. This allows to keep the logic of merging policies from different
-// sources simple, as all separate proxy policies should be considered as a
-// single whole during merging.
-void RemapProxyPolicies(PolicyMap* policies) {
-  // The highest (level, scope) pair for an existing proxy policy is determined
-  // first, and then only policies with those exact attributes are merged.
-  PolicyMap::Entry current_priority;  // Defaults to the lowest priority.
-  PolicySource inherited_source = POLICY_SOURCE_ENTERPRISE_DEFAULT;
-  base::Value proxy_settings(base::Value::Type::DICTIONARY);
-  for (size_t i = 0; i < base::size(kProxyPolicies); ++i) {
-    const PolicyMap::Entry* entry = policies->Get(kProxyPolicies[i]);
-    if (entry) {
-      if (entry->has_higher_priority_than(current_priority)) {
-        proxy_settings = base::Value(base::Value::Type::DICTIONARY);
-        current_priority = entry->DeepCopy();
-        if (entry->source > inherited_source)  // Higher priority?
-          inherited_source = entry->source;
-      }
-      if (!entry->has_higher_priority_than(current_priority) &&
-          !current_priority.has_higher_priority_than(*entry)) {
-        proxy_settings.SetKey(kProxyPolicies[i], entry->value()->Clone());
-      }
-      policies->Erase(kProxyPolicies[i]);
-    }
-  }
-  // Sets the new |proxy_settings| if kProxySettings isn't set yet, or if the
-  // new priority is higher.
-  const PolicyMap::Entry* existing = policies->Get(key::kProxySettings);
-  if (!proxy_settings.DictEmpty() &&
-      (!existing || current_priority.has_higher_priority_than(*existing))) {
-    policies->Set(key::kProxySettings, current_priority.level,
-                  current_priority.scope, inherited_source,
-                  std::move(proxy_settings), nullptr);
-  }
-}
-
 // Maps the separate renamed policies into a single Dictionary policy. This
 // allows to keep the logic of merging policies from different sources simple,
 // as all separate renamed policies should be considered as a single whole
@@ -377,7 +335,6 @@
   for (auto* provider : providers_) {
     PolicyBundle provided_bundle;
     provided_bundle.CopyFrom(provider->policies());
-    RemapProxyPolicies(&provided_bundle.Get(chrome_namespace));
     RemapRenamedPolicies(&provided_bundle.Get(chrome_namespace));
     DowngradeMetricsReportingToRecommendedPolicy(
         &provided_bundle.Get(chrome_namespace));
diff --git a/components/policy/core/common/policy_service_impl_unittest.cc b/components/policy/core/common/policy_service_impl_unittest.cc
index 6358a41a..9a8cabd 100644
--- a/components/policy/core/common/policy_service_impl_unittest.cc
+++ b/components/policy/core/common/policy_service_impl_unittest.cc
@@ -1212,54 +1212,6 @@
   policy_service_->RemoveObserver(POLICY_DOMAIN_SIGNIN_EXTENSIONS, &observer);
 }
 
-TEST_F(PolicyServiceTest, SeparateProxyPoliciesMerging) {
-  const PolicyNamespace chrome_namespace(POLICY_DOMAIN_CHROME, std::string());
-  const PolicyNamespace extension_namespace(POLICY_DOMAIN_EXTENSIONS, "xyz");
-
-  std::unique_ptr<PolicyBundle> policy_bundle(new PolicyBundle());
-  PolicyMap& policy_map = policy_bundle->Get(chrome_namespace);
-  // Individual proxy policy values in the Chrome namespace should be collected
-  // into a dictionary.
-  policy_map.Set(key::kProxyServerMode, POLICY_LEVEL_MANDATORY,
-                 POLICY_SCOPE_USER, POLICY_SOURCE_CLOUD, base::Value(3),
-                 nullptr);
-
-  // Both these policies should be ignored, since there's a higher priority
-  // policy available.
-  policy_map.Set(key::kProxyMode, POLICY_LEVEL_RECOMMENDED, POLICY_SCOPE_USER,
-                 POLICY_SOURCE_CLOUD, base::Value("pac_script"), nullptr);
-  policy_map.Set(key::kProxyPacUrl, POLICY_LEVEL_RECOMMENDED, POLICY_SCOPE_USER,
-                 POLICY_SOURCE_CLOUD,
-                 base::Value("http://example.com/wpad.dat"), nullptr);
-
-  // Add a value to a non-Chrome namespace.
-  policy_bundle->Get(extension_namespace)
-      .Set(key::kProxyServerMode, POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
-           POLICY_SOURCE_CLOUD, base::Value(3), nullptr);
-
-  // The resulting Chrome namespace map should have the collected policy.
-  PolicyMap expected_chrome;
-  base::Value expected_value(base::Value::Type::DICTIONARY);
-  expected_value.SetIntKey(key::kProxyServerMode, 3);
-  expected_chrome.Set(key::kProxySettings, POLICY_LEVEL_MANDATORY,
-                      POLICY_SCOPE_USER, POLICY_SOURCE_CLOUD,
-                      std::move(expected_value), nullptr);
-  expected_chrome.Set("migrated", POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
-                      POLICY_SOURCE_PLATFORM, base::Value(15), nullptr);
-
-  // The resulting Extensions namespace map shouldn't have been modified.
-  PolicyMap expected_extension;
-  expected_extension.Set(key::kProxyServerMode, POLICY_LEVEL_MANDATORY,
-                         POLICY_SCOPE_USER, POLICY_SOURCE_CLOUD, base::Value(3),
-                         nullptr);
-
-  provider0_.UpdatePolicy(std::move(policy_bundle));
-  RunUntilIdle();
-
-  EXPECT_TRUE(VerifyPolicies(chrome_namespace, expected_chrome));
-  EXPECT_TRUE(VerifyPolicies(extension_namespace, expected_extension));
-}
-
 TEST_F(PolicyServiceTest, DictionaryPoliciesMerging) {
   const PolicyNamespace chrome_namespace(POLICY_DOMAIN_CHROME, std::string());
 
diff --git a/components/power_scheduler/power_scheduler.cc b/components/power_scheduler/power_scheduler.cc
index 794234b..4a16a25 100644
--- a/components/power_scheduler/power_scheduler.cc
+++ b/components/power_scheduler/power_scheduler.cc
@@ -15,6 +15,7 @@
 #include "base/no_destructor.h"
 #include "base/process/process_handle.h"
 #include "base/process/process_metrics.h"
+#include "base/strings/string_number_conversions.h"
 #include "base/strings/string_split.h"
 #include "base/task/current_thread.h"
 #include "base/task/thread_pool.h"
diff --git a/components/proxy_config/BUILD.gn b/components/proxy_config/BUILD.gn
index 9a0386e0..4e09932 100644
--- a/components/proxy_config/BUILD.gn
+++ b/components/proxy_config/BUILD.gn
@@ -15,6 +15,8 @@
     "proxy_config_export.h",
     "proxy_config_pref_names.cc",
     "proxy_config_pref_names.h",
+    "proxy_policy_handler.cc",
+    "proxy_policy_handler.h",
     "proxy_prefs.cc",
     "proxy_prefs.h",
   ]
@@ -23,7 +25,11 @@
 
   deps = [
     "//base",
+    "//components/policy:generated",
+    "//components/policy/core/browser",
+    "//components/policy/core/common",
     "//components/prefs",
+    "//components/strings",
     "//net",
     "//url",
   ]
@@ -44,16 +50,22 @@
   sources = [
     "pref_proxy_config_tracker_impl_unittest.cc",
     "proxy_config_dictionary_unittest.cc",
+    "proxy_policy_handler_unittest.cc",
     "proxy_prefs_unittest.cc",
   ]
   deps = [
     ":proxy_config",
     "//base",
     "//base/test:test_support",
+    "//components/policy:generated",
+    "//components/policy/core/browser",
+    "//components/policy/core/browser:test_support",
+    "//components/policy/core/common",
     "//components/prefs:test_support",
     "//net",
     "//net:test_support",
     "//testing/gmock",
     "//testing/gtest",
+    "//third_party/abseil-cpp:absl",
   ]
 }
diff --git a/components/proxy_config/DEPS b/components/proxy_config/DEPS
index 069741e..9bf5de8ad 100644
--- a/components/proxy_config/DEPS
+++ b/components/proxy_config/DEPS
@@ -1,5 +1,9 @@
 include_rules = [
+  "+components/policy",
+  "+components/policy/core/browser",
+  "+components/policy/core/common",
   "+components/pref_registry",
   "+components/prefs",
+  "+components/strings/grit/components_strings.h",
   "+net"
 ]
diff --git a/components/policy/core/browser/proxy_policy_handler.cc b/components/proxy_config/proxy_policy_handler.cc
similarity index 62%
rename from components/policy/core/browser/proxy_policy_handler.cc
rename to components/proxy_config/proxy_policy_handler.cc
index 406b8a9..9f0c7ea 100644
--- a/components/policy/core/browser/proxy_policy_handler.cc
+++ b/components/proxy_config/proxy_policy_handler.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 "components/policy/core/browser/proxy_policy_handler.h"
+#include "components/proxy_config/proxy_policy_handler.h"
 
 #include <stddef.h>
 
@@ -23,6 +23,16 @@
 
 namespace {
 
+using policy::kProxyPacMandatory;
+using policy::PolicyErrorMap;
+using policy::PolicyMap;
+using policy::key::kProxyBypassList;
+using policy::key::kProxyMode;
+using policy::key::kProxyPacUrl;
+using policy::key::kProxyServer;
+using policy::key::kProxyServerMode;
+using policy::key::kProxySettings;
+
 // This is used to check whether for a given ProxyMode value, the ProxyPacUrl,
 // the ProxyBypassList and the ProxyServer policies are allowed to be specified.
 // |error_message_id| is the message id of the localized error message to show
@@ -52,9 +62,53 @@
      IDS_POLICY_PROXY_MODE_SYSTEM_ERROR},
 };
 
+const char* kDeprecatedProxyPolicies[] = {
+    kProxyMode, kProxyServerMode, kProxyServer, kProxyPacUrl, kProxyBypassList,
+};
+
+// Maps the separate deprecated policies for proxy settings into a single
+// Dictionary policy. This allows to keep the logic of merging policies from
+// different sources simple, as all separate proxy policies should be considered
+// as a single whole during merging. Returns proxy_settings value.
+base::Value RemapProxyPolicies(const PolicyMap& policies) {
+  // The highest (level, scope) pair for an existing proxy policy is determined
+  // first, and then only policies with those exact attributes are merged.
+  PolicyMap::Entry current_priority;  // Defaults to the lowest priority.
+  policy::PolicySource inherited_source =
+      policy::POLICY_SOURCE_ENTERPRISE_DEFAULT;
+  base::Value proxy_settings(base::Value::Type::DICTIONARY);
+  for (size_t i = 0; i < base::size(kDeprecatedProxyPolicies); ++i) {
+    const PolicyMap::Entry* entry = policies.Get(kDeprecatedProxyPolicies[i]);
+    if (!entry)
+      continue;
+    if (entry->has_higher_priority_than(current_priority)) {
+      proxy_settings = base::Value(base::Value::Type::DICTIONARY);
+      current_priority = entry->DeepCopy();
+      if (entry->source > inherited_source)  // Higher priority?
+        inherited_source = entry->source;
+    }
+    // If two priorities are the same.
+    if (!entry->has_higher_priority_than(current_priority) &&
+        !current_priority.has_higher_priority_than(*entry)) {
+      proxy_settings.SetKey(kDeprecatedProxyPolicies[i],
+                            entry->value()->Clone());
+    }
+  }
+  // Sets the new |proxy_settings| if kProxySettings isn't set yet, or if the
+  // new priority is higher.
+  const PolicyMap::Entry* existing = policies.Get(kProxySettings);
+  if (!proxy_settings.DictEmpty() &&
+      (!existing || current_priority.has_higher_priority_than(*existing))) {
+    return proxy_settings;
+  } else if (existing && existing->value()) {
+    return existing->value()->Clone();
+  }
+  return base::Value();
+}
+
 }  // namespace
 
-namespace policy {
+namespace proxy_config {
 
 // The proxy policies have the peculiarity that they are loaded from individual
 // policies, but the providers then expose them through a unified
@@ -62,30 +116,31 @@
 
 ProxyPolicyHandler::ProxyPolicyHandler() {}
 
-ProxyPolicyHandler::~ProxyPolicyHandler() {
-}
+ProxyPolicyHandler::~ProxyPolicyHandler() {}
 
 bool ProxyPolicyHandler::CheckPolicySettings(const PolicyMap& policies,
                                              PolicyErrorMap* errors) {
-  const base::Value* mode = GetProxyPolicyValue(policies, key::kProxyMode);
-  const base::Value* server = GetProxyPolicyValue(policies, key::kProxyServer);
+  base::Value proxy_settings = RemapProxyPolicies(policies);
+  const base::Value* mode = GetProxyPolicyValue(&proxy_settings, kProxyMode);
+  const base::Value* server =
+      GetProxyPolicyValue(&proxy_settings, kProxyServer);
   const base::Value* server_mode =
-      GetProxyPolicyValue(policies, key::kProxyServerMode);
-  const base::Value* pac_url = GetProxyPolicyValue(policies, key::kProxyPacUrl);
+      GetProxyPolicyValue(&proxy_settings, kProxyServerMode);
+  const base::Value* pac_url =
+      GetProxyPolicyValue(&proxy_settings, kProxyPacUrl);
   const base::Value* pac_mandatory =
-      GetProxyPolicyValue(policies, kProxyPacMandatory);
+      GetProxyPolicyValue(&proxy_settings, kProxyPacMandatory);
   const base::Value* bypass_list =
-      GetProxyPolicyValue(policies, key::kProxyBypassList);
+      GetProxyPolicyValue(&proxy_settings, kProxyBypassList);
 
   if ((server || pac_url || bypass_list) && !(mode || server_mode)) {
-    errors->AddError(key::kProxySettings,
-                     key::kProxyMode,
+    errors->AddError(kProxySettings, kProxyMode,
                      IDS_POLICY_NOT_SPECIFIED_ERROR);
     return false;
   }
 
   std::string mode_value;
-  if (!CheckProxyModeAndServerMode(policies, errors, &mode_value))
+  if (!CheckProxyModeAndServerMode(&proxy_settings, errors, &mode_value))
     return false;
 
   // If neither ProxyMode nor ProxyServerMode are specified, mode_value will be
@@ -102,23 +157,18 @@
     is_valid_mode = true;
 
     if (!entry.pac_url_allowed && pac_url) {
-      errors->AddError(key::kProxySettings,
-                       key::kProxyPacUrl,
-                       entry.error_message_id);
+      errors->AddError(kProxySettings, kProxyPacUrl, entry.error_message_id);
     }
     if (!entry.pac_mandatory_allowed && pac_mandatory) {
-      errors->AddError(key::kProxySettings, kProxyPacMandatory,
+      errors->AddError(kProxySettings, kProxyPacMandatory,
                        entry.error_message_id);
     }
     if (!entry.bypass_list_allowed && bypass_list) {
-      errors->AddError(key::kProxySettings,
-                       key::kProxyBypassList,
+      errors->AddError(kProxySettings, kProxyBypassList,
                        entry.error_message_id);
     }
     if (!entry.server_allowed && server) {
-      errors->AddError(key::kProxySettings,
-                       key::kProxyServer,
-                       entry.error_message_id);
+      errors->AddError(kProxySettings, kProxyServer, entry.error_message_id);
     }
 
     if ((!entry.pac_url_allowed && pac_url) ||
@@ -130,10 +180,8 @@
   }
 
   if (!is_valid_mode) {
-    errors->AddError(key::kProxySettings,
-                     mode ? key::kProxyMode : key::kProxyServerMode,
-                     IDS_POLICY_OUT_OF_RANGE_ERROR,
-                     mode_value);
+    errors->AddError(kProxySettings, mode ? kProxyMode : kProxyServerMode,
+                     IDS_POLICY_OUT_OF_RANGE_ERROR, mode_value);
     return false;
   }
   return true;
@@ -141,15 +189,18 @@
 
 void ProxyPolicyHandler::ApplyPolicySettings(const PolicyMap& policies,
                                              PrefValueMap* prefs) {
-  const base::Value* mode = GetProxyPolicyValue(policies, key::kProxyMode);
-  const base::Value* server = GetProxyPolicyValue(policies, key::kProxyServer);
+  base::Value proxy_settings = RemapProxyPolicies(policies);
+  const base::Value* mode = GetProxyPolicyValue(&proxy_settings, kProxyMode);
+  const base::Value* server =
+      GetProxyPolicyValue(&proxy_settings, kProxyServer);
   const base::Value* server_mode =
-      GetProxyPolicyValue(policies, key::kProxyServerMode);
-  const base::Value* pac_url = GetProxyPolicyValue(policies, key::kProxyPacUrl);
+      GetProxyPolicyValue(&proxy_settings, kProxyServerMode);
+  const base::Value* pac_url =
+      GetProxyPolicyValue(&proxy_settings, kProxyPacUrl);
   const base::Value* pac_mandatory =
-      GetProxyPolicyValue(policies, kProxyPacMandatory);
+      GetProxyPolicyValue(&proxy_settings, kProxyPacMandatory);
   const base::Value* bypass_list =
-      GetProxyPolicyValue(policies, key::kProxyBypassList);
+      GetProxyPolicyValue(&proxy_settings, kProxyBypassList);
 
   ProxyPrefs::ProxyMode proxy_mode;
   if (mode) {
@@ -221,9 +272,8 @@
 }
 
 const base::Value* ProxyPolicyHandler::GetProxyPolicyValue(
-    const PolicyMap& policies, const char* policy_name) {
-  // See note on the ProxyPolicyHandler implementation above.
-  const base::Value* value = policies.GetValue(key::kProxySettings);
+    const base::Value* value,
+    const char* policy_name) {
   const base::DictionaryValue* settings;
   if (!value || !value->GetAsDictionary(&settings))
     return nullptr;
@@ -237,27 +287,26 @@
   return policy_value;
 }
 
-bool ProxyPolicyHandler::CheckProxyModeAndServerMode(const PolicyMap& policies,
-                                                     PolicyErrorMap* errors,
-                                                     std::string* mode_value) {
-  const base::Value* mode = GetProxyPolicyValue(policies, key::kProxyMode);
-  const base::Value* server = GetProxyPolicyValue(policies, key::kProxyServer);
+bool ProxyPolicyHandler::CheckProxyModeAndServerMode(
+    const base::Value* proxy_settings,
+    PolicyErrorMap* errors,
+    std::string* mode_value) {
+  const base::Value* mode = GetProxyPolicyValue(proxy_settings, kProxyMode);
+  const base::Value* server = GetProxyPolicyValue(proxy_settings, kProxyServer);
   const base::Value* server_mode =
-      GetProxyPolicyValue(policies, key::kProxyServerMode);
-  const base::Value* pac_url = GetProxyPolicyValue(policies, key::kProxyPacUrl);
+      GetProxyPolicyValue(proxy_settings, kProxyServerMode);
+  const base::Value* pac_url =
+      GetProxyPolicyValue(proxy_settings, kProxyPacUrl);
 
   // If there's a server mode, convert it into a mode.
   // When both are specified, the mode takes precedence.
   if (mode) {
     if (server_mode) {
-      errors->AddError(key::kProxySettings,
-                       key::kProxyServerMode,
-                       IDS_POLICY_OVERRIDDEN,
-                       key::kProxyMode);
+      errors->AddError(kProxySettings, kProxyServerMode, IDS_POLICY_OVERRIDDEN,
+                       kProxyMode);
     }
     if (!mode->is_string()) {
-      errors->AddError(key::kProxySettings, key::kProxyMode,
-                       IDS_POLICY_TYPE_ERROR,
+      errors->AddError(kProxySettings, kProxyMode, IDS_POLICY_TYPE_ERROR,
                        base::Value::GetTypeName(base::Value::Type::BOOLEAN));
       return false;
     }
@@ -265,28 +314,24 @@
 
     ProxyPrefs::ProxyMode mode;
     if (!ProxyPrefs::StringToProxyMode(*mode_value, &mode)) {
-      errors->AddError(key::kProxySettings,
-                       key::kProxyMode,
+      errors->AddError(kProxySettings, kProxyMode,
                        IDS_POLICY_INVALID_PROXY_MODE_ERROR);
       return false;
     }
 
     if (mode == ProxyPrefs::MODE_PAC_SCRIPT && !pac_url) {
-      errors->AddError(key::kProxySettings,
-                       key::kProxyPacUrl,
+      errors->AddError(kProxySettings, kProxyPacUrl,
                        IDS_POLICY_NOT_SPECIFIED_ERROR);
       return false;
     }
     if (mode == ProxyPrefs::MODE_FIXED_SERVERS && !server) {
-      errors->AddError(key::kProxySettings,
-                       key::kProxyServer,
+      errors->AddError(kProxySettings, kProxyServer,
                        IDS_POLICY_NOT_SPECIFIED_ERROR);
       return false;
     }
   } else if (server_mode) {
     if (!server_mode->is_int()) {
-      errors->AddError(key::kProxySettings, key::kProxyServerMode,
-                       IDS_POLICY_TYPE_ERROR,
+      errors->AddError(kProxySettings, kProxyServerMode, IDS_POLICY_TYPE_ERROR,
                        base::Value::GetTypeName(base::Value::Type::INTEGER));
       return false;
     }
@@ -301,22 +346,14 @@
       case PROXY_MANUALLY_CONFIGURED_PROXY_SERVER_MODE:
         if (server && pac_url) {
           int message_id = IDS_POLICY_PROXY_BOTH_SPECIFIED_ERROR;
-          errors->AddError(key::kProxySettings,
-                           key::kProxyServer,
-                           message_id);
-          errors->AddError(key::kProxySettings,
-                           key::kProxyPacUrl,
-                           message_id);
+          errors->AddError(kProxySettings, kProxyServer, message_id);
+          errors->AddError(kProxySettings, kProxyPacUrl, message_id);
           return false;
         }
         if (!server && !pac_url) {
           int message_id = IDS_POLICY_PROXY_NEITHER_SPECIFIED_ERROR;
-          errors->AddError(key::kProxySettings,
-                           key::kProxyServer,
-                           message_id);
-          errors->AddError(key::kProxySettings,
-                           key::kProxyPacUrl,
-                           message_id);
+          errors->AddError(kProxySettings, kProxyServer, message_id);
+          errors->AddError(kProxySettings, kProxyPacUrl, message_id);
           return false;
         }
         *mode_value = pac_url ? ProxyPrefs::kPacScriptProxyModeName
@@ -326,7 +363,7 @@
         *mode_value = ProxyPrefs::kSystemProxyModeName;
         break;
       default:
-        errors->AddError(key::kProxySettings, key::kProxyServerMode,
+        errors->AddError(kProxySettings, kProxyServerMode,
                          IDS_POLICY_OUT_OF_RANGE_ERROR,
                          base::NumberToString(server_mode->GetInt()));
         return false;
@@ -335,4 +372,4 @@
   return true;
 }
 
-}  // namespace policy
+}  // namespace proxy_config
diff --git a/components/policy/core/browser/proxy_policy_handler.h b/components/proxy_config/proxy_policy_handler.h
similarity index 67%
rename from components/policy/core/browser/proxy_policy_handler.h
rename to components/proxy_config/proxy_policy_handler.h
index 90749a0..c8ca2b0e 100644
--- a/components/policy/core/browser/proxy_policy_handler.h
+++ b/components/proxy_config/proxy_policy_handler.h
@@ -2,17 +2,19 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef COMPONENTS_POLICY_CORE_BROWSER_PROXY_POLICY_HANDLER_H_
-#define COMPONENTS_POLICY_CORE_BROWSER_PROXY_POLICY_HANDLER_H_
+#ifndef COMPONENTS_PROXY_CONFIG_PROXY_POLICY_HANDLER_H_
+#define COMPONENTS_PROXY_CONFIG_PROXY_POLICY_HANDLER_H_
 
 #include <string>
 
 #include "components/policy/core/browser/configuration_policy_handler.h"
+#include "components/proxy_config/proxy_config_export.h"
 
-namespace policy {
+namespace proxy_config {
 
 // ConfigurationPolicyHandler for the proxy policies.
-class POLICY_EXPORT ProxyPolicyHandler : public ConfigurationPolicyHandler {
+class PROXY_CONFIG_EXPORT ProxyPolicyHandler
+    : public policy::ConfigurationPolicyHandler {
  public:
   // Constants for the "Proxy Server Mode" defined in the policies.
   // Note that these diverge from internal presentation defined in
@@ -38,23 +40,23 @@
   ~ProxyPolicyHandler() override;
 
   // ConfigurationPolicyHandler methods:
-  bool CheckPolicySettings(const PolicyMap& policies,
-                           PolicyErrorMap* errors) override;
-  void ApplyPolicySettings(const PolicyMap& policies,
+  bool CheckPolicySettings(const policy::PolicyMap& policies,
+                           policy::PolicyErrorMap* errors) override;
+  void ApplyPolicySettings(const policy::PolicyMap& policies,
                            PrefValueMap* prefs) override;
 
  private:
-  const base::Value* GetProxyPolicyValue(const PolicyMap& policies,
+  const base::Value* GetProxyPolicyValue(const base::Value* value,
                                          const char* policy_name);
 
   // Converts the deprecated ProxyServerMode policy value to a ProxyMode value
   // and places the result in |mode_value|. Returns whether the conversion
   // succeeded.
-  bool CheckProxyModeAndServerMode(const PolicyMap& policies,
-                                   PolicyErrorMap* errors,
+  bool CheckProxyModeAndServerMode(const base::Value* proxy_settings,
+                                   policy::PolicyErrorMap* errors,
                                    std::string* mode_value);
 };
 
-}  // namespace policy
+}  // namespace proxy_config
 
-#endif  // COMPONENTS_POLICY_CORE_BROWSER_PROXY_POLICY_HANDLER_H_
+#endif  // COMPONENTS_PROXY_CONFIG_PROXY_POLICY_HANDLER_H_
diff --git a/components/policy/core/browser/proxy_policy_handler_unittest.cc b/components/proxy_config/proxy_policy_handler_unittest.cc
similarity index 67%
rename from components/policy/core/browser/proxy_policy_handler_unittest.cc
rename to components/proxy_config/proxy_policy_handler_unittest.cc
index d544dd1..4c8b0dd0 100644
--- a/components/policy/core/browser/proxy_policy_handler_unittest.cc
+++ b/components/proxy_config/proxy_policy_handler_unittest.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "components/policy/core/browser/proxy_policy_handler.h"
+#include "components/proxy_config/proxy_policy_handler.h"
 
 #include <memory>
 #include <string>
@@ -20,11 +20,27 @@
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 
-namespace policy {
+using policy::ConfigurationPolicyHandler;
+using policy::ConfigurationPolicyPrefStore;
+using policy::ConfigurationPolicyPrefStoreTest;
+using policy::kProxyPacMandatory;
+using policy::POLICY_LEVEL_MANDATORY;
+using policy::POLICY_LEVEL_RECOMMENDED;
+using policy::POLICY_SCOPE_USER;
+using policy::POLICY_SOURCE_CLOUD;
+using policy::PolicyMap;
+using policy::PolicyServiceImpl;
+using policy::key::kProxyBypassList;
+using policy::key::kProxyMode;
+using policy::key::kProxyPacUrl;
+using policy::key::kProxyServer;
+using policy::key::kProxyServerMode;
+using policy::key::kProxySettings;
+
+namespace proxy_config {
 
 // Test cases for the proxy policy settings.
-class ProxyPolicyHandlerTest
-    : public ConfigurationPolicyPrefStoreTest {
+class ProxyPolicyHandlerTest : public ConfigurationPolicyPrefStoreTest {
  public:
   void SetUp() override {
     ConfigurationPolicyPrefStoreTest::SetUp();
@@ -84,13 +100,13 @@
 
 TEST_F(ProxyPolicyHandlerTest, ManualOptions) {
   PolicyMap policy;
-  policy.Set(key::kProxyBypassList, POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
+  policy.Set(kProxyBypassList, POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
              POLICY_SOURCE_CLOUD, base::Value("http://chromium.org/override"),
              nullptr);
-  policy.Set(key::kProxyServer, POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
+  policy.Set(kProxyServer, POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
              POLICY_SOURCE_CLOUD, base::Value("chromium.org"), nullptr);
   policy.Set(
-      key::kProxyServerMode, POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
+      kProxyServerMode, POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
       POLICY_SOURCE_CLOUD,
       base::Value(
           ProxyPolicyHandler::PROXY_MANUALLY_CONFIGURED_PROXY_SERVER_MODE),
@@ -105,15 +121,15 @@
 TEST_F(ProxyPolicyHandlerTest, ManualOptionsReversedApplyOrder) {
   PolicyMap policy;
   policy.Set(
-      key::kProxyServerMode, POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
+      kProxyServerMode, POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
       POLICY_SOURCE_CLOUD,
       base::Value(
           ProxyPolicyHandler::PROXY_MANUALLY_CONFIGURED_PROXY_SERVER_MODE),
       nullptr);
-  policy.Set(key::kProxyBypassList, POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
+  policy.Set(kProxyBypassList, POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
              POLICY_SOURCE_CLOUD, base::Value("http://chromium.org/override"),
              nullptr);
-  policy.Set(key::kProxyServer, POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
+  policy.Set(kProxyServer, POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
              POLICY_SOURCE_CLOUD, base::Value("chromium.org"), nullptr);
   UpdateProviderPolicy(policy);
 
@@ -125,7 +141,7 @@
 TEST_F(ProxyPolicyHandlerTest, ManualOptionsInvalid) {
   PolicyMap policy;
   policy.Set(
-      key::kProxyServerMode, POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
+      kProxyServerMode, POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
       POLICY_SOURCE_CLOUD,
       base::Value(
           ProxyPolicyHandler::PROXY_MANUALLY_CONFIGURED_PROXY_SERVER_MODE),
@@ -138,7 +154,7 @@
 
 TEST_F(ProxyPolicyHandlerTest, NoProxyServerMode) {
   PolicyMap policy;
-  policy.Set(key::kProxyServerMode, POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
+  policy.Set(kProxyServerMode, POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
              POLICY_SOURCE_CLOUD,
              base::Value(ProxyPolicyHandler::PROXY_SERVER_MODE), nullptr);
   UpdateProviderPolicy(policy);
@@ -148,7 +164,7 @@
 
 TEST_F(ProxyPolicyHandlerTest, NoProxyModeName) {
   PolicyMap policy;
-  policy.Set(key::kProxyMode, POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
+  policy.Set(kProxyMode, POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
              POLICY_SOURCE_CLOUD, base::Value(ProxyPrefs::kDirectProxyModeName),
              nullptr);
   UpdateProviderPolicy(policy);
@@ -159,7 +175,7 @@
 TEST_F(ProxyPolicyHandlerTest, AutoDetectProxyServerMode) {
   PolicyMap policy;
   policy.Set(
-      key::kProxyServerMode, POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
+      kProxyServerMode, POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
       POLICY_SOURCE_CLOUD,
       base::Value(ProxyPolicyHandler::PROXY_AUTO_DETECT_PROXY_SERVER_MODE),
       nullptr);
@@ -170,7 +186,7 @@
 
 TEST_F(ProxyPolicyHandlerTest, AutoDetectProxyModeName) {
   PolicyMap policy;
-  policy.Set(key::kProxyMode, POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
+  policy.Set(kProxyMode, POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
              POLICY_SOURCE_CLOUD,
              base::Value(ProxyPrefs::kAutoDetectProxyModeName), nullptr);
   UpdateProviderPolicy(policy);
@@ -180,22 +196,37 @@
 
 TEST_F(ProxyPolicyHandlerTest, PacScriptProxyMode) {
   PolicyMap policy;
-  policy.Set(key::kProxyPacUrl, POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
+  policy.Set(kProxyPacUrl, POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
              POLICY_SOURCE_CLOUD, base::Value("http://short.org/proxy.pac"),
              nullptr);
-  policy.Set(kProxyPacMandatory, POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
-             POLICY_SOURCE_CLOUD, base::Value(true), nullptr);
-  policy.Set(key::kProxyMode, POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
+  policy.Set(kProxyMode, POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
              POLICY_SOURCE_CLOUD,
              base::Value(ProxyPrefs::kPacScriptProxyModeName), nullptr);
   UpdateProviderPolicy(policy);
-  VerifyProxyPrefs(std::string(), "http://short.org/proxy.pac", true,
-                   std::string(), ProxyPrefs::MODE_PAC_SCRIPT);
+  VerifyProxyPrefs(std::string(), "http://short.org/proxy.pac",
+                   /* expected_proxy_pac_mandatory */ false, std::string(),
+                   ProxyPrefs::MODE_PAC_SCRIPT);
+}
+
+// ProxyPacMandatory can be set only via ProxySettings.
+TEST_F(ProxyPolicyHandlerTest, PacScriptProxyModeWithPacMandatory) {
+  base::Value proxy_settings(base::Value::Type::DICTIONARY);
+  proxy_settings.SetStringKey(kProxyPacUrl, "http://short.org/proxy.pac");
+  proxy_settings.SetStringKey(kProxyMode, ProxyPrefs::kPacScriptProxyModeName);
+  proxy_settings.SetBoolKey(kProxyPacMandatory, true);
+
+  PolicyMap policy;
+  policy.Set(kProxySettings, POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
+             POLICY_SOURCE_CLOUD, std::move(proxy_settings), nullptr);
+  UpdateProviderPolicy(policy);
+  VerifyProxyPrefs(std::string(), "http://short.org/proxy.pac",
+                   /* expected_proxy_pac_mandatory */ true, std::string(),
+                   ProxyPrefs::MODE_PAC_SCRIPT);
 }
 
 TEST_F(ProxyPolicyHandlerTest, PacScriptProxyModeInvalid) {
   PolicyMap policy;
-  policy.Set(key::kProxyMode, POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
+  policy.Set(kProxyMode, POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
              POLICY_SOURCE_CLOUD,
              base::Value(ProxyPrefs::kPacScriptProxyModeName), nullptr);
   UpdateProviderPolicy(policy);
@@ -207,12 +238,12 @@
 // empty strings for unset properties.
 TEST_F(ProxyPolicyHandlerTest, PacScriptProxyModeBug78016) {
   PolicyMap policy;
-  policy.Set(key::kProxyServer, POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
+  policy.Set(kProxyServer, POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
              POLICY_SOURCE_CLOUD, base::Value(std::string()), nullptr);
-  policy.Set(key::kProxyPacUrl, POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
+  policy.Set(kProxyPacUrl, POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
              POLICY_SOURCE_CLOUD, base::Value("http://short.org/proxy.pac"),
              nullptr);
-  policy.Set(key::kProxyMode, POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
+  policy.Set(kProxyMode, POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
              POLICY_SOURCE_CLOUD,
              base::Value(ProxyPrefs::kPacScriptProxyModeName), nullptr);
   UpdateProviderPolicy(policy);
@@ -223,7 +254,7 @@
 TEST_F(ProxyPolicyHandlerTest, UseSystemProxyServerMode) {
   PolicyMap policy;
   policy.Set(
-      key::kProxyServerMode, POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
+      kProxyServerMode, POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
       POLICY_SOURCE_CLOUD,
       base::Value(ProxyPolicyHandler::PROXY_USE_SYSTEM_PROXY_SERVER_MODE),
       nullptr);
@@ -234,7 +265,7 @@
 
 TEST_F(ProxyPolicyHandlerTest, UseSystemProxyMode) {
   PolicyMap policy;
-  policy.Set(key::kProxyMode, POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
+  policy.Set(kProxyMode, POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
              POLICY_SOURCE_CLOUD, base::Value(ProxyPrefs::kSystemProxyModeName),
              nullptr);
   UpdateProviderPolicy(policy);
@@ -242,13 +273,12 @@
                    ProxyPrefs::MODE_SYSTEM);
 }
 
-TEST_F(ProxyPolicyHandlerTest,
-       ProxyModeOverridesProxyServerMode) {
+TEST_F(ProxyPolicyHandlerTest, ProxyModeOverridesProxyServerMode) {
   PolicyMap policy;
-  policy.Set(key::kProxyServerMode, POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
+  policy.Set(kProxyServerMode, POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
              POLICY_SOURCE_CLOUD,
              base::Value(ProxyPolicyHandler::PROXY_SERVER_MODE), nullptr);
-  policy.Set(key::kProxyMode, POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
+  policy.Set(kProxyMode, POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
              POLICY_SOURCE_CLOUD,
              base::Value(ProxyPrefs::kAutoDetectProxyModeName), nullptr);
   UpdateProviderPolicy(policy);
@@ -259,16 +289,16 @@
 TEST_F(ProxyPolicyHandlerTest, ProxyInvalid) {
   // No mode expects all three parameters being set.
   PolicyMap policy;
-  policy.Set(key::kProxyPacUrl, POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
+  policy.Set(kProxyPacUrl, POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
              POLICY_SOURCE_CLOUD, base::Value("http://short.org/proxy.pac"),
              nullptr);
-  policy.Set(key::kProxyBypassList, POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
+  policy.Set(kProxyBypassList, POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
              POLICY_SOURCE_CLOUD, base::Value("http://chromium.org/override"),
              nullptr);
-  policy.Set(key::kProxyServer, POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
+  policy.Set(kProxyServer, POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
              POLICY_SOURCE_CLOUD, base::Value("chromium.org"), nullptr);
   for (int i = 0; i < ProxyPolicyHandler::MODE_COUNT; ++i) {
-    policy.Set(key::kProxyServerMode, POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
+    policy.Set(kProxyServerMode, POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
                POLICY_SOURCE_CLOUD, base::Value(i), nullptr);
     UpdateProviderPolicy(policy);
     const base::Value* value = nullptr;
@@ -276,4 +306,25 @@
   }
 }
 
-}  // namespace policy
+TEST_F(ProxyPolicyHandlerTest, SeparateProxyPoliciesMerging) {
+  PolicyMap policy;
+  // Individual proxy policy values should be collected into a dictionary.
+  policy.Set(
+      kProxyServerMode, POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
+      POLICY_SOURCE_CLOUD,
+      base::Value(ProxyPolicyHandler::PROXY_USE_SYSTEM_PROXY_SERVER_MODE),
+      nullptr);
+  // Both these policies should be ignored, since there's a higher priority
+  // policy available.
+  policy.Set(kProxyMode, POLICY_LEVEL_RECOMMENDED, POLICY_SCOPE_USER,
+             POLICY_SOURCE_CLOUD, base::Value("pac_script"), nullptr);
+  policy.Set(kProxyPacUrl, POLICY_LEVEL_RECOMMENDED, POLICY_SCOPE_USER,
+             POLICY_SOURCE_CLOUD, base::Value("http://example.com/wpad.dat"),
+             nullptr);
+
+  UpdateProviderPolicy(policy);
+  VerifyProxyPrefs(std::string(), std::string(), absl::nullopt, std::string(),
+                   ProxyPrefs::MODE_SYSTEM);
+}
+
+}  // namespace proxy_config
diff --git a/components/safe_browsing/core/common/features_unittest.cc b/components/safe_browsing/core/common/features_unittest.cc
index 3fa3ee8..4d9d973 100644
--- a/components/safe_browsing/core/common/features_unittest.cc
+++ b/components/safe_browsing/core/common/features_unittest.cc
@@ -4,6 +4,7 @@
 
 #include "components/safe_browsing/core/common/features.h"
 
+#include "base/strings/string_number_conversions.h"
 #include "base/system/sys_info.h"
 #include "base/test/scoped_feature_list.h"
 #include "testing/gtest/include/gtest/gtest.h"
diff --git a/components/search/ntp_features.cc b/components/search/ntp_features.cc
index a443f5c..ff0c6ff 100644
--- a/components/search/ntp_features.cc
+++ b/components/search/ntp_features.cc
@@ -123,6 +123,7 @@
     "NtpChromeCartModuleAbandonedCartDiscountUseUtmParam";
 const char kNtpChromeCartModuleHeuristicsImprovementParam[] =
     "NtpChromeCartModuleHeuristicsImprovementParam";
+const char kNtpChromeCartModuleCouponParam[] = "NtpChromeCartModuleCouponParam";
 const char kNtpDriveModuleDataParam[] = "NtpDriveModuleDataParam";
 const char kNtpDriveModuleManagedUsersOnlyParam[] =
     "NtpDriveModuleManagedUsersOnlyParam";
diff --git a/components/search/ntp_features.h b/components/search/ntp_features.h
index cd60b6d..f0d0f5c 100644
--- a/components/search/ntp_features.h
+++ b/components/search/ntp_features.h
@@ -62,6 +62,8 @@
 extern const char NtpChromeCartModuleAbandonedCartDiscountUseUtmParam[];
 // Parameter for enabling the cart heuristics improvement.
 extern const char kNtpChromeCartModuleHeuristicsImprovementParam[];
+// Parameter for enabling coupons on the Cart module.
+extern const char kNtpChromeCartModuleCouponParam[];
 // Parameter determining the type of Drive data to render.
 extern const char kNtpDriveModuleDataParam[];
 // Parameter for enabling the Drive module for managed users only.
diff --git a/components/segmentation_platform/internal/signals/histogram_signal_handler.cc b/components/segmentation_platform/internal/signals/histogram_signal_handler.cc
index f5d570f..933794c 100644
--- a/components/segmentation_platform/internal/signals/histogram_signal_handler.cc
+++ b/components/segmentation_platform/internal/signals/histogram_signal_handler.cc
@@ -4,6 +4,7 @@
 
 #include "components/segmentation_platform/internal/signals/histogram_signal_handler.h"
 
+#include "base/callback_helpers.h"
 #include "base/metrics/metrics_hashes.h"
 #include "components/segmentation_platform/internal/database/signal_database.h"
 
diff --git a/components/services/storage/BUILD.gn b/components/services/storage/BUILD.gn
index 3882cbec..007edf0 100644
--- a/components/services/storage/BUILD.gn
+++ b/components/services/storage/BUILD.gn
@@ -215,6 +215,5 @@
     "//base",
     "//mojo/public/cpp/bindings",
     "//testing/gmock",
-    "//third_party/blink/public/mojom/dom_storage",
   ]
 }
diff --git a/components/session_manager/core/session_manager.cc b/components/session_manager/core/session_manager.cc
index 8d7eff9a..b3245b6 100644
--- a/components/session_manager/core/session_manager.cc
+++ b/components/session_manager/core/session_manager.cc
@@ -107,6 +107,11 @@
     observer.OnUserProfileLoaded(account_id);
 }
 
+void SessionManager::NotifyNetworkErrorScreenShown() {
+  for (auto& observer : observers_)
+    observer.OnNetworkErrorScreenShown();
+}
+
 void SessionManager::NotifyUserLoggedIn(const AccountId& user_account_id,
                                         const std::string& user_id_hash,
                                         bool browser_restart,
diff --git a/components/session_manager/core/session_manager.h b/components/session_manager/core/session_manager.h
index 27a7bfad..985a876e 100644
--- a/components/session_manager/core/session_manager.h
+++ b/components/session_manager/core/session_manager.h
@@ -64,6 +64,7 @@
 
   // Various helpers to notify observers.
   void NotifyUserProfileLoaded(const AccountId& account_id);
+  void NotifyNetworkErrorScreenShown();
 
   SessionState session_state() const { return session_state_; }
   const std::vector<Session>& sessions() const { return sessions_; }
diff --git a/components/session_manager/core/session_manager_observer.h b/components/session_manager/core/session_manager_observer.h
index a7516f2..8c9fe08 100644
--- a/components/session_manager/core/session_manager_observer.h
+++ b/components/session_manager/core/session_manager_observer.h
@@ -28,6 +28,10 @@
   // UserSessionStateObserver::OnActiveUserChanged() is invoked immediately
   // after the user has logged in.
   virtual void OnUserSessionStarted(bool is_primary_user) {}
+
+  // Invoked when a network error message is displayed on the WebUI login
+  // screen.
+  virtual void OnNetworkErrorScreenShown() {}
 };
 
 }  // namespace session_manager
diff --git a/components/signin/internal/identity_manager/BUILD.gn b/components/signin/internal/identity_manager/BUILD.gn
index e04392e9..18e582e 100644
--- a/components/signin/internal/identity_manager/BUILD.gn
+++ b/components/signin/internal/identity_manager/BUILD.gn
@@ -98,6 +98,7 @@
   }
 
   if (is_chromeos_ash) {
+    deps += [ "//ash/constants" ]
     public_deps += [ "//ash/components/account_manager" ]
   } else {
     sources += [
@@ -183,7 +184,10 @@
   if (is_chromeos_ash) {
     sources += [ "profile_oauth2_token_service_delegate_chromeos_unittest.cc" ]
 
-    deps += [ "//ash/components/account_manager" ]
+    deps += [
+      "//ash/components/account_manager",
+      "//ash/constants",
+    ]
   } else {
     sources += [ "primary_account_policy_manager_impl_unittest.cc" ]
   }
diff --git a/components/signin/internal/identity_manager/DEPS b/components/signin/internal/identity_manager/DEPS
index ab4e4b1..dff6bbc 100644
--- a/components/signin/internal/identity_manager/DEPS
+++ b/components/signin/internal/identity_manager/DEPS
@@ -1,4 +1,5 @@
 include_rules = [
+  "+ash/constants",
   "+components/signin/internal/base",
   "+components/signin/public/base",
   "+components/signin/public/identity_manager",
diff --git a/components/signin/internal/identity_manager/account_fetcher_service.cc b/components/signin/internal/identity_manager/account_fetcher_service.cc
index dd9261f..390383a 100644
--- a/components/signin/internal/identity_manager/account_fetcher_service.cc
+++ b/components/signin/internal/identity_manager/account_fetcher_service.cc
@@ -10,7 +10,6 @@
 
 #include "base/bind.h"
 #include "base/command_line.h"
-#include "base/feature_list.h"
 #include "base/metrics/field_trial.h"
 #include "base/metrics/histogram_functions.h"
 #include "base/trace_event/trace_event.h"
@@ -32,6 +31,10 @@
 #include "net/http/http_status_code.h"
 #include "services/network/public/cpp/shared_url_loader_factory.h"
 
+#if BUILDFLAG(IS_CHROMEOS_ASH)
+#include "ash/constants/ash_features.h"
+#endif
+
 #if defined(OS_ANDROID)
 #include "components/signin/internal/identity_manager/child_account_info_fetcher_android.h"
 #include "components/signin/public/identity_manager/tribool.h"
@@ -238,7 +241,7 @@
     return true;
 
 #if BUILDFLAG(IS_CHROMEOS_ASH)
-  return base::FeatureList::IsEnabled(switches::kMinorModeSupport);
+  return ash::features::IsMinorModeRestrictionEnabled();
 #else
   return false;
 #endif
diff --git a/components/signin/internal/identity_manager/account_tracker_service_unittest.cc b/components/signin/internal/identity_manager/account_tracker_service_unittest.cc
index 4a203fa..c5e7f17 100644
--- a/components/signin/internal/identity_manager/account_tracker_service_unittest.cc
+++ b/components/signin/internal/identity_manager/account_tracker_service_unittest.cc
@@ -41,6 +41,10 @@
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
+#if BUILDFLAG(IS_CHROMEOS_ASH)
+#include "ash/constants/ash_features.h"
+#endif
+
 #if defined(OS_ANDROID)
 #include "components/signin/public/identity_manager/identity_test_utils.h"
 #endif
@@ -705,7 +709,8 @@
        TokenAvailable_AccountCapabilitiesFetcherDisabled) {
 #if BUILDFLAG(IS_CHROMEOS_ASH)
   base::test::ScopedFeatureList scoped_feature_list;
-  scoped_feature_list.InitAndDisableFeature(switches::kMinorModeSupport);
+  scoped_feature_list.InitAndDisableFeature(
+      ash::features::kMinorModeRestriction);
 #endif
 
   account_fetcher()->EnableAccountCapabilitiesFetcherForTest(false);
diff --git a/components/signin/public/base/signin_switches.cc b/components/signin/public/base/signin_switches.cc
index 016f849..75854e7 100644
--- a/components/signin/public/base/signin_switches.cc
+++ b/components/signin/public/base/signin_switches.cc
@@ -37,8 +37,10 @@
                                              base::FEATURE_DISABLED_BY_DEFAULT};
 #endif
 
+#if !BUILDFLAG(IS_CHROMEOS_ASH)
 const base::Feature kMinorModeSupport{"MinorModeSupport",
                                       base::FEATURE_DISABLED_BY_DEFAULT};
+#endif
 
 const base::Feature kForceDisableExtendedSyncPromos{
     "ForceDisableExtendedSyncPromos", base::FEATURE_DISABLED_BY_DEFAULT};
diff --git a/components/signin/public/base/signin_switches.h b/components/signin/public/base/signin_switches.h
index fc346a63..ea3b3ee 100644
--- a/components/signin/public/base/signin_switches.h
+++ b/components/signin/public/base/signin_switches.h
@@ -38,8 +38,10 @@
 extern const base::Feature kForceStartupSigninPromo;
 #endif
 
+#if !BUILDFLAG(IS_CHROMEOS_ASH)
 // Support for the minor mode.
 extern const base::Feature kMinorModeSupport;
+#endif
 
 // This feature disables all extended sync promos.
 extern const base::Feature kForceDisableExtendedSyncPromos;
diff --git a/components/sync/trusted_vault/standalone_trusted_vault_backend_unittest.cc b/components/sync/trusted_vault/standalone_trusted_vault_backend_unittest.cc
index 6868f90..6baf476b 100644
--- a/components/sync/trusted_vault/standalone_trusted_vault_backend_unittest.cc
+++ b/components/sync/trusted_vault/standalone_trusted_vault_backend_unittest.cc
@@ -9,6 +9,7 @@
 #include <utility>
 #include <vector>
 
+#include "base/callback_helpers.h"
 #include "base/files/file_util.h"
 #include "base/files/scoped_temp_dir.h"
 #include "base/test/bind.h"
diff --git a/components/tracing/common/graphics_memory_dump_provider_android.cc b/components/tracing/common/graphics_memory_dump_provider_android.cc
index bf0c58f4..4249d18 100644
--- a/components/tracing/common/graphics_memory_dump_provider_android.cc
+++ b/components/tracing/common/graphics_memory_dump_provider_android.cc
@@ -11,6 +11,7 @@
 #include <sys/time.h>
 #include <sys/types.h>
 #include <sys/un.h>
+#include <unistd.h>
 
 #include "base/files/scoped_file.h"
 #include "base/logging.h"
diff --git a/components/translate/core/browser/translate_manager.cc b/components/translate/core/browser/translate_manager.cc
index f5f8eaa..a86a2f0 100644
--- a/components/translate/core/browser/translate_manager.cc
+++ b/components/translate/core/browser/translate_manager.cc
@@ -278,7 +278,14 @@
   }
 
   const std::string source_language = language_state_.source_language();
-
+  if (source_language.empty()) {
+    if (!menuLogging)
+      return false;
+    TranslateBrowserMetrics::ReportMenuTranslationUnavailableReason(
+        TranslateBrowserMetrics::MenuTranslationUnavailableReason::
+            kSourceLangUnknown);
+    can_translate = false;
+  }
   // Translation of unknown source language pages is supported on Desktop
   // platforms, experimentally supported on Android and not supported on iOS.
   bool unknown_source_supported = true;
@@ -289,8 +296,7 @@
   unknown_source_supported = false;
 #endif
   if (!unknown_source_supported &&
-      (source_language == translate::kUnknownLanguageCode ||
-       source_language.empty())) {
+      source_language == translate::kUnknownLanguageCode) {
     if (!menuLogging)
       return false;
     TranslateBrowserMetrics::ReportMenuTranslationUnavailableReason(
diff --git a/components/translate/core/browser/translate_manager_unittest.cc b/components/translate/core/browser/translate_manager_unittest.cc
index 89fbff8..ee6a19b 100644
--- a/components/translate/core/browser/translate_manager_unittest.cc
+++ b/components/translate/core/browser/translate_manager_unittest.cc
@@ -1101,19 +1101,8 @@
 
   translate_manager_->GetLanguageState()->LanguageDetermined("", true);
 
-  // Translation before language detection is complete is not supported on iOS.
-  // Experiment in place for supporting it on Android.
-  bool unknown_source_supported = true;
-#if defined(OS_ANDROID)
-  if (!base::FeatureList::IsEnabled(language::kDetectedSourceLanguageOption))
-    unknown_source_supported = false;
-#elif defined(OS_IOS)
-  unknown_source_supported = false;
-#endif
-  EXPECT_EQ(translate_manager_->CanManuallyTranslate(),
-            unknown_source_supported);
-  EXPECT_EQ(translate_manager_->CanManuallyTranslate(true),
-            unknown_source_supported);
+  EXPECT_FALSE(translate_manager_->CanManuallyTranslate());
+  EXPECT_FALSE(translate_manager_->CanManuallyTranslate(true));
 }
 
 TEST_F(TranslateManagerTest, CanManuallyTranslate_UndefinedSourceLanguage) {
@@ -1140,8 +1129,6 @@
 #endif
   EXPECT_EQ(translate_manager_->CanManuallyTranslate(),
             unknown_source_supported);
-  EXPECT_EQ(translate_manager_->CanManuallyTranslate(true),
-            unknown_source_supported);
 }
 
 TEST_F(TranslateManagerTest, PredefinedTargetLanguage) {
diff --git a/components/viz/client/DEPS b/components/viz/client/DEPS
index ee810d9..509fd138 100644
--- a/components/viz/client/DEPS
+++ b/components/viz/client/DEPS
@@ -1,7 +1,6 @@
 # Please consult components/viz/README.md about allowable dependencies.
 
 include_rules = [
-  "-components/viz/common/switches.h",
   "+components/viz/client",
   "+gpu/GLES2/gl2extchromium.h",
   "+gpu/command_buffer/client",
diff --git a/components/viz/common/switches.cc b/components/viz/common/switches.cc
index 0b28190..14db420 100644
--- a/components/viz/common/switches.cc
+++ b/components/viz/common/switches.cc
@@ -5,6 +5,7 @@
 #include "components/viz/common/switches.h"
 
 #include "base/command_line.h"
+#include "base/logging.h"
 #include "base/strings/string_number_conversions.h"
 #include "build/chromeos_buildflags.h"
 #include "components/viz/common/constants.h"
@@ -24,6 +25,10 @@
 // Also implies --disable-gpu-vsync (see //ui/gl/gl_switches.h).
 const char kDisableFrameRateLimit[] = "disable-frame-rate-limit";
 
+// Slows down animations during a DocumentTransition for debugging.
+const char kDocumentTransitionSlowdownFactor[] =
+    "document-transition-slowdown-factor";
+
 // Sets the number of max pending frames in the GL buffer queue to 1.
 const char kDoubleBufferCompositing[] = "double-buffer-compositing";
 
@@ -89,4 +94,17 @@
   return activation_deadline_in_frames;
 }
 
+int GetDocumentTransitionSlowDownFactor() {
+  base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
+  if (!command_line->HasSwitch(kDocumentTransitionSlowdownFactor))
+    return 1;
+
+  auto factor_str =
+      command_line->GetSwitchValueASCII(kDocumentTransitionSlowdownFactor);
+  int factor = 0;
+  LOG_IF(ERROR, !base::StringToInt(factor_str, &factor))
+      << "Error parsing document transition slow down factor " << factor_str;
+  return std::max(1, factor);
+}
+
 }  // namespace switches
diff --git a/components/viz/common/switches.h b/components/viz/common/switches.h
index 9ed30ac6..64490ee 100644
--- a/components/viz/common/switches.h
+++ b/components/viz/common/switches.h
@@ -17,6 +17,7 @@
 VIZ_COMMON_EXPORT extern const char kDeJellyScreenWidth[];
 VIZ_COMMON_EXPORT extern const char kDeadlineToSynchronizeSurfaces[];
 VIZ_COMMON_EXPORT extern const char kDisableFrameRateLimit[];
+VIZ_COMMON_EXPORT extern const char kDocumentTransitionSlowdownFactor[];
 VIZ_COMMON_EXPORT extern const char kDoubleBufferCompositing[];
 VIZ_COMMON_EXPORT extern const char kEnableDeJelly[];
 VIZ_COMMON_EXPORT extern const char kEnableHardwareOverlays[];
@@ -39,6 +40,7 @@
 VIZ_COMMON_EXPORT extern const char kShowDCLayerDebugBorders[];
 
 VIZ_COMMON_EXPORT absl::optional<uint32_t> GetDeadlineToSynchronizeSurfaces();
+VIZ_COMMON_EXPORT int GetDocumentTransitionSlowDownFactor();
 
 }  // namespace switches
 
diff --git a/components/viz/host/DEPS b/components/viz/host/DEPS
index 5beac85..17d1490 100644
--- a/components/viz/host/DEPS
+++ b/components/viz/host/DEPS
@@ -2,7 +2,6 @@
 
 include_rules = [
   "+components/discardable_memory/public/mojom",
-  "-components/viz/common/switches.h",
   "+media/base/video_types.h",
   "+gpu/command_buffer/client",
   "+gpu/command_buffer/common",
diff --git a/components/viz/service/DEPS b/components/viz/service/DEPS
index 72b1bd39..53f869e 100644
--- a/components/viz/service/DEPS
+++ b/components/viz/service/DEPS
@@ -3,7 +3,6 @@
 include_rules = [
   "+cc",
   "+components/power_scheduler",
-  "-components/viz/common/switches.h",
   "-components/viz/service",
   "+components/viz/service/debugger/viz_debugger.h",
   "+components/viz/service/gl/gpu_service_impl.h",
diff --git a/components/viz/service/transitions/surface_animation_manager.cc b/components/viz/service/transitions/surface_animation_manager.cc
index 187824f..d3577243 100644
--- a/components/viz/service/transitions/surface_animation_manager.cc
+++ b/components/viz/service/transitions/surface_animation_manager.cc
@@ -20,6 +20,7 @@
 #include "components/viz/common/resources/resource_id.h"
 #include "components/viz/common/resources/returned_resource.h"
 #include "components/viz/common/resources/transferable_resource.h"
+#include "components/viz/common/switches.h"
 #include "components/viz/common/transition_utils.h"
 #include "components/viz/service/surfaces/surface.h"
 #include "components/viz/service/surfaces/surface_saved_frame_storage.h"
@@ -39,16 +40,14 @@
 namespace viz {
 namespace {
 
-constexpr int kAnimationSlowDownFactor = 1;
-
 constexpr base::TimeDelta kDefaultAnimationDuration =
-    base::TimeDelta::FromMilliseconds(250) * kAnimationSlowDownFactor;
+    base::TimeDelta::FromMilliseconds(250);
 
 constexpr base::TimeDelta kSharedOpacityAnimationDuration =
-    base::TimeDelta::FromMilliseconds(60) * kAnimationSlowDownFactor;
+    base::TimeDelta::FromMilliseconds(60);
 
 constexpr base::TimeDelta kSharedOpacityAnimationDelay =
-    base::TimeDelta::FromMilliseconds(60) * kAnimationSlowDownFactor;
+    base::TimeDelta::FromMilliseconds(60);
 
 // Scale the overall duration to produce the opacity duration. Opacity
 // transitions which reveal an element (i.e., transition opacity from 0 -> 1)
@@ -141,45 +140,49 @@
 std::unique_ptr<gfx::AnimationCurve> CreateOpacityCurve(
     float start_opacity,
     float end_opacity,
+    base::TimeDelta opacity_duration,
+    base::TimeDelta opacity_delay,
+    base::TimeDelta total_duration,
     gfx::FloatAnimationCurve::Target* target) {
   auto float_curve = gfx::KeyframedFloatAnimationCurve::Create();
 
   // The curve starts at opacity delay and runs for opacity animation, so it
   // potentially has 4 points:
   // time 0 == start opacity
-  // time 'delay' == start opacity
-  // time 'delay' + 'duration' == end opacity
-  // time end of animation == end opacity
+  // time 'opacity_delay' == start opacity
+  // time 'opacity_delay' + 'opacity_duration' == end opacity
+  // time 'total_duration' == end opacity
   float_curve->AddKeyframe(
       gfx::FloatKeyframe::Create(base::TimeDelta(), start_opacity, nullptr));
-  if (!kSharedOpacityAnimationDelay.is_zero()) {
-    float_curve->AddKeyframe(gfx::FloatKeyframe::Create(
-        kSharedOpacityAnimationDelay, start_opacity, nullptr));
+  if (!opacity_delay.is_zero()) {
+    float_curve->AddKeyframe(
+        gfx::FloatKeyframe::Create(opacity_delay, start_opacity, nullptr));
   }
   float_curve->AddKeyframe(gfx::FloatKeyframe::Create(
-      kSharedOpacityAnimationDuration + kSharedOpacityAnimationDelay,
-      end_opacity, nullptr));
-  float_curve->AddKeyframe(gfx::FloatKeyframe::Create(kDefaultAnimationDuration,
-                                                      end_opacity, nullptr));
+      opacity_duration + opacity_delay, end_opacity, nullptr));
+  float_curve->AddKeyframe(
+      gfx::FloatKeyframe::Create(total_duration, end_opacity, nullptr));
   float_curve->set_target(target);
   return float_curve;
 }
 
 std::unique_ptr<gfx::AnimationCurve> CreateSizeCurve(
     const gfx::SizeF& start_size,
+    base::TimeDelta duration,
     std::unique_ptr<gfx::TimingFunction> timing_function,
     gfx::SizeAnimationCurve::Target* target) {
   auto size_curve = gfx::KeyframedSizeAnimationCurve::Create();
   size_curve->AddKeyframe(gfx::SizeKeyframe::Create(
       base::TimeDelta(), start_size, timing_function->Clone()));
   size_curve->AddKeyframe(gfx::SizeKeyframe::Create(
-      kDefaultAnimationDuration, start_size, std::move(timing_function)));
+      duration, start_size, std::move(timing_function)));
   size_curve->set_target(target);
   return size_curve;
 }
 
 std::unique_ptr<gfx::AnimationCurve> CreateTransformCurve(
     const gfx::Transform& transform,
+    base::TimeDelta duration,
     std::unique_ptr<gfx::TimingFunction> timing_function,
     gfx::TransformAnimationCurve::Target* target) {
   gfx::TransformOperations transform_ops;
@@ -189,7 +192,7 @@
   transform_curve->AddKeyframe(gfx::TransformKeyframe::Create(
       base::TimeDelta(), transform_ops, timing_function->Clone()));
   transform_curve->AddKeyframe(gfx::TransformKeyframe::Create(
-      kDefaultAnimationDuration, transform_ops, std::move(timing_function)));
+      duration, transform_ops, std::move(timing_function)));
   transform_curve->set_target(target);
   return transform_curve;
 }
@@ -198,7 +201,9 @@
 
 SurfaceAnimationManager::SurfaceAnimationManager(
     SharedBitmapManager* shared_bitmap_manager)
-    : transferable_resource_tracker_(shared_bitmap_manager) {}
+    : animation_slowdown_factor_(
+          switches::GetDocumentTransitionSlowDownFactor()),
+      transferable_resource_tracker_(shared_bitmap_manager) {}
 
 SurfaceAnimationManager::~SurfaceAnimationManager() = default;
 
@@ -855,7 +860,8 @@
               : gfx::CubicBezierTimingFunction::EaseType::EASE_OUT);
 
   // Create the transform curve.
-  base::TimeDelta transform_duration = kDefaultAnimationDuration;
+  base::TimeDelta transform_duration =
+      ApplySlowdownFactor(kDefaultAnimationDuration);
 
   std::unique_ptr<gfx::KeyframedTransformAnimationCurve> transform_curve(
       gfx::KeyframedTransformAnimationCurve::Create());
@@ -912,6 +918,11 @@
   shared_animations_.resize(
       animate_directive_->shared_render_pass_ids().size());
 
+  const auto opacity_duration =
+      ApplySlowdownFactor(kSharedOpacityAnimationDuration);
+  const auto opacity_delay = ApplySlowdownFactor(kSharedOpacityAnimationDelay);
+  const auto total_duration = ApplySlowdownFactor(kDefaultAnimationDuration);
+
   // Since we don't have a target state yet, create animations as if all of the
   // shared elements are targeted to stay in place with opacity going to 0.
   for (size_t i = 0; i < saved_textures_->shared.size(); ++i) {
@@ -928,7 +939,9 @@
     //   element to gradually fade out.
     // The animation is re-targeted once the dest element values are known.
     float start_opacity = has_src_element ? shared->draw_data.opacity : 0.f;
-    auto opacity_curve = CreateOpacityCurve(start_opacity, 1.f, &state);
+    auto opacity_curve =
+        CreateOpacityCurve(start_opacity, 1.f, opacity_duration, opacity_delay,
+                           total_duration, &state);
     state.driver().AddKeyframeModel(gfx::KeyframeModel::Create(
         std::move(opacity_curve), gfx::KeyframeEffect::GetNextKeyframeModelId(),
         SharedAnimationState::kCombinedOpacity));
@@ -943,14 +956,16 @@
     // Interpolation between the 2 textures involves an opacity animation (to
     // cross-fade the content) and a scale animation to transition the content
     // size.
-    auto content_size_curve = CreateSizeCurve(
-        gfx::SizeF(shared->draw_data.size), ease_timing->Clone(), &state);
+    auto content_size_curve =
+        CreateSizeCurve(gfx::SizeF(shared->draw_data.size), total_duration,
+                        ease_timing->Clone(), &state);
     state.driver().AddKeyframeModel(gfx::KeyframeModel::Create(
         std::move(content_size_curve),
         gfx::KeyframeEffect::GetNextKeyframeModelId(),
         SharedAnimationState::kContentSize));
-    auto content_opacity_curve =
-        CreateOpacityCurve(/*start_opacity=*/0.f, /*end_opacity=*/1.f, &state);
+    auto content_opacity_curve = CreateOpacityCurve(
+        /*start_opacity=*/0.f, /*end_opacity=*/1.f, opacity_duration,
+        opacity_delay, total_duration, &state);
     state.driver().AddKeyframeModel(gfx::KeyframeModel::Create(
         std::move(content_opacity_curve),
         gfx::KeyframeEffect::GetNextKeyframeModelId(),
@@ -959,8 +974,9 @@
     // The screen space transform for the interpolated texture is animated from
     // src element to dest element value. The animation is re-targeted once the
     // dest element values are known.
-    auto transform_curve = CreateTransformCurve(
-        shared->draw_data.target_transform, ease_timing->Clone(), &state);
+    auto transform_curve =
+        CreateTransformCurve(shared->draw_data.target_transform, total_duration,
+                             ease_timing->Clone(), &state);
     state.driver().AddKeyframeModel(gfx::KeyframeModel::Create(
         std::move(transform_curve),
         gfx::KeyframeEffect::GetNextKeyframeModelId(),
@@ -968,6 +984,11 @@
   }
 }
 
+base::TimeDelta SurfaceAnimationManager::ApplySlowdownFactor(
+    base::TimeDelta original) const {
+  return original * animation_slowdown_factor_;
+}
+
 // RootAnimationState
 SurfaceAnimationManager::RootAnimationState::RootAnimationState() = default;
 SurfaceAnimationManager::RootAnimationState::RootAnimationState(
diff --git a/components/viz/service/transitions/surface_animation_manager.h b/components/viz/service/transitions/surface_animation_manager.h
index f5230ae..ef73269a1 100644
--- a/components/viz/service/transitions/surface_animation_manager.h
+++ b/components/viz/service/transitions/surface_animation_manager.h
@@ -134,12 +134,15 @@
   // Tick both the root and shared animations.
   void TickAnimations(base::TimeTicks new_time);
 
+  base::TimeDelta ApplySlowdownFactor(base::TimeDelta original) const;
+
   enum class State { kIdle, kAnimating, kLastFrame };
 
   TransitionDirectiveCompleteCallback sequence_id_finished_callback_;
 
   uint32_t last_processed_sequence_id_ = 0;
 
+  const int animation_slowdown_factor_ = 1;
   TransferableResourceTracker transferable_resource_tracker_;
 
   absl::optional<TransferableResourceTracker::ResourceFrame> saved_textures_;
diff --git a/content/browser/BUILD.gn b/content/browser/BUILD.gn
index f4db68a..5aa7e0c 100644
--- a/content/browser/BUILD.gn
+++ b/content/browser/BUILD.gn
@@ -212,8 +212,6 @@
     "//third_party/blink/public:scaled_resources",
     "//third_party/blink/public/common",
     "//third_party/blink/public/common:font_enumeration_table_proto",
-    "//third_party/blink/public/mojom/dom_storage",
-    "//third_party/blink/public/mojom/frame",
     "//third_party/blink/public/strings",
     "//third_party/boringssl",
     "//third_party/brotli:dec",
@@ -1069,8 +1067,6 @@
     "interest_group/interest_group_manager.h",
     "interest_group/interest_group_storage.cc",
     "interest_group/interest_group_storage.h",
-    "interest_group/restricted_interest_group_store_impl.cc",
-    "interest_group/restricted_interest_group_store_impl.h",
     "isolated_origin_util.cc",
     "isolated_origin_util.h",
     "isolation_context.cc",
diff --git a/content/browser/accessibility/accessibility_tree_formatter_android.cc b/content/browser/accessibility/accessibility_tree_formatter_android.cc
index 9d0a6ed..176bbfe 100644
--- a/content/browser/accessibility/accessibility_tree_formatter_android.cc
+++ b/content/browser/accessibility/accessibility_tree_formatter_android.cc
@@ -265,8 +265,8 @@
 
   for (unsigned i = 0; i < base::size(BOOL_ATTRIBUTES); i++) {
     const char* attribute_name = BOOL_ATTRIBUTES[i];
-    bool value;
-    if (dict.GetBoolean(attribute_name, &value) && value)
+    absl::optional<bool> value = dict.FindBoolPath(attribute_name);
+    if (value && *value)
       WriteAttribute(true, attribute_name, &line);
   }
 
@@ -289,8 +289,8 @@
 
   for (unsigned i = 0; i < base::size(ACTION_ATTRIBUTES); i++) {
     const char* attribute_name = ACTION_ATTRIBUTES[i];
-    bool value;
-    if (dict.GetBoolean(attribute_name, &value) && value) {
+    absl::optional<bool> value = dict.FindBoolPath(attribute_name);
+    if (value && *value) {
       WriteAttribute(false /* Exclude actions by default */, attribute_name,
                      &line);
     }
diff --git a/content/browser/accessibility/accessibility_tree_formatter_blink.cc b/content/browser/accessibility/accessibility_tree_formatter_blink.cc
index 3e9a1bf..7c6fc9d 100644
--- a/content/browser/accessibility/accessibility_tree_formatter_blink.cc
+++ b/content/browser/accessibility/accessibility_tree_formatter_blink.cc
@@ -635,13 +635,9 @@
   }
 
   // Offscreen and Focused states are not in the state list.
-  bool offscreen = false;
-  dict.GetBoolean(STATE_OFFSCREEN, &offscreen);
-  if (offscreen)
+  if (dict.FindBoolPath(STATE_OFFSCREEN).value_or(false))
     WriteAttribute(false, STATE_OFFSCREEN, &line);
-  bool focused = false;
-  dict.GetBoolean(STATE_FOCUSED, &focused);
-  if (focused)
+  if (dict.FindBoolPath(STATE_FOCUSED).value_or(false))
     WriteAttribute(false, STATE_FOCUSED, &line);
 
   if (dict.FindKey("boundsX") && dict.FindKey("boundsY")) {
@@ -656,9 +652,7 @@
         &line);
   }
 
-  bool ignored = false;
-  dict.GetBoolean("ignored", &ignored);
-  if (!ignored) {
+  if (!dict.FindBoolPath("ignored").value_or(false)) {
     if (dict.FindKey("pageBoundsX") && dict.FindKey("pageBoundsY")) {
       WriteAttribute(
           false,
@@ -687,8 +681,7 @@
     }
   }
 
-  bool transform;
-  if (dict.GetBoolean("transform", &transform) && transform)
+  if (dict.FindBoolPath("transform").value_or(false))
     WriteAttribute(false, "transform", &line);
 
   for (int attr_index = static_cast<int32_t>(ax::mojom::StringAttribute::kNone);
@@ -722,12 +715,12 @@
        attr_index <= static_cast<int32_t>(ax::mojom::BoolAttribute::kMaxValue);
        ++attr_index) {
     auto attr = static_cast<ax::mojom::BoolAttribute>(attr_index);
-    bool bool_value;
-    if (!dict.GetBoolean(ui::ToString(attr), &bool_value))
+    absl::optional<bool> bool_value = dict.FindBoolPath(ui::ToString(attr));
+    if (!bool_value)
       continue;
     WriteAttribute(false,
                    base::StringPrintf("%s=%s", ui::ToString(attr),
-                                      bool_value ? "true" : "false"),
+                                      *bool_value ? "true" : "false"),
                    &line);
   }
 
diff --git a/content/browser/accessibility/browser_accessibility_manager_android.cc b/content/browser/accessibility/browser_accessibility_manager_android.cc
index 3933cf02..728abdc 100644
--- a/content/browser/accessibility/browser_accessibility_manager_android.cc
+++ b/content/browser/accessibility/browser_accessibility_manager_android.cc
@@ -288,6 +288,7 @@
     case ui::AXEventGenerator::Event::ATOMIC_CHANGED:
     case ui::AXEventGenerator::Event::AUTO_COMPLETE_CHANGED:
     case ui::AXEventGenerator::Event::BUSY_CHANGED:
+    case ui::AXEventGenerator::Event::CARET_BOUNDS_CHANGED:
     case ui::AXEventGenerator::Event::CHECKED_STATE_DESCRIPTION_CHANGED:
     case ui::AXEventGenerator::Event::CHILDREN_CHANGED:
     case ui::AXEventGenerator::Event::CLASS_NAME_CHANGED:
diff --git a/content/browser/accessibility/browser_accessibility_manager_auralinux.cc b/content/browser/accessibility/browser_accessibility_manager_auralinux.cc
index 5fb4e974..935559e 100644
--- a/content/browser/accessibility/browser_accessibility_manager_auralinux.cc
+++ b/content/browser/accessibility/browser_accessibility_manager_auralinux.cc
@@ -277,6 +277,7 @@
     case ui::AXEventGenerator::Event::ATOMIC_CHANGED:
     case ui::AXEventGenerator::Event::AUTO_COMPLETE_CHANGED:
     case ui::AXEventGenerator::Event::BUSY_CHANGED:
+    case ui::AXEventGenerator::Event::CARET_BOUNDS_CHANGED:
     case ui::AXEventGenerator::Event::CHECKED_STATE_DESCRIPTION_CHANGED:
     case ui::AXEventGenerator::Event::CHILDREN_CHANGED:
     case ui::AXEventGenerator::Event::CONTROLS_CHANGED:
diff --git a/content/browser/accessibility/browser_accessibility_manager_mac.mm b/content/browser/accessibility/browser_accessibility_manager_mac.mm
index 91e7c04..8f70e52 100644
--- a/content/browser/accessibility/browser_accessibility_manager_mac.mm
+++ b/content/browser/accessibility/browser_accessibility_manager_mac.mm
@@ -368,6 +368,7 @@
     case ui::AXEventGenerator::Event::ATOMIC_CHANGED:
     case ui::AXEventGenerator::Event::AUTO_COMPLETE_CHANGED:
     case ui::AXEventGenerator::Event::BUSY_CHANGED:
+    case ui::AXEventGenerator::Event::CARET_BOUNDS_CHANGED:
     case ui::AXEventGenerator::Event::CHECKED_STATE_DESCRIPTION_CHANGED:
     case ui::AXEventGenerator::Event::CHILDREN_CHANGED:
     case ui::AXEventGenerator::Event::CONTROLS_CHANGED:
diff --git a/content/browser/accessibility/browser_accessibility_manager_win.cc b/content/browser/accessibility/browser_accessibility_manager_win.cc
index 4ce35f1..1a5d63f 100644
--- a/content/browser/accessibility/browser_accessibility_manager_win.cc
+++ b/content/browser/accessibility/browser_accessibility_manager_win.cc
@@ -492,6 +492,7 @@
     // Currently unused events on this platform.
     case ui::AXEventGenerator::Event::ATK_TEXT_OBJECT_ATTRIBUTE_CHANGED:
     case ui::AXEventGenerator::Event::AUTO_COMPLETE_CHANGED:
+    case ui::AXEventGenerator::Event::CARET_BOUNDS_CHANGED:
     case ui::AXEventGenerator::Event::CHECKED_STATE_DESCRIPTION_CHANGED:
     case ui::AXEventGenerator::Event::DETAILS_CHANGED:
     case ui::AXEventGenerator::Event::DOCUMENT_TITLE_CHANGED:
diff --git a/content/browser/browser_interface_binders.cc b/content/browser/browser_interface_binders.cc
index 7170e9d..68e131d 100644
--- a/content/browser/browser_interface_binders.cc
+++ b/content/browser/browser_interface_binders.cc
@@ -25,7 +25,6 @@
 #include "content/browser/handwriting/handwriting_recognition_service_factory.h"
 #include "content/browser/image_capture/image_capture_impl.h"
 #include "content/browser/interest_group/ad_auction_service_impl.h"
-#include "content/browser/interest_group/restricted_interest_group_store_impl.h"
 #include "content/browser/keyboard_lock/keyboard_lock_service_impl.h"
 #include "content/browser/loader/content_security_notifier.h"
 #include "content/browser/media/midi_host.h"
@@ -977,8 +976,6 @@
   if (base::FeatureList::IsEnabled(blink::features::kFledgeInterestGroups)) {
     map->Add<blink::mojom::AdAuctionService>(
         base::BindRepeating(&AdAuctionServiceImpl::CreateMojoService));
-    map->Add<blink::mojom::RestrictedInterestGroupStore>(base::BindRepeating(
-        &RestrictedInterestGroupStoreImpl::CreateMojoService));
   }
   map->Add<blink::mojom::MediaSessionService>(
       base::BindRepeating(&MediaSessionServiceImpl::Create));
diff --git a/content/browser/conversions/conversion_manager_impl.cc b/content/browser/conversions/conversion_manager_impl.cc
index 668861b..ddaa9e3d 100644
--- a/content/browser/conversions/conversion_manager_impl.cc
+++ b/content/browser/conversions/conversion_manager_impl.cc
@@ -11,6 +11,7 @@
 #include "base/callback_helpers.h"
 #include "base/command_line.h"
 #include "base/memory/ptr_util.h"
+#include "base/metrics/histogram_functions.h"
 #include "base/task/lazy_thread_pool_task_runner.h"
 #include "base/threading/sequence_bound.h"
 #include "base/time/default_clock.h"
@@ -68,6 +69,10 @@
   return info.should_retry && !past_max_allowed_age;
 }
 
+void RecordCreateReportStatus(ConversionStorage::CreateReportStatus result) {
+  base::UmaHistogramEnumeration("Conversions.CreateReportStatus", result);
+}
+
 }  // namespace
 
 const constexpr base::TimeDelta kConversionManagerQueueReportsInterval =
@@ -172,12 +177,10 @@
 }
 
 void ConversionManagerImpl::HandleConversion(StorableConversion conversion) {
-  // TODO(https://crbug.com/1043345): Add UMA for the number of conversions we
-  // are logging to storage, and the number of new reports logged to storage.
   conversion_storage_
       .AsyncCall(&ConversionStorage::MaybeCreateAndStoreConversionReport)
       .WithArgs(std::move(conversion))
-      .Then(base::DoNothing::Once<bool>());
+      .Then(base::BindOnce(&RecordCreateReportStatus));
 
   // If we are running in debug mode, we should also schedule a task to
   // gather and send any new reports.
diff --git a/content/browser/conversions/conversion_manager_impl_unittest.cc b/content/browser/conversions/conversion_manager_impl_unittest.cc
index d779043..9b8e702 100644
--- a/content/browser/conversions/conversion_manager_impl_unittest.cc
+++ b/content/browser/conversions/conversion_manager_impl_unittest.cc
@@ -17,6 +17,7 @@
 #include "base/run_loop.h"
 #include "base/sequenced_task_runner.h"
 #include "base/test/bind.h"
+#include "base/test/metrics/histogram_tester.h"
 #include "base/time/clock.h"
 #include "base/time/time.h"
 #include "build/build_config.h"
@@ -664,4 +665,13 @@
   EXPECT_EQ(3u, test_reporter_->num_reports());
 }
 
+TEST_F(ConversionManagerImplTest, HandleConversion_RecordsMetric) {
+  base::HistogramTester histograms;
+  conversion_manager_->HandleConversion(DefaultConversion());
+  ExpectNumStoredReports(0);
+  histograms.ExpectUniqueSample(
+      "Conversions.CreateReportStatus",
+      ConversionStorage::CreateReportStatus::kNoMatchingImpressions, 1);
+}
+
 }  // namespace content
diff --git a/content/browser/conversions/conversion_storage.h b/content/browser/conversions/conversion_storage.h
index eba4bd8..5caad566 100644
--- a/content/browser/conversions/conversion_storage.h
+++ b/content/browser/conversions/conversion_storage.h
@@ -120,12 +120,29 @@
   // reporting. Unconverted matching impressions are not modified.
   virtual void StoreImpression(const StorableImpression& impression) = 0;
 
+  // These values are persisted to logs. Entries should not be renumbered and
+  // numeric values should never be reused.
+  enum class CreateReportStatus {
+    kSuccess = 0,
+    // The report was stored successfully, but it replaced an existing report
+    // with a lower priority.
+    kSuccessDroppedLowerPriority = 1,
+    kInternalError = 2,
+    kNoCapacityForConversionDestination = 3,
+    kNoMatchingImpressions = 4,
+    kDeduplicated = 5,
+    kRateLimited = 6,
+    kPriorityTooLow = 7,
+    kDroppedForNoise = 8,
+    kMaxValue = kDroppedForNoise,
+  };
+
   // Finds all stored impressions matching a given `conversion`, and stores the
   // new associated conversion report. The delegate will receive a call to
   // `Delegate::ProcessNewConversionReports()` before the report is added to
   // storage. Only active impressions will receive new conversions. Returns
   // whether a new conversion report has been scheduled/added to storage.
-  virtual bool MaybeCreateAndStoreConversionReport(
+  virtual CreateReportStatus MaybeCreateAndStoreConversionReport(
       const StorableConversion& conversion) = 0;
 
   // Returns all of the conversion reports that should be sent before
diff --git a/content/browser/conversions/conversion_storage_sql.cc b/content/browser/conversions/conversion_storage_sql.cc
index 9739efc..ec5a7e92 100644
--- a/content/browser/conversions/conversion_storage_sql.cc
+++ b/content/browser/conversions/conversion_storage_sql.cc
@@ -35,6 +35,8 @@
 
 namespace {
 
+using CreateReportStatus = ::content::ConversionStorage::CreateReportStatus;
+
 const base::FilePath::CharType kInMemoryPath[] = FILE_PATH_LITERAL(":memory");
 
 const base::FilePath::CharType kDatabasePath[] =
@@ -440,20 +442,20 @@
                    kError;
 }
 
-bool ConversionStorageSql::MaybeCreateAndStoreConversionReport(
+CreateReportStatus ConversionStorageSql::MaybeCreateAndStoreConversionReport(
     const StorableConversion& conversion) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  if (!LazyInit(DbCreationPolicy::kIgnoreIfAbsent))
-    return false;
+  // We don't bother creating the DB here if it doesn't exist, because it's not
+  // possible for there to be a matching impression if there's no DB.
+  if (!LazyInit(DbCreationPolicy::kIgnoreIfAbsent)) {
+    return CreateReportStatus::kNoMatchingImpressions;
+  }
 
   const net::SchemefulSite& conversion_destination =
       conversion.conversion_destination();
   const std::string serialized_conversion_destination =
       conversion_destination.Serialize();
 
-  if (!HasCapacityForStoringConversion(serialized_conversion_destination))
-    return false;
-
   const url::Origin& reporting_origin = conversion.reporting_origin();
   DCHECK(!conversion_destination.opaque());
   DCHECK(!reporting_origin.opaque());
@@ -480,8 +482,12 @@
   matching_impressions_statement.BindTime(2, current_time);
 
   // If there are no matching impressions, return early.
-  if (!matching_impressions_statement.Step())
-    return false;
+  if (!matching_impressions_statement.Step()) {
+    return matching_impressions_statement.Succeeded()
+               ? CreateReportStatus::kNoMatchingImpressions
+               : CreateReportStatus::kInternalError;
+  }
+
   // The first one returned will be attributed; it has the highest priority.
   int64_t impression_id_to_attribute =
       matching_impressions_statement.ColumnInt64(0);
@@ -494,17 +500,33 @@
   }
   // Exit early if the last statement wasn't valid.
   if (!matching_impressions_statement.Succeeded())
-    return false;
+    return CreateReportStatus::kInternalError;
 
-  if (IsReportAlreadyStored(impression_id_to_attribute, conversion.dedup_key()))
-    return false;
+  switch (
+      ReportAlreadyStored(impression_id_to_attribute, conversion.dedup_key())) {
+    case ReportAlreadyStoredStatus::kNotStored:
+      break;
+    case ReportAlreadyStoredStatus::kStored:
+      return CreateReportStatus::kDeduplicated;
+    case ReportAlreadyStoredStatus::kError:
+      return CreateReportStatus::kInternalError;
+  }
+
+  switch (CapacityForStoringConversion(serialized_conversion_destination)) {
+    case ConversionCapacityStatus::kHasCapacity:
+      break;
+    case ConversionCapacityStatus::kNoCapacity:
+      return CreateReportStatus::kNoCapacityForConversionDestination;
+    case ConversionCapacityStatus::kError:
+      return CreateReportStatus::kInternalError;
+  }
 
   absl::optional<ImpressionToAttribute> impression_to_attribute =
       ReadImpressionToAttribute(db_.get(), impression_id_to_attribute,
                                 reporting_origin);
   // This is only possible if there is a corrupt DB.
   if (!impression_to_attribute.has_value())
-    return false;
+    return CreateReportStatus::kInternalError;
 
   const uint64_t conversion_data =
       impression_to_attribute->impression.source_type() ==
@@ -520,12 +542,19 @@
 
   report.report_time = delegate_->GetReportTime(report);
 
-  if (!rate_limit_table_.IsAttributionAllowed(db_.get(), report, current_time))
-    return false;
+  switch (
+      rate_limit_table_.AttributionAllowed(db_.get(), report, current_time)) {
+    case RateLimitTable::AttributionAllowedStatus::kAllowed:
+      break;
+    case RateLimitTable::AttributionAllowedStatus::kNotAllowed:
+      return CreateReportStatus::kRateLimited;
+    case RateLimitTable::AttributionAllowedStatus::kError:
+      return CreateReportStatus::kInternalError;
+  }
 
   sql::Transaction transaction(db_.get());
   if (!transaction.Begin())
-    return false;
+    return CreateReportStatus::kInternalError;
 
   const auto maybe_replace_lower_priority_report_result =
       MaybeReplaceLowerPriorityReport(report,
@@ -533,14 +562,14 @@
                                       conversion.priority());
   if (maybe_replace_lower_priority_report_result ==
       ConversionStorageSql::MaybeReplaceLowerPriorityReportResult::kError) {
-    return false;
+    return CreateReportStatus::kInternalError;
   }
 
   if (maybe_replace_lower_priority_report_result ==
       ConversionStorageSql::MaybeReplaceLowerPriorityReportResult::
           kDropNewReport) {
-    transaction.Commit();
-    return false;
+    return transaction.Commit() ? CreateReportStatus::kPriorityTooLow
+                                : CreateReportStatus::kInternalError;
   }
 
   // Reports with `AttributionLogic::kNever` should be included in all
@@ -553,7 +582,7 @@
     DCHECK(report.impression.impression_id().has_value());
     if (!StoreConversionReport(report, *report.impression.impression_id(),
                                conversion.priority())) {
-      return false;
+      return CreateReportStatus::kInternalError;
     }
   }
 
@@ -568,7 +597,7 @@
     insert_dedup_key_statement.BindInt64(0, *report.impression.impression_id());
     insert_dedup_key_statement.BindInt64(1, *conversion.dedup_key());
     if (!insert_dedup_key_statement.Run())
-      return false;
+      return CreateReportStatus::kInternalError;
   }
 
   // Only increment the number of conversions associated with the impression if
@@ -586,12 +615,12 @@
     impression_update_statement.BindInt64(0,
                                           *report.impression.impression_id());
     if (!impression_update_statement.Run())
-      return false;
+      return CreateReportStatus::kInternalError;
   }
 
   // Delete all unattributed impressions.
   if (!DeleteImpressions(impression_ids_to_delete))
-    return false;
+    return CreateReportStatus::kInternalError;
 
   // Based on the deletion logic here and the fact that we delete impressions
   // with |num_conversions > 1| when there is a new matching impression in
@@ -601,12 +630,19 @@
   // |RateLimitTable::ClearDataForImpressionIds()| here.
 
   if (create_report && !rate_limit_table_.AddRateLimit(db_.get(), report))
-    return false;
+    return CreateReportStatus::kInternalError;
 
   if (!transaction.Commit())
-    return false;
+    return CreateReportStatus::kInternalError;
 
-  return create_report;
+  if (!create_report)
+    return CreateReportStatus::kDroppedForNoise;
+
+  return maybe_replace_lower_priority_report_result ==
+                 ConversionStorageSql::MaybeReplaceLowerPriorityReportResult::
+                     kReplaceOldReport
+             ? CreateReportStatus::kSuccessDroppedLowerPriority
+             : CreateReportStatus::kSuccess;
 }
 
 bool ConversionStorageSql::StoreConversionReport(const ConversionReport& report,
@@ -1016,11 +1052,11 @@
   return count < delegate_->GetMaxImpressionsPerOrigin();
 }
 
-bool ConversionStorageSql::IsReportAlreadyStored(
-    int64_t impression_id,
-    absl::optional<int64_t> dedup_key) {
+ConversionStorageSql::ReportAlreadyStoredStatus
+ConversionStorageSql::ReportAlreadyStored(int64_t impression_id,
+                                          absl::optional<int64_t> dedup_key) {
   if (!dedup_key.has_value())
-    return false;
+    return ReportAlreadyStoredStatus::kNotStored;
 
   static constexpr char kCountConversionsSql[] =
       "SELECT COUNT(*)FROM dedup_keys "
@@ -1033,13 +1069,15 @@
   // If there's an error, return true so `MaybeCreateAndStoreConversionReport()`
   // returns early.
   if (!statement.Step())
-    return true;
+    return ReportAlreadyStoredStatus::kError;
 
   int64_t count = statement.ColumnInt64(0);
-  return count > 0;
+  return count > 0 ? ReportAlreadyStoredStatus::kStored
+                   : ReportAlreadyStoredStatus::kNotStored;
 }
 
-bool ConversionStorageSql::HasCapacityForStoringConversion(
+ConversionStorageSql::ConversionCapacityStatus
+ConversionStorageSql::CapacityForStoringConversion(
     const std::string& serialized_origin) {
   // This query should be reasonably optimized via
   // `kConversionDestinationIndexSql`. The conversion origin is the second
@@ -1058,9 +1096,11 @@
       db_->GetCachedStatement(SQL_FROM_HERE, kCountConversionsSql));
   statement.BindString(0, serialized_origin);
   if (!statement.Step())
-    return false;
+    return ConversionCapacityStatus::kError;
   int64_t count = statement.ColumnInt64(0);
-  return count < delegate_->GetMaxConversionsPerOrigin();
+  return count < delegate_->GetMaxConversionsPerOrigin()
+             ? ConversionCapacityStatus::kHasCapacity
+             : ConversionCapacityStatus::kNoCapacity;
 }
 
 std::vector<StorableImpression> ConversionStorageSql::GetActiveImpressions(
diff --git a/content/browser/conversions/conversion_storage_sql.h b/content/browser/conversions/conversion_storage_sql.h
index e6fef72..821897a 100644
--- a/content/browser/conversions/conversion_storage_sql.h
+++ b/content/browser/conversions/conversion_storage_sql.h
@@ -82,7 +82,7 @@
 
   // ConversionStorage
   void StoreImpression(const StorableImpression& impression) override;
-  bool MaybeCreateAndStoreConversionReport(
+  CreateReportStatus MaybeCreateAndStoreConversionReport(
       const StorableConversion& conversion) override;
   std::vector<ConversionReport> GetConversionsToReport(base::Time expiry_time,
                                                        int limit = -1) override;
@@ -115,10 +115,26 @@
 
   bool HasCapacityForStoringImpression(const std::string& serialized_origin)
       VALID_CONTEXT_REQUIRED(sequence_checker_) WARN_UNUSED_RESULT;
-  bool IsReportAlreadyStored(int64_t impression_id,
-                             absl::optional<int64_t> dedup_key)
+
+  enum class ReportAlreadyStoredStatus {
+    kNotStored,
+    kStored,
+    kError,
+  };
+
+  ReportAlreadyStoredStatus ReportAlreadyStored(
+      int64_t impression_id,
+      absl::optional<int64_t> dedup_key)
       VALID_CONTEXT_REQUIRED(sequence_checker_) WARN_UNUSED_RESULT;
-  bool HasCapacityForStoringConversion(const std::string& serialized_origin)
+
+  enum class ConversionCapacityStatus {
+    kHasCapacity,
+    kNoCapacity,
+    kError,
+  };
+
+  ConversionCapacityStatus CapacityForStoringConversion(
+      const std::string& serialized_origin)
       VALID_CONTEXT_REQUIRED(sequence_checker_) WARN_UNUSED_RESULT;
 
   enum class MaybeReplaceLowerPriorityReportResult {
diff --git a/content/browser/conversions/conversion_storage_sql_unittest.cc b/content/browser/conversions/conversion_storage_sql_unittest.cc
index e754d51d..3032570 100644
--- a/content/browser/conversions/conversion_storage_sql_unittest.cc
+++ b/content/browser/conversions/conversion_storage_sql_unittest.cc
@@ -25,6 +25,10 @@
 
 namespace content {
 
+namespace {
+
+using CreateReportStatus = ::content::ConversionStorage::CreateReportStatus;
+
 class ConversionStorageSqlTest : public testing::Test {
  public:
   ConversionStorageSqlTest() = default;
@@ -91,6 +95,8 @@
   base::SimpleTestClock clock_;
 };
 
+}  // namespace
+
 TEST_F(ConversionStorageSqlTest,
        DatabaseInitialized_TablesAndIndexesLazilyInitialized) {
   base::HistogramTester histograms;
@@ -182,11 +188,13 @@
   storage()->StoreImpression(impression);
 
   clock()->Advance(base::TimeDelta::FromDays(1));
-  EXPECT_TRUE(
+  EXPECT_EQ(
+      CreateReportStatus::kSuccess,
       storage()->MaybeCreateAndStoreConversionReport(DefaultConversion()));
 
   clock()->Advance(base::TimeDelta::FromDays(1));
-  EXPECT_TRUE(
+  EXPECT_EQ(
+      CreateReportStatus::kSuccess,
       storage()->MaybeCreateAndStoreConversionReport(DefaultConversion()));
 
   // Use a time range that only intersects the last conversion.
@@ -218,11 +226,13 @@
   storage()->StoreImpression(impression);
 
   clock()->Advance(base::TimeDelta::FromDays(1));
-  EXPECT_TRUE(
+  EXPECT_EQ(
+      CreateReportStatus::kSuccess,
       storage()->MaybeCreateAndStoreConversionReport(DefaultConversion()));
 
   clock()->Advance(base::TimeDelta::FromDays(1));
-  EXPECT_TRUE(
+  EXPECT_EQ(
+      CreateReportStatus::kSuccess,
       storage()->MaybeCreateAndStoreConversionReport(DefaultConversion()));
 
   // Use a time range that only intersects the last conversion.
@@ -256,10 +266,12 @@
     clock()->Advance(base::TimeDelta::FromDays(1));
   }
 
-  EXPECT_TRUE(
+  EXPECT_EQ(
+      CreateReportStatus::kSuccess,
       storage()->MaybeCreateAndStoreConversionReport(DefaultConversion()));
   clock()->Advance(base::TimeDelta::FromDays(1));
-  EXPECT_TRUE(
+  EXPECT_EQ(
+      CreateReportStatus::kSuccess,
       storage()->MaybeCreateAndStoreConversionReport(DefaultConversion()));
 
   auto null_filter = base::RepeatingCallback<bool(const url::Origin&)>();
@@ -283,7 +295,8 @@
   storage()->StoreImpression(ImpressionBuilder(clock()->Now()).Build());
   storage()->StoreImpression(ImpressionBuilder(clock()->Now()).Build());
   storage()->StoreImpression(ImpressionBuilder(clock()->Now()).Build());
-  EXPECT_TRUE(
+  EXPECT_EQ(
+      CreateReportStatus::kSuccess,
       storage()->MaybeCreateAndStoreConversionReport(DefaultConversion()));
 
   CloseDatabase();
@@ -301,11 +314,14 @@
   OpenDatabase();
   delegate()->set_max_conversions_per_origin(2);
   storage()->StoreImpression(ImpressionBuilder(clock()->Now()).Build());
-  EXPECT_TRUE(
+  EXPECT_EQ(
+      CreateReportStatus::kSuccess,
       storage()->MaybeCreateAndStoreConversionReport(DefaultConversion()));
-  EXPECT_TRUE(
+  EXPECT_EQ(
+      CreateReportStatus::kSuccess,
       storage()->MaybeCreateAndStoreConversionReport(DefaultConversion()));
-  EXPECT_FALSE(
+  EXPECT_EQ(
+      CreateReportStatus::kNoCapacityForConversionDestination,
       storage()->MaybeCreateAndStoreConversionReport(DefaultConversion()));
 
   CloseDatabase();
@@ -341,21 +357,25 @@
                                  .Build());
 
   clock()->Advance(base::TimeDelta::FromDays(1));
-  EXPECT_TRUE(storage()->MaybeCreateAndStoreConversionReport(
-      ConversionBuilder()
-          .SetConversionDestination(net::SchemefulSite(conversion_origin))
-          .SetReportingOrigin(reporting_origin)
-          .Build()));
+  EXPECT_EQ(
+      CreateReportStatus::kSuccess,
+      storage()->MaybeCreateAndStoreConversionReport(
+          ConversionBuilder()
+              .SetConversionDestination(net::SchemefulSite(conversion_origin))
+              .SetReportingOrigin(reporting_origin)
+              .Build()));
   EXPECT_EQ(1u, storage()->GetActiveImpressions().size());
 
   // Force the impression to be deactivated by ensuring that the next report is
   // in a different window.
   delegate()->set_report_time_ms(1);
-  EXPECT_FALSE(storage()->MaybeCreateAndStoreConversionReport(
-      ConversionBuilder()
-          .SetConversionDestination(net::SchemefulSite(conversion_origin))
-          .SetReportingOrigin(reporting_origin)
-          .Build()));
+  EXPECT_EQ(
+      CreateReportStatus::kPriorityTooLow,
+      storage()->MaybeCreateAndStoreConversionReport(
+          ConversionBuilder()
+              .SetConversionDestination(net::SchemefulSite(conversion_origin))
+              .SetReportingOrigin(reporting_origin)
+              .Build()));
   EXPECT_EQ(0u, storage()->GetActiveImpressions().size());
 
   clock()->Advance(base::TimeDelta::FromDays(1));
@@ -397,21 +417,25 @@
                                  .Build());
 
   clock()->Advance(base::TimeDelta::FromDays(1));
-  EXPECT_TRUE(storage()->MaybeCreateAndStoreConversionReport(
-      ConversionBuilder()
-          .SetConversionDestination(net::SchemefulSite(conversion_origin))
-          .SetReportingOrigin(reporting_origin)
-          .Build()));
+  EXPECT_EQ(
+      CreateReportStatus::kSuccess,
+      storage()->MaybeCreateAndStoreConversionReport(
+          ConversionBuilder()
+              .SetConversionDestination(net::SchemefulSite(conversion_origin))
+              .SetReportingOrigin(reporting_origin)
+              .Build()));
   EXPECT_EQ(1u, storage()->GetActiveImpressions().size());
 
   // Force the impression to be deactivated by ensuring that the next report is
   // in a different window.
   delegate()->set_report_time_ms(1);
-  EXPECT_FALSE(storage()->MaybeCreateAndStoreConversionReport(
-      ConversionBuilder()
-          .SetConversionDestination(net::SchemefulSite(conversion_origin))
-          .SetReportingOrigin(reporting_origin)
-          .Build()));
+  EXPECT_EQ(
+      CreateReportStatus::kPriorityTooLow,
+      storage()->MaybeCreateAndStoreConversionReport(
+          ConversionBuilder()
+              .SetConversionDestination(net::SchemefulSite(conversion_origin))
+              .SetReportingOrigin(reporting_origin)
+              .Build()));
   EXPECT_EQ(0u, storage()->GetActiveImpressions().size());
 
   clock()->Advance(base::TimeDelta::FromDays(1));
@@ -443,8 +467,8 @@
 
   // These calls should be no-ops.
   storage->StoreImpression(ImpressionBuilder(clock()->Now()).Build());
-  EXPECT_FALSE(
-      storage->MaybeCreateAndStoreConversionReport(DefaultConversion()));
+  EXPECT_EQ(CreateReportStatus::kNoMatchingImpressions,
+            storage->MaybeCreateAndStoreConversionReport(DefaultConversion()));
 }
 
 TEST_F(ConversionStorageSqlTest, DatabaseDirDoesExist_CreateDirAndOpenDB) {
@@ -457,8 +481,8 @@
 
   // The directory should be created, and the database opened.
   storage->StoreImpression(ImpressionBuilder(clock()->Now()).Build());
-  EXPECT_TRUE(
-      storage->MaybeCreateAndStoreConversionReport(DefaultConversion()));
+  EXPECT_EQ(CreateReportStatus::kSuccess,
+            storage->MaybeCreateAndStoreConversionReport(DefaultConversion()));
 }
 
 TEST_F(ConversionStorageSqlTest, DBinitializationSucceeds_HistogramRecorded) {
@@ -489,10 +513,12 @@
   EXPECT_EQ(1u, impressions.size());
   EXPECT_EQ(kMaxUint64, impressions[0].impression_data());
 
-  EXPECT_TRUE(storage()->MaybeCreateAndStoreConversionReport(StorableConversion(
-      /*conversion_data=*/kMaxUint64, impression.ConversionDestination(),
-      impression.reporting_origin(), /*event_source_trigger_data=*/0,
-      /*priority=*/0, /*dedup_key=*/absl::nullopt)));
+  EXPECT_EQ(
+      CreateReportStatus::kSuccess,
+      storage()->MaybeCreateAndStoreConversionReport(StorableConversion(
+          /*conversion_data=*/kMaxUint64, impression.ConversionDestination(),
+          impression.reporting_origin(), /*event_source_trigger_data=*/0,
+          /*priority=*/0, /*dedup_key=*/absl::nullopt)));
 
   std::vector<ConversionReport> reports =
       storage()->GetConversionsToReport(clock()->Now());
@@ -564,7 +590,8 @@
       ImpressionBuilder(clock()->Now())
           .SetExpiry(base::TimeDelta::FromMilliseconds(3))
           .Build());
-  EXPECT_TRUE(
+  EXPECT_EQ(
+      CreateReportStatus::kSuccess,
       storage()->MaybeCreateAndStoreConversionReport(DefaultConversion()));
 
   clock()->Advance(base::TimeDelta::FromMilliseconds(3));
@@ -611,7 +638,8 @@
       ImpressionBuilder(clock()->Now())
           .SetExpiry(base::TimeDelta::FromMilliseconds(3))
           .Build());
-  EXPECT_TRUE(
+  EXPECT_EQ(
+      CreateReportStatus::kSuccess,
       storage()->MaybeCreateAndStoreConversionReport(DefaultConversion()));
 
   clock()->Advance(base::TimeDelta::FromMilliseconds(3));
diff --git a/content/browser/conversions/conversion_storage_unittest.cc b/content/browser/conversions/conversion_storage_unittest.cc
index 8135a63b..0c33e11 100644
--- a/content/browser/conversions/conversion_storage_unittest.cc
+++ b/content/browser/conversions/conversion_storage_unittest.cc
@@ -33,6 +33,8 @@
 
 namespace {
 
+using CreateReportStatus = ::content::ConversionStorage::CreateReportStatus;
+
 using ::testing::ElementsAre;
 using ::testing::IsEmpty;
 
@@ -112,8 +114,8 @@
   // Test all public methods on ConversionStorage.
   EXPECT_NO_FATAL_FAILURE(
       storage->StoreImpression(ImpressionBuilder(clock()->Now()).Build()));
-  EXPECT_FALSE(
-      storage->MaybeCreateAndStoreConversionReport(DefaultConversion()));
+  EXPECT_EQ(CreateReportStatus::kNoMatchingImpressions,
+            storage->MaybeCreateAndStoreConversionReport(DefaultConversion()));
   EXPECT_TRUE(storage->GetConversionsToReport(clock()->Now()).empty());
   EXPECT_TRUE(storage->GetActiveImpressions().empty());
   EXPECT_EQ(0, storage->DeleteConversion(0));
@@ -149,21 +151,24 @@
 
 TEST_F(ConversionStorageTest,
        GetWithNoMatchingImpressions_NoImpressionsReturned) {
-  EXPECT_FALSE(
+  EXPECT_EQ(
+      CreateReportStatus::kNoMatchingImpressions,
       storage()->MaybeCreateAndStoreConversionReport(DefaultConversion()));
   EXPECT_TRUE(storage()->GetConversionsToReport(clock()->Now()).empty());
 }
 
 TEST_F(ConversionStorageTest, GetWithMatchingImpression_ImpressionReturned) {
   storage()->StoreImpression(ImpressionBuilder(clock()->Now()).Build());
-  EXPECT_TRUE(
+  EXPECT_EQ(
+      CreateReportStatus::kSuccess,
       storage()->MaybeCreateAndStoreConversionReport(DefaultConversion()));
 }
 
 TEST_F(ConversionStorageTest, MultipleImpressionsForConversion_OneConverts) {
   storage()->StoreImpression(ImpressionBuilder(clock()->Now()).Build());
   storage()->StoreImpression(ImpressionBuilder(clock()->Now()).Build());
-  EXPECT_TRUE(
+  EXPECT_EQ(
+      CreateReportStatus::kSuccess,
       storage()->MaybeCreateAndStoreConversionReport(DefaultConversion()));
 }
 
@@ -174,11 +179,13 @@
           .SetConversionOrigin(url::Origin::Create(GURL("https://sub.a.test")))
           .Build();
   storage()->StoreImpression(impression);
-  EXPECT_TRUE(storage()->MaybeCreateAndStoreConversionReport(
-      ConversionBuilder()
-          .SetConversionDestination(net::SchemefulSite(GURL("https://a.test")))
-          .SetReportingOrigin(impression.reporting_origin())
-          .Build()));
+  EXPECT_EQ(CreateReportStatus::kSuccess,
+            storage()->MaybeCreateAndStoreConversionReport(
+                ConversionBuilder()
+                    .SetConversionDestination(
+                        net::SchemefulSite(GURL("https://a.test")))
+                    .SetReportingOrigin(impression.reporting_origin())
+                    .Build()));
 }
 
 TEST_F(ConversionStorageTest, EventSourceImpressionsForConversion_Converts) {
@@ -186,8 +193,9 @@
       ImpressionBuilder(clock()->Now())
           .SetSourceType(StorableImpression::SourceType::kEvent)
           .Build());
-  EXPECT_TRUE(storage()->MaybeCreateAndStoreConversionReport(
-      ConversionBuilder().SetEventSourceTriggerData(456).Build()));
+  EXPECT_EQ(CreateReportStatus::kSuccess,
+            storage()->MaybeCreateAndStoreConversionReport(
+                ConversionBuilder().SetEventSourceTriggerData(456).Build()));
 
   clock()->Advance(base::TimeDelta::FromMilliseconds(kReportTime));
 
@@ -204,7 +212,8 @@
           .Build());
   clock()->Advance(base::TimeDelta::FromMilliseconds(2));
 
-  EXPECT_FALSE(
+  EXPECT_EQ(
+      CreateReportStatus::kNoMatchingImpressions,
       storage()->MaybeCreateAndStoreConversionReport(DefaultConversion()));
 }
 
@@ -216,12 +225,14 @@
 
   clock()->Advance(base::TimeDelta::FromMilliseconds(3));
 
-  EXPECT_TRUE(
+  EXPECT_EQ(
+      CreateReportStatus::kSuccess,
       storage()->MaybeCreateAndStoreConversionReport(DefaultConversion()));
 
   clock()->Advance(base::TimeDelta::FromMilliseconds(5));
 
-  EXPECT_FALSE(
+  EXPECT_EQ(
+      CreateReportStatus::kNoMatchingImpressions,
       storage()->MaybeCreateAndStoreConversionReport(DefaultConversion()));
 }
 
@@ -230,12 +241,14 @@
   storage()->StoreImpression(ImpressionBuilder(clock()->Now()).Build());
 
   for (int i = 0; i < kMaxConversions; i++) {
-    EXPECT_TRUE(
+    EXPECT_EQ(
+        CreateReportStatus::kSuccess,
         storage()->MaybeCreateAndStoreConversionReport(DefaultConversion()));
   }
 
   // No additional conversion reports should be created.
-  EXPECT_FALSE(
+  EXPECT_EQ(
+      CreateReportStatus::kPriorityTooLow,
       storage()->MaybeCreateAndStoreConversionReport(DefaultConversion()));
 }
 
@@ -244,7 +257,8 @@
   auto conversion = DefaultConversion();
 
   storage()->StoreImpression(impression);
-  EXPECT_TRUE(storage()->MaybeCreateAndStoreConversionReport(conversion));
+  EXPECT_EQ(CreateReportStatus::kSuccess,
+            storage()->MaybeCreateAndStoreConversionReport(conversion));
 
   ConversionReport expected_report = GetExpectedReport(impression, conversion);
 
@@ -262,7 +276,8 @@
                             url::Origin::Create(GURL("https://different.test")))
                         .Build();
   storage()->StoreImpression(impression);
-  EXPECT_FALSE(
+  EXPECT_EQ(
+      CreateReportStatus::kNoMatchingImpressions,
       storage()->MaybeCreateAndStoreConversionReport(DefaultConversion()));
 
   clock()->Advance(base::TimeDelta::FromMilliseconds(kReportTime));
@@ -277,7 +292,8 @@
                             url::Origin::Create(GURL("https://different.test")))
                         .Build();
   storage()->StoreImpression(impression);
-  EXPECT_FALSE(
+  EXPECT_EQ(
+      CreateReportStatus::kNoMatchingImpressions,
       storage()->MaybeCreateAndStoreConversionReport(DefaultConversion()));
 
   clock()->Advance(base::TimeDelta::FromMilliseconds(kReportTime));
@@ -287,7 +303,8 @@
 
 TEST_F(ConversionStorageTest, ConversionReportDeleted_RemovedFromStorage) {
   storage()->StoreImpression(ImpressionBuilder(clock()->Now()).Build());
-  EXPECT_TRUE(
+  EXPECT_EQ(
+      CreateReportStatus::kSuccess,
       storage()->MaybeCreateAndStoreConversionReport(DefaultConversion()));
 
   clock()->Advance(base::TimeDelta::FromMilliseconds(kReportTime));
@@ -310,13 +327,15 @@
   }
 
   for (int i = 0; i < kMaxConversions; i++) {
-    EXPECT_TRUE(
+    EXPECT_EQ(
+        CreateReportStatus::kSuccess,
         storage()->MaybeCreateAndStoreConversionReport(DefaultConversion()));
   }
 
   // No additional conversion reports should be created for any of the
   // impressions.
-  EXPECT_FALSE(
+  EXPECT_EQ(
+      CreateReportStatus::kPriorityTooLow,
       storage()->MaybeCreateAndStoreConversionReport(DefaultConversion()));
 }
 
@@ -333,7 +352,8 @@
   // The first impression should be active because even though
   // <reporting_origin, conversion_origin> matches, it has not converted yet.
   EXPECT_EQ(2u, storage()->GetActiveImpressions().size());
-  EXPECT_TRUE(
+  EXPECT_EQ(
+      CreateReportStatus::kSuccess,
       storage()->MaybeCreateAndStoreConversionReport(DefaultConversion()));
   EXPECT_EQ(1u, storage()->GetActiveImpressions().size());
 }
@@ -346,7 +366,8 @@
        NewImpressionForConvertedImpression_MarkedInactive) {
   storage()->StoreImpression(
       ImpressionBuilder(clock()->Now()).SetData(0).Build());
-  EXPECT_TRUE(
+  EXPECT_EQ(
+      CreateReportStatus::kSuccess,
       storage()->MaybeCreateAndStoreConversionReport(DefaultConversion()));
 
   clock()->Advance(base::TimeDelta::FromMilliseconds(kReportTime));
@@ -360,7 +381,8 @@
 
   // Only the new impression should convert.
   auto conversion = DefaultConversion();
-  EXPECT_TRUE(storage()->MaybeCreateAndStoreConversionReport(conversion));
+  EXPECT_EQ(CreateReportStatus::kSuccess,
+            storage()->MaybeCreateAndStoreConversionReport(conversion));
   ConversionReport expected_report =
       GetExpectedReport(new_impression, conversion);
 
@@ -377,7 +399,8 @@
   storage()->StoreImpression(first_impression);
 
   auto conversion = DefaultConversion();
-  EXPECT_TRUE(
+  EXPECT_EQ(
+      CreateReportStatus::kSuccess,
       storage()->MaybeCreateAndStoreConversionReport(DefaultConversion()));
 
   clock()->Advance(base::TimeDelta::FromMilliseconds(kReportTime));
@@ -393,7 +416,8 @@
   storage()->StoreImpression(new_impression);
 
   // The first impression should still be active and able to convert.
-  EXPECT_TRUE(storage()->MaybeCreateAndStoreConversionReport(conversion));
+  EXPECT_EQ(CreateReportStatus::kSuccess,
+            storage()->MaybeCreateAndStoreConversionReport(conversion));
 
   ConversionReport expected_report =
       GetExpectedReport(first_impression, conversion);
@@ -423,7 +447,8 @@
 
   ConversionReport third_expected_conversion =
       GetExpectedReport(third_impression, conversion);
-  EXPECT_TRUE(storage()->MaybeCreateAndStoreConversionReport(conversion));
+  EXPECT_EQ(CreateReportStatus::kSuccess,
+            storage()->MaybeCreateAndStoreConversionReport(conversion));
 
   clock()->Advance(base::TimeDelta::FromMilliseconds(kReportTime));
 
@@ -445,7 +470,8 @@
   clock()->Advance(base::TimeDelta::FromMilliseconds(3));
   storage()->StoreImpression(ImpressionBuilder(clock()->Now()).Build());
 
-  EXPECT_TRUE(
+  EXPECT_EQ(
+      CreateReportStatus::kSuccess,
       storage()->MaybeCreateAndStoreConversionReport(DefaultConversion()));
 
   // Advance to the first impression's report time and verify only its report is
@@ -462,7 +488,8 @@
 
 TEST_F(ConversionStorageTest, GetConversionsToReportMultipleTimes_SameResult) {
   storage()->StoreImpression(ImpressionBuilder(clock()->Now()).Build());
-  EXPECT_TRUE(
+  EXPECT_EQ(
+      CreateReportStatus::kSuccess,
       storage()->MaybeCreateAndStoreConversionReport(DefaultConversion()));
   clock()->Advance(base::TimeDelta::FromMilliseconds(kReportTime));
 
@@ -543,10 +570,12 @@
   delegate()->set_max_conversions_per_origin(1);
   storage()->StoreImpression(ImpressionBuilder(clock()->Now()).Build());
   storage()->StoreImpression(ImpressionBuilder(clock()->Now()).Build());
-  EXPECT_TRUE(
+  EXPECT_EQ(
+      CreateReportStatus::kSuccess,
       storage()->MaybeCreateAndStoreConversionReport(DefaultConversion()));
   // Verify that MaxConversionsPerOrigin is enforced.
-  EXPECT_FALSE(
+  EXPECT_EQ(
+      CreateReportStatus::kNoCapacityForConversionDestination,
       storage()->MaybeCreateAndStoreConversionReport(DefaultConversion()));
 }
 
@@ -556,7 +585,8 @@
   storage()->StoreImpression(impression);
   storage()->ClearData(
       now, now, GetMatcher(url::Origin::Create(GURL("https://no-match.com"))));
-  EXPECT_TRUE(
+  EXPECT_EQ(
+      CreateReportStatus::kSuccess,
       storage()->MaybeCreateAndStoreConversionReport(DefaultConversion()));
 }
 
@@ -568,7 +598,8 @@
   storage()->ClearData(now + base::TimeDelta::FromMinutes(10),
                        now + base::TimeDelta::FromMinutes(20),
                        GetMatcher(impression.impression_origin()));
-  EXPECT_TRUE(
+  EXPECT_EQ(
+      CreateReportStatus::kSuccess,
       storage()->MaybeCreateAndStoreConversionReport(DefaultConversion()));
 }
 
@@ -580,7 +611,8 @@
     storage()->StoreImpression(impression);
     storage()->ClearData(now, now + base::TimeDelta::FromMinutes(20),
                          GetMatcher(impression.conversion_origin()));
-    EXPECT_FALSE(
+    EXPECT_EQ(
+        CreateReportStatus::kNoMatchingImpressions,
         storage()->MaybeCreateAndStoreConversionReport(DefaultConversion()));
   }
 }
@@ -591,7 +623,8 @@
   auto conversion = DefaultConversion();
 
   storage()->StoreImpression(impression);
-  EXPECT_TRUE(storage()->MaybeCreateAndStoreConversionReport(conversion));
+  EXPECT_EQ(CreateReportStatus::kSuccess,
+            storage()->MaybeCreateAndStoreConversionReport(conversion));
 
   storage()->ClearData(now - base::TimeDelta::FromMinutes(20),
                        now + base::TimeDelta::FromMinutes(20),
@@ -620,21 +653,23 @@
   for (int i = 0; i < 5; i++) {
     auto origin =
         url::Origin::Create(GURL(base::StringPrintf("https://%d.com/", i)));
-    EXPECT_TRUE(storage()->MaybeCreateAndStoreConversionReport(
-        ConversionBuilder()
-            .SetConversionDestination(net::SchemefulSite(origin))
-            .SetReportingOrigin(origin)
-            .Build()));
+    EXPECT_EQ(CreateReportStatus::kSuccess,
+              storage()->MaybeCreateAndStoreConversionReport(
+                  ConversionBuilder()
+                      .SetConversionDestination(net::SchemefulSite(origin))
+                      .SetReportingOrigin(origin)
+                      .Build()));
   }
   clock()->Advance(base::TimeDelta::FromDays(1));
   for (int i = 5; i < 10; i++) {
     auto origin =
         url::Origin::Create(GURL(base::StringPrintf("https://%d.com/", i)));
-    EXPECT_TRUE(storage()->MaybeCreateAndStoreConversionReport(
-        ConversionBuilder()
-            .SetConversionDestination(net::SchemefulSite(origin))
-            .SetReportingOrigin(origin)
-            .Build()));
+    EXPECT_EQ(CreateReportStatus::kSuccess,
+              storage()->MaybeCreateAndStoreConversionReport(
+                  ConversionBuilder()
+                      .SetConversionDestination(net::SchemefulSite(origin))
+                      .SetReportingOrigin(origin)
+                      .Build()));
   }
 
   auto null_filter = base::RepeatingCallback<bool(const url::Origin&)>();
@@ -650,7 +685,8 @@
 
   storage()->StoreImpression(impression);
 
-  EXPECT_TRUE(storage()->MaybeCreateAndStoreConversionReport(conversion));
+  EXPECT_EQ(CreateReportStatus::kSuccess,
+            storage()->MaybeCreateAndStoreConversionReport(conversion));
   storage()->ClearData(clock()->Now(), clock()->Now(),
                        GetMatcher(impression.impression_origin()));
   EXPECT_TRUE(storage()->GetConversionsToReport(base::Time::Max()).empty());
@@ -671,7 +707,8 @@
   const ConversionReport expected_report =
       GetExpectedReport(impression, conversion);
 
-  EXPECT_TRUE(storage()->MaybeCreateAndStoreConversionReport(conversion));
+  EXPECT_EQ(CreateReportStatus::kSuccess,
+            storage()->MaybeCreateAndStoreConversionReport(conversion));
 
   storage()->ClearData(start + base::TimeDelta::FromMinutes(1),
                        start + base::TimeDelta::FromMinutes(10),
@@ -700,7 +737,8 @@
   storage()->StoreImpression(impression2);
   storage()->StoreImpression(impression3);
 
-  EXPECT_TRUE(
+  EXPECT_EQ(
+      CreateReportStatus::kSuccess,
       storage()->MaybeCreateAndStoreConversionReport(DefaultConversion()));
 
   // Only the first impression should overlap with this time range, but all the
@@ -721,10 +759,12 @@
     clock()->Advance(base::TimeDelta::FromDays(1));
   }
 
-  EXPECT_TRUE(
+  EXPECT_EQ(
+      CreateReportStatus::kSuccess,
       storage()->MaybeCreateAndStoreConversionReport(DefaultConversion()));
   clock()->Advance(base::TimeDelta::FromDays(1));
-  EXPECT_TRUE(
+  EXPECT_EQ(
+      CreateReportStatus::kSuccess,
       storage()->MaybeCreateAndStoreConversionReport(DefaultConversion()));
 
   auto null_filter = base::RepeatingCallback<bool(const url::Origin&)>();
@@ -746,10 +786,12 @@
     clock()->Advance(base::TimeDelta::FromDays(1));
   }
 
-  EXPECT_TRUE(
+  EXPECT_EQ(
+      CreateReportStatus::kSuccess,
       storage()->MaybeCreateAndStoreConversionReport(DefaultConversion()));
   clock()->Advance(base::TimeDelta::FromDays(1));
-  EXPECT_TRUE(
+  EXPECT_EQ(
+      CreateReportStatus::kSuccess,
       storage()->MaybeCreateAndStoreConversionReport(DefaultConversion()));
 
   auto null_filter = base::RepeatingCallback<bool(const url::Origin&)>();
@@ -769,9 +811,12 @@
   auto conversion = DefaultConversion();
 
   storage()->StoreImpression(ImpressionBuilder(clock()->Now()).Build());
-  EXPECT_TRUE(storage()->MaybeCreateAndStoreConversionReport(conversion));
-  EXPECT_TRUE(storage()->MaybeCreateAndStoreConversionReport(conversion));
-  EXPECT_FALSE(storage()->MaybeCreateAndStoreConversionReport(conversion));
+  EXPECT_EQ(CreateReportStatus::kSuccess,
+            storage()->MaybeCreateAndStoreConversionReport(conversion));
+  EXPECT_EQ(CreateReportStatus::kSuccess,
+            storage()->MaybeCreateAndStoreConversionReport(conversion));
+  EXPECT_EQ(CreateReportStatus::kRateLimited,
+            storage()->MaybeCreateAndStoreConversionReport(conversion));
 
   const ConversionReport expected_report =
       GetExpectedReport(impression, conversion);
@@ -792,7 +837,8 @@
       ImpressionBuilder(clock()->Now())
           .SetSourceType(StorableImpression::SourceType::kNavigation)
           .Build());
-  EXPECT_TRUE(
+  EXPECT_EQ(
+      CreateReportStatus::kSuccess,
       storage()->MaybeCreateAndStoreConversionReport(DefaultConversion()));
 
   storage()->StoreImpression(
@@ -801,21 +847,24 @@
           .Build());
   // This would fail if the source types had a combined limit or the incorrect
   // source type were stored.
-  EXPECT_TRUE(
+  EXPECT_EQ(
+      CreateReportStatus::kSuccess,
       storage()->MaybeCreateAndStoreConversionReport(DefaultConversion()));
 
   storage()->StoreImpression(
       ImpressionBuilder(clock()->Now())
           .SetSourceType(StorableImpression::SourceType::kEvent)
           .Build());
-  EXPECT_FALSE(
+  EXPECT_EQ(
+      CreateReportStatus::kRateLimited,
       storage()->MaybeCreateAndStoreConversionReport(DefaultConversion()));
 
   storage()->StoreImpression(
       ImpressionBuilder(clock()->Now())
           .SetSourceType(StorableImpression::SourceType::kNavigation)
           .Build());
-  EXPECT_FALSE(
+  EXPECT_EQ(
+      CreateReportStatus::kRateLimited,
       storage()->MaybeCreateAndStoreConversionReport(DefaultConversion()));
 }
 
@@ -825,7 +874,8 @@
   delegate()->set_max_conversions_per_impression(1);
   storage()->StoreImpression(ImpressionBuilder(clock()->Now()).Build());
 
-  EXPECT_FALSE(
+  EXPECT_EQ(
+      CreateReportStatus::kDroppedForNoise,
       storage()->MaybeCreateAndStoreConversionReport(DefaultConversion()));
 
   clock()->Advance(base::TimeDelta::FromMilliseconds(kReportTime));
@@ -842,7 +892,8 @@
   storage()->StoreImpression(
       ImpressionBuilder(clock()->Now()).SetData(3).Build());
 
-  EXPECT_FALSE(
+  EXPECT_EQ(
+      CreateReportStatus::kDroppedForNoise,
       storage()->MaybeCreateAndStoreConversionReport(DefaultConversion()));
 
   delegate()->set_attribution_logic(
@@ -850,8 +901,9 @@
   storage()->StoreImpression(
       ImpressionBuilder(clock()->Now()).SetData(5).Build());
 
-  EXPECT_TRUE(storage()->MaybeCreateAndStoreConversionReport(
-      ConversionBuilder().SetConversionData(7).Build()));
+  EXPECT_EQ(CreateReportStatus::kSuccess,
+            storage()->MaybeCreateAndStoreConversionReport(
+                ConversionBuilder().SetConversionData(7).Build()));
 
   clock()->Advance(base::TimeDelta::FromMilliseconds(kReportTime));
 
@@ -874,17 +926,20 @@
       ImpressionBuilder(clock()->Now()).SetData(5).Build());
 
   const auto conversion = DefaultConversion();
-  EXPECT_FALSE(storage()->MaybeCreateAndStoreConversionReport(conversion));
+  EXPECT_EQ(CreateReportStatus::kDroppedForNoise,
+            storage()->MaybeCreateAndStoreConversionReport(conversion));
 
   delegate()->set_attribution_logic(
       StorableImpression::AttributionLogic::kTruthfully);
   const auto impression = ImpressionBuilder(clock()->Now()).SetData(7).Build();
   storage()->StoreImpression(impression);
-  EXPECT_TRUE(storage()->MaybeCreateAndStoreConversionReport(conversion));
+  EXPECT_EQ(CreateReportStatus::kSuccess,
+            storage()->MaybeCreateAndStoreConversionReport(conversion));
 
   storage()->StoreImpression(
       ImpressionBuilder(clock()->Now()).SetData(9).Build());
-  EXPECT_FALSE(storage()->MaybeCreateAndStoreConversionReport(conversion));
+  EXPECT_EQ(CreateReportStatus::kRateLimited,
+            storage()->MaybeCreateAndStoreConversionReport(conversion));
 
   const ConversionReport expected_report =
       GetExpectedReport(impression, conversion);
@@ -909,8 +964,10 @@
   storage()->StoreImpression(ImpressionBuilder(clock()->Now()).Build());
 
   const auto conversion = DefaultConversion();
-  EXPECT_FALSE(storage()->MaybeCreateAndStoreConversionReport(conversion));
-  EXPECT_FALSE(storage()->MaybeCreateAndStoreConversionReport(conversion));
+  EXPECT_EQ(CreateReportStatus::kDroppedForNoise,
+            storage()->MaybeCreateAndStoreConversionReport(conversion));
+  EXPECT_EQ(CreateReportStatus::kDroppedForNoise,
+            storage()->MaybeCreateAndStoreConversionReport(conversion));
 
   clock()->Advance(base::TimeDelta::FromMilliseconds(kReportTime));
 
@@ -1035,14 +1092,16 @@
           .Build());
   EXPECT_EQ(1u, storage()->GetActiveImpressions().size());
 
-  EXPECT_TRUE(
+  EXPECT_EQ(
+      CreateReportStatus::kSuccess,
       storage()->MaybeCreateAndStoreConversionReport(DefaultConversion()));
   EXPECT_EQ(1u, storage()->GetActiveImpressions().size());
 
   // Force the impression to be deactivated by ensuring that the next report is
   // in a different window.
   delegate()->set_report_time_ms(kReportTime + 1);
-  EXPECT_FALSE(
+  EXPECT_EQ(
+      CreateReportStatus::kPriorityTooLow,
       storage()->MaybeCreateAndStoreConversionReport(DefaultConversion()));
   EXPECT_EQ(0u, storage()->GetActiveImpressions().size());
 
@@ -1092,7 +1151,8 @@
       ImpressionBuilder(clock()->Now()).SetData(5).Build());
 
   EXPECT_EQ(3u, storage()->GetActiveImpressions().size());
-  EXPECT_TRUE(
+  EXPECT_EQ(
+      CreateReportStatus::kSuccess,
       storage()->MaybeCreateAndStoreConversionReport(DefaultConversion()));
 
   clock()->Advance(base::TimeDelta::FromMilliseconds(kReportTime));
@@ -1117,7 +1177,8 @@
       ImpressionBuilder(clock()->Now()).SetPriority(200).SetData(7).Build());
 
   EXPECT_EQ(3u, storage()->GetActiveImpressions().size());
-  EXPECT_TRUE(
+  EXPECT_EQ(
+      CreateReportStatus::kSuccess,
       storage()->MaybeCreateAndStoreConversionReport(DefaultConversion()));
 
   clock()->Advance(base::TimeDelta::FromMilliseconds(kReportTime));
@@ -1135,7 +1196,8 @@
       ImpressionBuilder(clock()->Now()).SetData(5).SetPriority(1).Build());
   EXPECT_EQ(2u, storage()->GetActiveImpressions().size());
 
-  EXPECT_TRUE(
+  EXPECT_EQ(
+      CreateReportStatus::kSuccess,
       storage()->MaybeCreateAndStoreConversionReport(DefaultConversion()));
 
   // Because the impression with data 5 has the highest priority, it is selected
@@ -1178,7 +1240,8 @@
 
   // The falsely attributed impression should not be eligible for further
   // attribution.
-  EXPECT_FALSE(
+  EXPECT_EQ(
+      CreateReportStatus::kNoMatchingImpressions,
       storage()->MaybeCreateAndStoreConversionReport(DefaultConversion()));
 
   actual_reports = storage()->GetConversionsToReport(clock()->Now());
@@ -1193,22 +1256,28 @@
   storage()->StoreImpression(
       ImpressionBuilder(clock()->Now()).SetData(5).SetPriority(1).Build());
 
-  EXPECT_TRUE(
+  EXPECT_EQ(
+      CreateReportStatus::kSuccess,
       storage()->MaybeCreateAndStoreConversionReport(DefaultConversion()));
 
   // This conversion should replace the one above because it has a higher
   // priority.
-  EXPECT_TRUE(storage()->MaybeCreateAndStoreConversionReport(
-      ConversionBuilder().SetPriority(2).SetConversionData(21).Build()));
+  EXPECT_EQ(
+      CreateReportStatus::kSuccessDroppedLowerPriority,
+      storage()->MaybeCreateAndStoreConversionReport(
+          ConversionBuilder().SetPriority(2).SetConversionData(21).Build()));
 
   storage()->StoreImpression(
       ImpressionBuilder(clock()->Now()).SetData(7).SetPriority(2).Build());
 
-  EXPECT_TRUE(storage()->MaybeCreateAndStoreConversionReport(
-      ConversionBuilder().SetPriority(1).SetConversionData(22).Build()));
+  EXPECT_EQ(
+      CreateReportStatus::kSuccess,
+      storage()->MaybeCreateAndStoreConversionReport(
+          ConversionBuilder().SetPriority(1).SetConversionData(22).Build()));
   // This conversion should be dropped because it has a lower priority than the
   // one above.
-  EXPECT_FALSE(
+  EXPECT_EQ(
+      CreateReportStatus::kPriorityTooLow,
       storage()->MaybeCreateAndStoreConversionReport(DefaultConversion()));
 
   clock()->Advance(base::TimeDelta::FromMilliseconds(kReportTime));
@@ -1229,9 +1298,18 @@
 
   storage()->StoreImpression(ImpressionBuilder(clock()->Now()).Build());
 
-  for (int i = 0; i < 10; i++) {
-    EXPECT_TRUE(storage()->MaybeCreateAndStoreConversionReport(
-        ConversionBuilder().SetPriority(i).SetConversionData(i).Build()));
+  int i = 0;
+  EXPECT_EQ(
+      CreateReportStatus::kSuccess,
+      storage()->MaybeCreateAndStoreConversionReport(
+          ConversionBuilder().SetPriority(i).SetConversionData(i).Build()));
+  i++;
+
+  for (; i < 10; i++) {
+    EXPECT_EQ(
+        CreateReportStatus::kSuccessDroppedLowerPriority,
+        storage()->MaybeCreateAndStoreConversionReport(
+            ConversionBuilder().SetPriority(i).SetConversionData(i).Build()));
   }
 
   clock()->Advance(base::TimeDelta::FromMilliseconds(kReportTime));
@@ -1247,24 +1325,32 @@
 
   storage()->StoreImpression(ImpressionBuilder(clock()->Now()).Build());
 
-  EXPECT_TRUE(storage()->MaybeCreateAndStoreConversionReport(
-      ConversionBuilder().SetPriority(1).SetConversionData(3).Build()));
+  EXPECT_EQ(
+      CreateReportStatus::kSuccess,
+      storage()->MaybeCreateAndStoreConversionReport(
+          ConversionBuilder().SetPriority(1).SetConversionData(3).Build()));
 
   clock()->Advance(base::TimeDelta::FromMilliseconds(1));
-  EXPECT_TRUE(storage()->MaybeCreateAndStoreConversionReport(
-      ConversionBuilder().SetPriority(1).SetConversionData(2).Build()));
+  EXPECT_EQ(
+      CreateReportStatus::kSuccess,
+      storage()->MaybeCreateAndStoreConversionReport(
+          ConversionBuilder().SetPriority(1).SetConversionData(2).Build()));
 
   // This report should not be stored, as even though it has the same priority
   // as the previous two, it is the most recent.
   clock()->Advance(base::TimeDelta::FromMilliseconds(1));
-  EXPECT_FALSE(storage()->MaybeCreateAndStoreConversionReport(
-      ConversionBuilder().SetPriority(1).SetConversionData(8).Build()));
+  EXPECT_EQ(
+      CreateReportStatus::kPriorityTooLow,
+      storage()->MaybeCreateAndStoreConversionReport(
+          ConversionBuilder().SetPriority(1).SetConversionData(8).Build()));
 
   // This report should be stored by replacing the one with `conversion_data ==
   // 2`, which is the most recent of the two with `priority == 1`.
   clock()->Advance(base::TimeDelta::FromMilliseconds(1));
-  EXPECT_TRUE(storage()->MaybeCreateAndStoreConversionReport(
-      ConversionBuilder().SetPriority(2).SetConversionData(5).Build()));
+  EXPECT_EQ(
+      CreateReportStatus::kSuccessDroppedLowerPriority,
+      storage()->MaybeCreateAndStoreConversionReport(
+          ConversionBuilder().SetPriority(2).SetConversionData(5).Build()));
 
   std::vector<ConversionReport> actual_reports =
       storage()->GetConversionsToReport(base::Time::Max());
@@ -1282,7 +1368,8 @@
       ImpressionBuilder(clock()->Now()).SetData(5).SetPriority(1).Build());
   EXPECT_EQ(2u, storage()->GetActiveImpressions().size());
 
-  EXPECT_TRUE(
+  EXPECT_EQ(
+      CreateReportStatus::kSuccess,
       storage()->MaybeCreateAndStoreConversionReport(DefaultConversion()));
 
   // Because the impression with data 5 has the highest priority, it is selected
@@ -1298,8 +1385,9 @@
 
   // This conversion should not be stored because all reports for the attributed
   // impression were in an earlier window.
-  EXPECT_FALSE(storage()->MaybeCreateAndStoreConversionReport(
-      ConversionBuilder().SetPriority(2).Build()));
+  EXPECT_EQ(CreateReportStatus::kPriorityTooLow,
+            storage()->MaybeCreateAndStoreConversionReport(
+                ConversionBuilder().SetPriority(2).Build()));
 
   // As a result, the impression with data 5 should also be deactivated.
   EXPECT_TRUE(storage()->GetActiveImpressions().empty());
@@ -1321,51 +1409,56 @@
   EXPECT_THAT(active_impressions[0].dedup_keys(), IsEmpty());
   EXPECT_THAT(active_impressions[1].dedup_keys(), IsEmpty());
 
-  EXPECT_TRUE(storage()->MaybeCreateAndStoreConversionReport(
-      ConversionBuilder()
-          .SetConversionDestination(net::SchemefulSite(
-              url::Origin::Create(GURL("https://a.example"))))
-          .SetDedupKey(11)
-          .SetConversionData(71)
-          .Build()));
+  EXPECT_EQ(CreateReportStatus::kSuccess,
+            storage()->MaybeCreateAndStoreConversionReport(
+                ConversionBuilder()
+                    .SetConversionDestination(net::SchemefulSite(
+                        url::Origin::Create(GURL("https://a.example"))))
+                    .SetDedupKey(11)
+                    .SetConversionData(71)
+                    .Build()));
 
   // Should be stored because dedup key doesn't match even though conversion
   // destination does.
-  EXPECT_TRUE(storage()->MaybeCreateAndStoreConversionReport(
-      ConversionBuilder()
-          .SetConversionDestination(net::SchemefulSite(
-              url::Origin::Create(GURL("https://a.example"))))
-          .SetDedupKey(12)
-          .SetConversionData(72)
-          .Build()));
+  EXPECT_EQ(CreateReportStatus::kSuccess,
+            storage()->MaybeCreateAndStoreConversionReport(
+                ConversionBuilder()
+                    .SetConversionDestination(net::SchemefulSite(
+                        url::Origin::Create(GURL("https://a.example"))))
+                    .SetDedupKey(12)
+                    .SetConversionData(72)
+                    .Build()));
 
   // Should be stored because conversion destination doesn't match even though
   // dedup key does.
-  EXPECT_TRUE(storage()->MaybeCreateAndStoreConversionReport(
-      ConversionBuilder()
-          .SetConversionDestination(net::SchemefulSite(
-              url::Origin::Create(GURL("https://b.example"))))
-          .SetDedupKey(12)
-          .SetConversionData(73)
-          .Build()));
+  EXPECT_EQ(CreateReportStatus::kSuccess,
+            storage()->MaybeCreateAndStoreConversionReport(
+                ConversionBuilder()
+                    .SetConversionDestination(net::SchemefulSite(
+                        url::Origin::Create(GURL("https://b.example"))))
+                    .SetDedupKey(12)
+                    .SetConversionData(73)
+                    .Build()));
 
   // Shouldn't be stored because conversion destination and dedup key match.
-  EXPECT_FALSE(storage()->MaybeCreateAndStoreConversionReport(
-      ConversionBuilder()
-          .SetConversionDestination(net::SchemefulSite(
-              url::Origin::Create(GURL("https://a.example"))))
-          .SetDedupKey(11)
-          .SetConversionData(74)
-          .Build()));
+  EXPECT_EQ(CreateReportStatus::kDeduplicated,
+            storage()->MaybeCreateAndStoreConversionReport(
+                ConversionBuilder()
+                    .SetConversionDestination(net::SchemefulSite(
+                        url::Origin::Create(GURL("https://a.example"))))
+                    .SetDedupKey(11)
+                    .SetConversionData(74)
+                    .Build()));
 
   // Shouldn't be stored because conversion destination and dedup key match.
-  EXPECT_FALSE(storage()->MaybeCreateAndStoreConversionReport(
-      ConversionBuilder()
-          .SetConversionDestination(net::SchemefulSite(
-              url::Origin::Create(GURL("https://b.example"))))
-          .SetDedupKey(12)
-          .SetConversionData(75)
-          .Build()));
+  EXPECT_EQ(CreateReportStatus::kDeduplicated,
+            storage()->MaybeCreateAndStoreConversionReport(
+                ConversionBuilder()
+                    .SetConversionDestination(net::SchemefulSite(
+                        url::Origin::Create(GURL("https://b.example"))))
+                    .SetDedupKey(12)
+                    .SetConversionData(75)
+                    .Build()));
 
   clock()->Advance(base::TimeDelta::FromMilliseconds(kReportTime));
   std::vector<ConversionReport> actual_reports =
@@ -1391,13 +1484,14 @@
 
   clock()->Advance(base::TimeDelta::FromMilliseconds(1));
 
-  EXPECT_TRUE(storage()->MaybeCreateAndStoreConversionReport(
-      ConversionBuilder()
-          .SetConversionDestination(net::SchemefulSite(
-              url::Origin::Create(GURL("https://a.example"))))
-          .SetDedupKey(2)
-          .SetConversionData(3)
-          .Build()));
+  EXPECT_EQ(CreateReportStatus::kSuccess,
+            storage()->MaybeCreateAndStoreConversionReport(
+                ConversionBuilder()
+                    .SetConversionDestination(net::SchemefulSite(
+                        url::Origin::Create(GURL("https://a.example"))))
+                    .SetDedupKey(2)
+                    .SetConversionData(3)
+                    .Build()));
 
   clock()->Advance(base::TimeDelta::FromMilliseconds(kReportTime));
 
@@ -1413,13 +1507,14 @@
 
   // This report shouldn't be stored, as it should be deduped against the
   // previously stored one even though that previous one is no longer in the DB.
-  EXPECT_FALSE(storage()->MaybeCreateAndStoreConversionReport(
-      ConversionBuilder()
-          .SetConversionDestination(net::SchemefulSite(
-              url::Origin::Create(GURL("https://a.example"))))
-          .SetDedupKey(2)
-          .SetConversionData(5)
-          .Build()));
+  EXPECT_EQ(CreateReportStatus::kDeduplicated,
+            storage()->MaybeCreateAndStoreConversionReport(
+                ConversionBuilder()
+                    .SetConversionDestination(net::SchemefulSite(
+                        url::Origin::Create(GURL("https://a.example"))))
+                    .SetDedupKey(2)
+                    .SetConversionData(5)
+                    .Build()));
 
   clock()->Advance(base::TimeDelta::FromMilliseconds(kReportTime));
   EXPECT_THAT(storage()->GetConversionsToReport(clock()->Now()), IsEmpty());
diff --git a/content/browser/conversions/conversion_test_utils.cc b/content/browser/conversions/conversion_test_utils.cc
index b8ac3533..54d70787 100644
--- a/content/browser/conversions/conversion_test_utils.cc
+++ b/content/browser/conversions/conversion_test_utils.cc
@@ -22,6 +22,8 @@
 
 namespace {
 
+using CreateReportStatus = ::content::ConversionStorage::CreateReportStatus;
+
 const char kDefaultImpressionOrigin[] = "https://impression.test/";
 const char kDefaultConversionOrigin[] = "https://sub.conversion.test/";
 const char kDefaultConversionDestination[] = "https://conversion.test/";
@@ -372,6 +374,39 @@
   return tie(a) == tie(b);
 }
 
+std::ostream& operator<<(std::ostream& out, CreateReportStatus result) {
+  switch (result) {
+    case CreateReportStatus::kSuccess:
+      out << "kSuccess";
+      break;
+    case CreateReportStatus::kSuccessDroppedLowerPriority:
+      out << "kSuccessDroppedLowerPriority";
+      break;
+    case CreateReportStatus::kInternalError:
+      out << "kInternalError";
+      break;
+    case CreateReportStatus::kNoCapacityForConversionDestination:
+      out << "kNoCapacityForConversionDestination";
+      break;
+    case CreateReportStatus::kNoMatchingImpressions:
+      out << "kNoMatchingImpressions";
+      break;
+    case CreateReportStatus::kDeduplicated:
+      out << "kDeduplicated";
+      break;
+    case CreateReportStatus::kRateLimited:
+      out << "kRateLimited";
+      break;
+    case CreateReportStatus::kPriorityTooLow:
+      out << "kPriorityTooLow";
+      break;
+    case CreateReportStatus::kDroppedForNoise:
+      out << "kDroppedForNoise";
+      break;
+  }
+  return out;
+}
+
 std::vector<ConversionReport> GetConversionsToReportForTesting(
     ConversionManagerImpl* manager,
     base::Time max_report_time) {
diff --git a/content/browser/conversions/conversion_test_utils.h b/content/browser/conversions/conversion_test_utils.h
index b74b438..310939d3 100644
--- a/content/browser/conversions/conversion_test_utils.h
+++ b/content/browser/conversions/conversion_test_utils.h
@@ -7,6 +7,7 @@
 
 #include <stdint.h>
 
+#include <iosfwd>
 #include <vector>
 
 #include "base/containers/circular_deque.h"
@@ -329,6 +330,9 @@
 
 bool operator==(const SentReportInfo& a, const SentReportInfo& b);
 
+std::ostream& operator<<(std::ostream& out,
+                         ConversionStorage::CreateReportStatus result);
+
 std::vector<ConversionReport> GetConversionsToReportForTesting(
     ConversionManagerImpl* manager,
     base::Time max_report_time) WARN_UNUSED_RESULT;
diff --git a/content/browser/conversions/rate_limit_table.cc b/content/browser/conversions/rate_limit_table.cc
index 1e10265..3adc7f1 100644
--- a/content/browser/conversions/rate_limit_table.cc
+++ b/content/browser/conversions/rate_limit_table.cc
@@ -53,7 +53,7 @@
   if (!db->Execute(kRateLimitTableSql))
     return false;
 
-  // Optimizes calls to |IsAttributionAllowed()|.
+  // Optimizes calls to |AttributionAllowed()|.
   static constexpr char kRateLimitImpressionSiteTypeIndexSql[] =
       "CREATE INDEX IF NOT EXISTS rate_limit_impression_site_type_idx "
       "ON rate_limits(attribution_type,conversion_destination,"
@@ -111,9 +111,10 @@
   return statement.Run();
 }
 
-bool RateLimitTable::IsAttributionAllowed(sql::Database* db,
-                                          const ConversionReport& report,
-                                          base::Time now) {
+RateLimitTable::AttributionAllowedStatus RateLimitTable::AttributionAllowed(
+    sql::Database* db,
+    const ConversionReport& report,
+    base::Time now) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 
   ConversionStorage::Delegate::RateLimitConfig rate_limits =
@@ -136,10 +137,12 @@
                        report.impression.ConversionDestination().Serialize());
   statement.BindTime(3, min_timestamp);
   if (!statement.Step())
-    return false;
+    return AttributionAllowedStatus::kError;
 
   int64_t count = statement.ColumnInt64(0);
-  return count < rate_limits.max_attributions_per_window;
+  return count < rate_limits.max_attributions_per_window
+             ? AttributionAllowedStatus::kAllowed
+             : AttributionAllowedStatus::kNotAllowed;
 }
 
 bool RateLimitTable::ClearAllDataInRange(sql::Database* db,
diff --git a/content/browser/conversions/rate_limit_table.h b/content/browser/conversions/rate_limit_table.h
index 7517e48..7770170 100644
--- a/content/browser/conversions/rate_limit_table.h
+++ b/content/browser/conversions/rate_limit_table.h
@@ -53,11 +53,18 @@
   bool AddRateLimit(sql::Database* db,
                     const ConversionReport& report) WARN_UNUSED_RESULT;
 
+  enum class AttributionAllowedStatus {
+    kAllowed,
+    kNotAllowed,
+    kError,
+  };
+
   // Checks if the given attribution is allowed according to the data in the
   // table and policy as specified by the delegate.
-  bool IsAttributionAllowed(sql::Database* db,
-                            const ConversionReport& report,
-                            base::Time now) WARN_UNUSED_RESULT;
+  AttributionAllowedStatus AttributionAllowed(sql::Database* db,
+                                              const ConversionReport& report,
+                                              base::Time now)
+      WARN_UNUSED_RESULT;
 
   // These should be 1:1 with |ConversionStorageSql|'s |ClearData| functions.
   // Returns false on failure.
diff --git a/content/browser/conversions/rate_limit_table_unittest.cc b/content/browser/conversions/rate_limit_table_unittest.cc
index f7cf2ab..bacddd2d 100644
--- a/content/browser/conversions/rate_limit_table_unittest.cc
+++ b/content/browser/conversions/rate_limit_table_unittest.cc
@@ -24,6 +24,8 @@
 
 namespace {
 
+using AttributionAllowedStatus =
+    ::content::RateLimitTable::AttributionAllowedStatus;
 using ::testing::ElementsAre;
 
 class RateLimitTableTest : public testing::Test {
@@ -133,7 +135,7 @@
   EXPECT_EQ(1u, GetRateLimitRows(&db));
 }
 
-TEST_F(RateLimitTableTest, IsAttributionAllowed) {
+TEST_F(RateLimitTableTest, AttributionAllowed) {
   sql::Database db;
   EXPECT_TRUE(db.Open(db_path()));
   EXPECT_TRUE(table()->CreateTable(&db));
@@ -175,25 +177,35 @@
   const auto report_b_a = NewConversionReport(example_b, example_a);
 
   base::Time now = clock()->Now();
-  EXPECT_FALSE(table()->IsAttributionAllowed(&db, report_a_c, now));
-  EXPECT_TRUE(table()->IsAttributionAllowed(&db, report_a_d, now));
-  EXPECT_TRUE(table()->IsAttributionAllowed(&db, report_b_c, now));
-  EXPECT_TRUE(table()->IsAttributionAllowed(&db, report_a_b, now));
-  EXPECT_TRUE(table()->IsAttributionAllowed(&db, report_b_a, now));
+  EXPECT_EQ(AttributionAllowedStatus::kNotAllowed,
+            table()->AttributionAllowed(&db, report_a_c, now));
+  EXPECT_EQ(AttributionAllowedStatus::kAllowed,
+            table()->AttributionAllowed(&db, report_a_d, now));
+  EXPECT_EQ(AttributionAllowedStatus::kAllowed,
+            table()->AttributionAllowed(&db, report_b_c, now));
+  EXPECT_EQ(AttributionAllowedStatus::kAllowed,
+            table()->AttributionAllowed(&db, report_a_b, now));
+  EXPECT_EQ(AttributionAllowedStatus::kAllowed,
+            table()->AttributionAllowed(&db, report_b_a, now));
 
   // Expire the first row above by advancing to +10d.
   clock()->Advance(base::TimeDelta::FromDays(4));
   now = clock()->Now();
-  EXPECT_TRUE(table()->IsAttributionAllowed(&db, report_a_c, now));
-  EXPECT_TRUE(table()->IsAttributionAllowed(&db, report_a_d, now));
-  EXPECT_TRUE(table()->IsAttributionAllowed(&db, report_b_c, now));
-  EXPECT_TRUE(table()->IsAttributionAllowed(&db, report_a_b, now));
-  EXPECT_TRUE(table()->IsAttributionAllowed(&db, report_b_a, now));
+  EXPECT_EQ(AttributionAllowedStatus::kAllowed,
+            table()->AttributionAllowed(&db, report_a_c, now));
+  EXPECT_EQ(AttributionAllowedStatus::kAllowed,
+            table()->AttributionAllowed(&db, report_a_d, now));
+  EXPECT_EQ(AttributionAllowedStatus::kAllowed,
+            table()->AttributionAllowed(&db, report_b_c, now));
+  EXPECT_EQ(AttributionAllowedStatus::kAllowed,
+            table()->AttributionAllowed(&db, report_a_b, now));
+  EXPECT_EQ(AttributionAllowedStatus::kAllowed,
+            table()->AttributionAllowed(&db, report_b_a, now));
 
   EXPECT_EQ(3u, GetRateLimitRows(&db));
 }
 
-TEST_F(RateLimitTableTest, IsAttributionAllowed_SourceTypesIndependent) {
+TEST_F(RateLimitTableTest, CheckAttributionAllowed_SourceTypesIndependent) {
   // Tests that limits are calculated independently for each
   // `StorableImpression::SourceType`. In the future, we may change this so that
   // there is a combined calculation but each source type is weighted
@@ -224,22 +236,28 @@
   EXPECT_EQ(2u, GetRateLimitRows(&db));
 
   base::Time now = clock()->Now();
-  EXPECT_TRUE(table()->IsAttributionAllowed(&db, report_navigation, now));
-  EXPECT_TRUE(table()->IsAttributionAllowed(&db, report_event, now));
+  EXPECT_EQ(AttributionAllowedStatus::kAllowed,
+            table()->AttributionAllowed(&db, report_navigation, now));
+  EXPECT_EQ(AttributionAllowedStatus::kAllowed,
+            table()->AttributionAllowed(&db, report_event, now));
 
   EXPECT_TRUE(table()->AddRateLimit(&db, report_navigation));
   EXPECT_EQ(3u, GetRateLimitRows(&db));
-  EXPECT_FALSE(table()->IsAttributionAllowed(&db, report_navigation, now));
-  EXPECT_TRUE(table()->IsAttributionAllowed(&db, report_event, now));
+  EXPECT_EQ(AttributionAllowedStatus::kNotAllowed,
+            table()->AttributionAllowed(&db, report_navigation, now));
+  EXPECT_EQ(AttributionAllowedStatus::kAllowed,
+            table()->AttributionAllowed(&db, report_event, now));
 
   EXPECT_TRUE(table()->AddRateLimit(&db, report_event));
   EXPECT_EQ(4u, GetRateLimitRows(&db));
-  EXPECT_FALSE(table()->IsAttributionAllowed(&db, report_navigation, now));
-  EXPECT_FALSE(table()->IsAttributionAllowed(&db, report_event, now));
+  EXPECT_EQ(AttributionAllowedStatus::kNotAllowed,
+            table()->AttributionAllowed(&db, report_navigation, now));
+  EXPECT_EQ(AttributionAllowedStatus::kNotAllowed,
+            table()->AttributionAllowed(&db, report_event, now));
 }
 
 TEST_F(RateLimitTableTest,
-       IsAttributionAllowed_ConversionDestinationSubdomains) {
+       CheckAttributionAllowed_ConversionDestinationSubdomains) {
   sql::Database db;
   EXPECT_TRUE(db.Open(db_path()));
   EXPECT_TRUE(table()->CreateTable(&db));
@@ -265,15 +283,18 @@
       &db, NewConversionReport(example_a, example_c_sub_b)));
 
   base::Time now = clock()->Now();
-  EXPECT_FALSE(table()->IsAttributionAllowed(
-      &db, NewConversionReport(example_a, example_c_sub_a), now));
-  EXPECT_FALSE(table()->IsAttributionAllowed(
-      &db, NewConversionReport(example_a, example_c_sub_b), now));
-  EXPECT_FALSE(table()->IsAttributionAllowed(
-      &db, NewConversionReport(example_a, example_c), now));
+  EXPECT_EQ(AttributionAllowedStatus::kNotAllowed,
+            table()->AttributionAllowed(
+                &db, NewConversionReport(example_a, example_c_sub_a), now));
+  EXPECT_EQ(AttributionAllowedStatus::kNotAllowed,
+            table()->AttributionAllowed(
+                &db, NewConversionReport(example_a, example_c_sub_b), now));
+  EXPECT_EQ(AttributionAllowedStatus::kNotAllowed,
+            table()->AttributionAllowed(
+                &db, NewConversionReport(example_a, example_c), now));
 }
 
-TEST_F(RateLimitTableTest, IsAttributionAllowed_ImpressionSiteSubdomains) {
+TEST_F(RateLimitTableTest, CheckAttributionAllowed_ImpressionSiteSubdomains) {
   sql::Database db;
   EXPECT_TRUE(db.Open(db_path()));
   EXPECT_TRUE(table()->CreateTable(&db));
@@ -299,12 +320,15 @@
       &db, NewConversionReport(example_c_sub_b, example_a)));
 
   base::Time now = clock()->Now();
-  EXPECT_FALSE(table()->IsAttributionAllowed(
-      &db, NewConversionReport(example_c_sub_a, example_a), now));
-  EXPECT_FALSE(table()->IsAttributionAllowed(
-      &db, NewConversionReport(example_c_sub_b, example_a), now));
-  EXPECT_FALSE(table()->IsAttributionAllowed(
-      &db, NewConversionReport(example_c, example_a), now));
+  EXPECT_EQ(AttributionAllowedStatus::kNotAllowed,
+            table()->AttributionAllowed(
+                &db, NewConversionReport(example_c_sub_a, example_a), now));
+  EXPECT_EQ(AttributionAllowedStatus::kNotAllowed,
+            table()->AttributionAllowed(
+                &db, NewConversionReport(example_c_sub_b, example_a), now));
+  EXPECT_EQ(AttributionAllowedStatus::kNotAllowed,
+            table()->AttributionAllowed(
+                &db, NewConversionReport(example_c, example_a), now));
 }
 
 TEST_F(RateLimitTableTest, ClearAllDataAllTime) {
@@ -354,10 +378,12 @@
 
   base::Time now = clock()->Now();
 
-  EXPECT_FALSE(table()->IsAttributionAllowed(
-      &db, NewConversionReport(example_a, example_b), now));
-  EXPECT_FALSE(table()->IsAttributionAllowed(
-      &db, NewConversionReport(example_b, example_c), now));
+  EXPECT_EQ(AttributionAllowedStatus::kNotAllowed,
+            table()->AttributionAllowed(
+                &db, NewConversionReport(example_a, example_b), now));
+  EXPECT_EQ(AttributionAllowedStatus::kNotAllowed,
+            table()->AttributionAllowed(
+                &db, NewConversionReport(example_b, example_c), now));
 
   // Delete the first row: attribution should now be allowed for the site,
   // but the other rows should not be deleted.
@@ -365,10 +391,12 @@
                                            now - base::TimeDelta::FromDays(7),
                                            now - base::TimeDelta::FromDays(6)));
   EXPECT_EQ(3u, GetRateLimitRows(&db));
-  EXPECT_TRUE(table()->IsAttributionAllowed(
-      &db, NewConversionReport(example_a, example_b), now));
-  EXPECT_FALSE(table()->IsAttributionAllowed(
-      &db, NewConversionReport(example_b, example_c), now));
+  EXPECT_EQ(AttributionAllowedStatus::kAllowed,
+            table()->AttributionAllowed(
+                &db, NewConversionReport(example_a, example_b), now));
+  EXPECT_EQ(AttributionAllowedStatus::kNotAllowed,
+            table()->AttributionAllowed(
+                &db, NewConversionReport(example_b, example_c), now));
 }
 
 TEST_F(RateLimitTableTest, ClearDataForOriginsInRange) {
@@ -402,24 +430,27 @@
   EXPECT_EQ(3u, GetRateLimitRows(&db));
 
   base::Time now = clock()->Now();
-  EXPECT_FALSE(table()->IsAttributionAllowed(
-      &db, NewConversionReport(example_a, example_b), now));
+  EXPECT_EQ(AttributionAllowedStatus::kNotAllowed,
+            table()->AttributionAllowed(
+                &db, NewConversionReport(example_a, example_b), now));
 
   // Should delete nothing, because (example_d, example_c) is at now.
   EXPECT_TRUE(table()->ClearDataForOriginsInRange(
       &db, base::Time(), now - base::TimeDelta::FromDays(1),
       base::BindRepeating(std::equal_to<url::Origin>(), example_c)));
   EXPECT_EQ(3u, GetRateLimitRows(&db));
-  EXPECT_FALSE(table()->IsAttributionAllowed(
-      &db, NewConversionReport(example_a, example_b), now));
+  EXPECT_EQ(AttributionAllowedStatus::kNotAllowed,
+            table()->AttributionAllowed(
+                &db, NewConversionReport(example_a, example_b), now));
 
   // Should delete (example_a, example_ba).
   EXPECT_TRUE(table()->ClearDataForOriginsInRange(
       &db, base::Time(), base::Time::Max(),
       base::BindRepeating(std::equal_to<url::Origin>(), example_ba)));
   EXPECT_EQ(2u, GetRateLimitRows(&db));
-  EXPECT_TRUE(table()->IsAttributionAllowed(
-      &db, NewConversionReport(example_a, example_b), now));
+  EXPECT_EQ(AttributionAllowedStatus::kAllowed,
+            table()->AttributionAllowed(
+                &db, NewConversionReport(example_a, example_b), now));
 
   // Should delete (example_d, example_c), the only report >= now.
   EXPECT_TRUE(table()->ClearDataForOriginsInRange(
@@ -511,17 +542,21 @@
   EXPECT_TRUE(table()->AddRateLimit(
       &db, NewConversionReport(example_c, example_d, /*impression_id=*/4)));
   EXPECT_EQ(4u, GetRateLimitRows(&db));
-  EXPECT_FALSE(table()->IsAttributionAllowed(
-      &db, NewConversionReport(example_a, example_b), now));
-  EXPECT_FALSE(table()->IsAttributionAllowed(
-      &db, NewConversionReport(example_c, example_d), now));
+  EXPECT_EQ(AttributionAllowedStatus::kNotAllowed,
+            table()->AttributionAllowed(
+                &db, NewConversionReport(example_a, example_b), now));
+  EXPECT_EQ(AttributionAllowedStatus::kNotAllowed,
+            table()->AttributionAllowed(
+                &db, NewConversionReport(example_c, example_d), now));
 
   EXPECT_TRUE(table()->ClearDataForImpressionIds(&db, {1, 4}));
   EXPECT_EQ(2u, GetRateLimitRows(&db));
-  EXPECT_TRUE(table()->IsAttributionAllowed(
-      &db, NewConversionReport(example_a, example_b), now));
-  EXPECT_TRUE(table()->IsAttributionAllowed(
-      &db, NewConversionReport(example_c, example_d), now));
+  EXPECT_EQ(AttributionAllowedStatus::kAllowed,
+            table()->AttributionAllowed(
+                &db, NewConversionReport(example_a, example_b), now));
+  EXPECT_EQ(AttributionAllowedStatus::kAllowed,
+            table()->AttributionAllowed(
+                &db, NewConversionReport(example_c, example_d), now));
 }
 
 }  // namespace content
diff --git a/content/browser/devtools/protocol/devtools_protocol_browsertest.cc b/content/browser/devtools/protocol/devtools_protocol_browsertest.cc
index 1704804..73a105bd 100644
--- a/content/browser/devtools/protocol/devtools_protocol_browsertest.cc
+++ b/content/browser/devtools/protocol/devtools_protocol_browsertest.cc
@@ -1367,9 +1367,9 @@
   SendCommand("Page.handleJavaScriptDialog", std::move(params), false);
 
   params = WaitForNotification("Page.javascriptDialogClosed", true);
-  bool result = false;
-  EXPECT_TRUE(params->GetBoolean("result", &result));
+  absl::optional<bool> result = params->FindBoolPath("result");
   EXPECT_TRUE(result);
+  EXPECT_TRUE(*result);
 
   EXPECT_TRUE(dialog_manager.is_handled());
 
@@ -1500,12 +1500,13 @@
 
   // TODO(eseckler): Since the RenderView is closed asynchronously, we currently
   // don't verify that the command actually closes the shell.
-  bool success;
   params = std::make_unique<base::DictionaryValue>();
   params->SetString("targetId", target_id);
   SendCommand("Target.closeTarget", std::move(params), true);
-  EXPECT_TRUE(result_->GetBoolean("success", &success));
+
+  absl::optional<bool> success = result_->FindBoolPath("success");
   EXPECT_TRUE(success);
+  EXPECT_TRUE(*success);
 }
 
 IN_PROC_BROWSER_TEST_F(DevToolsProtocolTest, BrowserGetTargets) {
@@ -1788,7 +1789,7 @@
   std::set<std::string> ids;
   std::unique_ptr<base::DictionaryValue> command_params;
   std::unique_ptr<base::DictionaryValue> params;
-  bool is_attached;
+  absl::optional<bool> is_attached;
 
   ASSERT_TRUE(embedded_test_server()->Start());
   GURL first_url = embedded_test_server()->GetURL("/devtools/navigation.html");
@@ -1806,16 +1807,18 @@
   params = WaitForNotification("Target.targetCreated", true);
   EXPECT_TRUE(params->GetString("targetInfo.type", &temp));
   EXPECT_EQ("page", temp);
-  EXPECT_TRUE(params->GetBoolean("targetInfo.attached", &is_attached));
-  attached_count += is_attached ? 1 : 0;
+  is_attached = params->FindBoolPath("targetInfo.attached");
+  EXPECT_TRUE(is_attached);
+  attached_count += *is_attached ? 1 : 0;
   EXPECT_TRUE(params->GetString("targetInfo.targetId", &temp));
   EXPECT_TRUE(ids.find(temp) == ids.end());
   ids.insert(temp);
   params = WaitForNotification("Target.targetCreated", true);
   EXPECT_TRUE(params->GetString("targetInfo.type", &temp));
   EXPECT_EQ("page", temp);
-  EXPECT_TRUE(params->GetBoolean("targetInfo.attached", &is_attached));
-  attached_count += is_attached ? 1 : 0;
+  is_attached = params->FindBoolPath("targetInfo.attached");
+  EXPECT_TRUE(is_attached);
+  attached_count += *is_attached ? 1 : 0;
   EXPECT_TRUE(params->GetString("targetInfo.targetId", &temp));
   EXPECT_TRUE(ids.find(temp) == ids.end());
   ids.insert(temp);
@@ -1831,8 +1834,9 @@
   EXPECT_EQ("page", temp);
   EXPECT_TRUE(params->GetString("targetInfo.targetId", &temp));
   EXPECT_TRUE(ids.find(temp) == ids.end());
-  EXPECT_TRUE(params->GetBoolean("targetInfo.attached", &is_attached));
-  EXPECT_FALSE(is_attached);
+  is_attached = params->FindBoolPath("targetInfo.attached");
+  EXPECT_TRUE(is_attached);
+  EXPECT_FALSE(*is_attached);
   std::string attached_id = temp;
   ids.insert(temp);
 
@@ -1859,8 +1863,9 @@
   params = WaitForNotification("Target.targetInfoChanged", true);
   EXPECT_TRUE(params->GetString("targetInfo.targetId", &temp));
   EXPECT_EQ(attached_id, temp);
-  EXPECT_TRUE(params->GetBoolean("targetInfo.attached", &is_attached));
+  is_attached = params->FindBoolPath("targetInfo.attached");
   EXPECT_TRUE(is_attached);
+  EXPECT_TRUE(*is_attached);
   params = WaitForNotification("Target.attachedToTarget", true);
   std::string session_id;
   EXPECT_TRUE(params->GetString("sessionId", &session_id));
diff --git a/content/browser/devtools/protocol/devtools_protocol_test_support.cc b/content/browser/devtools/protocol/devtools_protocol_test_support.cc
index 68e0aac..bec1e8b6 100644
--- a/content/browser/devtools/protocol/devtools_protocol_test_support.cc
+++ b/content/browser/devtools/protocol/devtools_protocol_test_support.cc
@@ -201,8 +201,9 @@
     std::string interception_id;
     ASSERT_TRUE(params->GetString("interceptionId", &interception_id));
     bool is_redirect = params->HasKey("redirectUrl");
-    bool is_navigation;
-    ASSERT_TRUE(params->GetBoolean("isNavigationRequest", &is_navigation));
+    absl::optional<bool> is_navigation =
+        params->FindBoolPath("isNavigationRequest");
+    ASSERT_TRUE(is_navigation);
     std::string resource_type;
     ASSERT_TRUE(params->GetString("resourceType", &resource_type));
     std::string url;
@@ -212,7 +213,7 @@
     // The url will typically have a random port which we want to remove.
     url = RemovePort(GURL(url));
 
-    if (!is_navigation) {
+    if (*is_navigation) {
       params = std::make_unique<base::DictionaryValue>();
       params->SetString("interceptionId", interception_id);
       SendCommand("Network.continueInterceptedRequest", std::move(params),
diff --git a/content/browser/gpu/gpu_process_host.cc b/content/browser/gpu/gpu_process_host.cc
index 20c70c6..8d0e6bc8 100644
--- a/content/browser/gpu/gpu_process_host.cc
+++ b/content/browser/gpu/gpu_process_host.cc
@@ -254,6 +254,7 @@
     switches::kEnableLogging,
     switches::kEnableDeJelly,
     switches::kDeJellyScreenWidth,
+    switches::kDocumentTransitionSlowdownFactor,
     switches::kDoubleBufferCompositing,
     switches::kHeadless,
     switches::kLoggingLevel,
diff --git a/content/browser/interest_group/ad_auction_service_impl.cc b/content/browser/interest_group/ad_auction_service_impl.cc
index b7712e80..6d1eed72a 100644
--- a/content/browser/interest_group/ad_auction_service_impl.cc
+++ b/content/browser/interest_group/ad_auction_service_impl.cc
@@ -10,8 +10,10 @@
 #include "base/check.h"
 #include "base/containers/contains.h"
 #include "base/strings/stringprintf.h"
+#include "base/time/time.h"
 #include "content/browser/devtools/devtools_instrumentation.h"
 #include "content/browser/interest_group/auction_runner.h"
+#include "content/browser/interest_group/interest_group_manager.h"
 #include "content/browser/renderer_host/render_frame_host_impl.h"
 #include "content/browser/storage_partition_impl.h"
 #include "content/public/browser/browser_context.h"
@@ -29,6 +31,7 @@
 #include "services/network/public/mojom/url_loader_factory.mojom.h"
 #include "services/network/public/mojom/url_response_head.mojom.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
+#include "third_party/blink/public/common/interest_group/interest_group.h"
 #include "url/gurl.h"
 #include "url/origin.h"
 #include "url/url_constants.h"
@@ -37,6 +40,8 @@
 
 namespace {
 
+constexpr base::TimeDelta kMaxExpiry = base::TimeDelta::FromDays(30);
+
 constexpr net::NetworkTrafficAnnotationTag kTrafficAnnotation =
     net::DefineNetworkTrafficAnnotation("auction_report_sender", R"(
         semantics {
@@ -151,6 +156,59 @@
   new AdAuctionServiceImpl(render_frame_host, std::move(receiver));
 }
 
+void AdAuctionServiceImpl::JoinInterestGroup(
+    const blink::InterestGroup& group) {
+  // If the interest group API is not allowed for this origin do nothing.
+  if (!GetContentClient()->browser()->IsInterestGroupAPIAllowed(
+          render_frame_host()->GetBrowserContext(), origin(),
+          group.owner.GetURL())) {
+    return;
+  }
+
+  // Disallow setting interest groups for another origin. Eventually, this will
+  // need to perform a fetch to check for cross-origin permissions to add an
+  // interest group.
+  if (origin() != group.owner)
+    return;
+
+  RenderFrameHost* main_frame = render_frame_host()->GetMainFrame();
+  GURL main_frame_url = main_frame->GetLastCommittedURL();
+
+  blink::InterestGroup updated_group = group;
+  base::Time max_expiry = base::Time::Now() + kMaxExpiry;
+  if (updated_group.expiry > max_expiry)
+    updated_group.expiry = max_expiry;
+  GetInterestGroupManager().JoinInterestGroup(std::move(updated_group),
+                                              main_frame_url);
+}
+
+void AdAuctionServiceImpl::LeaveInterestGroup(const url::Origin& owner,
+                                              const std::string& name) {
+  // If the interest group API is not allowed for this origin do nothing.
+  if (!GetContentClient()->browser()->IsInterestGroupAPIAllowed(
+          render_frame_host()->GetBrowserContext(), origin(), owner.GetURL())) {
+    return;
+  }
+
+  if (origin().scheme() != url::kHttpsScheme)
+    return;
+
+  if (owner != origin())
+    return;
+
+  GetInterestGroupManager().LeaveInterestGroup(owner, name);
+}
+
+void AdAuctionServiceImpl::UpdateAdInterestGroups() {
+  // If the interest group API is not allowed for this origin do nothing.
+  if (!GetContentClient()->browser()->IsInterestGroupAPIAllowed(
+          render_frame_host()->GetBrowserContext(), origin(),
+          origin().GetURL())) {
+    return;
+  }
+  GetInterestGroupManager().UpdateInterestGroupsOfOwner(origin());
+}
+
 void AdAuctionServiceImpl::RunAdAuction(blink::mojom::AuctionAdConfigPtr config,
                                         RunAdAuctionCallback callback) {
   if (!IsAuctionValid(*config)) {
@@ -196,12 +254,8 @@
       std::move(top_frame_origin), config->seller);
 
   std::unique_ptr<AuctionRunner> auction = AuctionRunner::CreateAndStart(
-      this,
-      static_cast<StoragePartitionImpl*>(
-          render_frame_host()->GetStoragePartition())
-          ->GetInterestGroupManager(),
-      std::move(config), std::move(filtered_buyers), std::move(browser_signals),
-      frame_origin,
+      this, &GetInterestGroupManager(), std::move(config),
+      std::move(filtered_buyers), std::move(browser_signals), frame_origin,
       base::BindOnce(&AdAuctionServiceImpl::OnAuctionComplete,
                      base::Unretained(this), std::move(callback)));
   auctions_.insert(std::move(auction));
@@ -285,4 +339,10 @@
     FetchReport(factory, *seller_report_url, origin());
 }
 
+InterestGroupManager& AdAuctionServiceImpl::GetInterestGroupManager() const {
+  return *static_cast<StoragePartitionImpl*>(
+              render_frame_host()->GetStoragePartition())
+              ->GetInterestGroupManager();
+}
+
 }  // namespace content
diff --git a/content/browser/interest_group/ad_auction_service_impl.h b/content/browser/interest_group/ad_auction_service_impl.h
index 90a448a..5b600b9fc 100644
--- a/content/browser/interest_group/ad_auction_service_impl.h
+++ b/content/browser/interest_group/ad_auction_service_impl.h
@@ -10,7 +10,6 @@
 
 #include "base/containers/unique_ptr_adapters.h"
 #include "content/browser/interest_group/auction_runner.h"
-#include "content/browser/interest_group/interest_group_manager.h"
 #include "content/common/content_export.h"
 #include "content/public/browser/document_service_base.h"
 #include "mojo/public/cpp/bindings/pending_receiver.h"
@@ -24,6 +23,7 @@
 namespace content {
 
 class AuctionRunner;
+class InterestGroupManager;
 class RenderFrameHost;
 class RenderFrameHostImpl;
 
@@ -39,6 +39,10 @@
       mojo::PendingReceiver<blink::mojom::AdAuctionService> receiver);
 
   // blink::mojom::AdAuctionService.
+  void JoinInterestGroup(const blink::InterestGroup& group) override;
+  void LeaveInterestGroup(const url::Origin& owner,
+                          const std::string& name) override;
+  void UpdateAdInterestGroups() override;
   void RunAdAuction(blink::mojom::AuctionAdConfigPtr config,
                     RunAdAuctionCallback callback) override;
 
@@ -68,6 +72,8 @@
                          absl::optional<GURL> seller_report_url,
                          std::vector<std::string> errors);
 
+  InterestGroupManager& GetInterestGroupManager() const;
+
   // This must be above `auction_worklet_service_`, since auctions may own
   // callbacks over the AuctionWorkletService pipe, and mojo pipes must be
   // destroyed before any callbacks that are bound to them.
diff --git a/content/browser/interest_group/interest_group_browsertest.cc b/content/browser/interest_group/interest_group_browsertest.cc
index 129e1991..51ae19b 100644
--- a/content/browser/interest_group/interest_group_browsertest.cc
+++ b/content/browser/interest_group/interest_group_browsertest.cc
@@ -21,7 +21,6 @@
 #include "components/network_session_configurator/common/network_switches.h"
 #include "content/browser/interest_group/ad_auction_service_impl.h"
 #include "content/browser/interest_group/interest_group_manager.h"
-#include "content/browser/interest_group/restricted_interest_group_store_impl.h"
 #include "content/browser/renderer_host/render_frame_host_impl.h"
 #include "content/browser/storage_partition_impl.h"
 #include "content/public/browser/browser_context.h"
@@ -51,7 +50,6 @@
 #include "third_party/blink/public/common/interest_group/interest_group.h"
 #include "third_party/blink/public/mojom/interest_group/ad_auction_service.mojom.h"
 #include "third_party/blink/public/mojom/interest_group/interest_group_types.mojom.h"
-#include "third_party/blink/public/mojom/interest_group/restricted_interest_group_store.mojom.h"
 #include "url/gurl.h"
 #include "url/origin.h"
 
@@ -1646,6 +1644,74 @@
   EXPECT_EQ(bidding_interest_groups2.front().group->signals->bid_count, 3);
 }
 
+// Adding an interest group and then immediately running the ad acution, without
+// waiting in between, should always work because although adding the interest
+// group is async (and intentionally without completion notification), it should
+// complete before the auction runs.
+//
+// On regression, this test will likely only fail with very low frequency.
+IN_PROC_BROWSER_TEST_F(InterestGroupBrowserTest,
+                       AddInterestGroupRunAuctionWithWinnerWithoutWaiting) {
+  GURL test_url = https_server_->GetURL("a.test", "/echo");
+  ASSERT_TRUE(NavigateToURL(shell(), test_url));
+
+  std::string ads =
+      "[{renderUrl : 'https://example.com/render',"
+      "metadata : {ad:'metadata', here : [ 1, 2 ]}}]";
+
+  // Use JoinInterestGroupInJS() instead of JoinInterestGroupAndWaitInJs().
+
+  EXPECT_TRUE(JoinInterestGroupInJS(
+      blink::InterestGroup(
+          /*expiry=*/base::Time(),
+          /*owner=*/url::Origin::Create(test_url.GetOrigin()),
+          /*name=*/"cars",
+          /*bidding_url=*/
+          https_server_->GetURL("a.test", "/interest_group/bidding_logic.js"),
+          /*update_url=*/absl::nullopt,
+          /*trusted_bidding_signals_url=*/
+          https_server_->GetURL("a.test",
+                                "/interest_group/trusted_bidding_signals.json"),
+          /*trusted_bidding_signals_keys=*/absl::nullopt,
+          /*user_bidding_signals=*/"{some: 'json', data: {here: [1, 2]}}",
+          /*ads=*/absl::nullopt),
+      ads, "['key1']"));
+
+  EXPECT_EQ(
+      "https://example.com/render",
+      RunAuctionAndWait(JsReplace(
+          R"({
+    seller: $1,
+    decisionLogicUrl: $2,
+    interestGroupBuyers: [$1],
+    auctionSignals: {x: 1},
+    sellerSignals: {yet: 'more', info: 1},
+    perBuyerSignals: {$1: {even: 'more', x: 4.5}}
+  })",
+          test_url.GetOrigin().spec(),
+          https_server_->GetURL("a.test", "/interest_group/decision_logic.js")
+              .spec())));
+
+  // Leave the interest group, then re-run the auction. We shouldn't get a
+  // result.
+  LeaveInterestGroupInJS(/*owner=*/url::Origin::Create(test_url.GetOrigin()),
+                         /*name=*/"cars");
+  EXPECT_EQ(
+      nullptr,
+      RunAuctionAndWait(JsReplace(
+          R"({
+    seller: $1,
+    decisionLogicUrl: $2,
+    interestGroupBuyers: [$1],
+    auctionSignals: {x: 1},
+    sellerSignals: {yet: 'more', info: 1},
+    perBuyerSignals: {$1: {even: 'more', x: 4.5}}
+  })",
+          test_url.GetOrigin().spec(),
+          https_server_->GetURL("a.test", "/interest_group/decision_logic.js")
+              .spec())));
+}
+
 // The winning ad's render url is invalid (invalid url or has http scheme).
 IN_PROC_BROWSER_TEST_F(InterestGroupBrowserTest, RunAdAuctionWithInvalidAdUrl) {
   GURL test_url = https_server_->GetURL("a.test", "/echo");
@@ -2114,8 +2180,8 @@
     ASSERT_TRUE(test_url_a.SchemeIs(url::kHttpsScheme));
     ASSERT_TRUE(NavigateToURL(shell(), test_url_a));
 
-    mojo::Remote<blink::mojom::RestrictedInterestGroupStore> interest_service;
-    RestrictedInterestGroupStoreImpl::CreateMojoService(
+    mojo::Remote<blink::mojom::AdAuctionService> interest_service;
+    AdAuctionServiceImpl::CreateMojoService(
         shell()->web_contents()->GetMainFrame(),
         interest_service.BindNewPipeAndPassReceiver());
 
diff --git a/content/browser/interest_group/restricted_interest_group_store_impl.cc b/content/browser/interest_group/restricted_interest_group_store_impl.cc
deleted file mode 100644
index 33002d2..0000000
--- a/content/browser/interest_group/restricted_interest_group_store_impl.cc
+++ /dev/null
@@ -1,101 +0,0 @@
-// Copyright 2021 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "content/browser/interest_group/restricted_interest_group_store_impl.h"
-
-#include "base/time/time.h"
-#include "content/browser/interest_group/interest_group_manager.h"
-#include "content/browser/renderer_host/render_frame_host_impl.h"
-#include "content/browser/storage_partition_impl.h"
-#include "content/public/browser/browser_context.h"
-#include "content/public/browser/render_frame_host.h"
-#include "content/public/common/content_client.h"
-#include "third_party/blink/public/common/interest_group/interest_group.h"
-#include "third_party/blink/public/mojom/interest_group/interest_group_types.mojom.h"
-
-namespace content {
-
-namespace {
-
-constexpr base::TimeDelta kMaxExpiry = base::TimeDelta::FromDays(30);
-
-}  // namespace
-
-RestrictedInterestGroupStoreImpl::RestrictedInterestGroupStoreImpl(
-    RenderFrameHost* render_frame_host,
-    mojo::PendingReceiver<blink::mojom::RestrictedInterestGroupStore> receiver)
-    : DocumentServiceBase(render_frame_host, std::move(receiver)),
-      interest_group_manager_(*static_cast<StoragePartitionImpl*>(
-                                   render_frame_host->GetStoragePartition())
-                                   ->GetInterestGroupManager()) {}
-
-// static
-void RestrictedInterestGroupStoreImpl::CreateMojoService(
-    RenderFrameHost* render_frame_host,
-    mojo::PendingReceiver<blink::mojom::RestrictedInterestGroupStore>
-        receiver) {
-  DCHECK(render_frame_host);
-
-  // The object is bound to the lifetime of |render_frame_host| and the mojo
-  // connection. See DocumentServiceBase for details.
-  new RestrictedInterestGroupStoreImpl(render_frame_host, std::move(receiver));
-}
-
-void RestrictedInterestGroupStoreImpl::JoinInterestGroup(
-    const blink::InterestGroup& group) {
-  // If the interest group API is not allowed for this origin do nothing.
-  if (!GetContentClient()->browser()->IsInterestGroupAPIAllowed(
-          render_frame_host()->GetBrowserContext(), origin(),
-          group.owner.GetURL())) {
-    return;
-  }
-
-  // Disallow setting interest groups for another origin. Eventually, this will
-  // need to perform a fetch to check for cross-origin permissions to add an
-  // interest group.
-  if (origin() != group.owner)
-    return;
-
-  RenderFrameHost* main_frame = render_frame_host()->GetMainFrame();
-  GURL main_frame_url = main_frame->GetLastCommittedURL();
-
-  blink::InterestGroup updated_group = group;
-  base::Time max_expiry = base::Time::Now() + kMaxExpiry;
-  if (updated_group.expiry > max_expiry)
-    updated_group.expiry = max_expiry;
-  interest_group_manager_.JoinInterestGroup(std::move(updated_group),
-                                            main_frame_url);
-}
-
-void RestrictedInterestGroupStoreImpl::LeaveInterestGroup(
-    const url::Origin& owner,
-    const std::string& name) {
-  // If the interest group API is not allowed for this origin do nothing.
-  if (!GetContentClient()->browser()->IsInterestGroupAPIAllowed(
-          render_frame_host()->GetBrowserContext(), origin(), owner.GetURL())) {
-    return;
-  }
-
-  if (origin().scheme() != url::kHttpsScheme)
-    return;
-
-  if (owner != origin())
-    return;
-
-  interest_group_manager_.LeaveInterestGroup(owner, name);
-}
-
-void RestrictedInterestGroupStoreImpl::UpdateAdInterestGroups() {
-  // If the interest group API is not allowed for this origin do nothing.
-  if (!GetContentClient()->browser()->IsInterestGroupAPIAllowed(
-          render_frame_host()->GetBrowserContext(), origin(),
-          origin().GetURL())) {
-    return;
-  }
-  interest_group_manager_.UpdateInterestGroupsOfOwner(origin());
-}
-
-RestrictedInterestGroupStoreImpl::~RestrictedInterestGroupStoreImpl() = default;
-
-}  // namespace content
diff --git a/content/browser/interest_group/restricted_interest_group_store_impl.h b/content/browser/interest_group/restricted_interest_group_store_impl.h
deleted file mode 100644
index 10956d2f..0000000
--- a/content/browser/interest_group/restricted_interest_group_store_impl.h
+++ /dev/null
@@ -1,51 +0,0 @@
-// Copyright 2021 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CONTENT_BROWSER_INTEREST_GROUP_RESTRICTED_INTEREST_GROUP_STORE_IMPL_H_
-#define CONTENT_BROWSER_INTEREST_GROUP_RESTRICTED_INTEREST_GROUP_STORE_IMPL_H_
-
-#include "content/browser/interest_group/interest_group_manager.h"
-#include "content/common/content_export.h"
-#include "content/public/browser/document_service_base.h"
-#include "mojo/public/cpp/bindings/pending_receiver.h"
-#include "third_party/blink/public/mojom/interest_group/restricted_interest_group_store.mojom.h"
-
-namespace content {
-
-class RenderFrameHost;
-
-// Implements the RestrictedInterestGroupStore service called by Blink code.
-class CONTENT_EXPORT RestrictedInterestGroupStoreImpl final
-    : public DocumentServiceBase<blink::mojom::RestrictedInterestGroupStore> {
- public:
-  // Factory method for creating an instance of this interface that is bound
-  // to the lifetime of the frame or receiver (whichever is shorter).
-  static void CreateMojoService(
-      RenderFrameHost* render_frame_host,
-      mojo::PendingReceiver<blink::mojom::RestrictedInterestGroupStore>
-          receiver);
-
-  // blink::mojom::RestrictedInterestGroupStore.
-  void JoinInterestGroup(const blink::InterestGroup& group) override;
-  void LeaveInterestGroup(const url::Origin& owner,
-                          const std::string& name) override;
-  void UpdateAdInterestGroups() override;
-
- private:
-  // `render_frame_host` must not be null, and DocumentServiceBase guarantees
-  // `this` will not outlive the `render_frame_host`.
-  RestrictedInterestGroupStoreImpl(
-      RenderFrameHost* render_frame_host,
-      mojo::PendingReceiver<blink::mojom::RestrictedInterestGroupStore>
-          receiver);
-
-  // `this` can only be destroyed by DocumentServiceBase.
-  ~RestrictedInterestGroupStoreImpl() override;
-
-  InterestGroupManager& interest_group_manager_;
-};
-
-}  // namespace content
-
-#endif  // CONTENT_BROWSER_INTEREST_GROUP_RESTRICTED_INTEREST_GROUP_STORE_IMPL_H_
diff --git a/content/browser/interest_group/restricted_interest_group_store_impl_unittest.cc b/content/browser/interest_group/restricted_interest_group_store_impl_unittest.cc
index 3893ac5..0ba2b48 100644
--- a/content/browser/interest_group/restricted_interest_group_store_impl_unittest.cc
+++ b/content/browser/interest_group/restricted_interest_group_store_impl_unittest.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "content/browser/interest_group/restricted_interest_group_store_impl.h"
+#include "content/browser/interest_group/ad_auction_service_impl.h"
 
 #include <memory>
 #include <string>
@@ -29,8 +29,8 @@
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/blink/public/common/features.h"
 #include "third_party/blink/public/common/interest_group/interest_group.h"
+#include "third_party/blink/public/mojom/interest_group/ad_auction_service.mojom.h"
 #include "third_party/blink/public/mojom/interest_group/interest_group_types.mojom.h"
-#include "third_party/blink/public/mojom/interest_group/restricted_interest_group_store.mojom.h"
 #include "url/gurl.h"
 #include "url/origin.h"
 
@@ -144,6 +144,10 @@
 
 }  // namespace
 
+// Tests the interest group management functionality of AdAuctionService -- this
+// particular functionality used to be in a separate interface called
+// RestrictedInterestStore. The interfaces were combined so so that they'd share
+// a Mojo pipe (for message ordering consistency).
 class RestrictedInterestGroupStoreImplTest : public RenderViewHostTestHarness {
  public:
   RestrictedInterestGroupStoreImplTest()
@@ -199,18 +203,18 @@
     return 0;
   }
 
-  // Create a new RestrictedInterestGroupStoreImpl and use it to try and join
+  // Create a new AdAuctionServiceImpl and use it to try and join
   // `interest_group`. Flushes the Mojo pipe to force the Mojo message to be
   // handled before returning.
   //
-  // Creates a new RestrictedInterestGroupStoreImpl with each call so the RFH
+  // Creates a new AdAuctionServiceImpl with each call so the RFH
   // can be navigated between different sites. And
-  // RestrictedInterestGroupStoreImpl only handles one site (cross site navs use
+  // AdAuctionServiceImpl only handles one site (cross site navs use
   // different RestrictedInterestGroupStoreImpls, and generally use different
   // RFHs as well).
   void JoinInterestGroupAndFlush(const blink::InterestGroup& interest_group) {
-    mojo::Remote<blink::mojom::RestrictedInterestGroupStore> interest_service;
-    RestrictedInterestGroupStoreImpl::CreateMojoService(
+    mojo::Remote<blink::mojom::AdAuctionService> interest_service;
+    AdAuctionServiceImpl::CreateMojoService(
         web_contents()->GetMainFrame(),
         interest_service.BindNewPipeAndPassReceiver());
 
@@ -222,8 +226,8 @@
   // instead of joining one.
   void LeaveInterestGroupAndFlush(const url::Origin& owner,
                                   const std::string& name) {
-    mojo::Remote<blink::mojom::RestrictedInterestGroupStore> interest_service;
-    RestrictedInterestGroupStoreImpl::CreateMojoService(
+    mojo::Remote<blink::mojom::AdAuctionService> interest_service;
+    AdAuctionServiceImpl::CreateMojoService(
         web_contents()->GetMainFrame(),
         interest_service.BindNewPipeAndPassReceiver());
 
@@ -235,8 +239,8 @@
   // URL. Doesn't flush since the update operation requires a sequence of
   // asynchronous operations.
   void UpdateInterestGroupNoFlush() {
-    mojo::Remote<blink::mojom::RestrictedInterestGroupStore> interest_service;
-    RestrictedInterestGroupStoreImpl::CreateMojoService(
+    mojo::Remote<blink::mojom::AdAuctionService> interest_service;
+    AdAuctionServiceImpl::CreateMojoService(
         web_contents()->GetMainFrame(),
         interest_service.BindNewPipeAndPassReceiver());
 
diff --git a/content/browser/loader/cross_origin_read_blocking_checker.cc b/content/browser/loader/cross_origin_read_blocking_checker.cc
index c6a819af..7be352b 100644
--- a/content/browser/loader/cross_origin_read_blocking_checker.cc
+++ b/content/browser/loader/cross_origin_read_blocking_checker.cc
@@ -159,7 +159,7 @@
     return;
   }
   base::StringPiece data(buffer->data(), bytes_read);
-  corb_analyzer_->SniffResponseBody(data, 0);
+  corb_analyzer_->SniffResponseBody(data);
   if (corb_analyzer_->ShouldBlock()) {
     OnBlocked();
     return;
diff --git a/content/browser/media/active_media_session_controller.cc b/content/browser/media/active_media_session_controller.cc
index 3e5a986..04026af 100644
--- a/content/browser/media/active_media_session_controller.cc
+++ b/content/browser/media/active_media_session_controller.cc
@@ -199,6 +199,7 @@
     case MediaSessionAction::kToggleCamera:
     case MediaSessionAction::kHangUp:
     case MediaSessionAction::kRaise:
+    case MediaSessionAction::kSetMute:
       NOTREACHED();
       return;
   }
@@ -251,6 +252,7 @@
     case MediaSessionAction::kToggleCamera:
     case MediaSessionAction::kHangUp:
     case MediaSessionAction::kRaise:
+    case MediaSessionAction::kSetMute:
       return absl::nullopt;
   }
 }
diff --git a/content/browser/media/media_web_contents_observer.cc b/content/browser/media/media_web_contents_observer.cc
index 180d5b8..6bc9670 100644
--- a/content/browser/media/media_web_contents_observer.cc
+++ b/content/browser/media/media_web_contents_observer.cc
@@ -337,6 +337,9 @@
   media_web_contents_observer_->web_contents_impl()->MediaMutedStatusChanged(
       media_player_id_, muted);
 
+  media_web_contents_observer_->session_controllers_manager()
+      ->OnMediaMutedStatusChanged(media_player_id_, muted);
+
   PlayerInfo* player_info = GetPlayerInfo();
   if (!player_info)
     return;
diff --git a/content/browser/media/session/media_session_controller.cc b/content/browser/media/session/media_session_controller.cc
index 0d8e483..5ece795 100644
--- a/content/browser/media/session/media_session_controller.cc
+++ b/content/browser/media/session/media_session_controller.cc
@@ -141,6 +141,14 @@
       ->SetAudioSinkId(hashed_sink_id);
 }
 
+void MediaSessionController::OnSetMute(int player_id, bool mute) {
+  DCHECK_EQ(player_id_, player_id);
+
+  web_contents_->media_web_contents_observer()
+      ->GetMediaPlayerRemote(id_)
+      ->RequestMute(mute);
+}
+
 RenderFrameHost* MediaSessionController::render_frame_host() const {
   return RenderFrameHost::FromID(id_.frame_routing_id);
 }
@@ -185,6 +193,10 @@
   media_session_->RebuildAndNotifyMediaPositionChanged();
 }
 
+void MediaSessionController::OnMediaMutedStatusChanged(bool mute) {
+  media_session_->OnMediaMutedStatusChanged(mute);
+}
+
 void MediaSessionController::OnPictureInPictureAvailabilityChanged(
     bool available) {
   is_picture_in_picture_available_ = available;
diff --git a/content/browser/media/session/media_session_controller.h b/content/browser/media/session/media_session_controller.h
index 907f2a0..ae06260 100644
--- a/content/browser/media/session/media_session_controller.h
+++ b/content/browser/media/session/media_session_controller.h
@@ -60,6 +60,7 @@
   void OnExitPictureInPicture(int player_id) override;
   void OnSetAudioSinkId(int player_id,
                         const std::string& raw_device_id) override;
+  void OnSetMute(int player_id, bool mute) override;
   RenderFrameHost* render_frame_host() const override;
   absl::optional<media_session::MediaPosition> GetPosition(
       int player_id) const override;
@@ -82,6 +83,8 @@
   void OnMediaPositionStateChanged(
       const media_session::MediaPosition& position);
 
+  void OnMediaMutedStatusChanged(bool mute);
+
   // Called when the media picture-in-picture availability has changed.
   void OnPictureInPictureAvailabilityChanged(bool available);
 
diff --git a/content/browser/media/session/media_session_controller_unittest.cc b/content/browser/media/session/media_session_controller_unittest.cc
index d6a3a9d..75c92ab 100644
--- a/content/browser/media/session/media_session_controller_unittest.cc
+++ b/content/browser/media/session/media_session_controller_unittest.cc
@@ -138,6 +138,8 @@
 
   void RequestExitPictureInPicture() override {}
 
+  void RequestMute(bool mute) override {}
+
   void SetVolumeMultiplier(double multiplier) override {
     received_volume_multiplier_ = multiplier;
     if (run_loop_for_volume_)
diff --git a/content/browser/media/session/media_session_controllers_manager.cc b/content/browser/media/session/media_session_controllers_manager.cc
index 39a20474..15f877e 100644
--- a/content/browser/media/session/media_session_controllers_manager.cc
+++ b/content/browser/media/session/media_session_controllers_manager.cc
@@ -104,6 +104,16 @@
     entry.second->WebContentsMutedStateChanged(muted);
 }
 
+void MediaSessionControllersManager::OnMediaMutedStatusChanged(
+    const MediaPlayerId& id,
+    bool mute) {
+  if (!IsMediaSessionEnabled())
+    return;
+
+  MediaSessionController* const controller = FindOrCreateController(id);
+  controller->OnMediaMutedStatusChanged(mute);
+}
+
 void MediaSessionControllersManager::OnPictureInPictureAvailabilityChanged(
     const MediaPlayerId& id,
     bool available) {
diff --git a/content/browser/media/session/media_session_controllers_manager.h b/content/browser/media/session/media_session_controllers_manager.h
index e3490c4c..59db437 100644
--- a/content/browser/media/session/media_session_controllers_manager.h
+++ b/content/browser/media/session/media_session_controllers_manager.h
@@ -67,6 +67,9 @@
   // Called when the WebContents was muted or unmuted.
   void WebContentsMutedStateChanged(bool muted);
 
+  // Called when the player's mute status changed.
+  void OnMediaMutedStatusChanged(const MediaPlayerId& id, bool mute);
+
   // Called when picture-in-picture availability for the player |id| has
   // changed.
   void OnPictureInPictureAvailabilityChanged(const MediaPlayerId& id,
diff --git a/content/browser/media/session/media_session_impl.cc b/content/browser/media/session/media_session_impl.cc
index 83da123..202ae27c 100644
--- a/content/browser/media/session/media_session_impl.cc
+++ b/content/browser/media/session/media_session_impl.cc
@@ -149,6 +149,8 @@
       return MediaSessionUserAction::kHangUp;
     case media_session::mojom::MediaSessionAction::kRaise:
       return MediaSessionUserAction::kRaise;
+    case media_session::mojom::MediaSessionAction::kSetMute:
+      return MediaSessionUserAction::kSetMute;
   }
   NOTREACHED();
   return MediaSessionUserAction::kPlay;
@@ -1013,6 +1015,8 @@
     info->camera_state = media_session::mojom::CameraState::kUnknown;
   }
 
+  info->muted = is_muted_;
+
   return info;
 }
 
@@ -1173,6 +1177,12 @@
   delegate->ActivateContents(web_contents());
 }
 
+void MediaSessionImpl::SetMute(bool mute) {
+  DCHECK_EQ(normal_players_.size(), 1u);
+  normal_players_.begin()->first.observer->OnSetMute(
+      normal_players_.begin()->first.player_id, mute);
+}
+
 void MediaSessionImpl::GetMediaImageBitmap(
     const media_session::MediaImage& image,
     int minimum_size_px,
@@ -1481,6 +1491,11 @@
   return best_frame ? services_[best_frame->GetGlobalId()] : nullptr;
 }
 
+void MediaSessionImpl::OnMediaMutedStatusChanged(bool mute) {
+  is_muted_ = mute;
+  RebuildAndNotifyMediaSessionInfoChanged();
+}
+
 void MediaSessionImpl::OnPictureInPictureAvailabilityChanged() {
   if (normal_players_.size() != 1)
     return;
diff --git a/content/browser/media/session/media_session_impl.h b/content/browser/media/session/media_session_impl.h
index 5b1693b..67e5f9a 100644
--- a/content/browser/media/session/media_session_impl.h
+++ b/content/browser/media/session/media_session_impl.h
@@ -279,6 +279,9 @@
   // Brings the associated tab into focus.
   void Raise() override;
 
+  // Mute or unmute the media player.
+  void SetMute(bool mute) override;
+
   // Downloads the bitmap version of a MediaImage at least |minimum_size_px|
   // and closest to |desired_size_px|. If the download failed, was too small or
   // the image did not come from the media session then returns a null image.
@@ -292,6 +295,8 @@
     return audio_focus_group_id_;
   }
 
+  void OnMediaMutedStatusChanged(bool mute);
+
   void OnPictureInPictureAvailabilityChanged();
 
   // Called when any of the normal players have switched to a different audio
@@ -481,6 +486,8 @@
   // True if the WebContents associated with this MediaSessionImpl is focused.
   bool focused_ = false;
 
+  bool is_muted_ = false;
+
   // Used to persist audio device selection between navigations on the same
   // origin.
   url::Origin origin_;
diff --git a/content/browser/media/session/media_session_impl_service_routing_unittest.cc b/content/browser/media/session/media_session_impl_service_routing_unittest.cc
index 4479f297..3f23f7e 100644
--- a/content/browser/media/session/media_session_impl_service_routing_unittest.cc
+++ b/content/browser/media/session/media_session_impl_service_routing_unittest.cc
@@ -58,6 +58,7 @@
   MOCK_METHOD1(OnExitPictureInPicture, void(int player_id));
   MOCK_METHOD2(OnSetAudioSinkId,
                void(int player_id, const std::string& raw_device_id));
+  MOCK_METHOD2(OnSetMute, void(int player_id, bool mute));
 
   absl::optional<media_session::MediaPosition> GetPosition(
       int player_id) const override {
diff --git a/content/browser/media/session/media_session_impl_uma_unittest.cc b/content/browser/media/session/media_session_impl_uma_unittest.cc
index 468039d..c08ab9d 100644
--- a/content/browser/media/session/media_session_impl_uma_unittest.cc
+++ b/content/browser/media/session/media_session_impl_uma_unittest.cc
@@ -44,6 +44,7 @@
   void OnExitPictureInPicture(int player_id) override {}
   void OnSetAudioSinkId(int player_id,
                         const std::string& raw_device_id) override {}
+  void OnSetMute(int player_id, bool mute) override {}
 
   absl::optional<media_session::MediaPosition> GetPosition(
       int player_id) const override {
diff --git a/content/browser/media/session/media_session_player_observer.h b/content/browser/media/session/media_session_player_observer.h
index b327e59..f45351d 100644
--- a/content/browser/media/session/media_session_player_observer.h
+++ b/content/browser/media/session/media_session_player_observer.h
@@ -52,6 +52,9 @@
   virtual void OnSetAudioSinkId(int player_id,
                                 const std::string& raw_device_id) = 0;
 
+  // The given |player_id| has been requested to mute or unmute.
+  virtual void OnSetMute(int player_id, bool mute) = 0;
+
   // Returns the position for |player_id|.
   virtual absl::optional<media_session::MediaPosition> GetPosition(
       int player_id) const = 0;
diff --git a/content/browser/media/session/media_session_service_impl_browsertest.cc b/content/browser/media/session/media_session_service_impl_browsertest.cc
index 0229ad6..c047c6e 100644
--- a/content/browser/media/session/media_session_service_impl_browsertest.cc
+++ b/content/browser/media/session/media_session_service_impl_browsertest.cc
@@ -61,6 +61,7 @@
   void OnExitPictureInPicture(int player_id) override {}
   void OnSetAudioSinkId(int player_id,
                         const std::string& raw_device_id) override {}
+  void OnSetMute(int player_id, bool mute) override {}
 
   absl::optional<media_session::MediaPosition> GetPosition(
       int player_id) const override {
diff --git a/content/browser/media/session/media_session_uma_helper.h b/content/browser/media/session/media_session_uma_helper.h
index fada329..b8de023 100644
--- a/content/browser/media/session/media_session_uma_helper.h
+++ b/content/browser/media/session/media_session_uma_helper.h
@@ -50,7 +50,8 @@
     kToggleCamera = 17,
     kHangUp = 18,
     kRaise = 19,
-    kMaxValue = kRaise,
+    kSetMute = 20,
+    kMaxValue = kSetMute,
   };
 
   MediaSessionUmaHelper();
diff --git a/content/browser/media/session/mock_media_session_player_observer.cc b/content/browser/media/session/mock_media_session_player_observer.cc
index 2a7e548..2ef49f4 100644
--- a/content/browser/media/session/mock_media_session_player_observer.cc
+++ b/content/browser/media/session/mock_media_session_player_observer.cc
@@ -89,6 +89,11 @@
   players_[player_id].audio_sink_id_ = raw_device_id;
 }
 
+void MockMediaSessionPlayerObserver::OnSetMute(int player_id, bool mute) {
+  EXPECT_GE(player_id, 0);
+  EXPECT_GT(players_.size(), static_cast<size_t>(player_id));
+}
+
 absl::optional<media_session::MediaPosition>
 MockMediaSessionPlayerObserver::GetPosition(int player_id) const {
   EXPECT_GE(player_id, 0);
diff --git a/content/browser/media/session/mock_media_session_player_observer.h b/content/browser/media/session/mock_media_session_player_observer.h
index 09b0747..dfd3890 100644
--- a/content/browser/media/session/mock_media_session_player_observer.h
+++ b/content/browser/media/session/mock_media_session_player_observer.h
@@ -34,6 +34,7 @@
   void OnExitPictureInPicture(int player_id) override;
   void OnSetAudioSinkId(int player_id,
                         const std::string& raw_device_id) override;
+  void OnSetMute(int player_id, bool mute) override;
   absl::optional<media_session::MediaPosition> GetPosition(
       int player_id) const override;
   bool IsPictureInPictureAvailable(int player_id) const override;
diff --git a/content/browser/media/session/pepper_player_delegate.cc b/content/browser/media/session/pepper_player_delegate.cc
index e50b05f..95734aea 100644
--- a/content/browser/media/session/pepper_player_delegate.cc
+++ b/content/browser/media/session/pepper_player_delegate.cc
@@ -80,6 +80,8 @@
   NOTREACHED();
 }
 
+void PepperPlayerDelegate::OnSetMute(int player_id, bool mute) {}
+
 absl::optional<media_session::MediaPosition> PepperPlayerDelegate::GetPosition(
     int player_id) const {
   // Pepper does not support position data.
diff --git a/content/browser/media/session/pepper_player_delegate.h b/content/browser/media/session/pepper_player_delegate.h
index a5991aa..2e595a1c 100644
--- a/content/browser/media/session/pepper_player_delegate.h
+++ b/content/browser/media/session/pepper_player_delegate.h
@@ -34,6 +34,7 @@
   void OnExitPictureInPicture(int player_id) override;
   void OnSetAudioSinkId(int player_id,
                         const std::string& raw_device_id) override;
+  void OnSetMute(int player_id, bool mute) override;
   absl::optional<media_session::MediaPosition> GetPosition(
       int player_id) const override;
   bool IsPictureInPictureAvailable(int player_id) const override;
diff --git a/content/browser/picture_in_picture/picture_in_picture_service_impl_unittest.cc b/content/browser/picture_in_picture/picture_in_picture_service_impl_unittest.cc
index 8643b5f..3245e7a2e 100644
--- a/content/browser/picture_in_picture/picture_in_picture_service_impl_unittest.cc
+++ b/content/browser/picture_in_picture/picture_in_picture_service_impl_unittest.cc
@@ -130,6 +130,7 @@
   void RequestSeekTo(base::TimeDelta seek_time) override {}
   void RequestEnterPictureInPicture() override {}
   void RequestExitPictureInPicture() override {}
+  void RequestMute(bool mute) override {}
   void SetVolumeMultiplier(double multiplier) override {}
   void SetPersistentState(bool persistent) override {}
   void SetPowerExperimentState(bool enabled) override {}
diff --git a/content/browser/service_worker/service_worker_internals_ui.cc b/content/browser/service_worker/service_worker_internals_ui.cc
index 72f6472..b1f0aa2a 100644
--- a/content/browser/service_worker/service_worker_internals_ui.cc
+++ b/content/browser/service_worker/service_worker_internals_ui.cc
@@ -492,14 +492,23 @@
 }
 
 void ServiceWorkerInternalsHandler::HandleSetOption(const ListValue* args) {
-  std::string option_name;
-  bool option_boolean;
-  if (!args->GetString(0, &option_name) || option_name != "debug_on_start" ||
-      !args->GetBoolean(1, &option_boolean)) {
+  auto args_list = args->GetList();
+  if (args_list.size() < 2) {
+    return;
+  }
+
+  if (!args_list[0].is_string()) {
+    return;
+  }
+  if (args_list[0].GetString() != "debug_on_start") {
+    return;
+  }
+
+  if (!args_list[1].is_bool()) {
     return;
   }
   ServiceWorkerDevToolsManager::GetInstance()
-      ->set_debug_service_worker_on_start(option_boolean);
+      ->set_debug_service_worker_on_start(args_list[1].GetBool());
 }
 
 void ServiceWorkerInternalsHandler::HandleGetAllRegistrations(
diff --git a/content/browser/service_worker/service_worker_test_utils.h b/content/browser/service_worker/service_worker_test_utils.h
index 73b16604..274d28d 100644
--- a/content/browser/service_worker/service_worker_test_utils.h
+++ b/content/browser/service_worker/service_worker_test_utils.h
@@ -10,6 +10,7 @@
 #include "base/bind.h"
 #include "base/callback.h"
 #include "base/command_line.h"
+#include "base/containers/queue.h"
 #include "base/memory/weak_ptr.h"
 #include "base/run_loop.h"
 #include "components/services/storage/public/mojom/service_worker_storage_control.mojom.h"
diff --git a/content/browser/tracing/background_tracing_config_impl.cc b/content/browser/tracing/background_tracing_config_impl.cc
index 5761ebb..c998ce4 100644
--- a/content/browser/tracing/background_tracing_config_impl.cc
+++ b/content/browser/tracing/background_tracing_config_impl.cc
@@ -224,10 +224,9 @@
   if (config) {
     dict->GetString(kConfigScenarioName, &config->scenario_name_);
     config->SetBufferSizeLimits(dict);
-    bool value = false;
-    if (dict->GetBoolean(kConfigTraceBrowserProcessOnly, &value)) {
-      config->trace_browser_process_only_ = value;
-    }
+    config->trace_browser_process_only_ =
+        dict->FindBoolPath(kConfigTraceBrowserProcessOnly)
+            .value_or(config->trace_browser_process_only_);
   }
 
   return config;
diff --git a/content/browser/tracing/background_tracing_rule.cc b/content/browser/tracing/background_tracing_rule.cc
index ae231ae2..7cc273b 100644
--- a/content/browser/tracing/background_tracing_rule.cc
+++ b/content/browser/tracing/background_tracing_rule.cc
@@ -120,14 +120,15 @@
 void BackgroundTracingRule::Setup(const base::DictionaryValue* dict) {
   dict->GetDouble(kConfigRuleTriggerChance, &trigger_chance_);
   dict->GetInteger(kConfigRuleTriggerDelay, &trigger_delay_);
-  dict->GetBoolean(kConfigRuleStopTracingOnRepeatedReactive,
-                   &stop_tracing_on_repeated_reactive_);
+  stop_tracing_on_repeated_reactive_ =
+      dict->FindBoolPath(kConfigRuleStopTracingOnRepeatedReactive)
+          .value_or(stop_tracing_on_repeated_reactive_);
   if (dict->HasKey(kConfigRuleIdKey)) {
     dict->GetString(kConfigRuleIdKey, &rule_id_);
   } else {
     rule_id_ = GetDefaultRuleId();
   }
-  dict->GetBoolean(kConfigIsCrashKey, &is_crash_);
+  is_crash_ = dict->FindBoolPath(kConfigIsCrashKey).value_or(is_crash_);
 }
 
 namespace {
@@ -212,8 +213,8 @@
       return nullptr;
 
     // Optional parameter, so we don't need to check if the key exists.
-    bool repeat = true;
-    dict->GetBoolean(kConfigRuleHistogramRepeatKey, &repeat);
+    bool repeat =
+        dict->FindBoolPath(kConfigRuleHistogramRepeatKey).value_or(true);
 
     int histogram_lower_value;
     int histogram_upper_value = std::numeric_limits<int>::max();
diff --git a/content/browser/webid/federated_auth_request_impl_unittest.cc b/content/browser/webid/federated_auth_request_impl_unittest.cc
index c993ec371..df67db0 100644
--- a/content/browser/webid/federated_auth_request_impl_unittest.cc
+++ b/content/browser/webid/federated_auth_request_impl_unittest.cc
@@ -886,6 +886,7 @@
       test_case.inputs.client_id, test_case.inputs.nonce, test_case.inputs.mode,
       test_case.inputs.prefer_auto_sign_in);
 
+  ASSERT_FALSE(displayed_accounts.empty());
   EXPECT_EQ(displayed_accounts[0].login_state, LoginState::kSignUp);
   EXPECT_EQ(auth_response.second.value(), kToken);
 }
diff --git a/content/test/gpu/gpu_tests/test_expectations/pixel_expectations.txt b/content/test/gpu/gpu_tests/test_expectations/pixel_expectations.txt
index e442953..2dbf5376 100644
--- a/content/test/gpu/gpu_tests/test_expectations/pixel_expectations.txt
+++ b/content/test/gpu/gpu_tests/test_expectations/pixel_expectations.txt
@@ -353,6 +353,13 @@
 crbug.com/1170369 [ linux skia-renderer-dawn ] Pixel_WebGLPreservedAfterTabSwitch [ Failure ]
 crbug.com/1170369 [ win skia-renderer-dawn ] Pixel_WebGLPreservedAfterTabSwitch [ Failure ]
 
+# Failure on dawn_pixel_skia_gold_test on Linux. However they are currently detecting
+# skia-renderer-disabled. We will need to update these expectations after fixing the detections.
+crbug.com/1240840 [ linux skia-renderer-disabled ] Pixel_OffscreenCanvasWebGLSoftwareCompositing [ Failure ]
+crbug.com/1240840 [ linux skia-renderer-disabled ] Pixel_OffscreenCanvasWebGLSoftwareCompositingWorker [ Failure ]
+crbug.com/1240840 [ linux skia-renderer-disabled ] Pixel_RepeatedWebGLTo2D_SoftwareCompositing [ Failure ]
+crbug.com/1240840 [ linux skia-renderer-disabled ] Pixel_WebGLReadPixelsTabSwitch_SoftwareCompositing [ Failure ]
+
 # Failure of dawn_pixel_skia_gold_test on win dawn
 crbug.com/1228694 [ win skia-renderer-dawn ] Pixel_CSS3DBlueBox [ Failure ]
 
diff --git a/content/test/gpu/gpu_tests/test_expectations/webgl2_conformance_expectations.txt b/content/test/gpu/gpu_tests/test_expectations/webgl2_conformance_expectations.txt
index 0bc3aa7..91e6609 100644
--- a/content/test/gpu/gpu_tests/test_expectations/webgl2_conformance_expectations.txt
+++ b/content/test/gpu/gpu_tests/test_expectations/webgl2_conformance_expectations.txt
@@ -872,6 +872,7 @@
 crbug.com/1215698 [ chromeos chromeos-board-kevin no-passthrough ] deqp/functional/gles3/fborender/resize_01.html [ Failure ]
 crbug.com/1215698 [ chromeos chromeos-board-kevin no-passthrough ] deqp/functional/gles3/fborender/resize_02.html [ Failure ]
 crbug.com/1215698 [ chromeos chromeos-board-kevin no-passthrough ] deqp/functional/gles3/fborender/resize_03.html [ Failure ]
+crbug.com/1215698 [ chromeos chromeos-board-kevin no-passthrough ] deqp/functional/gles3/fborender/shared_depth_stencil.html [ Failure ]
 crbug.com/1218157 [ chromeos chromeos-board-kevin no-passthrough ] conformance/offscreencanvas/context-lost-restored.html [ Failure ]
 crbug.com/1218157 [ chromeos chromeos-board-kevin no-passthrough ] conformance/offscreencanvas/context-lost-restored-worker.html [ Failure ]
 crbug.com/1218162 [ chromeos chromeos-board-kevin no-passthrough ] deqp/functional/gles3/uniformbuffers/instance_array_basic_type.html [ Failure ]
@@ -896,6 +897,28 @@
 crbug.com/1219024 [ chromeos chromeos-board-kevin no-passthrough ] conformance2/rendering/framebuffer-render-to-layer.html [ Failure ]
 crbug.com/1219055 [ chromeos chromeos-board-kevin no-passthrough ] conformance2/textures/misc/generate-mipmap-with-large-base-level.html [ Failure ]
 crbug.com/1219057 [ chromeos chromeos-board-kevin no-passthrough ] conformance2/buffers/uniform-buffers.html [ Failure ]
+crbug.com/1212917 [ chromeos chromeos-board-kevin no-passthrough ] conformance/canvas/webgl-to-2d-canvas.html [ Failure ]
+crbug.com/1212917 [ chromeos chromeos-board-kevin no-passthrough ] conformance/context/context-attribute-preserve-drawing-buffer.html [ Failure ]
+crbug.com/1212917 [ chromeos chromeos-board-kevin no-passthrough ] conformance/context/context-creation-and-destruction.html [ Failure ]
+crbug.com/1212917 [ chromeos chromeos-board-kevin no-passthrough ] conformance/ogles/GL/atan/atan_001_to_008.html [ Failure ]
+crbug.com/1212917 [ chromeos chromeos-board-kevin no-passthrough ] conformance/ogles/GL/struct/struct_049_to_056.html [ Failure ]
+crbug.com/1212917 [ chromeos chromeos-board-kevin no-passthrough ] conformance/ogles/GL/swizzlers/swizzlers_009_to_016.html [ Failure ]
+crbug.com/1212917 [ chromeos chromeos-board-kevin no-passthrough ] conformance/rendering/color-mask-preserved-during-implicit-clears.html [ Failure ]
+crbug.com/1212917 [ chromeos chromeos-board-kevin no-passthrough ] conformance/textures/video/tex-2d-luminance-luminance-unsigned_byte.html [ Failure ]
+crbug.com/1212917 [ chromeos chromeos-board-kevin no-passthrough ] conformance/textures/video/tex-2d-luminance_alpha-luminance_alpha-unsigned_byte.html [ Failure ]
+crbug.com/1212917 [ chromeos chromeos-board-kevin no-passthrough ] conformance/textures/video/tex-2d-rgba-rgba-unsigned_byte.html [ Failure ]
+crbug.com/1212917 [ chromeos chromeos-board-kevin no-passthrough ] conformance/textures/video/tex-2d-rgba-rgba-unsigned_short_5_5_5_1.html [ Failure ]
+crbug.com/1212917 [ chromeos chromeos-board-kevin no-passthrough ] conformance/textures/webgl_canvas/tex-2d-luminance-luminance-unsigned_byte.html [ Failure ]
+crbug.com/1212917 [ chromeos chromeos-board-kevin no-passthrough ] conformance2/rendering/clear-func-buffer-type-match.html [ Failure ]
+crbug.com/1212917 [ chromeos chromeos-board-kevin no-passthrough ] conformance2/textures/image_bitmap_from_video/tex-2d-r32f-red-float.html [ Failure ]
+crbug.com/1212917 [ chromeos chromeos-board-kevin no-passthrough ] conformance2/textures/image_bitmap_from_video/tex-2d-rgba16f-rgba-half_float.html [ Failure ]
+crbug.com/1212917 [ chromeos chromeos-board-kevin no-passthrough ] conformance2/textures/image_bitmap_from_video/tex-2d-srgb8-rgb-unsigned_byte.html [ Failure ]
+crbug.com/1212917 [ chromeos chromeos-board-kevin no-passthrough ] conformance2/textures/video/tex-2d-r16f-red-half_float.html [ Failure ]
+crbug.com/1212917 [ chromeos chromeos-board-kevin no-passthrough ] conformance2/textures/video/tex-2d-r8ui-red_integer-unsigned_byte.html [ Failure ]
+crbug.com/1212917 [ chromeos chromeos-board-kevin no-passthrough ] conformance2/textures/video/tex-2d-rgb16f-rgb-half_float.html [ Failure ]
+crbug.com/1212917 [ chromeos chromeos-board-kevin no-passthrough ] conformance2/textures/video/tex-2d-rgb32f-rgb-float.html [ Failure ]
+crbug.com/1212917 [ chromeos chromeos-board-kevin no-passthrough ] conformance2/textures/video/tex-2d-rgba16f-rgba-half_float.html [ Failure ]
+crbug.com/1212917 [ chromeos chromeos-board-kevin no-passthrough ] conformance2/textures/video/tex-2d-rgba4-rgba-unsigned_byte.html [ Failure ]
 
 ##############################
 # Lacros-like Linux Failures #
@@ -908,7 +931,13 @@
 crbug.com/1215700 [ linux intel display-server-wayland no-passthrough ] deqp/functional/gles3/negativetextureapi.html [ Failure ]
 crbug.com/1218607 [ linux intel display-server-wayland no-passthrough ] conformance2/reading/read-pixels-from-fbo-test.html [ Failure ]
 crbug.com/1231736 [ linux intel display-server-wayland no-passthrough ] deqp/functional/gles3/clipping.html [ Failure ]
-
+crbug.com/1240707 [ linux intel display-server-wayland no-passthrough ] conformance2/textures/video/tex-2d-rg8ui-rg_integer-unsigned_byte.html [ RetryOnFailure ]
+crbug.com/1240707 [ linux intel display-server-wayland no-passthrough ] conformance2/textures/video/tex-2d-rgb10_a2-rgba-unsigned_int_2_10_10_10_rev.html [ RetryOnFailure ]
+crbug.com/1240707 [ linux intel display-server-wayland no-passthrough ] conformance2/textures/video/tex-2d-rgb565-rgb-unsigned_byte.html [ RetryOnFailure ]
+crbug.com/1240707 [ linux intel display-server-wayland no-passthrough ] conformance2/textures/video/tex-2d-rgba16f-rgba-float.html [ RetryOnFailure ]
+crbug.com/1240707 [ linux intel display-server-wayland no-passthrough ] conformance2/textures/video/tex-3d-r32f-red-float.html [ RetryOnFailure ]
+crbug.com/1240707 [ linux intel display-server-wayland no-passthrough ] conformance2/textures/video/tex-3d-rg16f-rg-float.html [ RetryOnFailure ]
+crbug.com/1240707 [ linux intel display-server-wayland no-passthrough ] conformance2/textures/video/tex-3d-rg16f-rg-half_float.html [ RetryOnFailure ]
 
 # Conflicting expectations to test that the
 # "Expectations have no collisions" unittest works.
diff --git a/content/test/gpu/gpu_tests/test_expectations/webgl_conformance_expectations.txt b/content/test/gpu/gpu_tests/test_expectations/webgl_conformance_expectations.txt
index b2bf2a8..d9ea6bd2 100644
--- a/content/test/gpu/gpu_tests/test_expectations/webgl_conformance_expectations.txt
+++ b/content/test/gpu/gpu_tests/test_expectations/webgl_conformance_expectations.txt
@@ -541,6 +541,8 @@
 # Introduced by upstreaming Apple's direct-to-metal backend
 crbug.com/angleproject/5505 [ mac apple-angle-metal-renderer:-apple-m1 angle-metal ] conformance/context/context-attributes-alpha-depth-stencil-antialias.html [ Failure ]
 crbug.com/angleproject/5505 [ mac apple-angle-metal-renderer:-apple-m1 angle-metal ] conformance/rendering/multisample-corruption.html [ Failure ]
+# No active browser window, but no crash stack.
+crbug.com/1240443 [ mac apple-apple-m1 passthrough angle-opengl ] conformance/renderbuffers/stencil-renderbuffer-initialization.html [ RetryOnFailure ]
 
 ####################
 # Linux failures   #
diff --git a/content/web_test/common/web_test_runtime_flags.h b/content/web_test/common/web_test_runtime_flags.h
index e83871d..fcd6e8e 100644
--- a/content/web_test/common/web_test_runtime_flags.h
+++ b/content/web_test/common/web_test_runtime_flags.h
@@ -26,13 +26,12 @@
 
   TrackedDictionary& tracked_dictionary() { return dict_; }
 
-#define DEFINE_BOOL_WEB_TEST_RUNTIME_FLAG(name)                     \
-  bool name() const {                                               \
-    bool result;                                                    \
-    bool found = dict_.current_values().GetBoolean(#name, &result); \
-    DCHECK(found);                                                  \
-    return result;                                                  \
-  }                                                                 \
+#define DEFINE_BOOL_WEB_TEST_RUNTIME_FLAG(name)                               \
+  bool name() const {                                                         \
+    absl::optional<bool> result = dict_.current_values().FindBoolPath(#name); \
+    DCHECK(result);                                                           \
+    return *result;                                                           \
+  }                                                                           \
   void set_##name(bool new_value) { dict_.SetBoolean(#name, new_value); }
 
 #define DEFINE_STRING_WEB_TEST_RUNTIME_FLAG(name)                  \
diff --git a/device/BUILD.gn b/device/BUILD.gn
index c39566b..ef0f077 100644
--- a/device/BUILD.gn
+++ b/device/BUILD.gn
@@ -258,6 +258,7 @@
       "bluetooth/bluez/bluetooth_socket_bluez_unittest.cc",
       "bluetooth/dbus/bluetooth_gatt_application_service_provider_unittest.cc",
       "bluetooth/dbus/bluetooth_gatt_characteristic_service_provider_unittest.cc",
+      "bluetooth/floss/floss_manager_client_unittest.cc",
       "bluetooth/test/bluetooth_test_bluez.cc",
       "bluetooth/test/bluetooth_test_bluez.h",
     ]
diff --git a/device/bluetooth/floss/floss_dbus_client.cc b/device/bluetooth/floss/floss_dbus_client.cc
index a9a96d8..e73dadc 100644
--- a/device/bluetooth/floss/floss_dbus_client.cc
+++ b/device/bluetooth/floss/floss_dbus_client.cc
@@ -39,7 +39,7 @@
 const char kGetFlossEnabled[] = "GetFlossEnabled";
 const char kSetFlossEnabled[] = "SetFlossEnabled";
 const char kGetState[] = "GetState";
-const char kListHciDevices[] = "ListHciDevices";
+const char kGetAvailableAdapters[] = "GetAvailableAdapters";
 const char kRegisterCallback[] = "RegisterCallback";
 const char kCallbackInterface[] = "org.chromium.bluetooth.ManagerCallback";
 const char kOnHciDeviceChanged[] = "OnHciDeviceChanged";
@@ -89,4 +89,27 @@
   return result;
 }
 
+void FlossDBusClient::DefaultResponseWithCallback(
+    ResponseCallback callback,
+    dbus::Response* response,
+    dbus::ErrorResponse* error_response) {
+  if (response) {
+    std::move(callback).Run(absl::nullopt);
+    return;
+  }
+
+  std::move(callback).Run(ErrorResponseToError(
+      kErrorNoResponse, /*default_message=*/std::string(), error_response));
+}
+
+void FlossDBusClient::DefaultResponse(const std::string& caller,
+                                      dbus::Response* response,
+                                      dbus::ErrorResponse* error_response) {
+  if (error_response) {
+    FlossDBusClient::LogErrorResponse(caller, error_response);
+  } else {
+    DVLOG(1) << caller << "::OnResponse";
+  }
+}
+
 }  // namespace floss
diff --git a/device/bluetooth/floss/floss_dbus_client.h b/device/bluetooth/floss/floss_dbus_client.h
index dfce9ef..7b63957 100644
--- a/device/bluetooth/floss/floss_dbus_client.h
+++ b/device/bluetooth/floss/floss_dbus_client.h
@@ -8,48 +8,50 @@
 #include <string>
 
 #include "base/callback.h"
+#include "device/bluetooth/bluetooth_export.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace dbus {
 class Bus;
+class Response;
 class ErrorResponse;
 }  // namespace dbus
 
 namespace floss {
 
 // TODO(b/189499077) - Expose via floss package
-extern const char kAdapterService[];
-extern const char kAdapterInterface[];
-extern const char kManagerInterface[];
-extern const char kManagerObject[];
-extern const char kAdapterObjectFormat[];
+extern DEVICE_BLUETOOTH_EXPORT const char kAdapterService[];
+extern DEVICE_BLUETOOTH_EXPORT const char kAdapterInterface[];
+extern DEVICE_BLUETOOTH_EXPORT const char kManagerInterface[];
+extern DEVICE_BLUETOOTH_EXPORT const char kManagerObject[];
+extern DEVICE_BLUETOOTH_EXPORT const char kAdapterObjectFormat[];
 
 namespace adapter {
-extern const char kGetAddress[];
-extern const char kStartDiscovery[];
-extern const char kCancelDiscovery[];
-extern const char kCreateBond[];
-extern const char kRegisterCallback[];
-extern const char kCallbackInterface[];
+extern DEVICE_BLUETOOTH_EXPORT const char kGetAddress[];
+extern DEVICE_BLUETOOTH_EXPORT const char kStartDiscovery[];
+extern DEVICE_BLUETOOTH_EXPORT const char kCancelDiscovery[];
+extern DEVICE_BLUETOOTH_EXPORT const char kCreateBond[];
+extern DEVICE_BLUETOOTH_EXPORT const char kRegisterCallback[];
+extern DEVICE_BLUETOOTH_EXPORT const char kCallbackInterface[];
 
-extern const char kOnAddressChanged[];
-extern const char kOnDeviceFound[];
-extern const char kOnDiscoveringChanged[];
-extern const char kOnSspRequest[];
+extern DEVICE_BLUETOOTH_EXPORT const char kOnAddressChanged[];
+extern DEVICE_BLUETOOTH_EXPORT const char kOnDeviceFound[];
+extern DEVICE_BLUETOOTH_EXPORT const char kOnDiscoveringChanged[];
+extern DEVICE_BLUETOOTH_EXPORT const char kOnSspRequest[];
 }  // namespace adapter
 
 namespace manager {
-extern const char kStart[];
-extern const char kStop[];
-extern const char kGetFlossEnabled[];
-extern const char kSetFlossEnabled[];
-extern const char kGetState[];
-extern const char kListHciDevices[];
-extern const char kRegisterCallback[];
-extern const char kCallbackInterface[];
+extern DEVICE_BLUETOOTH_EXPORT const char kStart[];
+extern DEVICE_BLUETOOTH_EXPORT const char kStop[];
+extern DEVICE_BLUETOOTH_EXPORT const char kGetFlossEnabled[];
+extern DEVICE_BLUETOOTH_EXPORT const char kSetFlossEnabled[];
+extern DEVICE_BLUETOOTH_EXPORT const char kGetState[];
+extern DEVICE_BLUETOOTH_EXPORT const char kGetAvailableAdapters[];
+extern DEVICE_BLUETOOTH_EXPORT const char kRegisterCallback[];
+extern DEVICE_BLUETOOTH_EXPORT const char kCallbackInterface[];
 
-extern const char kOnHciDeviceChanged[];
-extern const char kOnHciEnabledChanged[];
+extern DEVICE_BLUETOOTH_EXPORT const char kOnHciDeviceChanged[];
+extern DEVICE_BLUETOOTH_EXPORT const char kOnHciEnabledChanged[];
 }  // namespace manager
 
 // BluetoothDevice structure for DBus apis.
@@ -81,6 +83,11 @@
   FlossDBusClient(const FlossDBusClient&) = delete;
   FlossDBusClient& operator=(const FlossDBusClient&) = delete;
 
+  // Common init signature for all clients.
+  virtual void Init(dbus::Bus* bus,
+                    const std::string& bluetooth_service_name,
+                    const std::string& bluetooth_adapter_path) = 0;
+
  protected:
   // Convert a dbus::ErrorResponse into a floss::Error struct.
   static Error ErrorResponseToError(const std::string& default_name,
@@ -90,15 +97,21 @@
   FlossDBusClient();
   virtual ~FlossDBusClient();
 
-  virtual void Init(dbus::Bus* bus,
-                    const std::string& bluetooth_service_name,
-                    const std::string& bluetooth_adapter_path) = 0;
-
   // Log a dbus::ErrorResponse.
   void LogErrorResponse(const std::string& message, dbus::ErrorResponse* error);
 
- private:
-  friend class FlossDBusManager;
+  // Default handler that runs |callback| with the callback with an optional
+  // error.
+  void DefaultResponseWithCallback(ResponseCallback callback,
+                                   dbus::Response* response,
+                                   dbus::ErrorResponse* error_response);
+
+  // Default handler for a response. It will either log the error response or
+  // print |caller| to VLOG. |caller| should be the name of the DBus method that
+  // is being called.
+  void DefaultResponse(const std::string& caller,
+                       dbus::Response* response,
+                       dbus::ErrorResponse* error_response);
 };
 
 }  // namespace floss
diff --git a/device/bluetooth/floss/floss_manager_client.cc b/device/bluetooth/floss/floss_manager_client.cc
index a17b670..5e5275e08 100644
--- a/device/bluetooth/floss/floss_manager_client.cc
+++ b/device/bluetooth/floss/floss_manager_client.cc
@@ -13,394 +13,71 @@
 #include "base/callback.h"
 #include "base/callback_helpers.h"
 #include "base/containers/contains.h"
-#include "base/containers/flat_map.h"
 #include "base/logging.h"
 #include "base/strings/stringprintf.h"
 #include "dbus/bus.h"
 #include "dbus/exported_object.h"
 #include "dbus/message.h"
-#include "dbus/object_manager.h"
 #include "dbus/object_proxy.h"
 #include "device/bluetooth/floss/floss_dbus_client.h"
 
 namespace floss {
 
 namespace {
-constexpr char kExportedManagerPath[] = "/org/chromium/bluetooth/managerclient";
 constexpr char kUnknownManagerError[] = "org.chromium.Error.UnknownManager";
-constexpr char kObjectManagerPath[] = "/";
+constexpr char kHciInterfaceKey[] = "hci_interface";
+constexpr char kEnabledKey[] = "enabled";
+
+bool ParseAdapterWithEnabled(dbus::MessageReader& array,
+                             int* adapter,
+                             bool* enabled) {
+  dbus::MessageReader dict(nullptr);
+  bool found_adapter = false;
+  bool found_enabled = false;
+
+  while (array.PopDictEntry(&dict)) {
+    std::string key;
+    dict.PopString(&key);
+
+    if (key == kHciInterfaceKey) {
+      found_adapter = dict.PopVariantOfInt32(adapter);
+    } else if (key == kEnabledKey) {
+      found_enabled = dict.PopVariantOfBool(enabled);
+    }
+  }
+
+  return found_adapter && found_enabled;
+}
+
+void HandleExported(const std::string& method_name,
+                    const std::string& interface_name,
+                    const std::string& object_path,
+                    bool success) {
+  DVLOG(1) << (success ? "Successfully exported " : "Failed to export ")
+           << method_name << " on interface = " << interface_name
+           << ", object = " << object_path;
+}
+
 }  // namespace
 
-class FlossManagerClientImpl : public FlossManagerClient,
-                               public dbus::ObjectManager::Interface {
- public:
-  FlossManagerClientImpl() = default;
-  ~FlossManagerClientImpl() override {
-    if (object_manager_) {
-      object_manager_->UnregisterInterface(kManagerInterface);
-    }
+// static
+const char FlossManagerClient::kExportedCallbacksPath[] =
+    "/org/chromium/bluetooth/managerclient";
 
-    if (bus_) {
-      bus_->UnregisterExportedObject(dbus::ObjectPath(kExportedManagerPath));
-    }
-  }
-
-  std::vector<int> GetAdapters() const override {
-    std::vector<int> adapters;
-    for (auto kv : adapter_to_powered_) {
-      adapters.push_back(kv.first);
-    }
-
-    return adapters;
-  }
-
-  int GetDefaultAdapter() const override { return default_adapter_; }
-
-  bool GetAdapterPresent(int adapter) const override {
-    return base::Contains(adapter_to_powered_, adapter);
-  }
-
-  bool GetAdapterEnabled(int adapter) const override {
-    auto iter = adapter_to_powered_.find(adapter);
-    if (iter != adapter_to_powered_.end()) {
-      return iter->second;
-    }
-
-    return false;
-  }
-
-  void SetFlossEnabled(bool enabled) override {
-    dbus::ObjectProxy* object_proxy =
-        bus_->GetObjectProxy(service_name_, dbus::ObjectPath(kManagerObject));
-    if (!object_proxy) {
-      return;
-    }
-
-    DVLOG(1) << __func__;
-
-    dbus::MethodCall method_call(kManagerInterface, manager::kSetFlossEnabled);
-    dbus::MessageWriter writer(&method_call);
-    writer.AppendBool(enabled);
-
-    object_proxy->CallMethodWithErrorResponse(
-        &method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
-        base::BindOnce(&FlossManagerClientImpl::OnResponse,
-                       weak_ptr_factory_.GetWeakPtr(),
-                       "FlossManagerClient::SetFlossEnabled"));
-  }
-
-  void SetAdapterEnabled(int adapter,
-                         bool enabled,
-                         ResponseCallback callback) override {
-    dbus::ObjectProxy* object_proxy =
-        bus_->GetObjectProxy(service_name_, dbus::ObjectPath(kManagerObject));
-    if (!object_proxy) {
-      std::move(callback).Run(Error(kUnknownManagerError, std::string()));
-      return;
-    }
-
-    DVLOG(1) << __func__;
-
-    auto* command = enabled ? manager::kStart : manager::kStop;
-    dbus::MethodCall method_call(kManagerInterface, command);
-    dbus::MessageWriter writer(&method_call);
-    writer.AppendInt32(adapter);
-
-    object_proxy->CallMethodWithErrorResponse(
-        &method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
-        base::BindOnce(&FlossManagerClientImpl::OnResponseWithCallback,
-                       weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
-  }
-
- protected:
-  // Register manager client against manager.
-  void RegisterWithManager() {
-    DCHECK(!manager_available_);
-
-    dbus::ObjectProxy* object_proxy =
-        bus_->GetObjectProxy(service_name_, dbus::ObjectPath(kManagerObject));
-
-    // Get all hci devices available.
-    dbus::MethodCall method_call(kManagerInterface, manager::kListHciDevices);
-    object_proxy->CallMethodWithErrorResponse(
-        &method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
-        base::BindOnce(&FlossManagerClientImpl::OnListHciDevices,
-                       weak_ptr_factory_.GetWeakPtr()));
-
-    // Register for callbacks.
-    dbus::MethodCall register_callback(kManagerInterface,
-                                       manager::kRegisterCallback);
-    dbus::MessageWriter writer(&register_callback);
-    writer.AppendObjectPath(dbus::ObjectPath(kExportedManagerPath));
-
-    object_proxy->CallMethodWithErrorResponse(
-        &register_callback, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
-        base::BindOnce(&FlossManagerClientImpl::OnResponse,
-                       weak_ptr_factory_.GetWeakPtr(),
-                       manager::kRegisterCallback));
-
-    manager_available_ = true;
-  }
-
-  // Remove manager client (no longer available).
-  void RemoveManager() {
-    manager_available_ = false;
-
-    // Make copy of old adapters and clear existing ones.
-    auto previous_adapters = std::move(adapter_to_powered_);
-
-    // All old adapters need to be sent a `present = false` notification.
-    for (auto& kv : previous_adapters) {
-      for (auto& observer : observers_) {
-        observer.AdapterPresent(kv.first, false);
-      }
-    }
-  }
-
-  // The manager can manage multiple adapters so ignore the adapter path given
-  // here. It is unused.
-  void Init(dbus::Bus* bus,
-            const std::string& service_name,
-            const std::string& adapter_path) override {
-    bus_ = bus;
-    service_name_ = service_name;
-
-    dbus::ObjectProxy* object_proxy =
-        bus_->GetObjectProxy(service_name_, dbus::ObjectPath(kManagerObject));
-
-    // We should always have object proxy since the client initialization is
-    // gated on ObjectManager marking the manager interface as available.
-    if (!object_proxy) {
-      LOG(ERROR) << "FlossManagerClient couldn't init. Object proxy was null.";
-      return;
-    }
-
-    DVLOG(1) << __func__;
-
-    // Register callback object.
-    dbus::ExportedObject* callbacks =
-        bus_->GetExportedObject(dbus::ObjectPath(kExportedManagerPath));
-
-    if (!callbacks) {
-      LOG(ERROR) << "FlossManagerClient couldn't export client callbacks.";
-      return;
-    }
-
-    // Register callbacks for OnHciDeviceChanged and OnHciEnabledChanged.
-    callbacks->ExportMethod(
-        manager::kCallbackInterface, manager::kOnHciDeviceChanged,
-        base::BindRepeating(&FlossManagerClientImpl::OnHciDeviceChange,
-                            weak_ptr_factory_.GetWeakPtr()),
-        base::BindOnce(&FlossManagerClientImpl::OnExported,
-                       weak_ptr_factory_.GetWeakPtr(),
-                       manager::kOnHciDeviceChanged));
-
-    callbacks->ExportMethod(
-        manager::kCallbackInterface, manager::kOnHciEnabledChanged,
-        base::BindRepeating(&FlossManagerClientImpl::OnHciEnabledChange,
-                            weak_ptr_factory_.GetWeakPtr()),
-        base::BindOnce(&FlossManagerClientImpl::OnExported,
-                       weak_ptr_factory_.GetWeakPtr(),
-                       manager::kOnHciEnabledChanged));
-
-    // Register object manager for Manager.
-    object_manager_ = bus_->GetObjectManager(
-        service_name, dbus::ObjectPath(kObjectManagerPath));
-    object_manager_->RegisterInterface(kManagerInterface, this);
-
-    // Get manager ready.
-    RegisterWithManager();
-  }
-
-  void OnListHciDevices(dbus::Response* response,
-                        dbus::ErrorResponse* error_response) {
-    if (!response) {
-      FlossDBusClient::LogErrorResponse(
-          "FlossManagerClientImpl::OnListHciDevices", error_response);
-      return;
-    }
-
-    dbus::MessageReader msg(response);
-    dbus::MessageReader arr(nullptr);
-
-    if (msg.PopArray(&arr)) {
-      auto previous_adapters = std::move(adapter_to_powered_);
-
-      // Clear existing adapters.
-      adapter_to_powered_.clear();
-
-      int adapter = 0;
-      while (arr.PopInt32(&adapter)) {
-        DCHECK(adapter >= 0);
-        adapter_to_powered_.insert({adapter, false});
-      }
-
-      // Trigger the observers for adapter present on any new ones we listed.
-      for (auto& observer : observers_) {
-        // Emit present for new adapters that weren't in old list.
-        for (auto& kv : adapter_to_powered_) {
-          if (!base::Contains(previous_adapters, kv.first))
-            observer.AdapterPresent(kv.first, true);
-        }
-
-        // Emit not present for adapters that aren't in new list.
-        for (auto& kv : previous_adapters) {
-          if (!base::Contains(adapter_to_powered_, kv.first))
-            observer.AdapterPresent(kv.first, false);
-        }
-      }
-    }
-  }
-
-  void OnHciDeviceChange(dbus::MethodCall* method_call,
-                         dbus::ExportedObject::ResponseSender response_sender) {
-    dbus::MessageReader msg(method_call);
-    int adapter;
-    bool present;
-
-    if (!msg.PopInt32(&adapter) || !msg.PopBool(&present)) {
-      std::move(response_sender)
-          .Run(dbus::ErrorResponse::FromMethodCall(
-              method_call, kErrorInvalidParameters, std::string()));
-      return;
-    }
-
-    for (auto& observer : observers_) {
-      observer.AdapterPresent(adapter, present);
-    }
-
-    // Update the cached list of available adapters.
-    auto iter = adapter_to_powered_.find(adapter);
-    if (present && iter == adapter_to_powered_.end()) {
-      adapter_to_powered_.insert({adapter, false});
-    } else if (!present && iter != adapter_to_powered_.end()) {
-      adapter_to_powered_.erase(iter);
-    }
-
-    std::move(response_sender).Run(dbus::Response::FromMethodCall(method_call));
-  }
-
-  void OnHciEnabledChange(
-      dbus::MethodCall* method_call,
-      dbus::ExportedObject::ResponseSender response_sender) {
-    dbus::MessageReader msg(method_call);
-    int adapter;
-    bool enabled;
-
-    if (!msg.PopInt32(&adapter) || !msg.PopBool(&enabled)) {
-      std::move(response_sender)
-          .Run(dbus::ErrorResponse::FromMethodCall(
-              method_call, kErrorInvalidParameters, std::string()));
-      return;
-    }
-
-    adapter_to_powered_[adapter] = enabled;
-
-    for (auto& observer : observers_) {
-      observer.AdapterEnabledChanged(adapter, enabled);
-    }
-
-    std::move(response_sender).Run(dbus::Response::FromMethodCall(method_call));
-  }
-
-  void OnExported(const std::string& method_name,
-                  const std::string& interface_name,
-                  const std::string& object_path,
-                  bool success) {
-    DVLOG(1) << (success ? "Successfully exported " : "Failed to export ")
-             << method_name << " on interface = " << interface_name
-             << ", object = " << object_path;
-  }
-
-  void OnResponseWithCallback(ResponseCallback callback,
-                              dbus::Response* response,
-                              dbus::ErrorResponse* error_response) {
-    if (response) {
-      std::move(callback).Run(absl::nullopt);
-      return;
-    }
-
-    std::move(callback).Run(
-        ErrorResponseToError(kErrorNoResponse, std::string(), error_response));
-  }
-
-  void OnResponse(const std::string& caller,
-                  dbus::Response* response,
-                  dbus::ErrorResponse* error_response) {
-    if (error_response) {
-      FlossDBusClient::LogErrorResponse(caller, error_response);
-    } else {
-      DVLOG(1) << caller << "::OnResponse";
-    }
-  }
-
-  dbus::PropertySet* CreateProperties(
-      dbus::ObjectProxy* object_proxy,
-      const dbus::ObjectPath& object_path,
-      const std::string& interface_name) override {
-    return new dbus::PropertySet(object_proxy, interface_name,
-                                 base::DoNothing());
-  }
-
-  // Manager interface is available.
-  void ObjectAdded(const dbus::ObjectPath& object_path,
-                   const std::string& interface_name) override {
-    // TODO(b/193839304) - When manager exits, we're not getting the
-    //                     ObjectRemoved notification. So remove the manager
-    //                     before re-adding it here.
-    if (manager_available_) {
-      RemoveManager();
-    }
-
-    DVLOG(0) << __func__ << ": " << object_path.value() << ", "
-             << interface_name;
-
-    RegisterWithManager();
-  }
-
-  // Manager interface is gone (no longer present).
-  void ObjectRemoved(const dbus::ObjectPath& object_path,
-                     const std::string& interface_name) override {
-    if (!manager_available_)
-      return;
-
-    DVLOG(0) << __func__ << ": " << object_path.value() << ", "
-             << interface_name;
-
-    RemoveManager();
-  }
-
- private:
-  // Is there a manager available?
-  bool manager_available_ = false;
-
-  // Managed by FlossDBusManager - we keep local pointer to access object proxy.
-  dbus::Bus* bus_ = nullptr;
-
-  // Cached list of available adapters and their powered state indexed by hci
-  // index.
-  base::flat_map<int, bool> adapter_to_powered_;
-
-  // Default adapter to use.
-  // TODO(b/191906229) - Default adapter should be taken via manager api.
-  int default_adapter_ = 0;
-
-  // Name of service that implements manager interface.
-  std::string service_name_;
-
-  // Exported path. The actual exported object is managed by the bus connection
-  // itself and will be cleared when the bus is unregistered.
-  dbus::ObjectPath exported_path_;
-
-  // Keep track of the object manager so we can keep track of when the manager
-  // disappears. Managed by the bus object (do not delete).
-  dbus::ObjectManager* object_manager_ = nullptr;
-
-  base::WeakPtrFactory<FlossManagerClientImpl> weak_ptr_factory_{this};
-};
+// static
+const char FlossManagerClient::kObjectManagerPath[] = "/";
 
 FlossManagerClient::FlossManagerClient() = default;
-FlossManagerClient::~FlossManagerClient() = default;
+
+FlossManagerClient::~FlossManagerClient() {
+  if (object_manager_) {
+    object_manager_->UnregisterInterface(kManagerInterface);
+  }
+
+  if (bus_) {
+    bus_->UnregisterExportedObject(dbus::ObjectPath(kExportedCallbacksPath));
+  }
+}
 
 void FlossManagerClient::AddObserver(FlossManagerClient::Observer* observer) {
   observers_.AddObserver(observer);
@@ -411,6 +88,314 @@
   observers_.RemoveObserver(observer);
 }
 
+std::vector<int> FlossManagerClient::GetAdapters() const {
+  std::vector<int> adapters;
+  for (auto kv : adapter_to_powered_) {
+    adapters.push_back(kv.first);
+  }
+
+  return adapters;
+}
+
+int FlossManagerClient::GetDefaultAdapter() const {
+  return default_adapter_;
+}
+
+bool FlossManagerClient::GetAdapterPresent(int adapter) const {
+  return base::Contains(adapter_to_powered_, adapter);
+}
+
+bool FlossManagerClient::GetAdapterEnabled(int adapter) const {
+  auto iter = adapter_to_powered_.find(adapter);
+  if (iter != adapter_to_powered_.end()) {
+    return iter->second;
+  }
+
+  return false;
+}
+
+void FlossManagerClient::SetFlossEnabled(bool enabled) {
+  dbus::ObjectProxy* object_proxy =
+      bus_->GetObjectProxy(service_name_, dbus::ObjectPath(kManagerObject));
+  if (!object_proxy) {
+    return;
+  }
+
+  DVLOG(1) << __func__;
+
+  dbus::MethodCall method_call(kManagerInterface, manager::kSetFlossEnabled);
+  dbus::MessageWriter writer(&method_call);
+  writer.AppendBool(enabled);
+
+  object_proxy->CallMethodWithErrorResponse(
+      &method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
+      base::BindOnce(&FlossManagerClient::DefaultResponse,
+                     weak_ptr_factory_.GetWeakPtr(),
+                     "FlossManagerClient::SetFlossEnabled"));
+}
+
+void FlossManagerClient::SetAdapterEnabled(int adapter,
+                                           bool enabled,
+                                           ResponseCallback callback) {
+  dbus::ObjectProxy* object_proxy =
+      bus_->GetObjectProxy(service_name_, dbus::ObjectPath(kManagerObject));
+  if (!object_proxy) {
+    std::move(callback).Run(Error(kUnknownManagerError, std::string()));
+    return;
+  }
+
+  DVLOG(1) << __func__;
+
+  auto* command = enabled ? manager::kStart : manager::kStop;
+  dbus::MethodCall method_call(kManagerInterface, command);
+  dbus::MessageWriter writer(&method_call);
+  writer.AppendInt32(adapter);
+
+  object_proxy->CallMethodWithErrorResponse(
+      &method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
+      base::BindOnce(&FlossManagerClient::DefaultResponseWithCallback,
+                     weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
+}
+
+// Register manager client against manager.
+void FlossManagerClient::RegisterWithManager() {
+  DCHECK(!manager_available_);
+
+  dbus::ObjectProxy* object_proxy =
+      bus_->GetObjectProxy(service_name_, dbus::ObjectPath(kManagerObject));
+
+  // Get all hci devices available.
+  dbus::MethodCall method_call(kManagerInterface,
+                               manager::kGetAvailableAdapters);
+  object_proxy->CallMethodWithErrorResponse(
+      &method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
+      base::BindOnce(&FlossManagerClient::HandleGetAvailableAdapters,
+                     weak_ptr_factory_.GetWeakPtr()));
+
+  // Register for callbacks.
+  dbus::MethodCall register_callback(kManagerInterface,
+                                     manager::kRegisterCallback);
+  dbus::MessageWriter writer(&register_callback);
+  writer.AppendObjectPath(dbus::ObjectPath(kExportedCallbacksPath));
+
+  object_proxy->CallMethodWithErrorResponse(
+      &register_callback, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
+      base::BindOnce(&FlossManagerClient::DefaultResponse,
+                     weak_ptr_factory_.GetWeakPtr(),
+                     manager::kRegisterCallback));
+
+  manager_available_ = true;
+  for (auto& observer : observers_) {
+    observer.ManagerPresent(manager_available_);
+  }
+}
+
+// Remove manager client (no longer available).
+void FlossManagerClient::RemoveManager() {
+  // Make copy of old adapters and clear existing ones.
+  auto previous_adapters = std::move(adapter_to_powered_);
+  adapter_to_powered_.clear();
+
+  // All old adapters need to be sent a `present = false` notification.
+  for (auto& kv : previous_adapters) {
+    for (auto& observer : observers_) {
+      observer.AdapterPresent(kv.first, false);
+    }
+  }
+
+  manager_available_ = false;
+  for (auto& observer : observers_) {
+    observer.ManagerPresent(manager_available_);
+  }
+}
+
+// The manager can manage multiple adapters so ignore the adapter path given
+// here. It is unused.
+void FlossManagerClient::Init(dbus::Bus* bus,
+                              const std::string& service_name,
+                              const std::string& adapter_path) {
+  bus_ = bus;
+  service_name_ = service_name;
+
+  dbus::ObjectProxy* object_proxy =
+      bus_->GetObjectProxy(service_name_, dbus::ObjectPath(kManagerObject));
+
+  // We should always have object proxy since the client initialization is
+  // gated on ObjectManager marking the manager interface as available.
+  if (!object_proxy) {
+    LOG(ERROR) << "FlossManagerClient couldn't init. Object proxy was null.";
+    return;
+  }
+
+  DVLOG(1) << __func__;
+
+  // Register callback object.
+  dbus::ExportedObject* callbacks =
+      bus_->GetExportedObject(dbus::ObjectPath(kExportedCallbacksPath));
+
+  if (!callbacks) {
+    LOG(ERROR) << "FlossManagerClient couldn't export client callbacks.";
+    return;
+  }
+
+  // Register callbacks for OnHciDeviceChanged and OnHciEnabledChanged.
+  callbacks->ExportMethod(
+      manager::kCallbackInterface, manager::kOnHciDeviceChanged,
+      base::BindRepeating(&FlossManagerClient::OnHciDeviceChange,
+                          weak_ptr_factory_.GetWeakPtr()),
+      base::BindOnce(&HandleExported, manager::kOnHciDeviceChanged));
+
+  callbacks->ExportMethod(
+      manager::kCallbackInterface, manager::kOnHciEnabledChanged,
+      base::BindRepeating(&FlossManagerClient::OnHciEnabledChange,
+                          weak_ptr_factory_.GetWeakPtr()),
+      base::BindOnce(&HandleExported, manager::kOnHciEnabledChanged));
+
+  // Register object manager for Manager.
+  object_manager_ = bus_->GetObjectManager(
+      service_name, dbus::ObjectPath(kObjectManagerPath));
+  object_manager_->RegisterInterface(kManagerInterface, this);
+
+  // Get manager ready.
+  RegisterWithManager();
+}
+
+void FlossManagerClient::HandleGetAvailableAdapters(
+    dbus::Response* response,
+    dbus::ErrorResponse* error_response) {
+  if (!response) {
+    FlossDBusClient::LogErrorResponse(
+        "FlossManagerClient::HandleGetAvailableAdapters", error_response);
+    return;
+  }
+
+  dbus::MessageReader msg(response);
+  dbus::MessageReader arr(nullptr);
+
+  if (msg.PopArray(&arr)) {
+    auto previous_adapters = std::move(adapter_to_powered_);
+
+    // Clear existing adapters.
+    adapter_to_powered_.clear();
+
+    dbus::MessageReader propmap(nullptr);
+    while (arr.PopArray(&propmap)) {
+      int adapter = -1;
+      bool enabled = false;
+
+      if (ParseAdapterWithEnabled(propmap, &adapter, &enabled)) {
+        DCHECK(adapter >= 0);
+        adapter_to_powered_.emplace(adapter, enabled);
+      }
+    }
+
+    // Trigger the observers for adapter present on any new ones we listed.
+    for (auto& observer : observers_) {
+      // Emit present for new adapters that weren't in old list. Also emit the
+      // powered changed for them.
+      for (auto& kv : adapter_to_powered_) {
+        if (!base::Contains(previous_adapters, kv.first)) {
+          observer.AdapterPresent(kv.first, true);
+          observer.AdapterEnabledChanged(kv.first, kv.second);
+        }
+      }
+
+      // Emit not present for adapters that aren't in new list.
+      // We don't need to emit AdapterEnabledChanged since we emit
+      // AdapterPresent is false
+      for (auto& kv : previous_adapters) {
+        if (!base::Contains(adapter_to_powered_, kv.first))
+          observer.AdapterPresent(kv.first, false);
+      }
+    }
+  }
+}
+
+void FlossManagerClient::OnHciDeviceChange(
+    dbus::MethodCall* method_call,
+    dbus::ExportedObject::ResponseSender response_sender) {
+  dbus::MessageReader msg(method_call);
+  int adapter;
+  bool present;
+
+  if (!msg.PopInt32(&adapter) || !msg.PopBool(&present)) {
+    std::move(response_sender)
+        .Run(dbus::ErrorResponse::FromMethodCall(
+            method_call, kErrorInvalidParameters, std::string()));
+    return;
+  }
+
+  for (auto& observer : observers_) {
+    observer.AdapterPresent(adapter, present);
+  }
+
+  // Update the cached list of available adapters.
+  auto iter = adapter_to_powered_.find(adapter);
+  if (present && iter == adapter_to_powered_.end()) {
+    adapter_to_powered_.insert({adapter, false});
+  } else if (!present && iter != adapter_to_powered_.end()) {
+    adapter_to_powered_.erase(iter);
+  }
+
+  std::move(response_sender).Run(dbus::Response::FromMethodCall(method_call));
+}
+
+void FlossManagerClient::OnHciEnabledChange(
+    dbus::MethodCall* method_call,
+    dbus::ExportedObject::ResponseSender response_sender) {
+  dbus::MessageReader msg(method_call);
+  int adapter;
+  bool enabled;
+
+  if (!msg.PopInt32(&adapter) || !msg.PopBool(&enabled)) {
+    std::move(response_sender)
+        .Run(dbus::ErrorResponse::FromMethodCall(
+            method_call, kErrorInvalidParameters, std::string()));
+    return;
+  }
+
+  adapter_to_powered_[adapter] = enabled;
+
+  for (auto& observer : observers_) {
+    observer.AdapterEnabledChanged(adapter, enabled);
+  }
+
+  std::move(response_sender).Run(dbus::Response::FromMethodCall(method_call));
+}
+
+dbus::PropertySet* FlossManagerClient::CreateProperties(
+    dbus::ObjectProxy* object_proxy,
+    const dbus::ObjectPath& object_path,
+    const std::string& interface_name) {
+  return new dbus::PropertySet(object_proxy, interface_name, base::DoNothing());
+}
+
+// Manager interface is available.
+void FlossManagerClient::ObjectAdded(const dbus::ObjectPath& object_path,
+                                     const std::string& interface_name) {
+  // TODO(b/193839304) - When manager exits, we're not getting the
+  //                     ObjectRemoved notification. So remove the manager
+  //                     before re-adding it here.
+  if (manager_available_) {
+    RemoveManager();
+  }
+
+  DVLOG(0) << __func__ << ": " << object_path.value() << ", " << interface_name;
+
+  RegisterWithManager();
+}
+
+// Manager interface is gone (no longer present).
+void FlossManagerClient::ObjectRemoved(const dbus::ObjectPath& object_path,
+                                       const std::string& interface_name) {
+  if (!manager_available_)
+    return;
+
+  DVLOG(0) << __func__ << ": " << object_path.value() << ", " << interface_name;
+
+  RemoveManager();
+}
+
 // static
 dbus::ObjectPath FlossManagerClient::GenerateAdapterPath(int adapter) {
   return dbus::ObjectPath(base::StringPrintf(kAdapterObjectFormat, adapter));
@@ -418,6 +403,6 @@
 
 // static
 std::unique_ptr<FlossManagerClient> FlossManagerClient::Create() {
-  return std::make_unique<FlossManagerClientImpl>();
+  return std::make_unique<FlossManagerClient>();
 }
 }  // namespace floss
diff --git a/device/bluetooth/floss/floss_manager_client.h b/device/bluetooth/floss/floss_manager_client.h
index 21a89ed..9695812 100644
--- a/device/bluetooth/floss/floss_manager_client.h
+++ b/device/bluetooth/floss/floss_manager_client.h
@@ -9,11 +9,21 @@
 #include <vector>
 
 #include "base/callback.h"
+#include "base/containers/flat_map.h"
+#include "base/memory/weak_ptr.h"
 #include "base/observer_list.h"
+#include "dbus/exported_object.h"
+#include "dbus/object_manager.h"
 #include "dbus/object_path.h"
 #include "device/bluetooth/bluetooth_export.h"
 #include "device/bluetooth/floss/floss_dbus_client.h"
 
+namespace dbus {
+class ErrorResponse;
+class PropertySet;
+class Response;
+}  // namespace dbus
+
 namespace floss {
 
 // The adapter manager client observes the Manager interface which lists all
@@ -23,7 +33,9 @@
 // Only the manager interface implements ObjectManager so it should be the first
 // point of interaction with the platform adapters. It will also be used to
 // select the default adapter.
-class DEVICE_BLUETOOTH_EXPORT FlossManagerClient : public FlossDBusClient {
+class DEVICE_BLUETOOTH_EXPORT FlossManagerClient
+    : public FlossDBusClient,
+      public dbus::ObjectManager::Interface {
  public:
   class Observer : public base::CheckedObserver {
    public:
@@ -47,6 +59,7 @@
   FlossManagerClient(const FlossManagerClient&) = delete;
   FlossManagerClient& operator=(const FlossManagerClient&) = delete;
 
+  FlossManagerClient();
   ~FlossManagerClient() override;
 
   // Add observers on this client.
@@ -56,33 +69,99 @@
   void RemoveObserver(Observer* observer);
 
   // Get a list of adapters available on the system.
-  virtual std::vector<int> GetAdapters() const = 0;
+  virtual std::vector<int> GetAdapters() const;
 
   // Get the default adapter (index) to use.
-  virtual int GetDefaultAdapter() const = 0;
+  virtual int GetDefaultAdapter() const;
 
   // Check whether the given adapter is present on the system.
-  virtual bool GetAdapterPresent(int adapter) const = 0;
+  virtual bool GetAdapterPresent(int adapter) const;
 
   // Enable or disable Floss at the platform level. This will only be used while
   // Floss is being developed behind a feature flag. This api will be called on
   // the manager to stop Bluez from running and let Floss manage the adapters
   // instead.
-  virtual void SetFlossEnabled(bool enable) = 0;
+  virtual void SetFlossEnabled(bool enable);
 
   // Check whether an adapter is enabled.
-  virtual bool GetAdapterEnabled(int adapter) const = 0;
+  virtual bool GetAdapterEnabled(int adapter) const;
 
   // Enable or disable an adapter.
   virtual void SetAdapterEnabled(int adapter,
                                  bool enabled,
-                                 ResponseCallback callback) = 0;
+                                 ResponseCallback callback);
+
+  // Initializes the manager client.
+  void Init(dbus::Bus* bus,
+            const std::string& service_name,
+            const std::string& adapter_path) override;
 
  protected:
-  FlossManagerClient();
+  friend class FlossManagerClientTest;
+
+  // Handle response to |GetAvailableAdapters| DBus method call.
+  virtual void HandleGetAvailableAdapters(dbus::Response* response,
+                                          dbus::ErrorResponse* error);
+
+  // Handle callback |OnHciDeviceChange| on exported object path.
+  virtual void OnHciDeviceChange(
+      dbus::MethodCall* method_call,
+      dbus::ExportedObject::ResponseSender response_sender);
+
+  // Handle callback |OnHciEnabledChange| on exported object path.
+  virtual void OnHciEnabledChange(
+      dbus::MethodCall* method_call,
+      dbus::ExportedObject::ResponseSender response_sender);
+
+  // Get active adapters and register for callbacks with manager object.
+  void RegisterWithManager();
+
+  // Remove active adapters after manager is no longer available.
+  void RemoveManager();
+
+  // dbus::ObjectManager::Interface overrides
+  dbus::PropertySet* CreateProperties(
+      dbus::ObjectProxy* object_proxy,
+      const dbus::ObjectPath& object_path,
+      const std::string& interface_name) override;
+
+  void ObjectAdded(const dbus::ObjectPath& object_path,
+                   const std::string& interface_name) override;
+  void ObjectRemoved(const dbus::ObjectPath& object_path,
+                     const std::string& interface_name) override;
+
+  // Managed by FlossDBusManager - we keep local pointer to access object proxy.
+  dbus::Bus* bus_ = nullptr;
+
+  // Keep track of the object manager so we can keep track of when the manager
+  // disappears. Managed by the bus object (do not delete).
+  dbus::ObjectManager* object_manager_ = nullptr;
+
+  // Is there a manager available?
+  bool manager_available_ = false;
+
+  // Default adapter to use.
+  // TODO(b/191906229) - Default adapter should be taken via manager api.
+  int default_adapter_ = 0;
+
+  // Cached list of available adapters and their powered state indexed by hci
+  // index.
+  base::flat_map<int, bool> adapter_to_powered_;
+
+  // Name of service that implements manager interface.
+  std::string service_name_;
 
   // List of observers interested in event notifications from this client.
   base::ObserverList<Observer> observers_;
+
+ private:
+  // Object path for exported callbacks registered against manager interface.
+  static const char kExportedCallbacksPath[];
+
+  // Floss Manager registers ObjectManager at this path.
+  static const char kObjectManagerPath[];
+
+  base::WeakPtrFactory<FlossManagerClient> weak_ptr_factory_{this};
 };
 
 }  // namespace floss
diff --git a/device/bluetooth/floss/floss_manager_client_unittest.cc b/device/bluetooth/floss/floss_manager_client_unittest.cc
new file mode 100644
index 0000000..d464cdf
--- /dev/null
+++ b/device/bluetooth/floss/floss_manager_client_unittest.cc
@@ -0,0 +1,385 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "device/bluetooth/floss/floss_manager_client.h"
+
+#include <utility>
+#include <vector>
+
+#include "base/memory/ref_counted.h"
+#include "base/run_loop.h"
+#include "base/test/task_environment.h"
+#include "base/threading/sequenced_task_runner_handle.h"
+#include "dbus/bus.h"
+#include "dbus/message.h"
+#include "dbus/mock_bus.h"
+#include "dbus/mock_exported_object.h"
+#include "dbus/mock_object_proxy.h"
+#include "dbus/object_manager.h"
+#include "dbus/object_path.h"
+#include "device/bluetooth/floss/floss_dbus_client.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace floss {
+namespace {
+const std::vector<std::pair<int, bool>> kMockAdaptersAvailable = {{0, false},
+                                                                  {5, true}};
+
+class TestManagerObserver : public FlossManagerClient::Observer {
+ public:
+  explicit TestManagerObserver(FlossManagerClient* client) : client_(client) {
+    client_->AddObserver(this);
+  }
+
+  ~TestManagerObserver() override { client_->RemoveObserver(this); }
+
+  void ManagerPresent(bool present) override {
+    manager_present_count_++;
+    manager_present_ = present;
+  }
+
+  void AdapterPresent(int adapter, bool present) override {
+    adapter_present_count_++;
+    adapter_present_[adapter] = present;
+  }
+
+  void AdapterEnabledChanged(int adapter, bool enabled) override {
+    adapter_enabled_changed_count_++;
+    adapter_enabled_[adapter] = enabled;
+  }
+
+  int manager_present_count_ = 0;
+  int adapter_present_count_ = 0;
+  int adapter_enabled_changed_count_ = 0;
+
+  bool manager_present_ = false;
+  std::map<int, bool> adapter_present_;
+  std::map<int, bool> adapter_enabled_;
+
+ private:
+  FlossManagerClient* client_ = nullptr;
+};
+
+}  // namespace
+
+class FlossManagerClientTest : public testing::Test {
+ public:
+  FlossManagerClientTest() = default;
+
+  void SetUpMocks() {
+    auto obj_mgr_path =
+        ::dbus::ObjectPath(FlossManagerClient::kObjectManagerPath);
+
+    manager_object_proxy_ = base::MakeRefCounted<::dbus::MockObjectProxy>(
+        bus_.get(), kManagerInterface, ::dbus::ObjectPath(kManagerObject));
+
+    exported_callbacks_ = base::MakeRefCounted<::dbus::MockExportedObject>(
+        bus_.get(),
+        ::dbus::ObjectPath(FlossManagerClient::kExportedCallbacksPath));
+
+    // Make sure a valid object manager is returned
+    EXPECT_CALL(*bus_.get(), GetObjectProxy(kManagerInterface, obj_mgr_path))
+        .WillOnce(::testing::Return(manager_object_proxy_.get()));
+    EXPECT_CALL(*bus_.get(), GetDBusTaskRunner())
+        .WillOnce(
+            ::testing::Return(::base::SequencedTaskRunnerHandle::Get().get()));
+    object_manager_ = ::dbus::ObjectManager::Create(
+        bus_.get(), kManagerInterface, obj_mgr_path);
+    EXPECT_CALL(*bus_.get(),
+                GetObjectManager(kManagerInterface, dbus::ObjectPath("/")))
+        .WillOnce(::testing::Return(object_manager_.get()));
+
+    // Set up expects for remaining interfaces
+    EXPECT_CALL(*bus_.get(), GetObjectProxy(kManagerInterface,
+                                            ::dbus::ObjectPath(kManagerObject)))
+        .WillRepeatedly(::testing::Return(manager_object_proxy_.get()));
+    EXPECT_CALL(*bus_.get(), GetExportedObject)
+        .WillRepeatedly(::testing::Return(exported_callbacks_.get()));
+    EXPECT_CALL(*exported_callbacks_.get(), ExportMethod).Times(2);
+
+    // Handle method calls on the object proxy
+    ON_CALL(*manager_object_proxy_.get(), DoCallMethodWithErrorResponse)
+        .WillByDefault(
+            [this](::dbus::MethodCall* method_call, int timeout_ms,
+                   ::dbus::ObjectProxy::ResponseOrErrorCallback* cb) {
+              if (method_call->GetMember() == manager::kGetAvailableAdapters) {
+                HandleGetAvailableAdapters(method_call, timeout_ms, cb);
+              }
+
+              method_called_[method_call->GetMember()]++;
+            });
+  }
+
+  void SendHciDeviceCallback(int adapter,
+                             bool present,
+                             dbus::ExportedObject::ResponseSender response) {
+    dbus::MethodCall method_call(manager::kCallbackInterface,
+                                 manager::kOnHciDeviceChanged);
+    method_call.SetSerial(serial_++);
+    dbus::MessageWriter writer(&method_call);
+    writer.AppendInt32(adapter);
+    writer.AppendBool(present);
+
+    client_->OnHciDeviceChange(&method_call, std::move(response));
+  }
+
+  void SendInvalidHciDeviceCallback(dbus::ExportedObject::ResponseSender rsp) {
+    dbus::MethodCall method_call(manager::kCallbackInterface,
+                                 manager::kOnHciDeviceChanged);
+    method_call.SetSerial(serial_++);
+    client_->OnHciDeviceChange(&method_call, std::move(rsp));
+  }
+
+  void SendHciEnabledCallback(int adapter,
+                              bool enabled,
+                              dbus::ExportedObject::ResponseSender response) {
+    dbus::MethodCall method_call(manager::kCallbackInterface,
+                                 manager::kOnHciEnabledChanged);
+    method_call.SetSerial(serial_++);
+    dbus::MessageWriter writer(&method_call);
+    writer.AppendInt32(adapter);
+    writer.AppendBool(enabled);
+
+    client_->OnHciEnabledChange(&method_call, std::move(response));
+  }
+
+  void SendInvalidHciEnabledCallback(dbus::ExportedObject::ResponseSender rsp) {
+    dbus::MethodCall method_call(manager::kCallbackInterface,
+                                 manager::kOnHciEnabledChanged);
+    method_call.SetSerial(serial_++);
+    client_->OnHciEnabledChange(&method_call, std::move(rsp));
+  }
+
+  void SetUp() override {
+    ::dbus::Bus::Options options;
+    options.bus_type = ::dbus::Bus::BusType::SYSTEM;
+    bus_ = base::MakeRefCounted<::dbus::MockBus>(options);
+    client_ = FlossManagerClient::Create();
+
+    SetUpMocks();
+  }
+
+  void TearDown() override {
+    // Clean up the client first so it gets rid of all its references to the
+    // various buses, object proxies, etc.
+    client_.reset();
+    method_called_.clear();
+  }
+
+  void HandleGetAvailableAdapters(
+      ::dbus::MethodCall* method_call,
+      int timeout_ms,
+      ::dbus::ObjectProxy::ResponseOrErrorCallback* cb) {
+    // Return that there are 2 adapter objects
+    auto response = ::dbus::Response::CreateEmpty();
+
+    ::dbus::MessageWriter msg(response.get());
+    ::dbus::MessageWriter outer(nullptr);
+    msg.OpenArray("a{sv}", &outer);
+    for (auto kv : kMockAdaptersAvailable) {
+      ::dbus::MessageWriter inner(nullptr);
+      ::dbus::MessageWriter dict(nullptr);
+      outer.OpenArray("{sv}", &inner);
+
+      inner.OpenDictEntry(&dict);
+      dict.AppendString("hci_interface");
+      dict.AppendVariantOfInt32(kv.first);
+      inner.CloseContainer(&dict);
+
+      inner.OpenDictEntry(&dict);
+      dict.AppendString("enabled");
+      dict.AppendVariantOfBool(kv.second);
+      inner.CloseContainer(&dict);
+
+      outer.CloseContainer(&inner);
+    }
+    msg.CloseContainer(&outer);
+
+    std::move(*cb).Run(response.get(), nullptr);
+  }
+
+  void ExpectErrorResponse(std::unique_ptr<dbus::Response> response) {
+    EXPECT_EQ(response->GetMessageType(),
+              dbus::Message::MessageType::MESSAGE_ERROR);
+  }
+
+  void ExpectNormalResponse(std::unique_ptr<dbus::Response> response) {
+    EXPECT_NE(response->GetMessageType(),
+              dbus::Message::MessageType::MESSAGE_ERROR);
+  }
+
+  void TriggerObjectAdded(dbus::ObjectPath& path,
+                          const std::string& interface) {
+    client_->ObjectAdded(path, interface);
+  }
+
+  void TriggerObjectRemoved(dbus::ObjectPath& path,
+                            const std::string& interface) {
+    client_->ObjectRemoved(path, interface);
+  }
+
+  bool IsInterfaceRegisteredOnObjectManager(const std::string& interface) {
+    return client_->object_manager_ != nullptr &&
+           client_->object_manager_->IsInterfaceRegisteredForTesting(interface);
+  }
+
+ protected:
+  // DBus messages require an increasing serial number or the dbus libraries
+  // assert.
+  int serial_ = 1;
+  std::unique_ptr<FlossManagerClient> client_;
+  scoped_refptr<::dbus::MockBus> bus_;
+  scoped_refptr<::dbus::MockExportedObject> exported_callbacks_;
+  scoped_refptr<::dbus::MockObjectProxy> manager_object_proxy_;
+  scoped_refptr<::dbus::ObjectManager> object_manager_;
+  std::map<std::string, int> method_called_;
+
+  base::test::TaskEnvironment task_environment_;
+  base::WeakPtrFactory<FlossManagerClientTest> weak_ptr_factory_{this};
+};
+
+// Make sure adapter presence is updated on init
+TEST_F(FlossManagerClientTest, QueriesAdapterPresenceOnInit) {
+  TestManagerObserver observer(client_.get());
+  client_->Init(bus_.get(), kManagerInterface, /*adapter_path=*/std::string());
+  EXPECT_EQ(observer.manager_present_count_, 1);
+  EXPECT_TRUE(observer.manager_present_);
+
+  EXPECT_THAT(client_->GetAdapters(), ::testing::ElementsAre(0, 5));
+  EXPECT_TRUE(client_->GetAdapterPresent(0));
+  EXPECT_TRUE(client_->GetAdapterPresent(5));
+  EXPECT_FALSE(client_->GetAdapterPresent(1));
+
+  EXPECT_EQ(observer.adapter_present_count_, 2);
+  EXPECT_TRUE(observer.adapter_present_[0]);
+  EXPECT_TRUE(observer.adapter_present_[5]);
+}
+
+// Make sure adapter presence is plumbed through callbacks
+TEST_F(FlossManagerClientTest, VerifyAdapterPresent) {
+  TestManagerObserver observer(client_.get());
+  client_->Init(bus_.get(), kManagerInterface, /*adapter_path=*/std::string());
+  EXPECT_EQ(observer.adapter_present_count_, 2);
+  EXPECT_EQ(observer.adapter_enabled_changed_count_, 2);
+  EXPECT_TRUE(observer.adapter_present_[0]);
+
+  SendInvalidHciDeviceCallback(
+      base::BindOnce(&FlossManagerClientTest::ExpectErrorResponse,
+                     weak_ptr_factory_.GetWeakPtr()));
+
+  SendHciDeviceCallback(
+      0, false,
+      base::BindOnce(&FlossManagerClientTest::ExpectNormalResponse,
+                     weak_ptr_factory_.GetWeakPtr()));
+
+  EXPECT_THAT(client_->GetAdapters(), ::testing::ElementsAre(5));
+  EXPECT_TRUE(client_->GetAdapterPresent(5));
+  EXPECT_FALSE(client_->GetAdapterPresent(0));
+  EXPECT_FALSE(client_->GetAdapterEnabled(0));
+
+  // 2 to start and 1 to remove "0"
+  EXPECT_EQ(observer.adapter_present_count_, 3);
+  EXPECT_FALSE(observer.adapter_present_[0]);
+
+  // On present = false, the client may not be sent an additional enabled
+  // = false. It is implied and the client must act accordingly.
+  EXPECT_EQ(observer.adapter_enabled_changed_count_, 2);
+}
+
+// Make sure adapter powered is plumbed through callbacks
+TEST_F(FlossManagerClientTest, VerifyAdapterEnabled) {
+  TestManagerObserver observer(client_.get());
+  client_->Init(bus_.get(), kManagerInterface, /*adapter_path=*/std::string());
+  // Pre-conditions
+  EXPECT_FALSE(client_->GetAdapterEnabled(0));
+  EXPECT_TRUE(client_->GetAdapterEnabled(5));
+  EXPECT_EQ(observer.adapter_enabled_changed_count_, 2);
+
+  // Adapter 1 is not present at the beginning
+  EXPECT_FALSE(client_->GetAdapterEnabled(1));
+  EXPECT_FALSE(client_->GetAdapterPresent(1));
+
+  SendInvalidHciEnabledCallback(
+      base::BindOnce(&FlossManagerClientTest::ExpectErrorResponse,
+                     weak_ptr_factory_.GetWeakPtr()));
+
+  SendHciEnabledCallback(
+      0, true,
+      base::BindOnce(&FlossManagerClientTest::ExpectNormalResponse,
+                     weak_ptr_factory_.GetWeakPtr()));
+
+  EXPECT_TRUE(client_->GetAdapterEnabled(0));
+  EXPECT_EQ(observer.adapter_enabled_changed_count_, 3);
+  EXPECT_TRUE(observer.adapter_enabled_[0]);
+
+  // Enabled implies presence too
+  SendHciEnabledCallback(
+      1, true,
+      base::BindOnce(&FlossManagerClientTest::ExpectNormalResponse,
+                     weak_ptr_factory_.GetWeakPtr()));
+  EXPECT_TRUE(client_->GetAdapterPresent(1));
+  EXPECT_TRUE(client_->GetAdapterEnabled(1));
+
+  EXPECT_EQ(observer.adapter_enabled_changed_count_, 4);
+  EXPECT_TRUE(observer.adapter_enabled_[1]);
+  // On enabled = true, present = true is implied. The platform should emit both
+  // but the client shouldn't depend on it.
+  EXPECT_FALSE(observer.adapter_present_[1]);
+
+  // 5 was unchanged
+  EXPECT_TRUE(client_->GetAdapterEnabled(5));
+  EXPECT_TRUE(observer.adapter_enabled_[5]);
+}
+
+// Make sure manager presence is correctly detected
+TEST_F(FlossManagerClientTest, HandleManagerPresence) {
+  TestManagerObserver observer(client_.get());
+  client_->Init(bus_.get(), kManagerInterface, /*adapter_path=*/std::string());
+  dbus::ObjectPath opath = dbus::ObjectPath(kManagerObject);
+  EXPECT_EQ(observer.manager_present_count_, 1);
+
+  // Make sure we registered against ObjectManager
+  EXPECT_TRUE(IsInterfaceRegisteredOnObjectManager(kManagerInterface));
+
+  // By default, the manager should be available
+  EXPECT_TRUE(observer.manager_present_);
+
+  // Triggering an ObjectRemoved should clear all present adapters
+  TriggerObjectRemoved(opath, kManagerInterface);
+  EXPECT_THAT(client_->GetAdapters(), ::testing::ElementsAre());
+  EXPECT_FALSE(observer.manager_present_);
+
+  // Triggering ObjectRemoved while already unavailable should do nothing
+  TriggerObjectRemoved(opath, kManagerInterface);
+  EXPECT_THAT(client_->GetAdapters(), ::testing::ElementsAre());
+  EXPECT_FALSE(observer.manager_present_);
+
+  method_called_.clear();
+  // Triggering ObjectAdded should refill available hci devices and register
+  // callbacks
+  TriggerObjectAdded(opath, kManagerInterface);
+  EXPECT_TRUE(method_called_[manager::kGetAvailableAdapters] > 0);
+  EXPECT_TRUE(method_called_[manager::kRegisterCallback] > 0);
+  EXPECT_TRUE(observer.manager_present_);
+
+  // Clear present count to confirm a RemoveManager + RegisterManager occurred
+  observer.manager_present_count_ = 0;
+
+  // TODO(b/193839304) - Triggering ObjectAdded on an already added object
+  //                     should trigger a remove and then re-add
+  method_called_.clear();
+  SendHciDeviceCallback(
+      1, true,
+      base::BindOnce(&FlossManagerClientTest::ExpectNormalResponse,
+                     weak_ptr_factory_.GetWeakPtr()));
+  EXPECT_TRUE(client_->GetAdapterPresent(1));
+  TriggerObjectAdded(opath, kManagerInterface);
+  // ManagerPresent should be called once for remove and once for register.
+  EXPECT_EQ(observer.manager_present_count_, 2);
+  EXPECT_FALSE(client_->GetAdapterPresent(1));  // Cleared previous adapter list
+  EXPECT_TRUE(method_called_[manager::kGetAvailableAdapters] > 0);
+  EXPECT_TRUE(method_called_[manager::kRegisterCallback] > 0);
+}
+}  // namespace floss
diff --git a/device/gamepad/gamepad_id_list.h b/device/gamepad/gamepad_id_list.h
index 751b7618..e73e31b 100644
--- a/device/gamepad/gamepad_id_list.h
+++ b/device/gamepad/gamepad_id_list.h
@@ -12,6 +12,7 @@
 #include <vector>
 
 #include "base/lazy_instance.h"
+#include "base/strings/string_piece.h"
 #include "device/gamepad/gamepad_export.h"
 
 namespace device {
diff --git a/docs/android_dynamic_feature_modules.md b/docs/android_dynamic_feature_modules.md
index ff2c2281d..1d4440a 100644
--- a/docs/android_dynamic_feature_modules.md
+++ b/docs/android_dynamic_feature_modules.md
@@ -213,12 +213,14 @@
 
 ```shell
 $ autoninja -C $OUTDIR monochrome_public_bundle
-$ $OUTDIR/bin/monochrome_public_bundle install -m base -m foo
+$ $OUTDIR/bin/monochrome_public_bundle install -m foo
 ```
 
-This will install Foo alongside the rest of Chrome. The rest of Chrome is called
-_base_ module in the bundle world. The base module will always be put on the
-device when initially installing Chrome.
+This will install the `Foo` module, the `base` module, and all modules with an
+`AndroidManifest.xml` that:
+ * Sets `<module dist:onDemand="false">`, or
+ * Has `<dist:delivery>` conditions that are satisfied by the device being
+   installed to.
 
 *** note
 **Note:** The install script may install more modules than you specify, e.g.
@@ -237,7 +239,7 @@
 installed modules:
 
 ```shell
-$ $OUTDIR/bin/monochrome_public_bundle install -m base
+$ $OUTDIR/bin/monochrome_public_bundle install
 $ adb shell dumpsys package org.chromium.chrome | grep splits
 >   splits=[base, config.en]
 ```
@@ -871,7 +873,7 @@
 Fake-install and launch Chrome with the following command:
 
 ```shell
-$ $OUTDIR/bin/monochrome_public_bundle install -m base -f foo
+$ $OUTDIR/bin/monochrome_public_bundle install -f foo
 $ $OUTDIR/bin/monochrome_public_bundle launch
 ```
 
diff --git a/extensions/browser/api/feedback_private/feedback_private_api_chromeos_unittest.cc b/extensions/browser/api/feedback_private/feedback_private_api_chromeos_unittest.cc
index fc70bb8..c5afe4fc 100644
--- a/extensions/browser/api/feedback_private/feedback_private_api_chromeos_unittest.cc
+++ b/extensions/browser/api/feedback_private/feedback_private_api_chromeos_unittest.cc
@@ -127,8 +127,7 @@
     EXPECT_TRUE(values.is_list());
 
     std::unique_ptr<api::feedback_private::SendFeedback::Params> params =
-        api::feedback_private::SendFeedback::Params::Create(
-            base::Value::AsListValue(values));
+        api::feedback_private::SendFeedback::Params::Create(values.GetList());
     EXPECT_TRUE(params);
 
     scoped_refptr<FeedbackData> actual_feedback_data;
diff --git a/extensions/common/api/automation.idl b/extensions/common/api/automation.idl
index 59004ba..d8e50a3 100644
--- a/extensions/common/api/automation.idl
+++ b/extensions/common/api/automation.idl
@@ -23,6 +23,7 @@
     autocorrectionOccured,
     blur,
     busyChanged,
+    caretBoundsChanged,
     checkedStateChanged,
     checkedStateDescriptionChanged,
     childrenChanged,
@@ -910,6 +911,9 @@
     // The level of a heading or tree item.
     long? hierarchicalLevel;
 
+    // The current caret bounds in screen coordinates.
+    Rect? caretBounds;
+
     // The start and end index of each word in an inline text box.
     long[]? wordStarts;
     long[]? wordEnds;
diff --git a/extensions/common/api/storage.json b/extensions/common/api/storage.json
index b235a594..e6fb5658 100644
--- a/extensions/common/api/storage.json
+++ b/extensions/common/api/storage.json
@@ -58,20 +58,21 @@
                 ],
                 "description": "A single key to get, list of keys to get, or a dictionary specifying default values (see description of the object).  An empty list or object will return an empty result object.  Pass in <code>null</code> to get the entire contents of storage.",
                 "optional": true
+              },
+              {
+                "name": "callback",
+                "type": "function",
+                "description": "Callback with storage items, or on failure (in which case $(ref:runtime.lastError) will be set).",
+                "parameters": [
+                  {
+                    "name": "items",
+                    "type": "object",
+                    "additionalProperties": { "type": "any" },
+                    "description": "Object with items in their key-value mappings."
+                  }
+                ]
               }
-            ],
-            "returns_async": {
-              "name": "callback",
-              "description": "Callback with storage items, or on failure (in which case $(ref:runtime.lastError) will be set).",
-              "parameters": [
-                {
-                  "name": "items",
-                  "type": "object",
-                  "additionalProperties": { "type": "any" },
-                  "description": "Object with items in their key-value mappings."
-                }
-              ]
-            }
+            ]
           },
           {
             "name": "getBytesInUse",
@@ -86,19 +87,20 @@
                 ],
                 "description": "A single key or list of keys to get the total usage for. An empty list will return 0. Pass in <code>null</code> to get the total usage of all of storage.",
                 "optional": true
+              },
+              {
+                "name": "callback",
+                "type": "function",
+                "description": "Callback with the amount of space being used by storage, or on failure (in which case $(ref:runtime.lastError) will be set).",
+                "parameters": [
+                  {
+                    "name": "bytesInUse",
+                    "type": "integer",
+                    "description": "Amount of space being used in storage, in bytes."
+                  }
+                ]
               }
-            ],
-            "returns_async": {
-              "name": "callback",
-              "description": "Callback with the amount of space being used by storage, or on failure (in which case $(ref:runtime.lastError) will be set).",
-              "parameters": [
-                {
-                  "name": "bytesInUse",
-                  "type": "integer",
-                  "description": "Amount of space being used in storage, in bytes."
-                }
-              ]
-            }
+            ]
           },
           {
             "name": "set",
@@ -111,14 +113,15 @@
                 "additionalProperties": { "type": "any", "preserveNull": true },
                 "preserveNull": true,
                 "description": "<p>An object which gives each key/value pair to update storage with. Any other key/value pairs in storage will not be affected.</p><p>Primitive values such as numbers will serialize as expected. Values with a <code>typeof</code> <code>\"object\"</code> and <code>\"function\"</code> will typically serialize to <code>{}</code>, with the exception of <code>Array</code> (serializes as expected), <code>Date</code>, and <code>Regex</code> (serialize using their <code>String</code> representation).</p>"
+              },
+              {
+                "name": "callback",
+                "type": "function",
+                "description": "Callback on success, or on failure (in which case $(ref:runtime.lastError) will be set).",
+                "parameters": [],
+                "optional": true
               }
-            ],
-            "returns_async": {
-              "name": "callback",
-              "description": "Callback on success, or on failure (in which case $(ref:runtime.lastError) will be set).",
-              "parameters": [],
-              "optional": true
-            }
+            ]
           },
           {
             "name": "remove",
@@ -132,26 +135,29 @@
                   {"type": "array", "items": {"type": "string"}}
                 ],
                 "description": "A single key or a list of keys for items to remove."
+              },
+              {
+                "name": "callback",
+                "type": "function",
+                "description": "Callback on success, or on failure (in which case $(ref:runtime.lastError) will be set).",
+                "parameters": [],
+                "optional": true
               }
-            ],
-            "returns_async": {
-              "name": "callback",
-              "description": "Callback on success, or on failure (in which case $(ref:runtime.lastError) will be set).",
-              "parameters": [],
-              "optional": true
-            }
+            ]
           },
           {
             "name": "clear",
             "type": "function",
             "description": "Removes all items from storage.",
-            "parameters": [],
-            "returns_async":  {
-              "name": "callback",
-              "description": "Callback on success, or on failure (in which case $(ref:runtime.lastError) will be set).",
-              "parameters": [],
-              "optional": true
-            }
+            "parameters": [
+              {
+                "name": "callback",
+                "type": "function",
+                "description": "Callback on success, or on failure (in which case $(ref:runtime.lastError) will be set).",
+                "parameters": [],
+                "optional": true
+              }
+            ]
           }, {
             "name": "setAccessLevel",
             "nodoc": true,
@@ -167,14 +173,15 @@
                     "description": "The access level of the storage area."
                   }
                 }
+              },
+              {
+                "name": "callback",
+                "type": "function",
+                "description": "Callback on success, or on failure (in which case $(ref:runtime.lastError) will be set).",
+                "parameters": [],
+                "optional": true
               }
-            ],
-            "returns_async":  {
-              "name": "callback",
-              "description": "Callback on success, or on failure (in which case $(ref:runtime.lastError) will be set).",
-              "parameters": [],
-              "optional": true
-            }
+            ]
           }
         ],
         "events": [
diff --git a/extensions/renderer/api/automation/automation_api_util.cc b/extensions/renderer/api/automation/automation_api_util.cc
index e5b3e1e8..6b6b4cc 100644
--- a/extensions/renderer/api/automation/automation_api_util.cc
+++ b/extensions/renderer/api/automation/automation_api_util.cc
@@ -108,6 +108,7 @@
     case ui::AXEventGenerator::Event::ATOMIC_CHANGED:
     case ui::AXEventGenerator::Event::AUTO_COMPLETE_CHANGED:
     case ui::AXEventGenerator::Event::BUSY_CHANGED:
+    case ui::AXEventGenerator::Event::CARET_BOUNDS_CHANGED:
     case ui::AXEventGenerator::Event::CHECKED_STATE_CHANGED:
     case ui::AXEventGenerator::Event::CHECKED_STATE_DESCRIPTION_CHANGED:
     case ui::AXEventGenerator::Event::CHILDREN_CHANGED:
diff --git a/extensions/renderer/resources/automation/automation_node.js b/extensions/renderer/resources/automation/automation_node.js
index 0800310..2acc007f 100644
--- a/extensions/renderer/resources/automation/automation_node.js
+++ b/extensions/renderer/resources/automation/automation_node.js
@@ -656,6 +656,19 @@
     return GetChecked(this.treeID, this.id);
   },
 
+  get caretBounds() {
+    const data = GetIntListAttribute(this.treeID, this.id, 'caretBounds');
+    if (!data) {
+      return;
+    }
+
+    if (data.length !== 4) {
+      throw 'Internal encoding error for caret bounds.';
+    }
+
+    return {left: data[0], top: data[1], width: data[2], height: data[3]};
+  },
+
   get location() {
     return GetLocation(this.treeID, this.id);
   },
@@ -2049,6 +2062,7 @@
       [
         'ariaCurrentState',
         'bold',
+        'caretBounds',
         'checked',
         'children',
         'customActions',
diff --git a/fuchsia/base/mem_buffer_util.cc b/fuchsia/base/mem_buffer_util.cc
index 4e2e7db..f3cc1e8 100644
--- a/fuchsia/base/mem_buffer_util.cc
+++ b/fuchsia/base/mem_buffer_util.cc
@@ -14,6 +14,7 @@
 #include "base/files/file_util.h"
 #include "base/files/memory_mapped_file.h"
 #include "base/fuchsia/fuchsia_logging.h"
+#include "base/notreached.h"
 #include "base/threading/thread_restrictions.h"
 
 namespace cr_fuchsia {
diff --git a/fuchsia/engine/browser/media_player_impl.cc b/fuchsia/engine/browser/media_player_impl.cc
index 8461e41a..d4284f2 100644
--- a/fuchsia/engine/browser/media_player_impl.cc
+++ b/fuchsia/engine/browser/media_player_impl.cc
@@ -55,6 +55,8 @@
       return {};  // PlayerControl does not support hanging up.
     case MediaSessionAction::kRaise:
       return {};  // PlayerControl does not support raising.
+    case MediaSessionAction::kSetMute:
+      return {};  // TODO(crbug.com/1240811): implement set mute.
   }
 }
 
diff --git a/fuchsia/engine/browser/media_player_impl_unittest.cc b/fuchsia/engine/browser/media_player_impl_unittest.cc
index 9cf2ab5db1..e0d5540e 100644
--- a/fuchsia/engine/browser/media_player_impl_unittest.cc
+++ b/fuchsia/engine/browser/media_player_impl_unittest.cc
@@ -41,6 +41,7 @@
   MOCK_METHOD0(ToggleCamera, void());
   MOCK_METHOD0(HangUp, void());
   MOCK_METHOD0(Raise, void());
+  MOCK_METHOD1(SetMute, void(bool));
 
   // content::MediaSession APIs faked to implement testing behaviour.
   MOCK_METHOD1(DidReceiveAction,
diff --git a/fuchsia/engine/browser/navigation_controller_impl.cc b/fuchsia/engine/browser/navigation_controller_impl.cc
index 42d0e39..355a4cf7 100644
--- a/fuchsia/engine/browser/navigation_controller_impl.cc
+++ b/fuchsia/engine/browser/navigation_controller_impl.cc
@@ -4,6 +4,7 @@
 
 #include "fuchsia/engine/browser/navigation_controller_impl.h"
 
+#include "base/bits.h"
 #include "base/fuchsia/fuchsia_logging.h"
 #include "base/memory/page_size.h"
 #include "base/strings/strcat.h"
diff --git a/infra/config/generated/cr-buildbucket.cfg b/infra/config/generated/cr-buildbucket.cfg
index 00eacdf9..74b35fb 100644
--- a/infra/config/generated/cr-buildbucket.cfg
+++ b/infra/config/generated/cr-buildbucket.cfg
@@ -248,6 +248,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium.chromium_tests.use_rdb_results"
+        value: 5
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -3794,12 +3798,10 @@
         '    "use_luci_auth": true'
         '  },'
         '  "$build/reclient": {'
+        '    "cache_silo": "Comparison Linux - cache siloed",'
         '    "instance": "rbe-chromium-trusted",'
         '    "jobs": 250,'
-        '    "metrics_project": "chromium-reclient-metrics",'
-        '    "rewrapper_env": {'
-        '      "RBE_cache_silo": "Comparison Linux - cache siloed"'
-        '    }'
+        '    "metrics_project": "chromium-reclient-metrics"'
         '  },'
         '  "$kitchen": {'
         '    "devshell": true,'
@@ -3823,6 +3825,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium.chromium_tests.use_rdb_results"
+        value: 5
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -10173,6 +10179,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium.chromium_tests.use_rdb_results"
+        value: 5
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -10259,6 +10269,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium.chromium_tests.use_rdb_results"
+        value: 5
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -10345,6 +10359,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium.chromium_tests.use_rdb_results"
+        value: 5
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -10611,6 +10629,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium.chromium_tests.use_rdb_results"
+        value: 5
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -10697,6 +10719,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium.chromium_tests.use_rdb_results"
+        value: 5
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -10786,6 +10812,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium.chromium_tests.use_rdb_results"
+        value: 5
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -10875,6 +10905,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium.chromium_tests.use_rdb_results"
+        value: 5
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -13310,6 +13344,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium.chromium_tests.use_rdb_results"
+        value: 5
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -13398,6 +13436,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium.chromium_tests.use_rdb_results"
+        value: 5
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -14017,6 +14059,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium.chromium_tests.use_rdb_results"
+        value: 5
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -14966,6 +15012,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium.chromium_tests.use_rdb_results"
+        value: 5
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -16479,6 +16529,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium.chromium_tests.use_rdb_results"
+        value: 5
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -16562,6 +16616,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium.chromium_tests.use_rdb_results"
+        value: 5
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -18022,6 +18080,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium.chromium_tests.use_rdb_results"
+        value: 5
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -18197,6 +18259,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium.chromium_tests.use_rdb_results"
+        value: 5
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -18371,6 +18437,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium.chromium_tests.use_rdb_results"
+        value: 5
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -18460,6 +18530,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium.chromium_tests.use_rdb_results"
+        value: 5
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -18548,6 +18622,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium.chromium_tests.use_rdb_results"
+        value: 5
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -18636,6 +18714,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium.chromium_tests.use_rdb_results"
+        value: 5
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -18725,6 +18807,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium.chromium_tests.use_rdb_results"
+        value: 5
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -18813,6 +18899,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium.chromium_tests.use_rdb_results"
+        value: 5
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -18898,6 +18988,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium.chromium_tests.use_rdb_results"
+        value: 5
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -22103,6 +22197,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium.chromium_tests.use_rdb_results"
+        value: 5
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -22275,6 +22373,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium.chromium_tests.use_rdb_results"
+        value: 5
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -22361,6 +22463,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium.chromium_tests.use_rdb_results"
+        value: 5
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -22804,6 +22910,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium.chromium_tests.use_rdb_results"
+        value: 5
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -23501,6 +23611,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium.chromium_tests.use_rdb_results"
+        value: 5
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -23588,6 +23702,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium.chromium_tests.use_rdb_results"
+        value: 5
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -24857,6 +24975,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium.chromium_tests.use_rdb_results"
+        value: 5
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -26933,6 +27055,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium.chromium_tests.use_rdb_results"
+        value: 5
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -27019,6 +27145,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium.chromium_tests.use_rdb_results"
+        value: 5
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -27292,6 +27422,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium.chromium_tests.use_rdb_results"
+        value: 5
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -27385,6 +27519,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium.chromium_tests.use_rdb_results"
+        value: 5
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -30852,6 +30990,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium.chromium_tests.use_rdb_results"
+        value: 5
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -30938,6 +31080,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium.chromium_tests.use_rdb_results"
+        value: 5
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -31469,6 +31615,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium.chromium_tests.use_rdb_results"
+        value: 5
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -31557,6 +31707,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium.chromium_tests.use_rdb_results"
+        value: 5
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -31646,6 +31800,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium.chromium_tests.use_rdb_results"
+        value: 5
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -31732,6 +31890,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium.chromium_tests.use_rdb_results"
+        value: 5
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -32265,6 +32427,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium.chromium_tests.use_rdb_results"
+        value: 5
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -32351,6 +32517,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium.chromium_tests.use_rdb_results"
+        value: 5
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -32437,6 +32607,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium.chromium_tests.use_rdb_results"
+        value: 5
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -32523,6 +32697,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium.chromium_tests.use_rdb_results"
+        value: 5
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -32609,6 +32787,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium.chromium_tests.use_rdb_results"
+        value: 5
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -32695,6 +32877,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium.chromium_tests.use_rdb_results"
+        value: 5
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -33221,6 +33407,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium.chromium_tests.use_rdb_results"
+        value: 5
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -33502,6 +33692,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium.chromium_tests.use_rdb_results"
+        value: 5
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -33590,6 +33784,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium.chromium_tests.use_rdb_results"
+        value: 5
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -33770,6 +33968,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium.chromium_tests.use_rdb_results"
+        value: 5
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -33950,6 +34152,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium.chromium_tests.use_rdb_results"
+        value: 5
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -34038,6 +34244,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium.chromium_tests.use_rdb_results"
+        value: 5
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -34126,6 +34336,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium.chromium_tests.use_rdb_results"
+        value: 5
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -34214,6 +34428,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium.chromium_tests.use_rdb_results"
+        value: 5
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -34302,6 +34520,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium.chromium_tests.use_rdb_results"
+        value: 5
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -34390,6 +34612,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium.chromium_tests.use_rdb_results"
+        value: 5
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -34679,6 +34905,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium.chromium_tests.use_rdb_results"
+        value: 5
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -34767,6 +34997,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium.chromium_tests.use_rdb_results"
+        value: 5
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -34853,6 +35087,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium.chromium_tests.use_rdb_results"
+        value: 5
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -35526,6 +35764,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium.chromium_tests.use_rdb_results"
+        value: 5
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -35821,6 +36063,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium.chromium_tests.use_rdb_results"
+        value: 5
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -36012,6 +36258,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium.chromium_tests.use_rdb_results"
+        value: 5
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -36278,6 +36528,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium.chromium_tests.use_rdb_results"
+        value: 5
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -36364,6 +36618,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium.chromium_tests.use_rdb_results"
+        value: 5
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -36450,6 +36708,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium.chromium_tests.use_rdb_results"
+        value: 5
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -36536,6 +36798,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium.chromium_tests.use_rdb_results"
+        value: 5
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -36882,6 +37148,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium.chromium_tests.use_rdb_results"
+        value: 5
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -37060,6 +37330,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium.chromium_tests.use_rdb_results"
+        value: 5
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -37235,6 +37509,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium.chromium_tests.use_rdb_results"
+        value: 5
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -37321,6 +37599,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium.chromium_tests.use_rdb_results"
+        value: 5
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -37414,6 +37696,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium.chromium_tests.use_rdb_results"
+        value: 5
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -37670,6 +37956,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium.chromium_tests.use_rdb_results"
+        value: 5
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -37763,6 +38053,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium.chromium_tests.use_rdb_results"
+        value: 5
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -37939,6 +38233,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium.chromium_tests.use_rdb_results"
+        value: 5
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -38108,6 +38406,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium.chromium_tests.use_rdb_results"
+        value: 5
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -38283,6 +38585,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium.chromium_tests.use_rdb_results"
+        value: 5
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -38371,6 +38677,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium.chromium_tests.use_rdb_results"
+        value: 5
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -38464,6 +38774,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium.chromium_tests.use_rdb_results"
+        value: 5
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -38639,6 +38953,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium.chromium_tests.use_rdb_results"
+        value: 5
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -38725,6 +39043,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium.chromium_tests.use_rdb_results"
+        value: 5
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -38811,6 +39133,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium.chromium_tests.use_rdb_results"
+        value: 5
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -38986,6 +39312,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium.chromium_tests.use_rdb_results"
+        value: 5
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -39505,6 +39835,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium.chromium_tests.use_rdb_results"
+        value: 5
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -40127,6 +40461,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium.chromium_tests.use_rdb_results"
+        value: 5
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -40298,6 +40636,10 @@
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experimental: YES
       experiments {
+        key: "chromium.chromium_tests.use_rdb_results"
+        value: 5
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -40385,6 +40727,10 @@
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experimental: YES
       experiments {
+        key: "chromium.chromium_tests.use_rdb_results"
+        value: 5
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -40472,6 +40818,10 @@
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experimental: YES
       experiments {
+        key: "chromium.chromium_tests.use_rdb_results"
+        value: 5
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -41647,6 +41997,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium.chromium_tests.use_rdb_results"
+        value: 5
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -42080,6 +42434,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium.chromium_tests.use_rdb_results"
+        value: 5
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -42164,6 +42522,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium.chromium_tests.use_rdb_results"
+        value: 5
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -42420,6 +42782,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium.chromium_tests.use_rdb_results"
+        value: 5
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -42504,6 +42870,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium.chromium_tests.use_rdb_results"
+        value: 5
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -42588,6 +42958,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium.chromium_tests.use_rdb_results"
+        value: 5
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -42924,6 +43298,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium.chromium_tests.use_rdb_results"
+        value: 5
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -44558,6 +44936,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium.chromium_tests.use_rdb_results"
+        value: 5
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -44910,6 +45292,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium.chromium_tests.use_rdb_results"
+        value: 5
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -44996,6 +45382,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium.chromium_tests.use_rdb_results"
+        value: 5
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -45347,6 +45737,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium.chromium_tests.use_rdb_results"
+        value: 5
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -45433,6 +45827,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium.chromium_tests.use_rdb_results"
+        value: 5
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -46314,6 +46712,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium.chromium_tests.use_rdb_results"
+        value: 5
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -46731,6 +47133,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium.chromium_tests.use_rdb_results"
+        value: 5
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -47251,6 +47657,10 @@
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "chromium.chromium_tests.use_rdb_results"
+        value: 5
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
diff --git a/infra/config/lib/builders.star b/infra/config/lib/builders.star
index f3cf33e..7fff581 100644
--- a/infra/config/lib/builders.star
+++ b/infra/config/lib/builders.star
@@ -289,7 +289,7 @@
 
     return isolated or None
 
-def _reclient_property(*, instance, service, jobs, rewrapper_env, profiler_service, publish_trace):
+def _reclient_property(*, instance, service, jobs, rewrapper_env, profiler_service, publish_trace, cache_silo):
     reclient = {}
     instance = defaults.get_value("reclient_instance", instance)
     if instance:
@@ -314,6 +314,8 @@
     publish_trace = defaults.get_value("reclient_publish_trace", publish_trace)
     if publish_trace:
         reclient["publish_trace"] = True
+    if cache_silo:
+        reclient["cache_silo"] = cache_silo
     return reclient or None
 
 ################################################################################
@@ -359,6 +361,7 @@
     reclient_rewrapper_env = None,
     reclient_profiler_service = None,
     reclient_publish_trace = None,
+    reclient_cache_silo = None,
 
     # Provide vars for bucket and executable so users don't have to
     # unnecessarily make wrapper functions
@@ -410,6 +413,7 @@
         reclient_rewrapper_env = args.DEFAULT,
         reclient_profiler_service = args.DEFAULT,
         reclient_publish_trace = args.DEFAULT,
+        reclient_cache_silo = None,
         **kwargs):
     """Define a builder.
 
@@ -558,6 +562,8 @@
       * reclient_profiler_service - a string indicating service name for
         re-client's cloud profiler.
       * reclient_publish_trace - If True, it publish trace by rpl2cloudtrace.
+      * reclient_cache_silo - A string indicating a cache siling key to use for
+        remote caching.
       * kwargs - Additional keyword arguments to forward on to `luci.builder`.
     """
 
@@ -706,6 +712,7 @@
         rewrapper_env = reclient_rewrapper_env,
         profiler_service = reclient_profiler_service,
         publish_trace = reclient_publish_trace,
+        cache_silo = reclient_cache_silo,
     )
     if reclient != None:
         properties["$build/reclient"] = reclient
diff --git a/infra/config/lib/ci.star b/infra/config/lib/ci.star
index c15a78b..161f92b 100644
--- a/infra/config/lib/ci.star
+++ b/infra/config/lib/ci.star
@@ -420,6 +420,10 @@
         goma_backend = builders.goma.backend.RBE_PROD,
         **kwargs):
     kwargs.setdefault("os", os.LINUX_BIONIC_REMOVE)
+
+    # TODO(crbug.com/1135718): Promote out of experiment for all builders.
+    kwargs.setdefault("experiments", {})
+    kwargs["experiments"].setdefault("chromium.chromium_tests.use_rdb_results", 5)
     return ci.builder(
         name = name,
         builder_group = "chromium.fyi",
diff --git a/infra/config/subprojects/chromium/ci.star b/infra/config/subprojects/chromium/ci.star
index 5fa6669..e004fb6 100644
--- a/infra/config/subprojects/chromium/ci.star
+++ b/infra/config/subprojects/chromium/ci.star
@@ -3891,7 +3891,7 @@
     goma_jobs = 250,
     executable = "recipe:reclient_goma_comparison",
     execution_timeout = 6 * time.hour,
-    reclient_rewrapper_env = {"RBE_cache_silo": "Comparison Linux - cache siloed"},
+    reclient_cache_silo = "Comparison Linux - cache siloed",
     reclient_instance = rbe_instance.DEFAULT,
     reclient_jobs = 250,
     configure_kitchen = True,
diff --git a/ios/chrome/app/main_controller.mm b/ios/chrome/app/main_controller.mm
index a9aa9fa8..c185f1b8 100644
--- a/ios/chrome/app/main_controller.mm
+++ b/ios/chrome/app/main_controller.mm
@@ -21,6 +21,7 @@
 #include "components/component_updater/crl_set_remover.h"
 #include "components/component_updater/installer_policies/autofill_states_component_installer.h"
 #include "components/component_updater/installer_policies/on_device_head_suggest_component_installer.h"
+#import "components/component_updater/installer_policies/optimization_hints_component_installer.h"
 #include "components/component_updater/installer_policies/safety_tips_component_installer.h"
 #include "components/feature_engagement/public/event_constants.h"
 #include "components/feature_engagement/public/tracker.h"
@@ -200,6 +201,7 @@
   RegisterSafetyTipsComponent(cus);
   RegisterAutofillStatesComponent(cus,
                                   GetApplicationContext()->GetLocalState());
+  RegisterOptimizationHintsComponent(cus);
 }
 
 // The delay, in seconds, for cleaning external files.
diff --git a/ios/chrome/browser/metrics/ios_chrome_metrics_service_accessor.h b/ios/chrome/browser/metrics/ios_chrome_metrics_service_accessor.h
index 15c3d11..517ac73a 100644
--- a/ios/chrome/browser/metrics/ios_chrome_metrics_service_accessor.h
+++ b/ios/chrome/browser/metrics/ios_chrome_metrics_service_accessor.h
@@ -12,7 +12,7 @@
 #include "base/macros.h"
 #include "components/metrics/metrics_service_accessor.h"
 
-class IOSChromeDataReductionProxySettings;
+class OptimizationGuideService;
 
 namespace {
 class CrashesDOMHandler;
@@ -32,7 +32,7 @@
   friend class IOSChromeMetricsServicesManagerClient;
 
   friend class CrashesDOMHandler;
-  friend class IOSChromeDataReductionProxySettings;
+  friend class OptimizationGuideService;
   friend class IOSChromeMainParts;
 
   FRIEND_TEST_ALL_PREFIXES(IOSChromeMetricsServiceAccessorTest,
diff --git a/ios/chrome/browser/optimization_guide/BUILD.gn b/ios/chrome/browser/optimization_guide/BUILD.gn
index 26ffe14..6f7cfb3 100644
--- a/ios/chrome/browser/optimization_guide/BUILD.gn
+++ b/ios/chrome/browser/optimization_guide/BUILD.gn
@@ -4,10 +4,12 @@
 
 source_set("optimization_guide") {
   sources = [
-    "optimization_guide_service.cc",
+    "ios_chrome_hints_manager.h",
+    "ios_chrome_hints_manager.mm",
     "optimization_guide_service.h",
-    "optimization_guide_service_factory.cc",
+    "optimization_guide_service.mm",
     "optimization_guide_service_factory.h",
+    "optimization_guide_service_factory.mm",
     "tab_url_provider_impl.h",
     "tab_url_provider_impl.mm",
   ]
@@ -15,10 +17,13 @@
     "//base",
     "//components/keyed_service/core",
     "//components/keyed_service/ios",
+    "//components/leveldb_proto",
     "//components/optimization_guide/core",
+    "//components/optimization_guide/proto:optimization_guide_proto",
     "//ios/chrome/browser",
     "//ios/chrome/browser/browser_state",
     "//ios/chrome/browser/main:public",
+    "//ios/chrome/browser/metrics",
     "//ios/chrome/browser/web_state_list",
     "//ios/web",
   ]
@@ -30,18 +35,26 @@
   configs += [ "//build/config/compiler:enable_arc" ]
   testonly = true
   sources = [
-    "optimization_guide_service_factory_unittest.cc",
+    "optimization_guide_service_factory_unittest.mm",
+    "optimization_guide_service_unittest.mm",
     "tab_url_provider_impl_unittest.mm",
   ]
   deps = [
     ":optimization_guide",
     "//base/test:test_support",
     "//components/optimization_guide/core",
+    "//components/optimization_guide/core:test_support",
+    "//components/sync_preferences",
+    "//components/sync_preferences:test_support",
+    "//components/ukm:test_support",
+    "//components/unified_consent",
     "//ios/chrome/browser/browser_state:test_support",
     "//ios/chrome/browser/main:public",
     "//ios/chrome/browser/main:test_support",
+    "//ios/chrome/browser/prefs:browser_prefs",
     "//ios/chrome/browser/web_state_list",
     "//ios/web/public/test/fakes",
+    "//services/metrics/public/cpp:ukm_builders",
     "//testing/gtest",
   ]
 }
diff --git a/ios/chrome/browser/optimization_guide/DEPS b/ios/chrome/browser/optimization_guide/DEPS
new file mode 100644
index 0000000..05eea6c
--- /dev/null
+++ b/ios/chrome/browser/optimization_guide/DEPS
@@ -0,0 +1,5 @@
+include_rules = [
+  "+components/optimization_guide/core",
+  "+components/optimization_guide/proto",
+  "+ios/web/public",
+]
diff --git a/ios/chrome/browser/optimization_guide/ios_chrome_hints_manager.h b/ios/chrome/browser/optimization_guide/ios_chrome_hints_manager.h
new file mode 100644
index 0000000..da92908
--- /dev/null
+++ b/ios/chrome/browser/optimization_guide/ios_chrome_hints_manager.h
@@ -0,0 +1,35 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef IOS_CHROME_BROWSER_OPTIMIZATION_GUIDE_IOS_CHROME_HINTS_MANAGER_H_
+#define IOS_CHROME_BROWSER_OPTIMIZATION_GUIDE_IOS_CHROME_HINTS_MANAGER_H_
+
+#include "components/optimization_guide/core/hints_manager.h"
+
+namespace web {
+class BrowserState;
+}  // namespace web
+
+namespace optimization_guide {
+
+class IOSChromeHintsManager : public HintsManager {
+ public:
+  IOSChromeHintsManager(
+      web::BrowserState* browser_state,
+      PrefService* pref_service,
+      optimization_guide::OptimizationGuideStore* hint_store,
+      optimization_guide::TopHostProvider* top_host_provider,
+      optimization_guide::TabUrlProvider* tab_url_provider,
+      scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
+      network::NetworkConnectionTracker* network_connection_tracker);
+
+  ~IOSChromeHintsManager() override = default;
+
+  IOSChromeHintsManager(const IOSChromeHintsManager&) = delete;
+  IOSChromeHintsManager& operator=(const IOSChromeHintsManager&) = delete;
+};
+
+}  // namespace optimization_guide
+
+#endif  // IOS_CHROME_BROWSER_OPTIMIZATION_GUIDE_IOS_CHROME_HINTS_MANAGER_H_
diff --git a/ios/chrome/browser/optimization_guide/ios_chrome_hints_manager.mm b/ios/chrome/browser/optimization_guide/ios_chrome_hints_manager.mm
new file mode 100644
index 0000000..fcba3cb
--- /dev/null
+++ b/ios/chrome/browser/optimization_guide/ios_chrome_hints_manager.mm
@@ -0,0 +1,35 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ios/chrome/browser/optimization_guide/ios_chrome_hints_manager.h"
+
+#import "ios/chrome/browser/application_context.h"
+#import "ios/chrome/browser/browser_state/chrome_browser_state.h"
+#import "services/network/public/cpp/shared_url_loader_factory.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+namespace optimization_guide {
+
+IOSChromeHintsManager::IOSChromeHintsManager(
+    web::BrowserState* browser_state,
+    PrefService* pref_service,
+    optimization_guide::OptimizationGuideStore* hint_store,
+    optimization_guide::TopHostProvider* top_host_provider,
+    optimization_guide::TabUrlProvider* tab_url_provider,
+    scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
+    network::NetworkConnectionTracker* network_connection_tracker)
+    : HintsManager(browser_state->IsOffTheRecord(),
+                   GetApplicationContext()->GetApplicationLocale(),
+                   pref_service,
+                   hint_store,
+                   top_host_provider,
+                   tab_url_provider,
+                   url_loader_factory,
+                   network_connection_tracker,
+                   /*push_notification_manager=*/nullptr) {}
+
+}  // namespace optimization_guide
diff --git a/ios/chrome/browser/optimization_guide/optimization_guide_service.cc b/ios/chrome/browser/optimization_guide/optimization_guide_service.cc
deleted file mode 100644
index bd93a08d..0000000
--- a/ios/chrome/browser/optimization_guide/optimization_guide_service.cc
+++ /dev/null
@@ -1,8 +0,0 @@
-// Copyright 2021 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "ios/chrome/browser/optimization_guide/optimization_guide_service.h"
-
-OptimizationGuideService::OptimizationGuideService() = default;
-OptimizationGuideService::~OptimizationGuideService() = default;
diff --git a/ios/chrome/browser/optimization_guide/optimization_guide_service.h b/ios/chrome/browser/optimization_guide/optimization_guide_service.h
index 35ed2f3..b01854e 100644
--- a/ios/chrome/browser/optimization_guide/optimization_guide_service.h
+++ b/ios/chrome/browser/optimization_guide/optimization_guide_service.h
@@ -5,17 +5,101 @@
 #ifndef IOS_CHROME_BROWSER_OPTIMIZATION_GUIDE_OPTIMIZATION_GUIDE_SERVICE_H_
 #define IOS_CHROME_BROWSER_OPTIMIZATION_GUIDE_OPTIMIZATION_GUIDE_SERVICE_H_
 
+#include <vector>
+
+#include "base/callback_forward.h"
+#include "base/sequence_checker.h"
 #include "components/keyed_service/core/keyed_service.h"
+#include "components/optimization_guide/core/optimization_guide_decision.h"
+#include "components/optimization_guide/core/optimization_metadata.h"
+#include "components/optimization_guide/proto/hints.pb.h"
+#include "url/gurl.h"
+
+namespace optimization_guide {
+class TabUrlProvider;
+class TopHostProvider;
+class OptimizationGuideStore;
+class HintsManager;
+}  // namespace optimization_guide
+
+class OptimizationGuideNavigationData;
+
+namespace web {
+class BrowserState;
+class NavigationContext;
+}  // namespace web
 
 // A BrowserState keyed service that is used to own the underlying Optimization
-// Guide components.
+// Guide components. This is a rough copy of the OptimizationGuideKeyedService
+// in //chrome/browser that is used for non-iOS. It cannot be directly used due
+// to the platform differences of the common data structures -
+// NavigationContext vs NavigationHandle, BrowserState vs Profile, etc.
+// TODO(crbug.com/1240907): Add support for clearing the hints when browsing
+// data is cleared.
 class OptimizationGuideService : public KeyedService {
  public:
-  OptimizationGuideService();
+  explicit OptimizationGuideService(web::BrowserState* browser_state);
   ~OptimizationGuideService() override;
 
   OptimizationGuideService(const OptimizationGuideService&) = delete;
   OptimizationGuideService& operator=(const OptimizationGuideService&) = delete;
+
+  // Registers the optimization types that intend to be queried during the
+  // session. It is expected for this to be called right after the browser has
+  // been initialized.
+  void RegisterOptimizationTypes(
+      const std::vector<optimization_guide::proto::OptimizationType>&
+          optimization_types);
+
+  // Returns whether |optimization_type| can be applied for |url|. This should
+  // only be called for main frame navigations or future main frame navigations.
+  optimization_guide::OptimizationGuideDecision CanApplyOptimization(
+      const GURL& url,
+      optimization_guide::proto::OptimizationType optimization_type,
+      optimization_guide::OptimizationMetadata* optimization_metadata);
+
+  // Invokes |callback| with the decision for the URL contained in
+  // |navigation_context| and |optimization_type|, when sufficient information
+  // has been collected to make the decision. This should only be called for
+  // main frame navigations.
+  void CanApplyOptimizationAsync(
+      web::NavigationContext* navigation_context,
+      optimization_guide::proto::OptimizationType optimization_type,
+      optimization_guide::OptimizationGuideDecisionCallback callback);
+
+ private:
+  friend class OptimizationGuideServiceTest;
+
+  // Notifies |hints_manager_| that the navigation associated with
+  // |navigation_data| has started or redirected.
+  void OnNavigationStartOrRedirect(
+      OptimizationGuideNavigationData* navigation_data);
+
+  // Notifies |hints_manager_| that the navigation associated with
+  // |navigation_redirect_chain| has finished.
+  void OnNavigationFinish(const std::vector<GURL>& navigation_redirect_chain);
+
+  // KeyedService implementation:
+  void Shutdown() override;
+
+  optimization_guide::HintsManager* GetHintsManager();
+
+  // The store of hints.
+  std::unique_ptr<optimization_guide::OptimizationGuideStore> hint_store_;
+
+  // Manages the storing, loading, and fetching of hints.
+  std::unique_ptr<optimization_guide::HintsManager> hints_manager_;
+
+  // The top host provider to use for fetching information for the user's top
+  // hosts. Will be null if the user has not consented to this type of browser
+  // behavior.
+  std::unique_ptr<optimization_guide::TopHostProvider> top_host_provider_;
+
+  // The tab URL provider to use for fetching information for the user's active
+  // tabs. Will be null if the user is off the record.
+  std::unique_ptr<optimization_guide::TabUrlProvider> tab_url_provider_;
+
+  SEQUENCE_CHECKER(sequence_checker_);
 };
 
 #endif  // IOS_CHROME_BROWSER_OPTIMIZATION_GUIDE_OPTIMIZATION_GUIDE_SERVICE_H_
diff --git a/ios/chrome/browser/optimization_guide/optimization_guide_service.mm b/ios/chrome/browser/optimization_guide/optimization_guide_service.mm
new file mode 100644
index 0000000..360ce4d3
--- /dev/null
+++ b/ios/chrome/browser/optimization_guide/optimization_guide_service.mm
@@ -0,0 +1,158 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ios/chrome/browser/optimization_guide/optimization_guide_service.h"
+
+#import "base/callback.h"
+#import "base/metrics/histogram_functions.h"
+#import "base/task/thread_pool.h"
+#import "base/time/default_clock.h"
+#import "components/optimization_guide/core/command_line_top_host_provider.h"
+#import "components/optimization_guide/core/hints_processing_util.h"
+#import "components/optimization_guide/core/optimization_guide_constants.h"
+#import "components/optimization_guide/core/optimization_guide_features.h"
+#import "components/optimization_guide/core/optimization_guide_navigation_data.h"
+#import "components/optimization_guide/core/optimization_guide_permissions_util.h"
+#import "components/optimization_guide/core/optimization_guide_store.h"
+#import "components/optimization_guide/core/optimization_guide_util.h"
+#import "components/optimization_guide/core/top_host_provider.h"
+#import "ios/chrome/browser/application_context.h"
+#import "ios/chrome/browser/browser_state/chrome_browser_state.h"
+#import "ios/chrome/browser/metrics/ios_chrome_metrics_service_accessor.h"
+#import "ios/chrome/browser/optimization_guide/ios_chrome_hints_manager.h"
+#import "ios/chrome/browser/optimization_guide/optimization_guide_service_factory.h"
+#import "ios/chrome/browser/optimization_guide/tab_url_provider_impl.h"
+#import "ios/web/public/navigation/navigation_context.h"
+#import "services/network/public/cpp/shared_url_loader_factory.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+OptimizationGuideService::OptimizationGuideService(
+    web::BrowserState* browser_state) {
+  DCHECK(optimization_guide::features::IsOptimizationHintsEnabled());
+
+  ChromeBrowserState* chrome_browser_state =
+      ChromeBrowserState::FromBrowserState(browser_state);
+  DCHECK(chrome_browser_state);
+
+  // TODO(crbug.com/1239388): Handle incognito profile in IOS, the same way its
+  // handled in other platforms.
+  DCHECK(!browser_state->IsOffTheRecord());
+
+  // Regardless of whether the profile is off the record or not, initialize the
+  // Optimization Guide with the database associated with the original profile.
+  auto* proto_db_provider =
+      chrome_browser_state->GetOriginalChromeBrowserState()
+          ->GetProtoDatabaseProvider();
+  base::FilePath profile_path =
+      chrome_browser_state->GetOriginalChromeBrowserState()->GetStatePath();
+
+  // Only create a top host provider from the command line if provided.
+  top_host_provider_ =
+      optimization_guide::CommandLineTopHostProvider::CreateIfEnabled();
+  tab_url_provider_ = std::make_unique<TabUrlProviderImpl>(
+      chrome_browser_state, base::DefaultClock::GetInstance());
+
+  hint_store_ =
+      optimization_guide::features::ShouldPersistHintsToDisk()
+          ? std::make_unique<optimization_guide::OptimizationGuideStore>(
+                proto_db_provider,
+                profile_path.Append(
+                    optimization_guide::kOptimizationGuideHintStore),
+                base::ThreadPool::CreateSequencedTaskRunner(
+                    {base::MayBlock(), base::TaskPriority::BEST_EFFORT}))
+          : nullptr;
+
+  hints_manager_ = std::make_unique<optimization_guide::IOSChromeHintsManager>(
+      browser_state, chrome_browser_state->GetPrefs(), hint_store_.get(),
+      top_host_provider_.get(), tab_url_provider_.get(),
+      browser_state->GetSharedURLLoaderFactory(),
+      GetApplicationContext()->GetNetworkConnectionTracker());
+
+  bool optimization_guide_fetching_enabled =
+      optimization_guide::IsUserPermittedToFetchFromRemoteOptimizationGuide(
+          browser_state->IsOffTheRecord(), chrome_browser_state->GetPrefs());
+  base::UmaHistogramBoolean("OptimizationGuide.RemoteFetchingEnabled",
+                            optimization_guide_fetching_enabled);
+  IOSChromeMetricsServiceAccessor::RegisterSyntheticFieldTrial(
+      "SyntheticOptimizationGuideRemoteFetching",
+      optimization_guide_fetching_enabled ? "Enabled" : "Disabled");
+}
+
+OptimizationGuideService::~OptimizationGuideService() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+}
+
+optimization_guide::HintsManager* OptimizationGuideService::GetHintsManager() {
+  return hints_manager_.get();
+}
+
+void OptimizationGuideService::OnNavigationStartOrRedirect(
+    OptimizationGuideNavigationData* navigation_data) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  DCHECK(navigation_data);
+
+  base::flat_set<optimization_guide::proto::OptimizationType>
+      registered_optimization_types =
+          hints_manager_->registered_optimization_types();
+  if (!registered_optimization_types.empty()) {
+    hints_manager_->OnNavigationStartOrRedirect(navigation_data,
+                                                base::DoNothing());
+  }
+
+  navigation_data->set_registered_optimization_types(
+      hints_manager_->registered_optimization_types());
+}
+
+void OptimizationGuideService::OnNavigationFinish(
+    const std::vector<GURL>& navigation_redirect_chain) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+  hints_manager_->OnNavigationFinish(navigation_redirect_chain);
+}
+
+void OptimizationGuideService::RegisterOptimizationTypes(
+    const std::vector<optimization_guide::proto::OptimizationType>&
+        optimization_types) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  hints_manager_->RegisterOptimizationTypes(optimization_types);
+}
+
+optimization_guide::OptimizationGuideDecision
+OptimizationGuideService::CanApplyOptimization(
+    const GURL& url,
+    optimization_guide::proto::OptimizationType optimization_type,
+    optimization_guide::OptimizationMetadata* optimization_metadata) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  optimization_guide::OptimizationTypeDecision optimization_type_decision =
+      hints_manager_->CanApplyOptimization(url, /*navigation_id=*/absl::nullopt,
+                                           optimization_type,
+                                           optimization_metadata);
+  base::UmaHistogramEnumeration(
+      "OptimizationGuide.ApplyDecision." +
+          optimization_guide::GetStringNameForOptimizationType(
+              optimization_type),
+      optimization_type_decision);
+  return optimization_guide::HintsManager::
+      GetOptimizationGuideDecisionFromOptimizationTypeDecision(
+          optimization_type_decision);
+}
+
+void OptimizationGuideService::CanApplyOptimizationAsync(
+    web::NavigationContext* navigation_context,
+    optimization_guide::proto::OptimizationType optimization_type,
+    optimization_guide::OptimizationGuideDecisionCallback callback) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+  hints_manager_->CanApplyOptimizationAsync(
+      navigation_context->GetUrl(), navigation_context->GetNavigationId(),
+      optimization_type, std::move(callback));
+}
+
+void OptimizationGuideService::Shutdown() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  hints_manager_->Shutdown();
+}
diff --git a/ios/chrome/browser/optimization_guide/optimization_guide_service_factory.cc b/ios/chrome/browser/optimization_guide/optimization_guide_service_factory.cc
deleted file mode 100644
index 0cd5f537..0000000
--- a/ios/chrome/browser/optimization_guide/optimization_guide_service_factory.cc
+++ /dev/null
@@ -1,39 +0,0 @@
-// Copyright 2021 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "ios/chrome/browser/optimization_guide/optimization_guide_service_factory.h"
-
-#include "components/keyed_service/ios/browser_state_dependency_manager.h"
-#include "ios/chrome/browser/browser_state/chrome_browser_state.h"
-#include "ios/chrome/browser/main/browser_list_factory.h"
-#include "ios/chrome/browser/optimization_guide/optimization_guide_service.h"
-
-// static
-OptimizationGuideService* OptimizationGuideServiceFactory::GetForBrowserState(
-    ChromeBrowserState* context) {
-  return static_cast<OptimizationGuideService*>(
-      GetInstance()->GetServiceForBrowserState(context, /*create=*/true));
-}
-
-// static
-OptimizationGuideServiceFactory*
-OptimizationGuideServiceFactory::GetInstance() {
-  static base::NoDestructor<OptimizationGuideServiceFactory> instance;
-  return instance.get();
-}
-
-OptimizationGuideServiceFactory::OptimizationGuideServiceFactory()
-    : BrowserStateKeyedServiceFactory(
-          "OptimizationGuideService",
-          BrowserStateDependencyManager::GetInstance()) {
-  DependsOn(BrowserListFactory::GetInstance());
-}
-
-OptimizationGuideServiceFactory::~OptimizationGuideServiceFactory() = default;
-
-std::unique_ptr<KeyedService>
-OptimizationGuideServiceFactory::BuildServiceInstanceFor(
-    web::BrowserState* context) const {
-  return std::make_unique<OptimizationGuideService>();
-}
diff --git a/ios/chrome/browser/optimization_guide/optimization_guide_service_factory.h b/ios/chrome/browser/optimization_guide/optimization_guide_service_factory.h
index 6ba3d36..5cf2fc95 100644
--- a/ios/chrome/browser/optimization_guide/optimization_guide_service_factory.h
+++ b/ios/chrome/browser/optimization_guide/optimization_guide_service_factory.h
@@ -37,6 +37,7 @@
   // BrowserStateKeyedServiceFactory:
   std::unique_ptr<KeyedService> BuildServiceInstanceFor(
       web::BrowserState* context) const override;
+  bool ServiceIsCreatedWithBrowserState() const override;
 };
 
 #endif  // IOS_CHROME_BROWSER_OPTIMIZATION_GUIDE_OPTIMIZATION_GUIDE_SERVICE_FACTORY_H_
diff --git a/ios/chrome/browser/optimization_guide/optimization_guide_service_factory.mm b/ios/chrome/browser/optimization_guide/optimization_guide_service_factory.mm
new file mode 100644
index 0000000..f2d0ab2
--- /dev/null
+++ b/ios/chrome/browser/optimization_guide/optimization_guide_service_factory.mm
@@ -0,0 +1,51 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ios/chrome/browser/optimization_guide/optimization_guide_service_factory.h"
+
+#import "base/feature_list.h"
+#import "components/keyed_service/ios/browser_state_dependency_manager.h"
+#import "components/optimization_guide/core/optimization_guide_features.h"
+#import "ios/chrome/browser/browser_state/chrome_browser_state.h"
+#import "ios/chrome/browser/main/browser_list_factory.h"
+#import "ios/chrome/browser/optimization_guide/optimization_guide_service.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+// static
+OptimizationGuideService* OptimizationGuideServiceFactory::GetForBrowserState(
+    ChromeBrowserState* context) {
+  if (!optimization_guide::features::IsOptimizationHintsEnabled())
+    return nullptr;
+  return static_cast<OptimizationGuideService*>(
+      GetInstance()->GetServiceForBrowserState(context, /*create=*/true));
+}
+
+// static
+OptimizationGuideServiceFactory*
+OptimizationGuideServiceFactory::GetInstance() {
+  static base::NoDestructor<OptimizationGuideServiceFactory> instance;
+  return instance.get();
+}
+
+OptimizationGuideServiceFactory::OptimizationGuideServiceFactory()
+    : BrowserStateKeyedServiceFactory(
+          "OptimizationGuideService",
+          BrowserStateDependencyManager::GetInstance()) {
+  DependsOn(BrowserListFactory::GetInstance());
+}
+
+OptimizationGuideServiceFactory::~OptimizationGuideServiceFactory() = default;
+
+std::unique_ptr<KeyedService>
+OptimizationGuideServiceFactory::BuildServiceInstanceFor(
+    web::BrowserState* context) const {
+  return std::make_unique<OptimizationGuideService>(context);
+}
+
+bool OptimizationGuideServiceFactory::ServiceIsCreatedWithBrowserState() const {
+  return optimization_guide::features::IsOptimizationHintsEnabled();
+}
diff --git a/ios/chrome/browser/optimization_guide/optimization_guide_service_factory_unittest.cc b/ios/chrome/browser/optimization_guide/optimization_guide_service_factory_unittest.cc
deleted file mode 100644
index 09834d5..0000000
--- a/ios/chrome/browser/optimization_guide/optimization_guide_service_factory_unittest.cc
+++ /dev/null
@@ -1,39 +0,0 @@
-// Copyright 2021 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "ios/chrome/browser/optimization_guide/optimization_guide_service_factory.h"
-
-#include "base/test/task_environment.h"
-#include "ios/chrome/browser/browser_state/test_chrome_browser_state.h"
-#include "ios/chrome/browser/optimization_guide/optimization_guide_service.h"
-#include "testing/gtest/include/gtest/gtest.h"
-#include "testing/platform_test.h"
-
-namespace {
-
-class OptimizationGuideServiceFactoryTest : public PlatformTest {
- public:
-  OptimizationGuideServiceFactoryTest() {
-    TestChromeBrowserState::Builder builder;
-    browser_state_ = builder.Build();
-  }
-
-  ChromeBrowserState* browser_state() { return browser_state_.get(); }
-
- private:
-  base::test::TaskEnvironment task_environment_;
-  std::unique_ptr<ChromeBrowserState> browser_state_;
-};
-
-TEST_F(OptimizationGuideServiceFactoryTest, CheckNormalServiceNotNull) {
-  EXPECT_NE(nullptr, OptimizationGuideServiceFactory::GetForBrowserState(
-                         browser_state()));
-}
-
-TEST_F(OptimizationGuideServiceFactoryTest, CheckIncogitoServiceNull) {
-  EXPECT_EQ(nullptr, OptimizationGuideServiceFactory::GetForBrowserState(
-                         browser_state()->GetOffTheRecordChromeBrowserState()));
-}
-
-}  // namespace
diff --git a/ios/chrome/browser/optimization_guide/optimization_guide_service_factory_unittest.mm b/ios/chrome/browser/optimization_guide/optimization_guide_service_factory_unittest.mm
new file mode 100644
index 0000000..9897f2c2
--- /dev/null
+++ b/ios/chrome/browser/optimization_guide/optimization_guide_service_factory_unittest.mm
@@ -0,0 +1,75 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ios/chrome/browser/optimization_guide/optimization_guide_service_factory.h"
+
+#include "base/test/scoped_feature_list.h"
+#include "base/test/task_environment.h"
+#import "components/optimization_guide/core/optimization_guide_features.h"
+#include "ios/chrome/browser/browser_state/test_chrome_browser_state.h"
+#include "ios/chrome/browser/optimization_guide/optimization_guide_service.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "testing/platform_test.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+namespace {
+
+class OptimizationGuideServiceFactoryTest : public PlatformTest {
+ public:
+  OptimizationGuideServiceFactoryTest() = default;
+  ~OptimizationGuideServiceFactoryTest() override = default;
+
+  void SetUp() override {
+    PlatformTest::SetUp();
+    scoped_feature_list_.InitWithFeatures(
+        {optimization_guide::features::kOptimizationHints}, {});
+    TestChromeBrowserState::Builder builder;
+    browser_state_ = builder.Build();
+  }
+
+ protected:
+  base::test::TaskEnvironment task_environment_;
+  std::unique_ptr<ChromeBrowserState> browser_state_;
+  base::test::ScopedFeatureList scoped_feature_list_;
+};
+
+TEST_F(OptimizationGuideServiceFactoryTest, CheckNormalServiceNotNull) {
+  EXPECT_NE(nullptr, OptimizationGuideServiceFactory::GetForBrowserState(
+                         browser_state_.get()));
+}
+
+TEST_F(OptimizationGuideServiceFactoryTest, CheckIncogitoServiceNull) {
+  EXPECT_EQ(nullptr, OptimizationGuideServiceFactory::GetForBrowserState(
+                         browser_state_->GetOffTheRecordChromeBrowserState()));
+}
+
+class OptimizationGuideServiceFactoryFeatureDisabledTest : public PlatformTest {
+ public:
+  OptimizationGuideServiceFactoryFeatureDisabledTest() = default;
+  ~OptimizationGuideServiceFactoryFeatureDisabledTest() override = default;
+
+  void SetUp() override {
+    PlatformTest::SetUp();
+    scoped_feature_list_.InitAndDisableFeature(
+        {optimization_guide::features::kOptimizationHints});
+    TestChromeBrowserState::Builder builder;
+    browser_state_ = builder.Build();
+  }
+
+ protected:
+  base::test::TaskEnvironment task_environment_;
+  std::unique_ptr<ChromeBrowserState> browser_state_;
+  base::test::ScopedFeatureList scoped_feature_list_;
+};
+
+TEST_F(OptimizationGuideServiceFactoryFeatureDisabledTest,
+       CheckServiceNullWithoutOptimizationGuideHintsFeature) {
+  EXPECT_EQ(nullptr, OptimizationGuideServiceFactory::GetForBrowserState(
+                         browser_state_.get()));
+}
+
+}  // namespace
diff --git a/ios/chrome/browser/optimization_guide/optimization_guide_service_unittest.mm b/ios/chrome/browser/optimization_guide/optimization_guide_service_unittest.mm
new file mode 100644
index 0000000..29ce0947
--- /dev/null
+++ b/ios/chrome/browser/optimization_guide/optimization_guide_service_unittest.mm
@@ -0,0 +1,512 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ios/chrome/browser/optimization_guide/optimization_guide_service.h"
+
+#import "base/test/ios/wait_util.h"
+#import "base/test/metrics/histogram_tester.h"
+#import "base/test/scoped_feature_list.h"
+#import "base/test/task_environment.h"
+#import "components/optimization_guide/core/hints_component_util.h"
+#import "components/optimization_guide/core/optimization_guide_features.h"
+#import "components/optimization_guide/core/optimization_guide_navigation_data.h"
+#import "components/optimization_guide/core/optimization_guide_switches.h"
+#import "components/optimization_guide/core/optimization_guide_test_util.h"
+#import "components/optimization_guide/core/optimization_hints_component_update_listener.h"
+#import "components/optimization_guide/core/test_hints_component_creator.h"
+#import "components/sync_preferences/pref_service_syncable.h"
+#import "components/sync_preferences/testing_pref_service_syncable.h"
+#import "components/ukm/test_ukm_recorder.h"
+#import "components/unified_consent/pref_names.h"
+#import "components/unified_consent/unified_consent_service.h"
+#import "ios/chrome/browser/browser_state/test_chrome_browser_state.h"
+#import "ios/chrome/browser/optimization_guide/ios_chrome_hints_manager.h"
+#import "ios/chrome/browser/optimization_guide/optimization_guide_service_factory.h"
+#import "ios/chrome/browser/prefs/browser_prefs.h"
+#import "ios/web/public/navigation/navigation_manager.h"
+#import "ios/web/public/test/fakes/fake_navigation_context.h"
+#import "services/metrics/public/cpp/ukm_builders.h"
+#import "services/metrics/public/cpp/ukm_source.h"
+#import "testing/gtest/include/gtest/gtest.h"
+#import "testing/platform_test.h"
+#import "url/gurl.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+// These tests are roughly similarly to the tests in
+// optimization_guide_keyed_service_browsertest.cc
+
+namespace {
+
+constexpr char kHintsURL[] = "https://hints.com/with_hints.html";
+constexpr char kNoHintsURL[] = "https://nohints.com/no_hints.html";
+constexpr char kRedirectURL[] = "https://hints.com/redirect.html";
+
+// Wraps the NavigationContext and OptimizationGuideNavigationData together for
+// tests.
+class NavigationContextAndData {
+ public:
+  explicit NavigationContextAndData(const std::string& url) {
+    navigation_context_ = std::make_unique<web::FakeNavigationContext>();
+    navigation_context_->SetUrl(GURL(url));
+    navigation_data_ = std::make_unique<OptimizationGuideNavigationData>(
+        navigation_context_->GetNavigationId(),
+        /*navigation_start=*/base::TimeTicks::Now());
+    navigation_data_->set_navigation_url(navigation_context_->GetUrl());
+  }
+
+  std::unique_ptr<web::FakeNavigationContext> navigation_context_;
+  std::unique_ptr<OptimizationGuideNavigationData> navigation_data_;
+};
+
+void RetryForHistogramUntilCountReached(
+    const base::HistogramTester* histogram_tester,
+    const std::string& histogram_name,
+    int count) {
+  EXPECT_TRUE(base::test::ios::WaitUntilConditionOrTimeout(
+      base::test::ios::kWaitForPageLoadTimeout, ^{
+        base::RunLoop().RunUntilIdle();
+        int total = 0;
+        for (const auto& bucket :
+             histogram_tester->GetAllSamples(histogram_name)) {
+          total += bucket.count;
+        }
+        return total >= count;
+      }));
+}
+
+}  // namespace
+
+class OptimizationGuideServiceTest : public PlatformTest {
+ public:
+  OptimizationGuideServiceTest() {
+    base::CommandLine::ForCurrentProcess()->AppendSwitch(
+        optimization_guide::switches::kPurgeHintsStore);
+
+    // The tests are run in the same process and share the same
+    // OptimizationHintsComponentUpdateListener due to the global object usage
+    // in GetInstance(). So reset the state for each test.
+    optimization_guide::OptimizationHintsComponentUpdateListener::GetInstance()
+        ->ResetStateForTesting();
+  }
+
+  ~OptimizationGuideServiceTest() override = default;
+
+  void SetUp() override {
+    PlatformTest::SetUp();
+    auto testing_prefs =
+        std::make_unique<sync_preferences::TestingPrefServiceSyncable>();
+    RegisterBrowserStatePrefs(testing_prefs->registry());
+
+    std::vector<base::Feature> enabled_features;
+    enabled_features.push_back(
+        optimization_guide::features::kOptimizationHints);
+    if (url_keyed_anonymized_data_collection_enabled_) {
+      enabled_features.push_back(
+          optimization_guide::features::
+              kRemoteOptimizationGuideFetchingAnonymousDataConsent);
+      testing_prefs->SetBoolean(
+          unified_consent::prefs::kUrlKeyedAnonymizedDataCollectionEnabled,
+          true);
+    }
+    scoped_feature_list_.InitWithFeatures(enabled_features, {});
+
+    TestChromeBrowserState::Builder builder;
+    builder.SetPrefService(std::move(testing_prefs));
+    browser_state_ = builder.Build();
+    optimization_guide_service_ =
+        OptimizationGuideServiceFactory::GetForBrowserState(
+            browser_state_.get());
+  }
+
+  void PushHintsComponentAndWaitForCompletion() {
+    base::RunLoop run_loop;
+    optimization_guide_service()
+        ->GetHintsManager()
+        ->ListenForNextUpdateForTesting(run_loop.QuitClosure());
+    GURL hints_url(kHintsURL);
+
+    const optimization_guide::HintsComponentInfo& component_info =
+        test_hints_component_creator_.CreateHintsComponentInfoWithPageHints(
+            optimization_guide::proto::NOSCRIPT, {hints_url.host()},
+            hints_url.path().substr(1));
+
+    optimization_guide::OptimizationHintsComponentUpdateListener::GetInstance()
+        ->MaybeUpdateHintsComponent(component_info);
+
+    run_loop.Run();
+    RunUntilIdle();
+  }
+
+  void SimulateNavigation(
+      NavigationContextAndData* context_and_data,
+      const absl::optional<GURL> redirect_url = absl::nullopt) {
+    std::vector<GURL> navigation_redirect_chain;
+    navigation_redirect_chain.push_back(
+        context_and_data->navigation_context_->GetUrl());
+
+    optimization_guide_service_->OnNavigationStartOrRedirect(
+        context_and_data->navigation_data_.get());
+    RunUntilIdle();
+
+    if (redirect_url) {
+      context_and_data->navigation_data_->set_navigation_url(*redirect_url);
+      optimization_guide_service_->OnNavigationStartOrRedirect(
+          context_and_data->navigation_data_.get());
+      navigation_redirect_chain.push_back(*redirect_url);
+      RunUntilIdle();
+    }
+
+    optimization_guide_service_->OnNavigationFinish(navigation_redirect_chain);
+    RunUntilIdle();
+  }
+
+  void RegisterWithKeyedService() {
+    optimization_guide_service()->RegisterOptimizationTypes(
+        {optimization_guide::proto::NOSCRIPT});
+  }
+
+  // Calls the |CanApplyOptimizationAsync| and expects |expected_decision| when
+  // the decision is returned. |on_decision_callback| is called when the
+  // decision is called.
+  void VerifyCanApplyOptimizationAsyncDecision(
+      NavigationContextAndData* context_and_data,
+      base::OnceClosure on_decision_callback,
+      optimization_guide::OptimizationGuideDecision expected_decision) {
+    optimization_guide_service()->CanApplyOptimizationAsync(
+        context_and_data->navigation_context_.get(),
+        optimization_guide::proto::NOSCRIPT,
+        base::BindOnce(
+            [](base::OnceClosure on_decision_callback,
+               optimization_guide::OptimizationGuideDecision expected_decision,
+               optimization_guide::OptimizationGuideDecision decision,
+               const optimization_guide::OptimizationMetadata& metadata) {
+              EXPECT_EQ(expected_decision, decision);
+              std::move(on_decision_callback).Run();
+            },
+            std::move(on_decision_callback), expected_decision));
+  }
+
+  void SetUrlKeyedAnonymizedDataCollectionEnabled(bool enabled) {
+    url_keyed_anonymized_data_collection_enabled_ = enabled;
+  }
+
+  void RunUntilIdle() { base::RunLoop().RunUntilIdle(); }
+
+  OptimizationGuideService* optimization_guide_service() {
+    return optimization_guide_service_;
+  }
+
+  base::HistogramTester* histogram_tester() { return &histogram_tester_; }
+
+  TestChromeBrowserState* browser_state() { return browser_state_.get(); }
+
+ protected:
+  base::test::TaskEnvironment task_environment_;
+  base::HistogramTester histogram_tester_;
+  std::unique_ptr<TestChromeBrowserState> browser_state_;
+  OptimizationGuideService* optimization_guide_service_;
+  base::test::ScopedFeatureList scoped_feature_list_;
+  optimization_guide::testing::TestHintsComponentCreator
+      test_hints_component_creator_;
+  bool url_keyed_anonymized_data_collection_enabled_ = false;
+};
+
+TEST_F(OptimizationGuideServiceTest, RemoteFetchingDisabled) {
+  histogram_tester()->ExpectUniqueSample(
+      "OptimizationGuide.RemoteFetchingEnabled", false, 1);
+  // TODO(crbug.com/1240912): Verify the optimization guide fetching synthetic
+  // field trial is recorded.
+}
+
+TEST_F(OptimizationGuideServiceTest,
+       NavigateToPageWithHintsButNoRegistrationDoesNotAttemptToLoadHint) {
+  ukm::TestAutoSetUkmRecorder ukm_recorder;
+
+  PushHintsComponentAndWaitForCompletion();
+
+  NavigationContextAndData context_and_data(kHintsURL);
+  SimulateNavigation(&context_and_data);
+
+  histogram_tester()->ExpectTotalCount("OptimizationGuide.LoadedHint.Result",
+                                       0);
+
+  // Navigate away so UKM get recorded.
+  context_and_data = NavigationContextAndData(kHintsURL);
+  SimulateNavigation(&context_and_data);
+
+  auto entries = ukm_recorder.GetEntriesByName(
+      ukm::builders::OptimizationGuide::kEntryName);
+  EXPECT_EQ(0u, entries.size());
+}
+
+TEST_F(OptimizationGuideServiceTest,
+       NavigateToPageWithAsyncCallbackReturnsAnswerRedirect) {
+  PushHintsComponentAndWaitForCompletion();
+  RegisterWithKeyedService();
+
+  auto run_loop = std::make_unique<base::RunLoop>();
+  NavigationContextAndData context_and_data(kRedirectURL);
+
+  VerifyCanApplyOptimizationAsyncDecision(
+      &context_and_data, run_loop->QuitClosure(),
+      optimization_guide::OptimizationGuideDecision::kFalse);
+
+  SimulateNavigation(&context_and_data,
+                     /*redirect_url=*/GURL(kNoHintsURL));
+  run_loop->Run();
+}
+
+TEST_F(OptimizationGuideServiceTest,
+       NavigateToPageWithAsyncCallbackReturnsAnswer) {
+  PushHintsComponentAndWaitForCompletion();
+  RegisterWithKeyedService();
+
+  auto run_loop = std::make_unique<base::RunLoop>();
+  NavigationContextAndData context_and_data(kHintsURL);
+
+  VerifyCanApplyOptimizationAsyncDecision(
+      &context_and_data, run_loop->QuitClosure(),
+      optimization_guide::OptimizationGuideDecision::kTrue);
+
+  SimulateNavigation(&context_and_data);
+  run_loop->Run();
+}
+
+TEST_F(OptimizationGuideServiceTest,
+       NavigateToPageWithAsyncCallbackReturnsAnswerEventually) {
+  PushHintsComponentAndWaitForCompletion();
+  RegisterWithKeyedService();
+
+  auto run_loop = std::make_unique<base::RunLoop>();
+  NavigationContextAndData context_and_data(kNoHintsURL);
+
+  VerifyCanApplyOptimizationAsyncDecision(
+      &context_and_data, run_loop->QuitClosure(),
+      optimization_guide::OptimizationGuideDecision::kFalse);
+
+  SimulateNavigation(&context_and_data);
+  run_loop->Run();
+}
+
+TEST_F(OptimizationGuideServiceTest, NavigateToPageWithHintsLoadsHint) {
+  PushHintsComponentAndWaitForCompletion();
+  RegisterWithKeyedService();
+
+  ukm::TestAutoSetUkmRecorder ukm_recorder;
+  base::HistogramTester histogram_tester;
+
+  NavigationContextAndData context_and_data(kHintsURL);
+  SimulateNavigation(&context_and_data);
+
+  auto decision = optimization_guide_service()->CanApplyOptimization(
+      GURL(kHintsURL), optimization_guide::proto::NOSCRIPT,
+      /*optimization_metadata=*/nullptr);
+  RetryForHistogramUntilCountReached(&histogram_tester,
+                                     "OptimizationGuide.LoadedHint.Result", 1);
+
+  // There is a hint that matches this URL, so there should be an attempt to
+  // load a hint that succeeds.
+  histogram_tester.ExpectUniqueSample("OptimizationGuide.LoadedHint.Result",
+                                      true, 1);
+  // We had a hint and it was loaded.
+  EXPECT_EQ(optimization_guide::OptimizationGuideDecision::kTrue, decision);
+
+  // Navigate away so UKM get recorded.
+  context_and_data = NavigationContextAndData(kHintsURL);
+  SimulateNavigation(&context_and_data);
+
+  // Expect that UKM is recorded.
+  auto entries = ukm_recorder.GetEntriesByName(
+      ukm::builders::OptimizationGuide::kEntryName);
+  ASSERT_EQ(1u, entries.size());
+  auto* entry = entries[0];
+  EXPECT_TRUE(ukm_recorder.EntryHasMetric(
+      entry,
+      ukm::builders::OptimizationGuide::kRegisteredOptimizationTypesName));
+  // NOSCRIPT = 1, so bit mask is 10, which equals 2.
+  ukm_recorder.ExpectEntryMetric(
+      entry, ukm::builders::OptimizationGuide::kRegisteredOptimizationTypesName,
+      2);
+}
+
+TEST_F(OptimizationGuideServiceTest,
+       RecordsMetricsWhenNavigationDataDestroyed) {
+  PushHintsComponentAndWaitForCompletion();
+  RegisterWithKeyedService();
+
+  ukm::TestAutoSetUkmRecorder ukm_recorder;
+  base::HistogramTester histogram_tester;
+
+  auto context_and_data = std::make_unique<NavigationContextAndData>(kHintsURL);
+  SimulateNavigation(context_and_data.get());
+  auto decision = optimization_guide_service()->CanApplyOptimization(
+      GURL(kHintsURL), optimization_guide::proto::NOSCRIPT,
+      /*optimization_metadata=*/nullptr);
+
+  RetryForHistogramUntilCountReached(&histogram_tester,
+                                     "OptimizationGuide.LoadedHint.Result", 1);
+
+  // There is a hint that matches this URL, so there should be an attempt to
+  // load a hint that succeeds.
+  histogram_tester.ExpectUniqueSample("OptimizationGuide.LoadedHint.Result",
+                                      true, 1);
+  // We had a hint and it was loaded.
+  EXPECT_EQ(optimization_guide::OptimizationGuideDecision::kTrue, decision);
+
+  // Make sure metrics get recorded when navigation data is destroyed.
+  context_and_data.reset();
+  RunUntilIdle();
+
+  // Expect that the optimization guide UKM is recorded.
+  auto entries = ukm_recorder.GetEntriesByName(
+      ukm::builders::OptimizationGuide::kEntryName);
+  EXPECT_EQ(1u, entries.size());
+  auto* entry = entries[0];
+  EXPECT_TRUE(ukm_recorder.EntryHasMetric(
+      entry,
+      ukm::builders::OptimizationGuide::kRegisteredOptimizationTypesName));
+  // NOSCRIPT = 1, so bit mask is 10, which equals 2.
+  ukm_recorder.ExpectEntryMetric(
+      entry, ukm::builders::OptimizationGuide::kRegisteredOptimizationTypesName,
+      2);
+}
+
+TEST_F(OptimizationGuideServiceTest,
+       NavigateToPageThatRedirectsToUrlWithHintsShouldAttemptTwoLoads) {
+  PushHintsComponentAndWaitForCompletion();
+  RegisterWithKeyedService();
+
+  base::HistogramTester histogram_tester;
+
+  NavigationContextAndData context_and_data(kRedirectURL);
+  SimulateNavigation(&context_and_data,
+                     /*redirect_url=*/GURL(kHintsURL));
+  auto decision = optimization_guide_service()->CanApplyOptimization(
+      GURL(kHintsURL), optimization_guide::proto::NOSCRIPT,
+      /*optimization_metadata=*/nullptr);
+
+  RetryForHistogramUntilCountReached(&histogram_tester,
+                                     "OptimizationGuide.LoadedHint.Result", 2);
+
+  // Should attempt and succeed to load a hint once for the initial navigation
+  // and redirect.
+  histogram_tester.ExpectBucketCount("OptimizationGuide.LoadedHint.Result",
+                                     true, 2);
+  // Hint is still applicable so we expect it to be allowed to be applied.
+  EXPECT_EQ(optimization_guide::OptimizationGuideDecision::kTrue, decision);
+}
+
+TEST_F(OptimizationGuideServiceTest, NavigateToPageWithoutHint) {
+  PushHintsComponentAndWaitForCompletion();
+  RegisterWithKeyedService();
+
+  base::HistogramTester histogram_tester;
+
+  NavigationContextAndData context_and_data(kNoHintsURL);
+  SimulateNavigation(&context_and_data);
+  auto decision = optimization_guide_service()->CanApplyOptimization(
+      GURL(kNoHintsURL), optimization_guide::proto::NOSCRIPT,
+      /*optimization_metadata=*/nullptr);
+
+  RetryForHistogramUntilCountReached(&histogram_tester,
+                                     "OptimizationGuide.LoadedHint.Result", 1);
+
+  // There were no hints that match this URL, but there should still be an
+  // attempt to load a hint but still fail.
+  histogram_tester.ExpectUniqueSample("OptimizationGuide.LoadedHint.Result",
+                                      false, 1);
+  EXPECT_EQ(optimization_guide::OptimizationGuideDecision::kFalse, decision);
+  histogram_tester.ExpectUniqueSample(
+      "OptimizationGuide.ApplyDecision.NoScript",
+      static_cast<int>(
+          optimization_guide::OptimizationTypeDecision::kNoHintAvailable),
+      1);
+}
+
+TEST_F(OptimizationGuideServiceTest, CheckForBlocklistFilter) {
+  PushHintsComponentAndWaitForCompletion();
+
+  OptimizationGuideService* ogks =
+      OptimizationGuideServiceFactory::GetForBrowserState(browser_state());
+
+  {
+    base::HistogramTester histogram_tester;
+
+    // Register an optimization type with an optimization filter.
+    ogks->RegisterOptimizationTypes(
+        {optimization_guide::proto::FAST_HOST_HINTS});
+    // Wait until filter is loaded. This histogram will record twice: once when
+    // the config is found and once when the filter is created.
+    RetryForHistogramUntilCountReached(
+        &histogram_tester,
+        "OptimizationGuide.OptimizationFilterStatus.FastHostHints", 2);
+    histogram_tester.ExpectBucketCount(
+        "OptimizationGuide.OptimizationFilterStatus.FastHostHints",
+        optimization_guide::OptimizationFilterStatus::kFoundServerFilterConfig,
+        1);
+    histogram_tester.ExpectBucketCount(
+        "OptimizationGuide.OptimizationFilterStatus.FastHostHints",
+        optimization_guide::OptimizationFilterStatus::kCreatedServerFilter, 1);
+
+    EXPECT_EQ(optimization_guide::OptimizationGuideDecision::kFalse,
+              ogks->CanApplyOptimization(
+                  GURL("https://blockedhost.com/whatever"),
+                  optimization_guide::proto::FAST_HOST_HINTS, nullptr));
+    histogram_tester.ExpectUniqueSample(
+        "OptimizationGuide.ApplyDecision.FastHostHints",
+        static_cast<int>(optimization_guide::OptimizationTypeDecision::
+                             kNotAllowedByOptimizationFilter),
+        1);
+  }
+
+  // Register another type with optimization filter.
+  {
+    base::HistogramTester histogram_tester;
+    ogks->RegisterOptimizationTypes(
+        {optimization_guide::proto::LITE_PAGE_REDIRECT});
+    // Wait until filter is loaded. This histogram will record twice: once when
+    // the config is found and once when the filter is created.
+    RetryForHistogramUntilCountReached(
+        &histogram_tester,
+        "OptimizationGuide.OptimizationFilterStatus.LitePageRedirect", 2);
+    histogram_tester.ExpectBucketCount(
+        "OptimizationGuide.OptimizationFilterStatus.LitePageRedirect",
+        optimization_guide::OptimizationFilterStatus::kCreatedServerFilter, 1);
+    histogram_tester.ExpectBucketCount(
+        "OptimizationGuide.OptimizationFilterStatus.LitePageRedirect",
+        optimization_guide::OptimizationFilterStatus::kFoundServerFilterConfig,
+        1);
+
+    // The previously loaded filter should still be loaded and give the same
+    // result.
+    EXPECT_EQ(optimization_guide::OptimizationGuideDecision::kFalse,
+              ogks->CanApplyOptimization(
+                  GURL("https://blockedhost.com/whatever"),
+                  optimization_guide::proto::FAST_HOST_HINTS, nullptr));
+    histogram_tester.ExpectUniqueSample(
+        "OptimizationGuide.ApplyDecision.FastHostHints",
+        static_cast<int>(optimization_guide::OptimizationTypeDecision::
+                             kNotAllowedByOptimizationFilter),
+        1);
+  }
+}
+
+class OptimizationGuideServiceMSBBUserTest
+    : public OptimizationGuideServiceTest {
+ public:
+  void SetUp() override {
+    SetUrlKeyedAnonymizedDataCollectionEnabled(true);
+    OptimizationGuideServiceTest::SetUp();
+  }
+};
+
+TEST_F(OptimizationGuideServiceMSBBUserTest, RemoteFetchingEnabled) {
+  histogram_tester()->ExpectUniqueSample(
+      "OptimizationGuide.RemoteFetchingEnabled", true, 1);
+  // TODO(crbug.com/1240912): Verify the optimization guide fetching synthetic
+  // field trial is recorded.
+}
diff --git a/ios/chrome/browser/prefs/BUILD.gn b/ios/chrome/browser/prefs/BUILD.gn
index f614c2bf..0525a67 100644
--- a/ios/chrome/browser/prefs/BUILD.gn
+++ b/ios/chrome/browser/prefs/BUILD.gn
@@ -50,6 +50,7 @@
     "//components/ntp_snippets",
     "//components/ntp_tiles",
     "//components/omnibox/browser",
+    "//components/optimization_guide/core",
     "//components/password_manager/core/browser",
     "//components/payments/core",
     "//components/policy/core/browser",
diff --git a/ios/chrome/browser/prefs/browser_prefs.mm b/ios/chrome/browser/prefs/browser_prefs.mm
index d0facd07..c3fb222 100644
--- a/ios/chrome/browser/prefs/browser_prefs.mm
+++ b/ios/chrome/browser/prefs/browser_prefs.mm
@@ -30,6 +30,7 @@
 #include "components/ntp_tiles/most_visited_sites.h"
 #include "components/ntp_tiles/popular_sites_impl.h"
 #include "components/omnibox/browser/zero_suggest_provider.h"
+#import "components/optimization_guide/core/optimization_guide_prefs.h"
 #include "components/password_manager/core/browser/password_manager.h"
 #include "components/payments/core/payment_prefs.h"
 #include "components/policy/core/browser/browser_policy_connector.h"
@@ -204,6 +205,7 @@
   ntp_snippets::UserClassifier::RegisterProfilePrefs(registry);
   ntp_tiles::MostVisitedSites::RegisterProfilePrefs(registry);
   ntp_tiles::PopularSitesImpl::RegisterProfilePrefs(registry);
+  optimization_guide::prefs::RegisterProfilePrefs(registry);
   password_manager::PasswordManager::RegisterProfilePrefs(registry);
   payments::RegisterProfilePrefs(registry);
   policy::URLBlocklistManager::RegisterProfilePrefs(registry);
diff --git a/ios/chrome/browser/providers/chromium_browser_provider.h b/ios/chrome/browser/providers/chromium_browser_provider.h
index 4a68801..c1efae9 100644
--- a/ios/chrome/browser/providers/chromium_browser_provider.h
+++ b/ios/chrome/browser/providers/chromium_browser_provider.h
@@ -14,9 +14,6 @@
 
   // ChromeBrowserProvider implementation
   ios::SigninResourcesProvider* GetSigninResourcesProvider() override;
-  void SetChromeIdentityServiceForTesting(
-      std::unique_ptr<ios::ChromeIdentityService> service) override;
-  ios::ChromeIdentityService* GetChromeIdentityService() override;
   UITextField* CreateStyledTextField() const override NS_RETURNS_RETAINED;
   VoiceSearchProvider* GetVoiceSearchProvider() const override;
 
@@ -25,10 +22,11 @@
   UserFeedbackProvider* GetUserFeedbackProvider() const override;
   OverridesProvider* GetOverridesProvider() const override;
   DiscoverFeedProvider* GetDiscoverFeedProvider() const override;
+  std::unique_ptr<ios::ChromeIdentityService> CreateChromeIdentityService()
+      override;
 
  private:
   std::unique_ptr<ios::SigninResourcesProvider> signin_resources_provider_;
-  std::unique_ptr<ios::ChromeIdentityService> chrome_identity_service_;
   std::unique_ptr<UserFeedbackProvider> user_feedback_provider_;
   std::unique_ptr<VoiceSearchProvider> voice_search_provider_;
   std::unique_ptr<OverridesProvider> overrides_provider_;
diff --git a/ios/chrome/browser/providers/chromium_browser_provider.mm b/ios/chrome/browser/providers/chromium_browser_provider.mm
index 7f7dd1f..edfa63c 100644
--- a/ios/chrome/browser/providers/chromium_browser_provider.mm
+++ b/ios/chrome/browser/providers/chromium_browser_provider.mm
@@ -33,20 +33,6 @@
   return signin_resources_provider_.get();
 }
 
-void ChromiumBrowserProvider::SetChromeIdentityServiceForTesting(
-    std::unique_ptr<ios::ChromeIdentityService> service) {
-  chrome_identity_service_ = std::move(service);
-  FireChromeIdentityServiceDidChange(chrome_identity_service_.get());
-}
-
-ios::ChromeIdentityService*
-ChromiumBrowserProvider::GetChromeIdentityService() {
-  if (!chrome_identity_service_) {
-    chrome_identity_service_ = std::make_unique<ios::ChromeIdentityService>();
-  }
-  return chrome_identity_service_.get();
-}
-
 UITextField* ChromiumBrowserProvider::CreateStyledTextField() const {
   return [[UITextField alloc] initWithFrame:CGRectZero];
 }
@@ -72,3 +58,8 @@
 DiscoverFeedProvider* ChromiumBrowserProvider::GetDiscoverFeedProvider() const {
   return discover_feed_provider_.get();
 }
+
+std::unique_ptr<ios::ChromeIdentityService>
+ChromiumBrowserProvider::CreateChromeIdentityService() {
+  return std::make_unique<ios::ChromeIdentityService>();
+}
diff --git a/ios/chrome/browser/signin/BUILD.gn b/ios/chrome/browser/signin/BUILD.gn
index 14ff068..4d9ed0d2 100644
--- a/ios/chrome/browser/signin/BUILD.gn
+++ b/ios/chrome/browser/signin/BUILD.gn
@@ -138,6 +138,7 @@
     "gaia_auth_fetcher_ios_ns_url_session_bridge_unittests.mm",
     "gaia_auth_fetcher_ios_unittest.mm",
     "pattern_account_restriction_unittest.mm",
+    "resized_avatar_cache_unittest.mm",
     "signin_browser_state_info_updater_unittest.mm",
     "user_approved_account_list_manager_unittest.mm",
   ]
diff --git a/ios/chrome/browser/signin/authentication_service.h b/ios/chrome/browser/signin/authentication_service.h
index 3587990..80ee50c 100644
--- a/ios/chrome/browser/signin/authentication_service.h
+++ b/ios/chrome/browser/signin/authentication_service.h
@@ -34,6 +34,7 @@
 // authentication library.
 class AuthenticationService : public KeyedService,
                               public signin::IdentityManager::Observer,
+                              public ios::ChromeBrowserProvider::Observer,
                               public ios::ChromeIdentityService::Observer,
                               public ChromeAccountManagerService::Observer {
  public:
@@ -127,11 +128,6 @@
   // error. Returns true if |identity| had an associated error, false otherwise.
   bool ShowMDMErrorDialogForIdentity(ChromeIdentity* identity);
 
-  // Resets the ChromeIdentityService observer to the one available in the
-  // ChromeBrowserProvider. Used for testing when changing the
-  // ChromeIdentityService to or from a fake one.
-  void ResetChromeIdentityServiceObserverForTesting();
-
   // Returns a weak pointer of this.
   base::WeakPtr<AuthenticationService> GetWeakPtr();
 
@@ -139,6 +135,11 @@
   // sync the accounts between the IdentityManager and the SSO library.
   void OnApplicationWillEnterForeground();
 
+  // ChromeBrowserProvider implementation.
+  void OnChromeIdentityServiceDidChange(
+      ios::ChromeIdentityService* new_service) override;
+  void OnChromeBrowserProviderWillBeDestroyed() override;
+
  private:
   friend class AuthenticationServiceTest;
   friend class AuthenticationServiceFake;
diff --git a/ios/chrome/browser/signin/authentication_service.mm b/ios/chrome/browser/signin/authentication_service.mm
index b09eb067..c2601c9 100644
--- a/ios/chrome/browser/signin/authentication_service.mm
+++ b/ios/chrome/browser/signin/authentication_service.mm
@@ -201,6 +201,16 @@
       current_accounts_info);
 }
 
+void AuthenticationService::OnChromeIdentityServiceDidChange(
+    ios::ChromeIdentityService* new_service) {
+  identity_service_observation_.Observe(
+      ios::GetChromeBrowserProvider().GetChromeIdentityService());
+}
+
+void AuthenticationService::OnChromeBrowserProviderWillBeDestroyed() {
+  identity_service_observation_.Reset();
+}
+
 void AuthenticationService::MigrateAccountsStoredInPrefsIfNeeded() {
   if (identity_manager_->GetAccountIdMigrationState() ==
       signin::IdentityManager::AccountIdMigrationState::MIGRATION_NOT_STARTED) {
@@ -412,12 +422,6 @@
   return true;
 }
 
-void AuthenticationService::ResetChromeIdentityServiceObserverForTesting() {
-  DCHECK(!identity_service_observation_.IsObserving());
-  identity_service_observation_.Observe(
-      ios::GetChromeBrowserProvider().GetChromeIdentityService());
-}
-
 base::WeakPtr<AuthenticationService> AuthenticationService::GetWeakPtr() {
   return weak_pointer_factory_.GetWeakPtr();
 }
diff --git a/ios/chrome/browser/signin/resized_avatar_cache.mm b/ios/chrome/browser/signin/resized_avatar_cache.mm
index 664090c..6f092457 100644
--- a/ios/chrome/browser/signin/resized_avatar_cache.mm
+++ b/ios/chrome/browser/signin/resized_avatar_cache.mm
@@ -16,18 +16,21 @@
 #endif
 
 @interface ResizedAvatarCache ()
+
 // Size of resized avatar.
 @property(nonatomic, assign) CGSize expectedSize;
+// Default avatar at |self.expectedSize| size.
+@property(nonatomic, strong) UIImage* defaultResizedAvatar;
+// Retains resized images. Key is Chrome Identity.
+@property(nonatomic, strong) NSCache<ChromeIdentity*, UIImage*>* resizedImages;
+// Holds weak references to the cached avatar image from the
+// ChromeIdentityService. Key is Chrome Identity.
+@property(nonatomic, strong)
+    NSMapTable<ChromeIdentity*, UIImage*>* originalImages;
+
 @end
 
-@implementation ResizedAvatarCache {
-  // Retains resized images. Key is Chrome Identity.
-  NSCache<ChromeIdentity*, UIImage*>* _resizedImages;
-
-  // Holds weak references to the cached avatar image from the
-  // ChromeIdentityService. Key is Chrome Identity.
-  NSMapTable<ChromeIdentity*, UIImage*>* _originalImages;
-}
+@implementation ResizedAvatarCache
 
 - (instancetype)initWithSize:(CGSize)size {
   self = [super init];
@@ -35,6 +38,11 @@
     _expectedSize = size;
     _resizedImages = [[NSCache alloc] init];
     _originalImages = [NSMapTable strongToWeakObjectsMapTable];
+    [[NSNotificationCenter defaultCenter]
+        addObserver:self
+           selector:@selector(notificicationReceived:)
+               name:UIApplicationDidReceiveMemoryWarningNotification
+             object:nil];
   }
   return self;
 }
@@ -49,12 +57,11 @@
                        .GetChromeIdentityService()
                        ->GetCachedAvatarForIdentity(identity);
   if (!image) {
-    image = ios::provider::GetSigninDefaultAvatar();
-
     // No cached image, trigger a fetch, which will notify all observers.
     ios::GetChromeBrowserProvider()
         .GetChromeIdentityService()
         ->GetAvatarForIdentity(identity, nil);
+    return self.defaultResizedAvatar;
   }
 
   // If the currently used image has already been resized, use it.
@@ -72,4 +79,22 @@
   return image;
 }
 
+- (void)notificicationReceived:(NSNotification*)notification {
+  if ([notification.name
+          isEqual:UIApplicationDidReceiveMemoryWarningNotification]) {
+    self.defaultResizedAvatar = nil;
+  }
+}
+
+#pragma mark - Properties
+
+- (UIImage*)defaultResizedAvatar {
+  if (!_defaultResizedAvatar) {
+    UIImage* image = ios::provider::GetSigninDefaultAvatar();
+    _defaultResizedAvatar =
+        ResizeImage(image, self.expectedSize, ProjectionMode::kAspectFit);
+  }
+  return _defaultResizedAvatar;
+}
+
 @end
diff --git a/ios/chrome/browser/signin/resized_avatar_cache_unittest.mm b/ios/chrome/browser/signin/resized_avatar_cache_unittest.mm
new file mode 100644
index 0000000..8e52ec5
--- /dev/null
+++ b/ios/chrome/browser/signin/resized_avatar_cache_unittest.mm
@@ -0,0 +1,92 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import "ios/chrome/browser/signin/resized_avatar_cache.h"
+
+#import "base/values.h"
+#import "ios/chrome/browser/signin/signin_util.h"
+#import "ios/public/provider/chrome/browser/signin/fake_chrome_identity.h"
+#import "ios/public/provider/chrome/browser/signin/fake_chrome_identity_service.h"
+#import "testing/gtest_mac.h"
+#import "testing/platform_test.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+// Test fixture for testing ResizedAvatarCache class.
+class ResizedAvatarCacheTest : public PlatformTest {
+ protected:
+  void SetUp() override {
+    PlatformTest::SetUp();
+    identity_service_ =
+        ios::FakeChromeIdentityService::GetInstanceFromChromeProvider();
+    resized_avatar_cache_ = [[ResizedAvatarCache alloc]
+        initWithIdentityAvatarSize:IdentityAvatarSize::TableViewIcon];
+    identity1_ = [FakeChromeIdentity identityWithEmail:@"test1@email.com"
+                                                gaiaID:@"gaiaID1"
+                                                  name:@"Test Name1"];
+    identity2_ = [FakeChromeIdentity identityWithEmail:@"test2@email.com"
+                                                gaiaID:@"gaiaID2"
+                                                  name:@"Test Name2"];
+  }
+
+  ios::FakeChromeIdentityService* identity_service_ = nil;
+  ResizedAvatarCache* resized_avatar_cache_ = nil;
+  ChromeIdentity* identity1_ = nil;
+  ChromeIdentity* identity2_ = nil;
+};
+
+// Tests that the default avatar is the same between 2 identities.
+TEST_F(ResizedAvatarCacheTest, DefaultAvatarSize) {
+  UIImage* avatar1 =
+      [resized_avatar_cache_ resizedAvatarForIdentity:identity1_];
+  EXPECT_NE(nil, avatar1);
+  // Check the size.
+  CGSize expected_size =
+      GetSizeForIdentityAvatarSize(IdentityAvatarSize::TableViewIcon);
+  EXPECT_TRUE(CGSizeEqualToSize(expected_size, avatar1.size));
+  // Asking for an avatar on another identity should return the same image:
+  // the default avatar.
+  UIImage* avatar2 =
+      [resized_avatar_cache_ resizedAvatarForIdentity:identity2_];
+  EXPECT_EQ(avatar1, avatar2);
+}
+
+// Tests that the avatar is updated after waiting for the fetch.
+TEST_F(ResizedAvatarCacheTest, FetchAvatar) {
+  UIImage* default_avatar =
+      [resized_avatar_cache_ resizedAvatarForIdentity:identity1_];
+  EXPECT_NE(nil, default_avatar);
+  // Asking again for the avatar, the same image is expected again (default
+  // avatar).
+  UIImage* same_default_avatar =
+      [resized_avatar_cache_ resizedAvatarForIdentity:identity1_];
+  EXPECT_EQ(default_avatar, same_default_avatar);
+  // Wait for the end of the fetch.
+  identity_service_->WaitForServiceCallbacksToComplete();
+  // Asking again, the fecthed avatar is expected (instead of the default
+  // avatar)
+  UIImage* identity_avatar =
+      [resized_avatar_cache_ resizedAvatarForIdentity:identity1_];
+  EXPECT_NE(default_avatar, identity_avatar);
+  // Check the size.
+  CGSize expected_size =
+      GetSizeForIdentityAvatarSize(IdentityAvatarSize::TableViewIcon);
+  EXPECT_TRUE(CGSizeEqualToSize(expected_size, identity_avatar.size));
+}
+
+// Tests that the default avatar is forgotten after a memory warning
+// notification.
+TEST_F(ResizedAvatarCacheTest, MemoryWarning) {
+  UIImage* first_default_avatar =
+      [resized_avatar_cache_ resizedAvatarForIdentity:identity1_];
+  EXPECT_NE(nil, first_default_avatar);
+  [[NSNotificationCenter defaultCenter]
+      postNotificationName:UIApplicationDidReceiveMemoryWarningNotification
+                    object:nil];
+  UIImage* second_default_avatar =
+      [resized_avatar_cache_ resizedAvatarForIdentity:identity2_];
+  EXPECT_NE(first_default_avatar, second_default_avatar);
+}
diff --git a/ios/chrome/browser/ui/authentication/signin/signin_coordinator_egtest.mm b/ios/chrome/browser/ui/authentication/signin/signin_coordinator_egtest.mm
index 256e5be..86fc28d 100644
--- a/ios/chrome/browser/ui/authentication/signin/signin_coordinator_egtest.mm
+++ b/ios/chrome/browser/ui/authentication/signin/signin_coordinator_egtest.mm
@@ -39,7 +39,6 @@
 using chrome_test_util::ClearBrowsingDataButton;
 using chrome_test_util::ConfirmClearBrowsingDataButton;
 using chrome_test_util::GoogleServicesSettingsButton;
-using chrome_test_util::GoogleServicesSettingsView;
 using chrome_test_util::IdentityCellMatcherForEmail;
 using chrome_test_util::PrimarySignInButton;
 using chrome_test_util::SecondarySignInButton;
@@ -50,10 +49,8 @@
 using chrome_test_util::SettingsImportDataImportButton;
 using chrome_test_util::SettingsImportDataKeepSeparateButton;
 using chrome_test_util::SettingsLink;
-using chrome_test_util::SettingsMenuBackButton;
 using chrome_test_util::SettingsMenuPrivacyButton;
 using chrome_test_util::StaticTextWithAccessibilityLabelId;
-using chrome_test_util::SyncSettingsConfirmButton;
 using l10n_util::GetNSString;
 using testing::ButtonWithAccessibilityLabel;
 
@@ -68,13 +65,6 @@
 
 NSString* const kPassphrase = @"hello";
 
-// Returns a matcher for |userEmail| in IdentityChooserViewController.
-id<GREYMatcher> identityChooserButtonMatcherWithEmail(NSString* userEmail) {
-  return grey_allOf(grey_accessibilityID(userEmail),
-                    grey_kindOfClassName(@"TableViewIdentityCell"),
-                    grey_sufficientlyVisible(), nil);
-}
-
 void ChooseImportOrKeepDataSepareteDialog(id<GREYMatcher> choiceButtonMatcher) {
   // Set up the fake identities.
   FakeChromeIdentity* fakeIdentity1 = [SigninEarlGrey fakeIdentity1];
@@ -552,14 +542,14 @@
   // Open the identity chooser.
   [ChromeEarlGreyUI openSettingsMenu];
   [ChromeEarlGreyUI tapSettingsMenuButton:SecondarySignInButton()];
-  [ChromeEarlGrey waitForMatcher:identityChooserButtonMatcherWithEmail(
-                                     fakeIdentity.userEmail)];
+  [ChromeEarlGrey
+      waitForMatcher:IdentityCellMatcherForEmail(fakeIdentity.userEmail)];
 
   // Remove the fake identity.
   [SigninEarlGrey forgetFakeIdentity:fakeIdentity];
 
   // Check that the identity has been removed.
-  [[EarlGrey selectElementWithMatcher:identityChooserButtonMatcherWithEmail(
+  [[EarlGrey selectElementWithMatcher:IdentityCellMatcherForEmail(
                                           fakeIdentity.userEmail)]
       assertWithMatcher:grey_notVisible()];
 }
@@ -626,7 +616,7 @@
   // progress this test will likely be flaky.
   [SigninEarlGrey forgetFakeIdentity:fakeIdentity];
   // Check that the identity has been removed.
-  [[EarlGrey selectElementWithMatcher:identityChooserButtonMatcherWithEmail(
+  [[EarlGrey selectElementWithMatcher:IdentityCellMatcherForEmail(
                                           fakeIdentity.userEmail)]
       assertWithMatcher:grey_notVisible()];
 }
diff --git a/ios/chrome/browser/ui/authentication/signin_earl_grey_app_interface.h b/ios/chrome/browser/ui/authentication/signin_earl_grey_app_interface.h
index cc84dba..57157ee 100644
--- a/ios/chrome/browser/ui/authentication/signin_earl_grey_app_interface.h
+++ b/ios/chrome/browser/ui/authentication/signin_earl_grey_app_interface.h
@@ -61,11 +61,6 @@
 // directly the current SceneController.
 + (void)triggerConsistencyPromoSigninDialog;
 
-// Sign-in matchers
-
-// Returns a matcher for an identity picker cell for |email|.
-+ (id<GREYMatcher>)identityCellMatcherForEmail:(NSString*)email;
-
 @end
 
 #endif  // IOS_CHROME_BROWSER_UI_AUTHENTICATION_SIGNIN_EARL_GREY_APP_INTERFACE_H_
diff --git a/ios/chrome/browser/ui/authentication/signin_earl_grey_app_interface.mm b/ios/chrome/browser/ui/authentication/signin_earl_grey_app_interface.mm
index 4ef8bdd..957586c 100644
--- a/ios/chrome/browser/ui/authentication/signin_earl_grey_app_interface.mm
+++ b/ios/chrome/browser/ui/authentication/signin_earl_grey_app_interface.mm
@@ -142,10 +142,4 @@
                                                       URL:gURL];
 }
 
-+ (id<GREYMatcher>)identityCellMatcherForEmail:(NSString*)email {
-  return grey_allOf(grey_accessibilityID(email),
-                    grey_kindOfClass([TableViewIdentityCell class]),
-                    grey_sufficientlyVisible(), nil);
-}
-
 @end
diff --git a/ios/chrome/browser/ui/authentication/signin_matchers.mm b/ios/chrome/browser/ui/authentication/signin_matchers.mm
index fa83a305..8dbf0baf 100644
--- a/ios/chrome/browser/ui/authentication/signin_matchers.mm
+++ b/ios/chrome/browser/ui/authentication/signin_matchers.mm
@@ -5,7 +5,6 @@
 #import "ios/chrome/browser/ui/authentication/signin_matchers.h"
 
 #import "ios/chrome/browser/ui/authentication/signin/signin_constants.h"
-#import "ios/chrome/browser/ui/authentication/signin_earl_grey_app_interface.h"
 #import "ios/chrome/browser/ui/settings/settings_table_view_controller_constants.h"
 #import "ios/testing/earl_grey/earl_grey_test.h"
 
@@ -16,7 +15,9 @@
 namespace chrome_test_util {
 
 id<GREYMatcher> IdentityCellMatcherForEmail(NSString* email) {
-  return [SigninEarlGreyAppInterface identityCellMatcherForEmail:email];
+  return grey_allOf(grey_accessibilityID(email),
+                    grey_kindOfClassName(@"TableViewIdentityCell"),
+                    grey_sufficientlyVisible(), nil);
 }
 
 id<GREYMatcher> SettingsLink() {
diff --git a/ios/chrome/browser/ui/download/download_manager_egtest.mm b/ios/chrome/browser/ui/download/download_manager_egtest.mm
index a014ffa..d01f381 100644
--- a/ios/chrome/browser/ui/download/download_manager_egtest.mm
+++ b/ios/chrome/browser/ui/download/download_manager_egtest.mm
@@ -54,7 +54,7 @@
   return base::test::ios::WaitUntilConditionOrTimeout(kLongDownloadTimeout, ^{
     NSError* error = nil;
     [[EarlGrey selectElementWithMatcher:chrome_test_util::OpenInButton()]
-        assertWithMatcher:grey_notNil()
+        assertWithMatcher:grey_interactable()
                     error:&error];
     return (error == nil);
   });
@@ -67,7 +67,7 @@
       base::test::ios::kWaitForPageLoadTimeout, ^{
         NSError* error = nil;
         [[EarlGrey selectElementWithMatcher:DownloadButton()]
-            assertWithMatcher:grey_notNil()
+            assertWithMatcher:grey_interactable()
                         error:&error];
         return (error == nil);
       });
@@ -81,7 +81,7 @@
         NSError* error = nil;
         [[EarlGrey selectElementWithMatcher:grey_text(l10n_util::GetNSString(
                                                 IDS_IOS_OPEN_IN_DOWNLOADS))]
-            assertWithMatcher:grey_notNil()
+            assertWithMatcher:grey_interactable()
                         error:&error];
         return (error == nil);
       });
diff --git a/ios/chrome/browser/web/stop_loading_egtest.mm b/ios/chrome/browser/web/stop_loading_egtest.mm
index 4cbab4af..31eee077 100644
--- a/ios/chrome/browser/web/stop_loading_egtest.mm
+++ b/ios/chrome/browser/web/stop_loading_egtest.mm
@@ -6,6 +6,7 @@
 
 #include "base/strings/stringprintf.h"
 #import "base/test/ios/wait_util.h"
+#include "base/threading/platform_thread.h"
 #include "base/time/time.h"
 #import "ios/chrome/test/earl_grey/chrome_earl_grey.h"
 #import "ios/chrome/test/earl_grey/chrome_earl_grey_ui.h"
diff --git a/ios/chrome/credential_provider_extension/ui/credential_list_view_controller.mm b/ios/chrome/credential_provider_extension/ui/credential_list_view_controller.mm
index 2ade6024..c2000b7 100644
--- a/ios/chrome/credential_provider_extension/ui/credential_list_view_controller.mm
+++ b/ios/chrome/credential_provider_extension/ui/credential_list_view_controller.mm
@@ -198,6 +198,7 @@
       cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle
                                     reuseIdentifier:kNewPasswordCellIdentifier];
     }
+    cell.backgroundColor = [UIColor colorNamed:kBackgroundColor];
     cell.textLabel.text =
         NSLocalizedString(@"IDS_IOS_CREDENTIAL_PROVIDER_CREATE_PASSWORD_ROW",
                           @"Add New Password");
diff --git a/ios/chrome/test/app/signin_test_util.mm b/ios/chrome/test/app/signin_test_util.mm
index b7e23c5..cac62c6 100644
--- a/ios/chrome/test/app/signin_test_util.mm
+++ b/ios/chrome/test/app/signin_test_util.mm
@@ -58,14 +58,10 @@
   service->SetUpForIntegrationTests();
   ios::GetChromeBrowserProvider().SetChromeIdentityServiceForTesting(
       std::move(service));
-  AuthenticationServiceFactory::GetForBrowserState(GetOriginalBrowserState())
-      ->ResetChromeIdentityServiceObserverForTesting();
 }
 
 void TearDownMockAuthentication() {
   ios::GetChromeBrowserProvider().SetChromeIdentityServiceForTesting(nullptr);
-  AuthenticationServiceFactory::GetForBrowserState(GetOriginalBrowserState())
-      ->ResetChromeIdentityServiceObserverForTesting();
 }
 
 void SignOutAndClearIdentities() {
diff --git a/ios/google_internal/frameworks/chrome_internal_dynamic_framework.arm64.zip.sha1 b/ios/google_internal/frameworks/chrome_internal_dynamic_framework.arm64.zip.sha1
index e7354b1e..5eca3d53 100644
--- a/ios/google_internal/frameworks/chrome_internal_dynamic_framework.arm64.zip.sha1
+++ b/ios/google_internal/frameworks/chrome_internal_dynamic_framework.arm64.zip.sha1
@@ -1 +1 @@
-170744c1f11964742db733949e702bd5b4baa4c8
\ No newline at end of file
+11e34792e5ab4ec16234904b43fa59f708ead394
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_internal_dynamic_framework.x64.zip.sha1 b/ios/google_internal/frameworks/chrome_internal_dynamic_framework.x64.zip.sha1
index 9008f09..1764b9d 100644
--- a/ios/google_internal/frameworks/chrome_internal_dynamic_framework.x64.zip.sha1
+++ b/ios/google_internal/frameworks/chrome_internal_dynamic_framework.x64.zip.sha1
@@ -1 +1 @@
-0564d4eeb73adc08736aef8eb6cec191c967299b
\ No newline at end of file
+1db08e9ae5923684d35414865e91247494559e81
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.arm64.zip.sha1 b/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.arm64.zip.sha1
index bb3a0da..3eaef8a 100644
--- a/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.arm64.zip.sha1
+++ b/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.arm64.zip.sha1
@@ -1 +1 @@
-754b125a13cbb941e1a273ddf07aa95a6fcffac8
\ No newline at end of file
+fa4b83563309080ca4d3d0ae39f7afc907e3280f
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.x64.zip.sha1 b/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.x64.zip.sha1
index c25307e1..8b307c4c 100644
--- a/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.x64.zip.sha1
+++ b/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.x64.zip.sha1
@@ -1 +1 @@
-1d993091ee125ce83c35e9e1f924161de40d28c2
\ No newline at end of file
+5e16e07fe274694f5da3e2c0589182a7574a8893
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.arm64.zip.sha1 b/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.arm64.zip.sha1
index cb8b5a4..849c6a78 100644
--- a/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.arm64.zip.sha1
+++ b/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.arm64.zip.sha1
@@ -1 +1 @@
-c3b5af92dd4ef8a50d8e9f5e3f6b1ca2a0c936fa
\ No newline at end of file
+9951ebcc572254751eb298b6d4b78a5003143b98
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.x64.zip.sha1 b/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.x64.zip.sha1
index 9eda69d..3dcca30 100644
--- a/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.x64.zip.sha1
+++ b/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.x64.zip.sha1
@@ -1 +1 @@
-3503512fb7c3ffa8e5a923ad6cb166eb3b866d63
\ No newline at end of file
+1e0b792beeb5d053172c1435636a96b73fa441ab
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/remoting_internal_dynamic_framework.arm64.zip.sha1 b/ios/google_internal/frameworks/remoting_internal_dynamic_framework.arm64.zip.sha1
index fbd0ff1..48ddb93 100644
--- a/ios/google_internal/frameworks/remoting_internal_dynamic_framework.arm64.zip.sha1
+++ b/ios/google_internal/frameworks/remoting_internal_dynamic_framework.arm64.zip.sha1
@@ -1 +1 @@
-96a366fdbe7b97142f9ff695eb919783652db0a6
\ No newline at end of file
+87a367f7f5472ea9915f4cd85d1cc0227bfc29c9
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/remoting_internal_dynamic_framework.x64.zip.sha1 b/ios/google_internal/frameworks/remoting_internal_dynamic_framework.x64.zip.sha1
index 1b4e8ab1..0d465b8b 100644
--- a/ios/google_internal/frameworks/remoting_internal_dynamic_framework.x64.zip.sha1
+++ b/ios/google_internal/frameworks/remoting_internal_dynamic_framework.x64.zip.sha1
@@ -1 +1 @@
-a25c9809a6fc83b0f986e31e26a8fa737cb90d5b
\ No newline at end of file
+cfeb6d5e83e6a6c36be50b3472bbbff2e98684bc
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.arm64.zip.sha1 b/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.arm64.zip.sha1
index 9307e84..39d467f 100644
--- a/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.arm64.zip.sha1
+++ b/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.arm64.zip.sha1
@@ -1 +1 @@
-26a8a7049a36e3cee21195274d52f924a1065aed
\ No newline at end of file
+7b1df26f08c78ff4bf0153edde82ca5d9a54602f
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.x64.zip.sha1 b/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.x64.zip.sha1
index faacb76..a3fccd2 100644
--- a/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.x64.zip.sha1
+++ b/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.x64.zip.sha1
@@ -1 +1 @@
-d8422582a5a3dcbcf29642813b54c8e635d0ad8c
\ No newline at end of file
+358668bffa04f15d66dad655e62a93e20956ba55
\ No newline at end of file
diff --git a/ios/public/provider/chrome/browser/chrome_browser_provider.h b/ios/public/provider/chrome/browser/chrome_browser_provider.h
index a97c569..a95dbaa 100644
--- a/ios/public/provider/chrome/browser/chrome_browser_provider.h
+++ b/ios/public/provider/chrome/browser/chrome_browser_provider.h
@@ -146,9 +146,15 @@
   // Fires |OnChromeIdentityServiceDidChange| on all observers.
   void FireChromeIdentityServiceDidChange(ChromeIdentityService* new_service);
 
+  // Creates a ChromeIdentityService. This methods has to be be implemented
+  // in subclasses.
+  virtual std::unique_ptr<ios::ChromeIdentityService>
+  CreateChromeIdentityService();
+
  private:
   base::ObserverList<Observer, true>::Unchecked observer_list_;
   std::unique_ptr<MailtoHandlerProvider> mailto_handler_provider_;
+  std::unique_ptr<ios::ChromeIdentityService> chrome_identity_service_;
 };
 
 }  // namespace ios
diff --git a/ios/public/provider/chrome/browser/chrome_browser_provider.mm b/ios/public/provider/chrome/browser/chrome_browser_provider.mm
index 066855f..5b4f12d 100644
--- a/ios/public/provider/chrome/browser/chrome_browser_provider.mm
+++ b/ios/public/provider/chrome/browser/chrome_browser_provider.mm
@@ -40,6 +40,7 @@
     : mailto_handler_provider_(std::make_unique<MailtoHandlerProvider>()) {}
 
 ChromeBrowserProvider::~ChromeBrowserProvider() {
+  chrome_identity_service_.reset();
   for (auto& observer : observer_list_)
     observer.OnChromeBrowserProviderWillBeDestroyed();
 }
@@ -55,10 +56,16 @@
 }
 
 void ChromeBrowserProvider::SetChromeIdentityServiceForTesting(
-    std::unique_ptr<ChromeIdentityService> service) {}
+    std::unique_ptr<ChromeIdentityService> service) {
+  chrome_identity_service_ = std::move(service);
+  FireChromeIdentityServiceDidChange(chrome_identity_service_.get());
+}
 
 ChromeIdentityService* ChromeBrowserProvider::GetChromeIdentityService() {
-  return nullptr;
+  if (!chrome_identity_service_) {
+    chrome_identity_service_ = CreateChromeIdentityService();
+  }
+  return chrome_identity_service_.get();
 }
 
 ChromeTrustedVaultService*
@@ -125,4 +132,9 @@
     observer.OnChromeIdentityServiceDidChange(new_service);
 }
 
+std::unique_ptr<ios::ChromeIdentityService>
+ChromeBrowserProvider::CreateChromeIdentityService() {
+  return nullptr;
+}
+
 }  // namespace ios
diff --git a/ios/public/provider/chrome/browser/test_chrome_browser_provider.h b/ios/public/provider/chrome/browser/test_chrome_browser_provider.h
index 8f2c41c..a1cdde82 100644
--- a/ios/public/provider/chrome/browser/test_chrome_browser_provider.h
+++ b/ios/public/provider/chrome/browser/test_chrome_browser_provider.h
@@ -25,9 +25,6 @@
 
   // ChromeBrowserProvider:
   SigninResourcesProvider* GetSigninResourcesProvider() override;
-  void SetChromeIdentityServiceForTesting(
-      std::unique_ptr<ChromeIdentityService> service) override;
-  ChromeIdentityService* GetChromeIdentityService() override;
   ChromeTrustedVaultService* GetChromeTrustedVaultService() override;
   UITextField* CreateStyledTextField() const override NS_RETURNS_RETAINED;
   VoiceSearchProvider* GetVoiceSearchProvider() const override;
@@ -37,7 +34,9 @@
   DiscoverFeedProvider* GetDiscoverFeedProvider() const override;
 
  private:
-  std::unique_ptr<ChromeIdentityService> chrome_identity_service_;
+  // ChromeBrowserProvider:
+  std::unique_ptr<ChromeIdentityService> CreateChromeIdentityService() override;
+
   std::unique_ptr<ChromeTrustedVaultService> chrome_trusted_vault_service_;
   std::unique_ptr<OmahaServiceProvider> omaha_service_provider_;
   std::unique_ptr<SigninResourcesProvider> signin_resources_provider_;
diff --git a/ios/public/provider/chrome/browser/test_chrome_browser_provider.mm b/ios/public/provider/chrome/browser/test_chrome_browser_provider.mm
index 69f7236..61bb242 100644
--- a/ios/public/provider/chrome/browser/test_chrome_browser_provider.mm
+++ b/ios/public/provider/chrome/browser/test_chrome_browser_provider.mm
@@ -45,19 +45,6 @@
   return signin_resources_provider_.get();
 }
 
-void TestChromeBrowserProvider::SetChromeIdentityServiceForTesting(
-    std::unique_ptr<ChromeIdentityService> service) {
-  chrome_identity_service_ = std::move(service);
-  FireChromeIdentityServiceDidChange(chrome_identity_service_.get());
-}
-
-ChromeIdentityService* TestChromeBrowserProvider::GetChromeIdentityService() {
-  if (!chrome_identity_service_) {
-    chrome_identity_service_.reset(new FakeChromeIdentityService());
-  }
-  return chrome_identity_service_.get();
-}
-
 ChromeTrustedVaultService*
 TestChromeBrowserProvider::GetChromeTrustedVaultService() {
   if (!chrome_trusted_vault_service_) {
@@ -94,4 +81,9 @@
   return discover_feed_provider_.get();
 }
 
+std::unique_ptr<ChromeIdentityService>
+TestChromeBrowserProvider::CreateChromeIdentityService() {
+  return std::make_unique<FakeChromeIdentityService>();
+}
+
 }  // namespace ios
diff --git a/media/base/use_after_free_checker.h b/media/base/use_after_free_checker.h
index aefcaae2..1ffde9b 100644
--- a/media/base/use_after_free_checker.h
+++ b/media/base/use_after_free_checker.h
@@ -5,6 +5,7 @@
 #ifndef MEDIA_BASE_USE_AFTER_FREE_CHECKER_H_
 #define MEDIA_BASE_USE_AFTER_FREE_CHECKER_H_
 
+#include "base/debug/crash_logging.h"
 #include "base/debug/dump_without_crashing.h"
 #include "base/location.h"
 #include "media/base/media_export.h"
diff --git a/media/capture/video/linux/v4l2_capture_delegate.cc b/media/capture/video/linux/v4l2_capture_delegate.cc
index 26e6348..3a0ef10 100644
--- a/media/capture/video/linux/v4l2_capture_delegate.cc
+++ b/media/capture/video/linux/v4l2_capture_delegate.cc
@@ -974,7 +974,9 @@
 
 bool V4L2CaptureDelegate::StopStream() {
   DCHECK(v4l2_task_runner_->BelongsToCurrentThread());
-  DCHECK(is_capturing_);
+  if (!is_capturing_)
+    return false;
+
   is_capturing_ = false;
 
   // The order is important: stop streaming, clear |buffer_pool_|,
@@ -1003,7 +1005,6 @@
                                         const base::Location& from_here,
                                         const std::string& reason) {
   DCHECK(v4l2_task_runner_->BelongsToCurrentThread());
-  is_capturing_ = false;
   client_->OnError(error, from_here, reason);
 }
 
diff --git a/media/cast/sender/video_sender_unittest.cc b/media/cast/sender/video_sender_unittest.cc
index 05584fe..6b20378 100644
--- a/media/cast/sender/video_sender_unittest.cc
+++ b/media/cast/sender/video_sender_unittest.cc
@@ -11,6 +11,7 @@
 #include <vector>
 
 #include "base/bind.h"
+#include "base/callback_helpers.h"
 #include "base/logging.h"
 #include "base/macros.h"
 #include "base/memory/ptr_util.h"
diff --git a/media/cast/test/sender.cc b/media/cast/test/sender.cc
index eae227d..d8ad38a 100644
--- a/media/cast/test/sender.cc
+++ b/media/cast/test/sender.cc
@@ -13,6 +13,7 @@
 #include "base/at_exit.h"
 #include "base/base_paths.h"
 #include "base/bind.h"
+#include "base/callback_helpers.h"
 #include "base/command_line.h"
 #include "base/files/file_path.h"
 #include "base/json/json_writer.h"
diff --git a/media/cast/test/simulator.cc b/media/cast/test/simulator.cc
index 6c0cb79..1bafff9 100644
--- a/media/cast/test/simulator.cc
+++ b/media/cast/test/simulator.cc
@@ -43,6 +43,7 @@
 #include "base/at_exit.h"
 #include "base/base_paths.h"
 #include "base/bind.h"
+#include "base/callback_helpers.h"
 #include "base/command_line.h"
 #include "base/containers/queue.h"
 #include "base/containers/span.h"
diff --git a/media/gpu/v4l2/test/v4l2_stateless_decoder.cc b/media/gpu/v4l2/test/v4l2_stateless_decoder.cc
index 687f21c6..a7bdf11 100644
--- a/media/gpu/v4l2/test/v4l2_stateless_decoder.cc
+++ b/media/gpu/v4l2/test/v4l2_stateless_decoder.cc
@@ -19,6 +19,7 @@
 using media::v4l2_test::Vp9Decoder;
 
 namespace {
+static const char* kDecodeDevice = "/dev/video-dec0";
 
 constexpr char kUsageMsg[] =
     "usage: v4l2_stateless_decoder\n"
@@ -42,11 +43,25 @@
 
 }  // namespace
 
+// For stateless API, fourcc |VP9F| is needed instead of |VP90| for VP9 codec.
+// https://www.kernel.org/doc/html/latest/userspace-api/media/v4l/pixfmt-compressed.html
+// Converts fourcc |VP90| from file header to fourcc |VP9F|, which is
+// a format supported on driver.
+uint32_t FileFourccToDriverFourcc(uint32_t header_fourcc) {
+  if (header_fourcc == V4L2_PIX_FMT_VP9) {
+    LOG(INFO) << "OUTPUT format mapped from VP90 to VP9F.";
+    return V4L2_PIX_FMT_VP9_FRAME;
+  }
+
+  return header_fourcc;
+}
+
 // Creates the appropriate decoder for |stream|, which points to IVF data.
 // Returns nullptr on failure.
-std::unique_ptr<Vp9Decoder> CreateDecoder(
-    const base::MemoryMappedFile& stream) {
+std::unique_ptr<Vp9Decoder> CreateDecoder(const base::MemoryMappedFile& stream,
+                                          base::File& v4l_fd) {
   CHECK(stream.IsValid());
+  CHECK(v4l_fd.IsValid());
 
   // Set up video parser.
   auto ivf_parser = std::make_unique<media::IvfParser>();
@@ -66,7 +81,13 @@
       << " not supported.\n"
       << kUsageMsg;
 
-  return std::make_unique<Vp9Decoder>(std::move(ivf_parser));
+  const auto driver_codec_fourcc = FileFourccToDriverFourcc(file_header.fourcc);
+
+  CHECK_EQ(driver_codec_fourcc, V4L2_PIX_FMT_VP9_FRAME)
+      << "Only VP9 is supported, got: "
+      << media::FourccToString(driver_codec_fourcc);
+
+  return Vp9Decoder::Create(std::move(ivf_parser), v4l_fd);
 }
 
 int main(int argc, char** argv) {
@@ -107,7 +128,12 @@
     return EXIT_FAILURE;
   }
 
-  const std::unique_ptr<Vp9Decoder> dec = CreateDecoder(stream);
+  auto v4l_fd = base::File(
+      base::FilePath::FromUTF8Unsafe(kDecodeDevice),
+      base::File::FLAG_OPEN | base::File::FLAG_READ | base::File::FLAG_WRITE);
+  PCHECK(v4l_fd.IsValid()) << "Failed to open " << kDecodeDevice;
+
+  const std::unique_ptr<Vp9Decoder> dec = CreateDecoder(stream, v4l_fd);
   if (!dec) {
     LOG(ERROR) << "Failed to create decoder for file: " << video_path;
     return EXIT_FAILURE;
diff --git a/media/gpu/v4l2/test/vp9_decoder.cc b/media/gpu/v4l2/test/vp9_decoder.cc
index c550a98..23909dbf 100644
--- a/media/gpu/v4l2/test/vp9_decoder.cc
+++ b/media/gpu/v4l2/test/vp9_decoder.cc
@@ -4,6 +4,12 @@
 
 #include "media/gpu/v4l2/test/vp9_decoder.h"
 
+#include <sys/ioctl.h>
+
+#include "base/files/memory_mapped_file.h"
+#include "base/logging.h"
+#include "base/memory/ptr_util.h"
+#include "media/base/video_types.h"
 #include "media/filters/ivf_parser.h"
 #include "media/filters/vp9_parser.h"
 
@@ -11,12 +17,85 @@
 
 namespace v4l2_test {
 
-Vp9Decoder::Vp9Decoder(std::unique_ptr<IvfParser> ivf_parser)
+Vp9Decoder::Vp9Decoder(std::unique_ptr<IvfParser> ivf_parser,
+                       base::File& v4l2_dev_file)
     : vp9_parser_(
           std::make_unique<Vp9Parser>(/*parsing_compressed_header=*/false)) {}
 
 Vp9Decoder::~Vp9Decoder() = default;
 
+// static
+std::unique_ptr<Vp9Decoder> Vp9Decoder::Create(
+    std::unique_ptr<IvfParser> ivf_parser,
+    base::File& v4l2_dev_file) {
+  DCHECK(v4l2_dev_file.IsValid());
+
+  constexpr uint32_t kDriverCodecFourcc = V4L2_PIX_FMT_VP9_FRAME;
+
+  // MM21 is an uncompressed opaque format that is produced by MediaTek
+  // video decoders.
+  const uint32_t kUncompressedFourcc = v4l2_fourcc('M', 'M', '2', '1');
+
+  if (!VerifyCapabilities(v4l2_dev_file.GetPlatformFile(), kDriverCodecFourcc,
+                          kUncompressedFourcc)) {
+    LOG(ERROR) << "Device doesn't support the provided FourCCs.";
+    return nullptr;
+  }
+
+  return base::WrapUnique(new Vp9Decoder(std::move(ivf_parser), v4l2_dev_file));
+}
+
+// static
+bool Vp9Decoder::QueryFormat(base::PlatformFile v4l_fd,
+                             enum v4l2_buf_type type,
+                             uint32_t fourcc) {
+  struct v4l2_fmtdesc fmtdesc;
+  memset(&fmtdesc, 0, sizeof(fmtdesc));
+
+  fmtdesc.type = type;
+
+  while (ioctl(v4l_fd, VIDIOC_ENUM_FMT, &fmtdesc) == 0) {
+    if (fourcc == fmtdesc.pixelformat)
+      return true;
+
+    fmtdesc.index++;
+  }
+
+  return false;
+}
+
+// static
+bool Vp9Decoder::VerifyCapabilities(base::PlatformFile v4l_fd,
+                                    uint32_t compressed_format,
+                                    uint32_t uncompressed_format) {
+  struct v4l2_capability cap;
+  memset(&cap, 0, sizeof(cap));
+
+  if (ioctl(v4l_fd, VIDIOC_QUERYCAP, &cap) != 0) {
+    PLOG(ERROR) << "VIDIOC_QUERYCAP failed";
+    return false;
+  }
+
+  LOG(INFO) << "Driver=\"" << cap.driver << "\" bus_info=\"" << cap.bus_info
+            << "\" card=\"" << cap.card << "\" fd=0x" << std::hex << v4l_fd;
+
+  const bool is_compressed_format_supported =
+      QueryFormat(v4l_fd, V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE, compressed_format);
+
+  LOG_IF(ERROR, !is_compressed_format_supported)
+      << media::FourccToString(compressed_format)
+      << " is not a supported compressed OUTPUT format.";
+
+  const bool is_uncompressed_format_supported = QueryFormat(
+      v4l_fd, V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE, uncompressed_format);
+
+  LOG_IF(ERROR, !is_uncompressed_format_supported)
+      << media::FourccToString(uncompressed_format)
+      << " is not a supported uncompressed CAPTURE format.";
+
+  return is_compressed_format_supported && is_uncompressed_format_supported;
+}
+
 Vp9Parser::Result Vp9Decoder::ReadNextFrame(Vp9FrameHeader* vp9_frame_header,
                                             gfx::Size& size) {
   DCHECK(vp9_frame_header);
diff --git a/media/gpu/v4l2/test/vp9_decoder.h b/media/gpu/v4l2/test/vp9_decoder.h
index ee146375..80d12e2 100644
--- a/media/gpu/v4l2/test/vp9_decoder.h
+++ b/media/gpu/v4l2/test/vp9_decoder.h
@@ -5,6 +5,9 @@
 #ifndef MEDIA_GPU_V4L2_TEST_VP9_DECODER_H_
 #define MEDIA_GPU_V4L2_TEST_VP9_DECODER_H_
 
+#include <linux/videodev2.h>
+
+#include "base/files/memory_mapped_file.h"
 #include "media/filters/vp9_parser.h"
 
 namespace media {
@@ -16,12 +19,31 @@
 // A Vp9Decoder decodes VP9-encoded IVF streams using v4l2 ioctl calls.
 class Vp9Decoder {
  public:
-  explicit Vp9Decoder(std::unique_ptr<IvfParser> ivf_parser);
   Vp9Decoder(const Vp9Decoder&) = delete;
   Vp9Decoder& operator=(const Vp9Decoder&) = delete;
   ~Vp9Decoder();
 
+  // Creates |Vp9Decoder| after calling VerifyCapabilities() function to make
+  // sure provided OUTPUT and CAPTURE formats are supported.
+  static std::unique_ptr<Vp9Decoder> Create(
+      std::unique_ptr<IvfParser> ivf_parser,
+      base::File& v4l2_dev_file);
+
  private:
+  Vp9Decoder(std::unique_ptr<IvfParser> ivf_parser, base::File& v4l2_dev_file);
+
+  // Queries |v4l_fd| to see if it can use the specified |fourcc| format
+  // for the given buffer |type|.
+  static bool QueryFormat(base::PlatformFile v4l_fd,
+                          enum v4l2_buf_type type,
+                          uint32_t fourcc);
+
+  // Verifies |v4l_fd| supports |compressed_format| for OUTPUT queues
+  // and |uncompressed_format| for CAPTURE queues, respectively.
+  static bool VerifyCapabilities(base::PlatformFile v4l_fd,
+                                 uint32_t compressed_format,
+                                 uint32_t uncompressed_format);
+
   // Reads next frame from IVF stream and its size into |vp9_frame_header|
   // and |size| respectively.
   Vp9Parser::Result ReadNextFrame(Vp9FrameHeader* vp9_frame_header,
diff --git a/media/gpu/video_decode_accelerator_tests.cc b/media/gpu/video_decode_accelerator_tests.cc
index 11188b2a..b1c5c8f3 100644
--- a/media/gpu/video_decode_accelerator_tests.cc
+++ b/media/gpu/video_decode_accelerator_tests.cc
@@ -4,6 +4,7 @@
 
 #include <limits>
 
+#include "base/callback_helpers.h"
 #include "base/command_line.h"
 #include "base/files/file_util.h"
 #include "base/numerics/safe_conversions.h"
diff --git a/media/gpu/video_encode_accelerator_perf_tests.cc b/media/gpu/video_encode_accelerator_perf_tests.cc
index d431d432e..02930ef 100644
--- a/media/gpu/video_encode_accelerator_perf_tests.cc
+++ b/media/gpu/video_encode_accelerator_perf_tests.cc
@@ -10,6 +10,7 @@
 #include "base/command_line.h"
 #include "base/files/file_util.h"
 #include "base/json/json_writer.h"
+#include "base/strings/string_number_conversions.h"
 #include "media/base/bitstream_buffer.h"
 #include "media/base/media_util.h"
 #include "media/base/test_data_util.h"
diff --git a/media/mojo/mojom/media_player.mojom b/media/mojo/mojom/media_player.mojom
index 2d7ce73..fe8f5ee 100644
--- a/media/mojo/mojom/media_player.mojom
+++ b/media/mojo/mojom/media_player.mojom
@@ -9,7 +9,8 @@
 import "services/media_session/public/mojom/media_session.mojom";
 import "ui/gfx/geometry/mojom/geometry.mojom";
 
-// Implemented by HTMLMediaElement in the renderer process.
+// Implemented by HTMLMediaElement in the renderer process to allow the
+// browser to control media playback.
 interface MediaPlayer {
   // Requests the media player to start or resume media playback.
   RequestPlay();
@@ -32,6 +33,9 @@
   // Requests the media player to exit the Picture-in-Picture mode.
   RequestExitPictureInPicture();
 
+  // Requests the media player to mute or unmute.
+  RequestMute(bool mute);
+
   // Set the volume multiplier to control audio ducking.
   // Output volume should be set to |player_volume| * |multiplier|. The range
   // of |multiplier| is [0, 1], where 1 indicates normal (non-ducked) playback.
diff --git a/net/base/host_port_pair.cc b/net/base/host_port_pair.cc
index 5e4c2d3a..32e48c53 100644
--- a/net/base/host_port_pair.cc
+++ b/net/base/host_port_pair.cc
@@ -5,6 +5,7 @@
 #include "net/base/host_port_pair.h"
 
 #include "base/logging.h"
+#include "base/numerics/safe_conversions.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/string_piece.h"
 #include "base/strings/string_split.h"
@@ -12,8 +13,7 @@
 #include "base/strings/stringprintf.h"
 #include "base/trace_event/memory_usage_estimator.h"
 #include "net/base/ip_endpoint.h"
-#include "net/base/parse_number.h"
-#include "net/base/port_util.h"
+#include "net/base/url_util.h"
 #include "url/gurl.h"
 #include "url/scheme_host_port.h"
 
@@ -50,20 +50,30 @@
 }
 
 // static
-HostPortPair HostPortPair::FromString(const std::string& str) {
-  std::vector<base::StringPiece> key_port = base::SplitStringPiece(
-      str, ":", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL);
-  if (key_port.size() != 2)
+HostPortPair HostPortPair::FromString(base::StringPiece str) {
+  // Input with more than one ':' is ambiguous unless it contains an IPv6
+  // literal (signified by starting with a '['). ParseHostAndPort() allows such
+  // input and always uses the last ':' as the host/port delimiter, but because
+  // HostPortPair often deals with IPv6 literals without brackets, disallow such
+  // input here to prevent a common error.
+  if (base::SplitStringPiece(str, ":", base::KEEP_WHITESPACE,
+                             base::SPLIT_WANT_ALL)
+              .size() > 2 &&
+      str.front() != '[') {
     return HostPortPair();
+  }
+
+  std::string host;
   int port;
-  if (!ParseInt32(key_port[1], ParseIntFormat::NON_NEGATIVE, &port))
+  if (!ParseHostAndPort(str, &host, &port))
     return HostPortPair();
-  if (!IsPortValid(port))
+
+  // Require a valid port.
+  if (port == -1)
     return HostPortPair();
-  HostPortPair host_port_pair;
-  host_port_pair.set_host(std::string(key_port[0]));
-  host_port_pair.set_port(static_cast<uint16_t>(port));
-  return host_port_pair;
+  DCHECK(base::IsValueInRangeForNumericType<uint16_t>(port));
+
+  return HostPortPair(host, port);
 }
 
 std::string HostPortPair::ToString() const {
diff --git a/net/base/host_port_pair.h b/net/base/host_port_pair.h
index 6a006da..ae37263 100644
--- a/net/base/host_port_pair.h
+++ b/net/base/host_port_pair.h
@@ -40,7 +40,7 @@
 
   // Creates a HostPortPair from a string formatted in same manner as
   // ToString().
-  static HostPortPair FromString(const std::string& str);
+  static HostPortPair FromString(base::StringPiece str);
 
   // TODO(willchan): Define a functor instead.
   // Comparator function so this can be placed in a std::map.
diff --git a/net/base/host_port_pair_unittest.cc b/net/base/host_port_pair_unittest.cc
index 99e7c89..f50de395 100644
--- a/net/base/host_port_pair_unittest.cc
+++ b/net/base/host_port_pair_unittest.cc
@@ -37,13 +37,24 @@
   EXPECT_TRUE(foo.Equals(bar));
 }
 
+TEST(HostPortPairTest, ParsingIpv6) {
+  HostPortPair foo("2001:db8::42", 100);
+  string foo_str = foo.ToString();
+  EXPECT_EQ("[2001:db8::42]:100", foo_str);
+  HostPortPair bar = HostPortPair::FromString(foo_str);
+  EXPECT_TRUE(foo.Equals(bar));
+}
+
 TEST(HostPortPairTest, BadString) {
-  const char* kBadStrings[] = {
-      "foo.com:2:3",       "bar.com:two",     "www.google.com:-1",
-      "www.google.com:+1", "127.0.0.1:65536", "[2001:db8::42]:65536",
-  };
+  const char* kBadStrings[] = {"foo.com",           "foo.com:",
+                               "foo.com:2:3",       "bar.com:two",
+                               "www.google.com:-1", "www.google.com:+1",
+                               "127.0.0.1:65536",   "[2001:db8::42]:65536",
+                               "[2001:db8::42",     "2001:db8::42",
+                               "2001:db8::42:100",  "[2001:db8::42]"};
 
   for (const auto* const test : kBadStrings) {
+    SCOPED_TRACE(test);
     HostPortPair foo = HostPortPair::FromString(test);
     EXPECT_TRUE(foo.host().empty());
     EXPECT_EQ(0, foo.port());
diff --git a/net/quic/test_task_runner.cc b/net/quic/test_task_runner.cc
index a91b285d..e7ca259 100644
--- a/net/quic/test_task_runner.cc
+++ b/net/quic/test_task_runner.cc
@@ -33,7 +33,7 @@
   EXPECT_GE(delay, base::TimeDelta());
   tasks_.push_back(PostedTask(from_here, std::move(task), NowInTicks(*clock_),
                               delay, base::TestPendingTask::NESTABLE));
-  return false;
+  return true;
 }
 
 bool TestTaskRunner::PostNonNestableDelayedTask(const base::Location& from_here,
diff --git a/pdf/pdfium/pdfium_engine.cc b/pdf/pdfium/pdfium_engine.cc
index 341b1839..847038f7 100644
--- a/pdf/pdfium/pdfium_engine.cc
+++ b/pdf/pdfium/pdfium_engine.cc
@@ -25,6 +25,7 @@
 #include "base/location.h"
 #include "base/notreached.h"
 #include "base/stl_util.h"
+#include "base/strings/string_number_conversions.h"
 #include "base/strings/string_util.h"
 #include "base/strings/stringprintf.h"
 #include "base/strings/utf_string_conversions.h"
diff --git a/remoting/client/jni/jni_directory_service.cc b/remoting/client/jni/jni_directory_service.cc
index 711645fb..75d706b 100644
--- a/remoting/client/jni/jni_directory_service.cc
+++ b/remoting/client/jni/jni_directory_service.cc
@@ -34,14 +34,11 @@
   switch (status_code) {
     case ProtobufHttpStatus::Code::UNAVAILABLE:
       return JniDirectoryService::RequestError::SERVICE_UNAVAILABLE;
-      break;
     case ProtobufHttpStatus::Code::PERMISSION_DENIED:
     case ProtobufHttpStatus::Code::UNAUTHENTICATED:
       return JniDirectoryService::RequestError::AUTH_FAILED;
-      break;
     default:
       return JniDirectoryService::RequestError::UNKNOWN;
-      break;
   }
 }
 
diff --git a/remoting/protocol/webrtc_transport.cc b/remoting/protocol/webrtc_transport.cc
index d58569d..6485bf4 100644
--- a/remoting/protocol/webrtc_transport.cc
+++ b/remoting/protocol/webrtc_transport.cc
@@ -15,6 +15,7 @@
 #include "base/command_line.h"
 #include "base/logging.h"
 #include "base/macros.h"
+#include "base/memory/ptr_util.h"
 #include "base/single_thread_task_runner.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/string_split.h"
diff --git a/sandbox/policy/mojom/sandbox.mojom b/sandbox/policy/mojom/sandbox.mojom
index b0cddf3e..891e924 100644
--- a/sandbox/policy/mojom/sandbox.mojom
+++ b/sandbox/policy/mojom/sandbox.mojom
@@ -55,6 +55,10 @@
   [EnableIf=is_win]
   kNoSandboxAndElevatedPrivileges,
 
+  // Like kUtility, but patches GDI during child startup in pdf_child_init.
+  [EnableIf=is_win]
+  kPdfConversion,
+
   // Like kUtility but also disables dynamic code.
   [EnableIf=is_win]
   kProxyResolver,
@@ -75,4 +79,8 @@
   // Chrome branded builds.
   [EnableIf=is_chromeos_ash]
   kLibassistant,
+
+  // Like kUtility but allows access to IOSurface on macOS.
+  [EnableIf=is_mac]
+  kMirroring,
 };
diff --git a/sandbox/policy/sandbox_type.h b/sandbox/policy/sandbox_type.h
index 5db2845..1d48c21 100644
--- a/sandbox/policy/sandbox_type.h
+++ b/sandbox/policy/sandbox_type.h
@@ -142,11 +142,17 @@
       return sandbox::policy::SandboxType::kMediaFoundationCdm;
     case sandbox::mojom::Sandbox::kNoSandboxAndElevatedPrivileges:
       return sandbox::policy::SandboxType::kNoSandboxAndElevatedPrivileges;
+    case sandbox::mojom::Sandbox::kPdfConversion:
+      return sandbox::policy::SandboxType::kPdfConversion;
     case sandbox::mojom::Sandbox::kProxyResolver:
       return sandbox::policy::SandboxType::kProxyResolver;
     case sandbox::mojom::Sandbox::kXrCompositing:
       return sandbox::policy::SandboxType::kXrCompositing;
 #endif  // OS_WIN
+#if defined(OS_MAC)
+    case sandbox::mojom::Sandbox::kMirroring:
+      return sandbox::policy::SandboxType::kMirroring;
+#endif  // defined(OS_MAC)
 #if BUILDFLAG(IS_CHROMEOS_ASH)
     case sandbox::mojom::Sandbox::kIme:
       return sandbox::policy::SandboxType::kIme;
diff --git a/sandbox/win/src/app_container_test.cc b/sandbox/win/src/app_container_test.cc
index 29d83ed..05164b71 100644
--- a/sandbox/win/src/app_container_test.cc
+++ b/sandbox/win/src/app_container_test.cc
@@ -21,6 +21,7 @@
 #include "base/rand_util.h"
 #include "base/scoped_native_library.h"
 #include "base/strings/strcat.h"
+#include "base/strings/string_number_conversions.h"
 #include "base/strings/stringprintf.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/task/thread_pool.h"
diff --git a/services/cert_verifier/trial_comparison_cert_verifier_mojo.h b/services/cert_verifier/trial_comparison_cert_verifier_mojo.h
index 9b5b0a0..49adbc6 100644
--- a/services/cert_verifier/trial_comparison_cert_verifier_mojo.h
+++ b/services/cert_verifier/trial_comparison_cert_verifier_mojo.h
@@ -28,7 +28,7 @@
 
 // Wrapper around TrialComparisonCertVerifier that does trial configuration and
 // reporting over Mojo pipes.
-class COMPONENT_EXPORT(NETWORK_SERVICE) TrialComparisonCertVerifierMojo
+class TrialComparisonCertVerifierMojo
     : public net::CertVerifier,
       public mojom::TrialComparisonCertVerifierConfigClient {
  public:
diff --git a/services/device/generic_sensor/platform_sensor_and_provider_unittest_linux.cc b/services/device/generic_sensor/platform_sensor_and_provider_unittest_linux.cc
index ad1e6c7..05b36db 100644
--- a/services/device/generic_sensor/platform_sensor_and_provider_unittest_linux.cc
+++ b/services/device/generic_sensor/platform_sensor_and_provider_unittest_linux.cc
@@ -167,7 +167,6 @@
 // error and data changes notifications.
 class LinuxMockPlatformSensorClient : public PlatformSensor::Client {
  public:
-  LinuxMockPlatformSensorClient() = default;
   explicit LinuxMockPlatformSensorClient(scoped_refptr<PlatformSensor> sensor)
       : sensor_(sensor) {
     if (sensor_)
diff --git a/services/device/usb/usb_descriptors.cc b/services/device/usb/usb_descriptors.cc
index 498e855..54962c2 100644
--- a/services/device/usb/usb_descriptors.cc
+++ b/services/device/usb/usb_descriptors.cc
@@ -199,27 +199,52 @@
     size_t length) {
   std::u16string string;
   if (status == UsbTransferStatus::COMPLETED &&
-      ParseUsbStringDescriptor(
-          std::vector<uint8_t>(buffer->front(), buffer->front() + length),
-          &string)) {
+      ParseUsbStringDescriptor(base::make_span(buffer->front(), length),
+                               &string)) {
     std::move(callback).Run(string);
   } else {
     std::move(callback).Run(std::u16string());
   }
 }
 
+void OnReadStringDescriptorHeader(
+    scoped_refptr<UsbDeviceHandle> device_handle,
+    uint8_t index,
+    uint16_t language_id,
+    base::OnceCallback<void(const std::u16string&)> callback,
+    UsbTransferStatus status,
+    scoped_refptr<base::RefCountedBytes> header,
+    size_t length) {
+  if (status == UsbTransferStatus::COMPLETED && length == 2) {
+    const uint8_t* data = header->front();
+    uint8_t actual_length = data[0];
+    auto buffer = base::MakeRefCounted<base::RefCountedBytes>(actual_length);
+    device_handle->ControlTransfer(
+        UsbTransferDirection::INBOUND, UsbControlTransferType::STANDARD,
+        UsbControlTransferRecipient::DEVICE, kGetDescriptorRequest,
+        kStringDescriptorType << 8 | index, language_id, buffer,
+        kControlTransferTimeoutMs,
+        base::BindOnce(&OnReadStringDescriptor, std::move(callback)));
+  } else {
+    LOG(ERROR) << "Failed to read length for string " << static_cast<int>(index)
+               << ".";
+    std::move(callback).Run(std::u16string());
+  }
+}
+
 void ReadStringDescriptor(
     scoped_refptr<UsbDeviceHandle> device_handle,
     uint8_t index,
     uint16_t language_id,
     base::OnceCallback<void(const std::u16string&)> callback) {
-  auto buffer = base::MakeRefCounted<base::RefCountedBytes>(255);
+  auto buffer = base::MakeRefCounted<base::RefCountedBytes>(2);
   device_handle->ControlTransfer(
       UsbTransferDirection::INBOUND, UsbControlTransferType::STANDARD,
       UsbControlTransferRecipient::DEVICE, kGetDescriptorRequest,
       kStringDescriptorType << 8 | index, language_id, buffer,
       kControlTransferTimeoutMs,
-      base::BindOnce(&OnReadStringDescriptor, std::move(callback)));
+      base::BindOnce(&OnReadStringDescriptorHeader, device_handle, index,
+                     language_id, std::move(callback)));
 }
 
 void OnReadLanguageIds(scoped_refptr<UsbDeviceHandle> device_handle,
@@ -361,7 +386,7 @@
                      std::move(callback)));
 }
 
-bool ParseUsbStringDescriptor(const std::vector<uint8_t>& descriptor,
+bool ParseUsbStringDescriptor(base::span<const uint8_t> descriptor,
                               std::u16string* output) {
   if (descriptor.size() < 2 || descriptor[1] != kStringDescriptorType)
     return false;
diff --git a/services/device/usb/usb_descriptors.h b/services/device/usb/usb_descriptors.h
index eca03af1..6bd9267 100644
--- a/services/device/usb/usb_descriptors.h
+++ b/services/device/usb/usb_descriptors.h
@@ -10,9 +10,9 @@
 #include <map>
 #include <memory>
 #include <string>
-#include <vector>
 
 #include "base/callback_forward.h"
+#include "base/containers/span.h"
 #include "base/memory/ref_counted.h"
 #include "services/device/public/mojom/usb_device.mojom.h"
 
@@ -54,7 +54,7 @@
     scoped_refptr<UsbDeviceHandle> device_handle,
     base::OnceCallback<void(std::unique_ptr<UsbDeviceDescriptor>)> callback);
 
-bool ParseUsbStringDescriptor(const std::vector<uint8_t>& descriptor,
+bool ParseUsbStringDescriptor(base::span<const uint8_t> descriptor,
                               std::u16string* output);
 
 void ReadUsbStringDescriptors(
diff --git a/services/device/usb/usb_descriptors_unittest.cc b/services/device/usb/usb_descriptors_unittest.cc
index 19602b40..8137ce0 100644
--- a/services/device/usb/usb_descriptors_unittest.cc
+++ b/services/device/usb/usb_descriptors_unittest.cc
@@ -427,7 +427,9 @@
                                       UsbControlTransferType::STANDARD,
                                       UsbControlTransferRecipient::DEVICE, 0x06,
                                       0x0300, 0x0000, _, _, _))
+      .WillOnce(InvokeCallback(kStringDescriptor0, size_t{2}))
       .WillOnce(InvokeCallback(kStringDescriptor0, sizeof(kStringDescriptor0)));
+
   static const uint8_t kStringDescriptor1[] = {0x12, 0x03, 'S', 0, 't', 0,
                                                'r',  0,    'i', 0, 'n', 0,
                                                'g',  0,    ' ', 0, '1', 0};
@@ -436,7 +438,9 @@
                                       UsbControlTransferType::STANDARD,
                                       UsbControlTransferRecipient::DEVICE, 0x06,
                                       0x0301, 0x4321, _, _, _))
+      .WillOnce(InvokeCallback(kStringDescriptor1, size_t{2}))
       .WillOnce(InvokeCallback(kStringDescriptor1, sizeof(kStringDescriptor1)));
+
   static const uint8_t kStringDescriptor2[] = {0x12, 0x03, 'S', 0, 't', 0,
                                                'r',  0,    'i', 0, 'n', 0,
                                                'g',  0,    ' ', 0, '2', 0};
@@ -445,7 +449,9 @@
                                       UsbControlTransferType::STANDARD,
                                       UsbControlTransferRecipient::DEVICE, 0x06,
                                       0x0302, 0x4321, _, _, _))
+      .WillOnce(InvokeCallback(kStringDescriptor2, size_t{2}))
       .WillOnce(InvokeCallback(kStringDescriptor2, sizeof(kStringDescriptor2)));
+
   static const uint8_t kStringDescriptor3[] = {0x12, 0x03, 'S', 0, 't', 0,
                                                'r',  0,    'i', 0, 'n', 0,
                                                'g',  0,    ' ', 0, '3', 0};
@@ -454,6 +460,7 @@
                                       UsbControlTransferType::STANDARD,
                                       UsbControlTransferRecipient::DEVICE, 0x06,
                                       0x0303, 0x4321, _, _, _))
+      .WillOnce(InvokeCallback(kStringDescriptor3, size_t{2}))
       .WillOnce(InvokeCallback(kStringDescriptor3, sizeof(kStringDescriptor3)));
 
   ReadUsbStringDescriptors(device_handle, std::move(string_map),
diff --git a/services/media_session/media_controller.cc b/services/media_session/media_controller.cc
index 19b8ef46..a07d15e9 100644
--- a/services/media_session/media_controller.cc
+++ b/services/media_session/media_controller.cc
@@ -328,6 +328,13 @@
     session_->ipc()->Raise();
 }
 
+void MediaController::SetMute(bool mute) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+  if (session_)
+    session_->ipc()->SetMute(mute);
+}
+
 void MediaController::SetMediaSession(AudioFocusRequest* session) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 
diff --git a/services/media_session/media_controller.h b/services/media_session/media_controller.h
index 0177aac..3e3ece08 100644
--- a/services/media_session/media_controller.h
+++ b/services/media_session/media_controller.h
@@ -57,6 +57,7 @@
   void ToggleCamera() override;
   void HangUp() override;
   void Raise() override;
+  void SetMute(bool mute) override;
 
   // mojom::MediaSessionObserver overrides.
   void MediaSessionInfoChanged(
diff --git a/services/media_session/public/cpp/test/mock_media_session.h b/services/media_session/public/cpp/test/mock_media_session.h
index 53e4605..265ab41a 100644
--- a/services/media_session/public/cpp/test/mock_media_session.h
+++ b/services/media_session/public/cpp/test/mock_media_session.h
@@ -168,6 +168,7 @@
   void ToggleCamera() override {}
   void HangUp() override {}
   void Raise() override {}
+  void SetMute(bool mute) override {}
 
   void SetIsControllable(bool value);
   void SetPreferStop(bool value) { prefer_stop_ = value; }
diff --git a/services/media_session/public/cpp/test/test_media_controller.h b/services/media_session/public/cpp/test/test_media_controller.h
index 9d31b635..9a89eb76 100644
--- a/services/media_session/public/cpp/test/test_media_controller.h
+++ b/services/media_session/public/cpp/test/test_media_controller.h
@@ -160,6 +160,7 @@
   void ToggleCamera() override {}
   void HangUp() override {}
   void Raise() override {}
+  void SetMute(bool mute) override {}
 
   int toggle_suspend_resume_count() const {
     return toggle_suspend_resume_count_;
diff --git a/services/media_session/public/cpp/util.cc b/services/media_session/public/cpp/util.cc
index 9954917..0aba3467 100644
--- a/services/media_session/public/cpp/util.cc
+++ b/services/media_session/public/cpp/util.cc
@@ -58,6 +58,7 @@
     case mojom::MediaSessionAction::kRaise:
       media_controller_remote->Raise();
       break;
+    case mojom::MediaSessionAction::kSetMute:
     case mojom::MediaSessionAction::kSkipAd:
     case mojom::MediaSessionAction::kSeekTo:
     case mojom::MediaSessionAction::kScrubTo:
diff --git a/services/media_session/public/mojom/media_controller.mojom b/services/media_session/public/mojom/media_controller.mojom
index 9e5ddef..b312a33 100644
--- a/services/media_session/public/mojom/media_controller.mojom
+++ b/services/media_session/public/mojom/media_controller.mojom
@@ -33,7 +33,7 @@
 // session, otherwise media sessions will be associated with a particular media
 // session provided to the service when creating the media controller. If the
 // media session is not controllable then the commands will be no-ops.
-// Next Method ID: 18
+// Next Method ID: 19
 [Stable]
 interface MediaController {
   // Suspend the media session.
@@ -109,6 +109,9 @@
   // Display the source of the MediaSession (e.g. show the tab or the
   // application).
   [MinVersion=2] Raise@17();
+
+  // Mute or unmute the media player.
+  [MinVersion=3] SetMute@18(bool mute);
 };
 
 // The observer for observing media controller events. This is different to a
diff --git a/services/media_session/public/mojom/media_session.mojom b/services/media_session/public/mojom/media_session.mojom
index 4658fcd..b376d2a 100644
--- a/services/media_session/public/mojom/media_session.mojom
+++ b/services/media_session/public/mojom/media_session.mojom
@@ -36,6 +36,7 @@
   [MinVersion=11] kToggleCamera,
   [MinVersion=11] kHangUp,
   [MinVersion=12] kRaise,
+  [MinVersion=13] kSetMute,
 };
 
 [Stable, Extensible]
@@ -189,6 +190,9 @@
 
   // Tracks whether the camera is turned on in WebRTC sessions.
   [MinVersion=11] CameraState camera_state;
+
+  // Tracks whether the media player is muted.
+  [MinVersion=12] bool muted;
 };
 
 // Contains debugging information about a MediaSession. This will be displayed
@@ -331,4 +335,7 @@
   // Display the source of the MediaSession (e.g. show the tab or the
   // application).
   [MinVersion=12] Raise@21();
+
+  // Mute or unmute the media player.
+  [MinVersion=13] SetMute@22(bool mute);
 };
diff --git a/services/network/public/cpp/cross_origin_read_blocking.cc b/services/network/public/cpp/cross_origin_read_blocking.cc
index 391f488..2a2991e7 100644
--- a/services/network/public/cpp/cross_origin_read_blocking.cc
+++ b/services/network/public/cpp/cross_origin_read_blocking.cc
@@ -551,11 +551,8 @@
   virtual ~ConfirmationSniffer() = default;
 
   // Called after data is read from the network. |sniffing_buffer| contains the
-  // entire response body delivered thus far. To support streaming,
-  // |new_data_offset| gives the offset into |sniffing_buffer| at which new data
-  // was appended since the last read.
-  virtual void OnDataAvailable(base::StringPiece sniffing_buffer,
-                               size_t new_data_offset) = 0;
+  // entire response body delivered thus far.
+  virtual void OnDataAvailable(base::StringPiece sniffing_buffer) = 0;
 
   // Returns true if the return value of IsConfirmedContentType() might change
   // with the addition of more data. Returns false if a final decision is
@@ -581,13 +578,7 @@
       : sniffer_function_(sniffer_function) {}
   ~SimpleConfirmationSniffer() override = default;
 
-  void OnDataAvailable(base::StringPiece sniffing_buffer,
-                       size_t new_data_offset) final {
-    DCHECK_LE(new_data_offset, sniffing_buffer.length());
-    if (new_data_offset == sniffing_buffer.length()) {
-      // No new data -- do nothing. This happens at end-of-stream.
-      return;
-    }
+  void OnDataAvailable(base::StringPiece sniffing_buffer) final {
     // The sniffing functions don't support streaming, so with each new chunk of
     // data, call the sniffer on the whole buffer.
     last_sniff_result_ = (*sniffer_function_)(sniffing_buffer);
@@ -964,19 +955,15 @@
 }
 
 void CrossOriginReadBlocking::ResponseAnalyzer::SniffResponseBody(
-    base::StringPiece data,
-    size_t new_data_offset) {
+    base::StringPiece data) {
   DCHECK(needs_sniffing());
   DCHECK(!sniffers_.empty());
   DCHECK(!found_blockable_content_);
 
   DCHECK_LE(data.size(), static_cast<size_t>(net::kMaxBytesToSniff));
-  DCHECK_LE(new_data_offset, data.size());
-  bool has_new_data = (new_data_offset < data.size());
 
   for (size_t i = 0; i < sniffers_.size();) {
-    if (has_new_data)
-      sniffers_[i]->OnDataAvailable(data, new_data_offset);
+    sniffers_[i]->OnDataAvailable(data);
 
     if (sniffers_[i]->WantsMoreData()) {
       i++;
diff --git a/services/network/public/cpp/cross_origin_read_blocking.h b/services/network/public/cpp/cross_origin_read_blocking.h
index be43e7b..ecef7e1 100644
--- a/services/network/public/cpp/cross_origin_read_blocking.h
+++ b/services/network/public/cpp/cross_origin_read_blocking.h
@@ -151,7 +151,7 @@
     }
 
     // Allows ResponseAnalyzer to sniff the response body.
-    void SniffResponseBody(base::StringPiece data, size_t new_data_offset);
+    void SniffResponseBody(base::StringPiece data);
 
     class ConfirmationSniffer;
     class SimpleConfirmationSniffer;
diff --git a/services/network/public/cpp/cross_origin_read_blocking_unittest.cc b/services/network/public/cpp/cross_origin_read_blocking_unittest.cc
index 4f2b0d5..50780568 100644
--- a/services/network/public/cpp/cross_origin_read_blocking_unittest.cc
+++ b/services/network/public/cpp/cross_origin_read_blocking_unittest.cc
@@ -1953,9 +1953,8 @@
         bytes_to_append = net::kMaxBytesToSniff - data_offset;
       data_buffer.append(packets_vector[packet_index], bytes_to_append);
 
-      // Hand |analyzer_| the data and an offset indicating what point it last
-      // read to.
-      analyzer_->SniffResponseBody(data_buffer, data_offset);
+      // Hand |analyzer_| the data to sniff.
+      analyzer_->SniffResponseBody(data_buffer);
       data_offset += bytes_to_append;
 
       // If the latest packet was empty, or we reached net::kMaxBytesToSniff
diff --git a/services/network/url_loader.cc b/services/network/url_loader.cc
index 0c0f52fd..2b6e06e7 100644
--- a/services/network/url_loader.cc
+++ b/services/network/url_loader.cc
@@ -1531,8 +1531,10 @@
         response_->did_mime_sniff = true;
       }
 
-      if (is_more_corb_sniffing_needed_) {
-        corb_analyzer_->SniffResponseBody(data, new_data_offset);
+      // `has_new_data_to_sniff` can be false at the end-of-stream.
+      bool has_new_data_to_sniff = new_data_offset < data.length();
+      if (is_more_corb_sniffing_needed_ && has_new_data_to_sniff) {
+        corb_analyzer_->SniffResponseBody(data);
         if (corb_analyzer_->ShouldBlock()) {
           corb_analyzer_->LogBlockedResponse();
           is_more_corb_sniffing_needed_ = false;
diff --git a/storage/browser/file_system/file_system_quota_client.cc b/storage/browser/file_system/file_system_quota_client.cc
index 0e6e601e..195e211 100644
--- a/storage/browser/file_system/file_system_quota_client.cc
+++ b/storage/browser/file_system/file_system_quota_client.cc
@@ -4,12 +4,17 @@
 
 #include "storage/browser/file_system/file_system_quota_client.h"
 
+#include <numeric>
 #include <string>
 #include <utility>
 #include <vector>
 
+#include "base/barrier_callback.h"
+#include "base/barrier_closure.h"
 #include "base/bind.h"
 #include "base/check.h"
+#include "base/containers/span.h"
+#include "base/feature_list.h"
 #include "base/files/file.h"
 #include "base/location.h"
 #include "base/sequence_checker.h"
@@ -18,6 +23,7 @@
 #include "storage/browser/file_system/file_system_context.h"
 #include "storage/common/file_system/file_system_types.h"
 #include "storage/common/file_system/file_system_util.h"
+#include "third_party/blink/public/common/features.h"
 #include "third_party/blink/public/common/storage_key/storage_key.h"
 #include "third_party/blink/public/mojom/quota/quota_types.mojom.h"
 #include "url/origin.h"
@@ -26,6 +32,14 @@
 
 namespace {
 
+static const FileSystemType kTemporaryAndPersistent[] = {
+    kFileSystemTypeTemporary,
+    kFileSystemTypePersistent,
+};
+static const FileSystemType kTemporary[] = {kFileSystemTypeTemporary};
+static const FileSystemType kPersistent[] = {kFileSystemTypePersistent};
+static const FileSystemType kSyncable[] = {kFileSystemTypeSyncable};
+
 std::vector<blink::StorageKey> ToStorageKeys(
     const std::vector<url::Origin>& origins) {
   std::vector<blink::StorageKey> storage_keys;
@@ -35,13 +49,53 @@
   return storage_keys;
 }
 
+template <typename T>
+std::vector<T> MergeWithoutDuplicates(const std::vector<std::vector<T>>& tss) {
+  if (tss.size() == 1) {
+    // We assume that each vector contains no duplicates, already.
+    return tss[0];
+  }
+  std::vector<T> merged;
+  merged.reserve(std::accumulate(
+      tss.begin(), tss.end(), 0U,
+      [](size_t acc, const std::vector<T>& ts) { return acc + ts.size(); }));
+  for (const auto& ts : tss) {
+    merged.insert(merged.end(), ts.begin(), ts.end());
+  }
+  base::ranges::sort(merged);
+  merged.erase(base::ranges::unique(merged), merged.end());
+  return merged;
+}
+
+// Converts StorageType to the FileSystemTypes that are used for that quota
+// type.
+base::span<const FileSystemType> QuotaStorageTypeToFileSystemTypes(
+    blink::mojom::StorageType storage_type) {
+  using StorageType = blink::mojom::StorageType;
+
+  if (base::FeatureList::IsEnabled(
+          blink::features::kPersistentQuotaIsTemporaryQuota)) {
+    DCHECK_NE(storage_type, StorageType::kPersistent);
+    if (storage_type == StorageType::kTemporary)
+      return kTemporaryAndPersistent;
+  }
+  switch (storage_type) {
+    case StorageType::kTemporary:
+      return kTemporary;
+    case StorageType::kPersistent:
+      return kPersistent;
+    case StorageType::kSyncable:
+      return kSyncable;
+    case StorageType::kQuotaNotManaged:
+    case StorageType::kUnknown:
+      NOTREACHED();
+      return {};
+  }
+}
+
 std::vector<blink::StorageKey> GetStorageKeysForTypeOnFileTaskRunner(
     FileSystemContext* context,
-    blink::mojom::StorageType storage_type) {
-  FileSystemType type =
-      FileSystemQuotaClient::QuotaStorageTypeToFileSystemType(storage_type);
-  DCHECK(type != kFileSystemTypeUnknown);
-
+    FileSystemType type) {
   FileSystemQuotaUtil* quota_util = context->GetQuotaUtil(type);
   if (!quota_util)
     return {};
@@ -50,12 +104,8 @@
 
 std::vector<blink::StorageKey> GetStorageKeysForHostOnFileTaskRunner(
     FileSystemContext* context,
-    blink::mojom::StorageType storage_type,
+    FileSystemType type,
     const std::string& host) {
-  FileSystemType type =
-      FileSystemQuotaClient::QuotaStorageTypeToFileSystemType(storage_type);
-  DCHECK(type != kFileSystemTypeUnknown);
-
   FileSystemQuotaUtil* quota_util = context->GetQuotaUtil(type);
   if (!quota_util)
     return {};
@@ -107,24 +157,31 @@
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   DCHECK(!callback.is_null());
 
-  FileSystemType type =
-      FileSystemQuotaClient::QuotaStorageTypeToFileSystemType(storage_type);
-  DCHECK(type != kFileSystemTypeUnknown);
+  base::span<const FileSystemType> types =
+      QuotaStorageTypeToFileSystemTypes(storage_type);
 
-  FileSystemQuotaUtil* quota_util = file_system_context_->GetQuotaUtil(type);
-  if (!quota_util) {
-    std::move(callback).Run(0);
-    return;
+  base::RepeatingCallback<void(int64_t)> barrier =
+      base::BarrierCallback<int64_t>(
+          types.size(), base::BindOnce([](std::vector<int64_t> usages) {
+                          return std::accumulate(usages.begin(), usages.end(),
+                                                 0U);
+                        }).Then(std::move(callback)));
+
+  for (auto type : types) {
+    FileSystemQuotaUtil* quota_util = file_system_context_->GetQuotaUtil(type);
+    if (quota_util) {
+      file_task_runner()->PostTaskAndReplyWithResult(
+          FROM_HERE,
+          // It is safe to pass Unretained(quota_util) since context owns it.
+          base::BindOnce(&FileSystemQuotaUtil::GetOriginUsageOnFileTaskRunner,
+                         base::Unretained(quota_util),
+                         base::RetainedRef(file_system_context_),
+                         storage_key.origin(), type),
+          barrier);
+    } else {
+      barrier.Run(0);
+    }
   }
-
-  file_task_runner()->PostTaskAndReplyWithResult(
-      FROM_HERE,
-      // It is safe to pass Unretained(quota_util) since context owns it.
-      base::BindOnce(&FileSystemQuotaUtil::GetOriginUsageOnFileTaskRunner,
-                     base::Unretained(quota_util),
-                     base::RetainedRef(file_system_context_),
-                     storage_key.origin(), type),
-      std::move(callback));
 }
 
 void FileSystemQuotaClient::GetStorageKeysForType(
@@ -133,11 +190,22 @@
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   DCHECK(!callback.is_null());
 
-  file_task_runner()->PostTaskAndReplyWithResult(
-      FROM_HERE,
-      base::BindOnce(&GetStorageKeysForTypeOnFileTaskRunner,
-                     base::RetainedRef(file_system_context_), storage_type),
-      std::move(callback));
+  base::span<const FileSystemType> types =
+      QuotaStorageTypeToFileSystemTypes(storage_type);
+
+  base::RepeatingCallback<void(std::vector<blink::StorageKey>)> barrier =
+      base::BarrierCallback<std::vector<blink::StorageKey>>(
+          types.size(),
+          base::BindOnce(&MergeWithoutDuplicates<blink::StorageKey>)
+              .Then(std::move(callback)));
+
+  for (auto type : types) {
+    file_task_runner()->PostTaskAndReplyWithResult(
+        FROM_HERE,
+        base::BindOnce(&GetStorageKeysForTypeOnFileTaskRunner,
+                       base::RetainedRef(file_system_context_), type),
+        barrier);
+  }
 }
 
 void FileSystemQuotaClient::GetStorageKeysForHost(
@@ -147,12 +215,22 @@
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   DCHECK(!callback.is_null());
 
-  file_task_runner()->PostTaskAndReplyWithResult(
-      FROM_HERE,
-      base::BindOnce(&GetStorageKeysForHostOnFileTaskRunner,
-                     base::RetainedRef(file_system_context_), storage_type,
-                     host),
-      std::move(callback));
+  base::span<const FileSystemType> types =
+      QuotaStorageTypeToFileSystemTypes(storage_type);
+
+  base::RepeatingCallback<void(std::vector<blink::StorageKey>)> barrier =
+      base::BarrierCallback<std::vector<blink::StorageKey>>(
+          types.size(),
+          base::BindOnce(&MergeWithoutDuplicates<blink::StorageKey>)
+              .Then(std::move(callback)));
+
+  for (auto type : types) {
+    file_task_runner()->PostTaskAndReplyWithResult(
+        FROM_HERE,
+        base::BindOnce(&GetStorageKeysForHostOnFileTaskRunner,
+                       base::RetainedRef(file_system_context_), type, host),
+        barrier);
+  }
 }
 
 void FileSystemQuotaClient::DeleteStorageKeyData(
@@ -162,15 +240,29 @@
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   DCHECK(!callback.is_null());
 
-  FileSystemType fs_type = QuotaStorageTypeToFileSystemType(type);
-  DCHECK(fs_type != kFileSystemTypeUnknown);
+  base::span<const FileSystemType> fs_types =
+      QuotaStorageTypeToFileSystemTypes(type);
 
-  file_task_runner()->PostTaskAndReplyWithResult(
-      FROM_HERE,
-      base::BindOnce(&DeleteStorageKeyOnFileTaskRunner,
-                     base::RetainedRef(file_system_context_), storage_key,
-                     fs_type),
-      std::move(callback));
+  base::RepeatingCallback<void(blink::mojom::QuotaStatusCode)> barrier =
+      base::BarrierCallback<blink::mojom::QuotaStatusCode>(
+          fs_types.size(),
+          base::BindOnce([](const std::vector<blink::mojom::QuotaStatusCode>&
+                                statuses) {
+            for (auto status : statuses) {
+              if (status != blink::mojom::QuotaStatusCode::kOk)
+                return status;
+            }
+            return blink::mojom::QuotaStatusCode::kOk;
+          }).Then(std::move(callback)));
+
+  for (const auto fs_type : fs_types) {
+    file_task_runner()->PostTaskAndReplyWithResult(
+        FROM_HERE,
+        base::BindOnce(&DeleteStorageKeyOnFileTaskRunner,
+                       base::RetainedRef(file_system_context_), storage_key,
+                       fs_type),
+        barrier);
+  }
 }
 
 void FileSystemQuotaClient::PerformStorageCleanup(
@@ -179,30 +271,19 @@
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   DCHECK(!callback.is_null());
 
-  FileSystemType fs_type = QuotaStorageTypeToFileSystemType(type);
-  DCHECK(fs_type != kFileSystemTypeUnknown);
-  file_task_runner()->PostTaskAndReply(
-      FROM_HERE,
-      base::BindOnce(&PerformStorageCleanupOnFileTaskRunner,
-                     base::RetainedRef(file_system_context_), fs_type),
-      std::move(callback));
-}
+  base::span<const FileSystemType> fs_types =
+      QuotaStorageTypeToFileSystemTypes(type);
 
-// static
-FileSystemType FileSystemQuotaClient::QuotaStorageTypeToFileSystemType(
-    blink::mojom::StorageType storage_type) {
-  switch (storage_type) {
-    case blink::mojom::StorageType::kTemporary:
-      return kFileSystemTypeTemporary;
-    case blink::mojom::StorageType::kPersistent:
-      return kFileSystemTypePersistent;
-    case blink::mojom::StorageType::kSyncable:
-      return kFileSystemTypeSyncable;
-    case blink::mojom::StorageType::kQuotaNotManaged:
-    case blink::mojom::StorageType::kUnknown:
-      return kFileSystemTypeUnknown;
+  base::RepeatingClosure barrier =
+      base::BarrierClosure(fs_types.size(), std::move(callback));
+
+  for (auto fs_type : fs_types) {
+    file_task_runner()->PostTaskAndReply(
+        FROM_HERE,
+        base::BindOnce(&PerformStorageCleanupOnFileTaskRunner,
+                       base::RetainedRef(file_system_context_), fs_type),
+        barrier);
   }
-  return kFileSystemTypeUnknown;
 }
 
 base::SequencedTaskRunner* FileSystemQuotaClient::file_task_runner() const {
diff --git a/storage/browser/file_system/file_system_quota_client.h b/storage/browser/file_system/file_system_quota_client.h
index fee07e6..f686c0e 100644
--- a/storage/browser/file_system/file_system_quota_client.h
+++ b/storage/browser/file_system/file_system_quota_client.h
@@ -55,14 +55,6 @@
   void PerformStorageCleanup(blink::mojom::StorageType type,
                              PerformStorageCleanupCallback callback) override;
 
-  // Converts FileSystemType `type` to/from the StorageType `storage_type` that
-  // is used for the unified quota system.
-  // (Basically this naively maps TEMPORARY storage type to TEMPORARY filesystem
-  // type, PERSISTENT storage type to PERSISTENT filesystem type and vice
-  // versa.)
-  static FileSystemType QuotaStorageTypeToFileSystemType(
-      blink::mojom::StorageType storage_type);
-
  private:
   base::SequencedTaskRunner* file_task_runner() const;
 
diff --git a/storage/browser/file_system/file_system_quota_client_unittest.cc b/storage/browser/file_system/file_system_quota_client_unittest.cc
index cb8bdfd..e5c2086 100644
--- a/storage/browser/file_system/file_system_quota_client_unittest.cc
+++ b/storage/browser/file_system/file_system_quota_client_unittest.cc
@@ -13,6 +13,7 @@
 #include "base/files/scoped_temp_dir.h"
 #include "base/macros.h"
 #include "base/run_loop.h"
+#include "base/test/scoped_feature_list.h"
 #include "base/test/task_environment.h"
 #include "components/services/storage/public/mojom/quota_client.mojom.h"
 #include "storage/browser/file_system/file_system_context.h"
@@ -27,6 +28,7 @@
 #include "storage/common/file_system/file_system_util.h"
 #include "testing/gmock/include/gmock/gmock-matchers.h"
 #include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/public/common/features.h"
 #include "third_party/blink/public/common/storage_key/storage_key.h"
 #include "third_party/blink/public/mojom/quota/quota_types.mojom.h"
 #include "url/gurl.h"
@@ -48,17 +50,26 @@
 
 }  // namespace
 
-class FileSystemQuotaClientTest : public testing::Test {
+class FileSystemQuotaClientTest : public testing::TestWithParam<bool> {
  public:
   FileSystemQuotaClientTest() = default;
   ~FileSystemQuotaClientTest() override = default;
 
   void SetUp() override {
+    if (persistent_quota_is_temporary_quota()) {
+      feature_list_.InitAndEnableFeature(
+          blink::features::kPersistentQuotaIsTemporaryQuota);
+    } else {
+      feature_list_.InitAndDisableFeature(
+          blink::features::kPersistentQuotaIsTemporaryQuota);
+    }
     ASSERT_TRUE(data_dir_.CreateUniqueTempDir());
     file_system_context_ = CreateFileSystemContextForTesting(
         /*quota_manager_proxy=*/nullptr, data_dir_.GetPath());
   }
 
+  bool persistent_quota_is_temporary_quota() const { return GetParam(); }
+
   struct TestFile {
     bool isDirectory;
     const char* name;
@@ -169,9 +180,8 @@
           // create it later, this will fail due to a quota mismatch.  If we
           // call this before we create the root, it succeeds, but hasn't
           // actually created the cache.
-          ASSERT_EQ(0, GetStorageKeyUsage(
-                           quota_client, file.origin_url,
-                           FileSystemTypeToQuotaStorageType(file.type)));
+          GetStorageKeyUsage(quota_client, file.origin_url,
+                             FileSystemTypeToQuotaStorageType(file.type));
         }
       } else {
         ASSERT_TRUE(
@@ -232,6 +242,7 @@
     deletion_status_ = status;
   }
 
+  base::test::ScopedFeatureList feature_list_;
   base::ScopedTempDir data_dir_;
   base::test::TaskEnvironment task_environment_;
   scoped_refptr<FileSystemContext> file_system_context_;
@@ -243,13 +254,13 @@
   base::WeakPtrFactory<FileSystemQuotaClientTest> weak_factory_{this};
 };
 
-TEST_F(FileSystemQuotaClientTest, NoFileSystemTest) {
+TEST_P(FileSystemQuotaClientTest, NoFileSystemTest) {
   FileSystemQuotaClient quota_client(GetFileSystemContext());
 
   EXPECT_EQ(0, GetStorageKeyUsage(quota_client, kDummyURL1, kTemporary));
 }
 
-TEST_F(FileSystemQuotaClientTest, NoFileTest) {
+TEST_P(FileSystemQuotaClientTest, NoFileTest) {
   FileSystemQuotaClient quota_client(GetFileSystemContext());
 
   InitializeOriginFiles(quota_client,
@@ -260,7 +271,7 @@
   }
 }
 
-TEST_F(FileSystemQuotaClientTest, OneFileTest) {
+TEST_P(FileSystemQuotaClientTest, OneFileTest) {
   FileSystemQuotaClient quota_client(GetFileSystemContext());
   const std::vector<TestFile> kFiles = {
       {true, "", 0, kDummyURL1, kFileSystemTypeTemporary},
@@ -276,7 +287,7 @@
   }
 }
 
-TEST_F(FileSystemQuotaClientTest, TwoFilesTest) {
+TEST_P(FileSystemQuotaClientTest, TwoFilesTest) {
   FileSystemQuotaClient quota_client(GetFileSystemContext());
   const std::vector<TestFile> kFiles = {
       {true, "", 0, kDummyURL1, kFileSystemTypeTemporary},
@@ -293,7 +304,7 @@
   }
 }
 
-TEST_F(FileSystemQuotaClientTest, EmptyFilesTest) {
+TEST_P(FileSystemQuotaClientTest, EmptyFilesTest) {
   FileSystemQuotaClient quota_client(GetFileSystemContext());
   const std::vector<TestFile> kFiles = {
       {true, "", 0, kDummyURL1, kFileSystemTypeTemporary},
@@ -311,7 +322,7 @@
   }
 }
 
-TEST_F(FileSystemQuotaClientTest, SubDirectoryTest) {
+TEST_P(FileSystemQuotaClientTest, SubDirectoryTest) {
   FileSystemQuotaClient quota_client(GetFileSystemContext());
   const std::vector<TestFile> kFiles = {
       {true, "", 0, kDummyURL1, kFileSystemTypeTemporary},
@@ -329,7 +340,7 @@
   }
 }
 
-TEST_F(FileSystemQuotaClientTest, MultiTypeTest) {
+TEST_P(FileSystemQuotaClientTest, MultiTypeTest) {
   FileSystemQuotaClient quota_client(GetFileSystemContext());
   const std::vector<TestFile> kFiles = {
       {true, "", 0, kDummyURL1, kFileSystemTypeTemporary},
@@ -350,14 +361,20 @@
                                            kFileSystemTypePersistent);
 
   for (int i = 0; i < 2; i++) {
-    EXPECT_EQ(133 + 14 + file_paths_cost_temporary,
-              GetStorageKeyUsage(quota_client, kDummyURL1, kTemporary));
-    EXPECT_EQ(193 + 9 + file_paths_cost_persistent,
-              GetStorageKeyUsage(quota_client, kDummyURL1, kPersistent));
+    if (persistent_quota_is_temporary_quota()) {
+      EXPECT_EQ(133 + 14 + file_paths_cost_temporary + 193 + 9 +
+                    file_paths_cost_persistent,
+                GetStorageKeyUsage(quota_client, kDummyURL1, kTemporary));
+    } else {
+      EXPECT_EQ(133 + 14 + file_paths_cost_temporary,
+                GetStorageKeyUsage(quota_client, kDummyURL1, kTemporary));
+      EXPECT_EQ(193 + 9 + file_paths_cost_persistent,
+                GetStorageKeyUsage(quota_client, kDummyURL1, kPersistent));
+    }
   }
 }
 
-TEST_F(FileSystemQuotaClientTest, MultiDomainTest) {
+TEST_P(FileSystemQuotaClientTest, MultiDomainTest) {
   FileSystemQuotaClient quota_client(GetFileSystemContext());
   const std::vector<TestFile> kFiles = {
       {true, "", 0, kDummyURL1, kFileSystemTypeTemporary},
@@ -392,18 +409,27 @@
                                            kFileSystemTypePersistent);
 
   for (int i = 0; i < 2; i++) {
-    EXPECT_EQ(1331 + 134 + file_paths_cost_temporary1,
-              GetStorageKeyUsage(quota_client, kDummyURL1, kTemporary));
-    EXPECT_EQ(1903 + 19 + file_paths_cost_persistent1,
-              GetStorageKeyUsage(quota_client, kDummyURL1, kPersistent));
-    EXPECT_EQ(1319 + 113 + file_paths_cost_temporary2,
-              GetStorageKeyUsage(quota_client, kDummyURL2, kTemporary));
-    EXPECT_EQ(2013 + 18 + file_paths_cost_persistent2,
-              GetStorageKeyUsage(quota_client, kDummyURL2, kPersistent));
+    if (persistent_quota_is_temporary_quota()) {
+      EXPECT_EQ(1331 + 134 + file_paths_cost_temporary1 + 1903 + 19 +
+                    file_paths_cost_persistent1,
+                GetStorageKeyUsage(quota_client, kDummyURL1, kTemporary));
+      EXPECT_EQ(1319 + 113 + file_paths_cost_temporary2 + 2013 + 18 +
+                    file_paths_cost_persistent2,
+                GetStorageKeyUsage(quota_client, kDummyURL2, kTemporary));
+    } else {
+      EXPECT_EQ(1331 + 134 + file_paths_cost_temporary1,
+                GetStorageKeyUsage(quota_client, kDummyURL1, kTemporary));
+      EXPECT_EQ(1903 + 19 + file_paths_cost_persistent1,
+                GetStorageKeyUsage(quota_client, kDummyURL1, kPersistent));
+      EXPECT_EQ(1319 + 113 + file_paths_cost_temporary2,
+                GetStorageKeyUsage(quota_client, kDummyURL2, kTemporary));
+      EXPECT_EQ(2013 + 18 + file_paths_cost_persistent2,
+                GetStorageKeyUsage(quota_client, kDummyURL2, kPersistent));
+    }
   }
 }
 
-TEST_F(FileSystemQuotaClientTest, GetUsage_MultipleTasks) {
+TEST_P(FileSystemQuotaClientTest, GetUsage_MultipleTasks) {
   FileSystemQuotaClient quota_client(GetFileSystemContext());
   const std::vector<TestFile> kFiles = {
       {true, "", 0, kDummyURL1, kFileSystemTypeTemporary},
@@ -433,7 +459,7 @@
   EXPECT_EQ(2, additional_callback_count());
 }
 
-TEST_F(FileSystemQuotaClientTest, GetStorageKeysForType) {
+TEST_P(FileSystemQuotaClientTest, GetStorageKeysForType) {
   FileSystemQuotaClient quota_client(GetFileSystemContext());
   InitializeOriginFiles(
       quota_client, {
@@ -442,13 +468,21 @@
                         {true, "", 0, kDummyURL3, kFileSystemTypePersistent},
                     });
 
-  EXPECT_THAT(GetStorageKeysForType(quota_client, kTemporary),
-              testing::UnorderedElementsAre(
-                  StorageKey::CreateFromStringForTesting(kDummyURL1),
-                  StorageKey::CreateFromStringForTesting(kDummyURL2)));
+  if (persistent_quota_is_temporary_quota()) {
+    EXPECT_THAT(GetStorageKeysForType(quota_client, kTemporary),
+                testing::UnorderedElementsAre(
+                    StorageKey::CreateFromStringForTesting(kDummyURL1),
+                    StorageKey::CreateFromStringForTesting(kDummyURL2),
+                    StorageKey::CreateFromStringForTesting(kDummyURL3)));
+  } else {
+    EXPECT_THAT(GetStorageKeysForType(quota_client, kTemporary),
+                testing::UnorderedElementsAre(
+                    StorageKey::CreateFromStringForTesting(kDummyURL1),
+                    StorageKey::CreateFromStringForTesting(kDummyURL2)));
+  }
 }
 
-TEST_F(FileSystemQuotaClientTest, GetStorageKeysForHost) {
+TEST_P(FileSystemQuotaClientTest, GetStorageKeysForHost) {
   FileSystemQuotaClient quota_client(GetFileSystemContext());
   const char* kURL1 = "http://foo.com/";
   const char* kURL2 = "https://foo.com/";
@@ -464,14 +498,23 @@
                             {true, "", 0, kURL5, kFileSystemTypePersistent},
                         });
 
-  EXPECT_THAT(GetStorageKeysForHost(quota_client, kTemporary, "foo.com"),
-              testing::UnorderedElementsAre(
-                  StorageKey::CreateFromStringForTesting(kURL1),
-                  StorageKey::CreateFromStringForTesting(kURL2),
-                  StorageKey::CreateFromStringForTesting(kURL3)));
+  if (persistent_quota_is_temporary_quota()) {
+    EXPECT_THAT(GetStorageKeysForHost(quota_client, kTemporary, "foo.com"),
+                testing::UnorderedElementsAre(
+                    StorageKey::CreateFromStringForTesting(kURL1),
+                    StorageKey::CreateFromStringForTesting(kURL2),
+                    StorageKey::CreateFromStringForTesting(kURL3),
+                    StorageKey::CreateFromStringForTesting(kURL5)));
+  } else {
+    EXPECT_THAT(GetStorageKeysForHost(quota_client, kTemporary, "foo.com"),
+                testing::UnorderedElementsAre(
+                    StorageKey::CreateFromStringForTesting(kURL1),
+                    StorageKey::CreateFromStringForTesting(kURL2),
+                    StorageKey::CreateFromStringForTesting(kURL3)));
+  }
 }
 
-TEST_F(FileSystemQuotaClientTest, DeleteOriginTest) {
+TEST_P(FileSystemQuotaClientTest, DeleteOriginTest) {
   FileSystemQuotaClient quota_client(GetFileSystemContext());
   const std::vector<TestFile> kFiles = {
       {true, "", 0, "http://foo.com/", kFileSystemTypeTemporary},
@@ -499,6 +542,9 @@
   const int64_t file_paths_cost_temporary_bar =
       ComputeFilePathsCostForOriginAndType(kFiles, "http://bar.com/",
                                            kFileSystemTypeTemporary);
+  const int64_t file_paths_cost_persistent_bar =
+      ComputeFilePathsCostForOriginAndType(kFiles, "http://bar.com/",
+                                           kFileSystemTypePersistent);
   const int64_t file_paths_cost_temporary_bar_https =
       ComputeFilePathsCostForOriginAndType(kFiles, "https://bar.com/",
                                            kFileSystemTypeTemporary);
@@ -510,29 +556,44 @@
   base::RunLoop().RunUntilIdle();
   EXPECT_EQ(blink::mojom::QuotaStatusCode::kOk, status());
 
-  DeleteStorageKeyData(&quota_client, "http://bar.com/", kPersistent);
-  base::RunLoop().RunUntilIdle();
-  EXPECT_EQ(blink::mojom::QuotaStatusCode::kOk, status());
+  if (!persistent_quota_is_temporary_quota()) {
+    DeleteStorageKeyData(&quota_client, "http://bar.com/", kPersistent);
+    base::RunLoop().RunUntilIdle();
+    EXPECT_EQ(blink::mojom::QuotaStatusCode::kOk, status());
+  }
 
   DeleteStorageKeyData(&quota_client, "http://buz.com/", kTemporary);
   base::RunLoop().RunUntilIdle();
   EXPECT_EQ(blink::mojom::QuotaStatusCode::kOk, status());
 
   EXPECT_EQ(0, GetStorageKeyUsage(quota_client, "http://foo.com/", kTemporary));
-  EXPECT_EQ(0,
-            GetStorageKeyUsage(quota_client, "http://bar.com/", kPersistent));
   EXPECT_EQ(0, GetStorageKeyUsage(quota_client, "http://buz.com/", kTemporary));
-
   EXPECT_EQ(2 + file_paths_cost_temporary_foo_https,
             GetStorageKeyUsage(quota_client, "https://foo.com/", kTemporary));
-  EXPECT_EQ(4 + file_paths_cost_persistent_foo,
-            GetStorageKeyUsage(quota_client, "http://foo.com/", kPersistent));
-  EXPECT_EQ(8 + file_paths_cost_temporary_bar,
+  EXPECT_EQ(8 + file_paths_cost_temporary_bar +
+                (persistent_quota_is_temporary_quota()
+                     ? 16 + file_paths_cost_persistent_bar
+                     : 0),
             GetStorageKeyUsage(quota_client, "http://bar.com/", kTemporary));
-  EXPECT_EQ(32 + file_paths_cost_persistent_bar_https,
-            GetStorageKeyUsage(quota_client, "https://bar.com/", kPersistent));
-  EXPECT_EQ(64 + file_paths_cost_temporary_bar_https,
+  EXPECT_EQ(64 + file_paths_cost_temporary_bar_https +
+                (persistent_quota_is_temporary_quota()
+                     ? 32 + file_paths_cost_persistent_bar_https
+                     : 0),
             GetStorageKeyUsage(quota_client, "https://bar.com/", kTemporary));
+
+  if (!persistent_quota_is_temporary_quota()) {
+    EXPECT_EQ(0,
+              GetStorageKeyUsage(quota_client, "http://bar.com/", kPersistent));
+    EXPECT_EQ(4 + file_paths_cost_persistent_foo,
+              GetStorageKeyUsage(quota_client, "http://foo.com/", kPersistent));
+    EXPECT_EQ(
+        32 + file_paths_cost_persistent_bar_https,
+        GetStorageKeyUsage(quota_client, "https://bar.com/", kPersistent));
+  }
 }
 
+INSTANTIATE_TEST_SUITE_P(FileSystemQuotaClientTests,
+                         FileSystemQuotaClientTest,
+                         testing::Bool());
+
 }  // namespace storage
diff --git a/storage/browser/file_system/file_system_util.cc b/storage/browser/file_system/file_system_util.cc
index 95b190f..a32a53f 100644
--- a/storage/browser/file_system/file_system_util.cc
+++ b/storage/browser/file_system/file_system_util.cc
@@ -4,13 +4,20 @@
 
 #include "storage/browser/file_system/file_system_util.h"
 
+#include "base/feature_list.h"
 #include "storage/common/file_system/file_system_types.h"
+#include "third_party/blink/public/common/features.h"
 #include "third_party/blink/public/mojom/quota/quota_types.mojom.h"
 
 namespace storage {
 
 blink::mojom::StorageType FileSystemTypeToQuotaStorageType(
     FileSystemType type) {
+  if (base::FeatureList::IsEnabled(
+          blink::features::kPersistentQuotaIsTemporaryQuota) &&
+      (type == kFileSystemTypeTemporary || type == kFileSystemTypePersistent)) {
+    return blink::mojom::StorageType::kTemporary;
+  }
   switch (type) {
     case kFileSystemTypeTemporary:
       return blink::mojom::StorageType::kTemporary;
diff --git a/storage/browser/quota/quota_features.cc b/storage/browser/quota/quota_features.cc
index cfb874fb..04985ab 100644
--- a/storage/browser/quota/quota_features.cc
+++ b/storage/browser/quota/quota_features.cc
@@ -3,6 +3,7 @@
 // found in the LICENSE file.
 
 #include "storage/browser/quota/quota_features.h"
+#include "base/feature_list.h"
 
 namespace storage {
 
diff --git a/testing/buildbot/chromium.clang.json b/testing/buildbot/chromium.clang.json
index 31900497..4fa5198 100644
--- a/testing/buildbot/chromium.clang.json
+++ b/testing/buildbot/chromium.clang.json
@@ -3197,30 +3197,6 @@
         "test_id_prefix": "ninja://chrome/updater:updater_tests/"
       },
       {
-        "args": [
-          "--test-launcher-timeout=90000",
-          "--ui-test-action-max-timeout=45000",
-          "--ui-test-action-timeout=40000"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "integrity": "high",
-              "os": "Windows-10-19042"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "updater_tests_system",
-        "test_id_prefix": "ninja://chrome/updater:updater_tests_system/"
-      },
-      {
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
@@ -4937,30 +4913,6 @@
         "test_id_prefix": "ninja://chrome/updater:updater_tests/"
       },
       {
-        "args": [
-          "--test-launcher-timeout=90000",
-          "--ui-test-action-max-timeout=45000",
-          "--ui-test-action-timeout=40000"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "integrity": "high",
-              "os": "Windows-10-19042"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "updater_tests_system",
-        "test_id_prefix": "ninja://chrome/updater:updater_tests_system/"
-      },
-      {
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
@@ -29011,28 +28963,6 @@
         "test_id_prefix": "ninja://chrome/updater:updater_tests/"
       },
       {
-        "args": [
-          "--test-launcher-timeout=90000",
-          "--ui-test-action-max-timeout=45000",
-          "--ui-test-action-timeout=40000"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "integrity": "high"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "updater_tests_system",
-        "test_id_prefix": "ninja://chrome/updater:updater_tests_system/"
-      },
-      {
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
@@ -30308,28 +30238,6 @@
         "test_id_prefix": "ninja://chrome/updater:updater_tests/"
       },
       {
-        "args": [
-          "--test-launcher-timeout=90000",
-          "--ui-test-action-max-timeout=45000",
-          "--ui-test-action-timeout=40000"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "integrity": "high"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "updater_tests_system",
-        "test_id_prefix": "ninja://chrome/updater:updater_tests_system/"
-      },
-      {
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
@@ -31573,28 +31481,6 @@
         "test_id_prefix": "ninja://chrome/updater:updater_tests/"
       },
       {
-        "args": [
-          "--test-launcher-timeout=90000",
-          "--ui-test-action-max-timeout=45000",
-          "--ui-test-action-timeout=40000"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "integrity": "high"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "updater_tests_system",
-        "test_id_prefix": "ninja://chrome/updater:updater_tests_system/"
-      },
-      {
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
@@ -33386,30 +33272,6 @@
         "test_id_prefix": "ninja://chrome/updater:updater_tests/"
       },
       {
-        "args": [
-          "--test-launcher-timeout=90000",
-          "--ui-test-action-max-timeout=45000",
-          "--ui-test-action-timeout=40000"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "integrity": "high",
-              "os": "Windows-10-19042"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "updater_tests_system",
-        "test_id_prefix": "ninja://chrome/updater:updater_tests_system/"
-      },
-      {
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
@@ -35281,30 +35143,6 @@
         "test_id_prefix": "ninja://chrome/updater:updater_tests/"
       },
       {
-        "args": [
-          "--test-launcher-timeout=90000",
-          "--ui-test-action-max-timeout=45000",
-          "--ui-test-action-timeout=40000"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "integrity": "high",
-              "os": "Windows-10-19042"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "updater_tests_system",
-        "test_id_prefix": "ninja://chrome/updater:updater_tests_system/"
-      },
-      {
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
@@ -37176,30 +37014,6 @@
         "test_id_prefix": "ninja://chrome/updater:updater_tests/"
       },
       {
-        "args": [
-          "--test-launcher-timeout=90000",
-          "--ui-test-action-max-timeout=45000",
-          "--ui-test-action-timeout=40000"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "integrity": "high",
-              "os": "Windows-10-19042"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "updater_tests_system",
-        "test_id_prefix": "ninja://chrome/updater:updater_tests_system/"
-      },
-      {
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
@@ -39092,28 +38906,6 @@
         "test_id_prefix": "ninja://chrome/updater:updater_tests/"
       },
       {
-        "args": [
-          "--test-launcher-timeout=90000",
-          "--ui-test-action-max-timeout=45000",
-          "--ui-test-action-timeout=40000"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "integrity": "high"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "updater_tests_system",
-        "test_id_prefix": "ninja://chrome/updater:updater_tests_system/"
-      },
-      {
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
@@ -40374,28 +40166,6 @@
         "test_id_prefix": "ninja://chrome/updater:updater_tests/"
       },
       {
-        "args": [
-          "--test-launcher-timeout=90000",
-          "--ui-test-action-max-timeout=45000",
-          "--ui-test-action-timeout=40000"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "integrity": "high"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "updater_tests_system",
-        "test_id_prefix": "ninja://chrome/updater:updater_tests_system/"
-      },
-      {
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
@@ -42172,30 +41942,6 @@
         "test_id_prefix": "ninja://chrome/updater:updater_tests/"
       },
       {
-        "args": [
-          "--test-launcher-timeout=90000",
-          "--ui-test-action-max-timeout=45000",
-          "--ui-test-action-timeout=40000"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "integrity": "high",
-              "os": "Windows-10",
-              "pool": "chrome.tests"
-            }
-          ],
-          "service_account": "chrome-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "updater_tests_system",
-        "test_id_prefix": "ninja://chrome/updater:updater_tests_system/"
-      },
-      {
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
@@ -44067,30 +43813,6 @@
         "test_id_prefix": "ninja://chrome/updater:updater_tests/"
       },
       {
-        "args": [
-          "--test-launcher-timeout=90000",
-          "--ui-test-action-max-timeout=45000",
-          "--ui-test-action-timeout=40000"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "integrity": "high",
-              "os": "Windows-10",
-              "pool": "chrome.tests"
-            }
-          ],
-          "service_account": "chrome-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "updater_tests_system",
-        "test_id_prefix": "ninja://chrome/updater:updater_tests_system/"
-      },
-      {
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
@@ -47615,29 +47337,6 @@
         "test_id_prefix": "ninja://chrome/updater:updater_tests/"
       },
       {
-        "args": [
-          "--test-launcher-timeout=90000",
-          "--ui-test-action-max-timeout=45000",
-          "--ui-test-action-timeout=40000"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "integrity": "high",
-              "os": "Windows-10-19042"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "updater_tests_system",
-        "test_id_prefix": "ninja://chrome/updater:updater_tests_system/"
-      },
-      {
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
diff --git a/testing/buildbot/chromium.fyi.json b/testing/buildbot/chromium.fyi.json
index 952bdfd..1378213 100644
--- a/testing/buildbot/chromium.fyi.json
+++ b/testing/buildbot/chromium.fyi.json
@@ -1668,22 +1668,6 @@
         "test_id_prefix": "ninja://chrome/updater:updater_tests/"
       },
       {
-        "args": [
-          "--test-launcher-timeout=90000",
-          "--ui-test-action-max-timeout=45000",
-          "--ui-test-action-timeout=40000"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": false
-        },
-        "test": "updater_tests_system",
-        "test_id_prefix": "ninja://chrome/updater:updater_tests_system/"
-      },
-      {
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
@@ -3600,31 +3584,6 @@
         "test_id_prefix": "ninja://chrome/updater:updater_tests/"
       },
       {
-        "args": [
-          "--test-launcher-timeout=90000",
-          "--ui-test-action-max-timeout=45000",
-          "--ui-test-action-timeout=40000"
-        ],
-        "isolate_profile_data": true,
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "integrity": "high",
-              "os": "Windows-10-19042"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "updater_tests_system",
-        "test_id_prefix": "ninja://chrome/updater:updater_tests_system/"
-      },
-      {
         "isolate_profile_data": true,
         "merge": {
           "args": [],
@@ -94907,30 +94866,6 @@
         "test_id_prefix": "ninja://chrome/updater:updater_tests/"
       },
       {
-        "args": [
-          "--test-launcher-timeout=90000",
-          "--ui-test-action-max-timeout=45000",
-          "--ui-test-action-timeout=40000"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "integrity": "high",
-              "os": "Windows-10-18363"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "updater_tests_system",
-        "test_id_prefix": "ninja://chrome/updater:updater_tests_system/"
-      },
-      {
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
@@ -96505,29 +96440,6 @@
         "test_id_prefix": "ninja://chrome/updater:updater_tests/"
       },
       {
-        "args": [
-          "--test-launcher-timeout=90000",
-          "--ui-test-action-max-timeout=45000",
-          "--ui-test-action-timeout=40000"
-        ],
-        "isolate_profile_data": true,
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "integrity": "high"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "updater_tests_system",
-        "test_id_prefix": "ninja://chrome/updater:updater_tests_system/"
-      },
-      {
         "isolate_profile_data": true,
         "merge": {
           "args": [],
diff --git a/testing/buildbot/chromium.memory.json b/testing/buildbot/chromium.memory.json
index 570d14b..6c7fd1d 100644
--- a/testing/buildbot/chromium.memory.json
+++ b/testing/buildbot/chromium.memory.json
@@ -19092,29 +19092,6 @@
         "test_id_prefix": "ninja://chrome/updater:updater_tests/"
       },
       {
-        "args": [
-          "--test-launcher-timeout=90000",
-          "--ui-test-action-max-timeout=45000",
-          "--ui-test-action-timeout=40000"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "integrity": "high",
-              "os": "Windows-10-15063"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "updater_tests_system",
-        "test_id_prefix": "ninja://chrome/updater:updater_tests_system/"
-      },
-      {
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
diff --git a/testing/buildbot/chromium.win.json b/testing/buildbot/chromium.win.json
index 7cd92fbc..64e32195 100644
--- a/testing/buildbot/chromium.win.json
+++ b/testing/buildbot/chromium.win.json
@@ -1110,28 +1110,6 @@
         "test_id_prefix": "ninja://chrome/updater:updater_tests/"
       },
       {
-        "args": [
-          "--test-launcher-timeout=90000",
-          "--ui-test-action-max-timeout=45000",
-          "--ui-test-action-timeout=40000"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "integrity": "high"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "updater_tests_system",
-        "test_id_prefix": "ninja://chrome/updater:updater_tests_system/"
-      },
-      {
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
@@ -3309,31 +3287,6 @@
         "test_id_prefix": "ninja://chrome/updater:updater_tests/"
       },
       {
-        "args": [
-          "--test-launcher-timeout=90000",
-          "--ui-test-action-max-timeout=45000",
-          "--ui-test-action-timeout=40000"
-        ],
-        "isolate_profile_data": true,
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "integrity": "high",
-              "os": "Windows-10-18363"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "updater_tests_system",
-        "test_id_prefix": "ninja://chrome/updater:updater_tests_system/"
-      },
-      {
         "isolate_profile_data": true,
         "merge": {
           "args": [],
@@ -5525,30 +5478,6 @@
         "test_id_prefix": "ninja://chrome/updater:updater_tests/"
       },
       {
-        "args": [
-          "--test-launcher-timeout=90000",
-          "--ui-test-action-max-timeout=45000",
-          "--ui-test-action-timeout=40000"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "integrity": "high",
-              "os": "Windows-10-19042"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "updater_tests_system",
-        "test_id_prefix": "ninja://chrome/updater:updater_tests_system/"
-      },
-      {
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
@@ -7165,28 +7094,6 @@
         "test_id_prefix": "ninja://chrome/updater:updater_tests/"
       },
       {
-        "args": [
-          "--test-launcher-timeout=90000",
-          "--ui-test-action-max-timeout=45000",
-          "--ui-test-action-timeout=40000"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "integrity": "high"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "updater_tests_system",
-        "test_id_prefix": "ninja://chrome/updater:updater_tests_system/"
-      },
-      {
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
@@ -8684,28 +8591,6 @@
         "test_id_prefix": "ninja://chrome/updater:updater_tests/"
       },
       {
-        "args": [
-          "--test-launcher-timeout=90000",
-          "--ui-test-action-max-timeout=45000",
-          "--ui-test-action-timeout=40000"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "integrity": "high"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "updater_tests_system",
-        "test_id_prefix": "ninja://chrome/updater:updater_tests_system/"
-      },
-      {
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
diff --git a/testing/buildbot/internal.chrome.fyi.json b/testing/buildbot/internal.chrome.fyi.json
index f77a52f..8c34d978 100644
--- a/testing/buildbot/internal.chrome.fyi.json
+++ b/testing/buildbot/internal.chrome.fyi.json
@@ -31,5 +31,35 @@
         "test_id_prefix": "ninja://chrome/test:variations_smoke_tests/"
       }
     ]
+  },
+  "win-chrome-finch-fyi": {
+    "additional_compile_targets": [
+      "chrome"
+    ],
+    "isolated_scripts": [
+      {
+        "isolate_name": "variations_smoke_tests",
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "variations_smoke_tests",
+        "resultdb": {
+          "enable": true,
+          "result_format": "single"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Windows-10-15063",
+              "pool": "chrome.tests"
+            }
+          ],
+          "service_account": "chrome-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test_id_prefix": "ninja://chrome/test:variations_smoke_tests/"
+      }
+    ]
   }
 }
diff --git a/testing/buildbot/mixins.pyl b/testing/buildbot/mixins.pyl
index 6a21635..2f82cbfd 100644
--- a/testing/buildbot/mixins.pyl
+++ b/testing/buildbot/mixins.pyl
@@ -1106,6 +1106,13 @@
       },
     },
   },
+  'win10-1703' : {
+    'swarming': {
+      'dimensions': {
+        'os': 'Windows-10-15063',
+      },
+    },
+  },
   'win10-20h2': {
     'swarming': {
       'dimensions': {
diff --git a/testing/buildbot/test_suites.pyl b/testing/buildbot/test_suites.pyl
index 8be7360..5662d7dc 100644
--- a/testing/buildbot/test_suites.pyl
+++ b/testing/buildbot/test_suites.pyl
@@ -5579,21 +5579,6 @@
           '--ui-test-action-timeout=40000',
         ],
       },
-      'updater_tests_system': {
-        'args': [
-          # Timeouts based on empirical observations of test runtimes, 2021-07.
-          '--test-launcher-timeout=90000',
-          '--ui-test-action-max-timeout=45000',
-          '--ui-test-action-timeout=40000',
-        ],
-        'swarming': {
-          'dimension_sets': [
-            {
-              'integrity': 'high',
-            }
-          ],
-        },
-      },
       'zucchini_unittests': {},
     },
 
diff --git a/testing/buildbot/waterfalls.pyl b/testing/buildbot/waterfalls.pyl
index e498517..f8349656 100644
--- a/testing/buildbot/waterfalls.pyl
+++ b/testing/buildbot/waterfalls.pyl
@@ -6494,6 +6494,18 @@
           'isolated_scripts': 'variations_smoke_tests',
         },
       },
+      'win-chrome-finch-fyi': {
+        'additional_compile_targets': [
+          'chrome',
+        ],
+        'mixins': [
+          'chrome-swarming-pool',
+          'win10-1703',
+        ],
+        'test_suites': {
+          'isolated_scripts': 'variations_smoke_tests',
+        },
+      },
     },
   },
   {
diff --git a/testing/variations/fieldtrial_testing_config.json b/testing/variations/fieldtrial_testing_config.json
index dd3b5a9..86cc0045 100644
--- a/testing/variations/fieldtrial_testing_config.json
+++ b/testing/variations/fieldtrial_testing_config.json
@@ -2474,27 +2474,6 @@
             ]
         }
     ],
-    "CopyLinkToTextDesktop": [
-        {
-            "platforms": [
-                "chromeos",
-                "chromeos_lacros",
-                "linux",
-                "mac",
-                "windows"
-            ],
-            "experiments": [
-                {
-                    "name": "Enabled",
-                    "enable_features": [
-                        "CopyLinkToText",
-                        "PreemptiveLinkToTextGeneration",
-                        "SharedHighlightingV2"
-                    ]
-                }
-            ]
-        }
-    ],
     "CrOSAutoSelect": [
         {
             "platforms": [
@@ -3669,6 +3648,31 @@
             ]
         }
     ],
+    "FeedPullToRefresh": [
+        {
+            "platforms": [
+                "android"
+            ],
+            "experiments": [
+                {
+                    "name": "EnabledWithIph",
+                    "enable_features": [
+                        "FeedInteractiveRefresh",
+                        "IPH_FeedSwipeRefresh"
+                    ]
+                },
+                {
+                    "name": "EnabledWithoutIph",
+                    "enable_features": [
+                        "FeedInteractiveRefresh"
+                    ],
+                    "disable_features": [
+                        "IPH_FeedSwipeRefresh"
+                    ]
+                }
+            ]
+        }
+    ],
     "FillingAcrossAffiliatedWebsites": [
         {
             "platforms": [
@@ -5703,30 +5707,120 @@
             ],
             "experiments": [
                 {
-                    "name": "Adaptive_AlwaysNewTab_20210428",
+                    "name": "Segmentation_AutoNoWait_20210811",
+                    "params": {
+                        "default_segment": "new-tab",
+                        "disable_ui": "false",
+                        "ignore_segmentation_results": "false",
+                        "min_version_adaptive": "2",
+                        "segment_selection_ttl_days": "14",
+                        "show_ui_only_after_ready": "false"
+                    },
+                    "enable_features": [
+                        "AdaptiveButtonInTopToolbarCustomizationV2",
+                        "SegmentationPlatform"
+                    ],
+                    "disable_features": [
+                        "AdaptiveButtonInTopToolbar"
+                    ]
+                },
+                {
+                    "name": "Segmentation_Auto_20210811",
+                    "params": {
+                        "default_segment": "new-tab",
+                        "disable_ui": "false",
+                        "ignore_segmentation_results": "false",
+                        "min_version_adaptive": "2",
+                        "segment_selection_ttl_days": "14",
+                        "show_ui_only_after_ready": "true"
+                    },
+                    "enable_features": [
+                        "AdaptiveButtonInTopToolbarCustomizationV2",
+                        "SegmentationPlatform"
+                    ],
+                    "disable_features": [
+                        "AdaptiveButtonInTopToolbar"
+                    ]
+                },
+                {
+                    "name": "Segmentation_Default_Segment_20210811",
+                    "params": {
+                        "default_segment": "new-tab",
+                        "disable_ui": "false",
+                        "ignore_segmentation_results": "true",
+                        "min_version_adaptive": "2",
+                        "segment_selection_ttl_days": "14",
+                        "show_ui_only_after_ready": "true"
+                    },
+                    "enable_features": [
+                        "AdaptiveButtonInTopToolbarCustomizationV2",
+                        "SegmentationPlatform"
+                    ],
+                    "disable_features": [
+                        "AdaptiveButtonInTopToolbar"
+                    ]
+                },
+                {
+                    "name": "Segmentation_Control_20210811",
+                    "params": {
+                        "default_segment": "new-tab",
+                        "disable_ui": "false",
+                        "ignore_segmentation_results": "false",
+                        "segment_selection_ttl_days": "14",
+                        "show_ui_only_after_ready": "true"
+                    },
+                    "disable_features": [
+                        "AdaptiveButtonInTopToolbar",
+                        "AdaptiveButtonInTopToolbarCustomizationV2",
+                        "SegmentationPlatform"
+                    ]
+                },
+                {
+                    "name": "Adaptive_AlwaysNewTab_20210730",
                     "params": {
                         "mode": "always-new-tab"
                     },
                     "enable_features": [
                         "AdaptiveButtonInTopToolbar"
+                    ],
+                    "disable_features": [
+                        "AdaptiveButtonInTopToolbarCustomizationV2"
                     ]
                 },
                 {
-                    "name": "Adaptive_AlwaysShare_20210428",
+                    "name": "Adaptive_AlwaysShare_20210730",
                     "params": {
                         "mode": "always-share"
                     },
                     "enable_features": [
                         "AdaptiveButtonInTopToolbar"
+                    ],
+                    "disable_features": [
+                        "AdaptiveButtonInTopToolbarCustomizationV2"
                     ]
                 },
                 {
-                    "name": "Adaptive_AlwaysVoice_20210428",
+                    "name": "Adaptive_AlwaysVoice_20210730",
                     "params": {
                         "mode": "always-voice"
                     },
                     "enable_features": [
                         "AdaptiveButtonInTopToolbar"
+                    ],
+                    "disable_features": [
+                        "AdaptiveButtonInTopToolbarCustomizationV2"
+                    ]
+                },
+                {
+                    "name": "Adaptive_Control_20210730",
+                    "params": {
+                        "mode": "always-none"
+                    },
+                    "enable_features": [
+                        "AdaptiveButtonInTopToolbar"
+                    ],
+                    "disable_features": [
+                        "AdaptiveButtonInTopToolbarCustomizationV2"
                     ]
                 },
                 {
@@ -7738,6 +7832,25 @@
             ]
         }
     ],
+    "SharedHighlightingDesktopV2": [
+        {
+            "platforms": [
+                "chromeos",
+                "chromeos_lacros",
+                "linux",
+                "mac",
+                "windows"
+            ],
+            "experiments": [
+                {
+                    "name": "Enabled",
+                    "enable_features": [
+                        "SharedHighlightingV2"
+                    ]
+                }
+            ]
+        }
+    ],
     "SharedHighlightingIOS": [
         {
             "platforms": [
@@ -8266,6 +8379,28 @@
             ]
         }
     ],
+    "TransformInterop": [
+        {
+            "platforms": [
+                "android",
+                "android_weblayer",
+                "android_webview",
+                "chromeos",
+                "chromeos_lacros",
+                "linux",
+                "mac",
+                "windows"
+            ],
+            "experiments": [
+                {
+                    "name": "Enabled",
+                    "enable_features": [
+                        "TransformInterop"
+                    ]
+                }
+            ]
+        }
+    ],
     "TranslateRankerModel": [
         {
             "platforms": [
diff --git a/third_party/blink/common/features.cc b/third_party/blink/common/features.cc
index 7878b143..45905c380 100644
--- a/third_party/blink/common/features.cc
+++ b/third_party/blink/common/features.cc
@@ -1028,5 +1028,8 @@
 const base::Feature kSyncLoadDataUrlFonts{"SyncLoadDataUrlFonts",
                                           base::FEATURE_DISABLED_BY_DEFAULT};
 
+const base::Feature kPersistentQuotaIsTemporaryQuota{
+    "PersistentQuotaIsTemporaryQuota", base::FEATURE_DISABLED_BY_DEFAULT};
+
 }  // namespace features
 }  // namespace blink
diff --git a/third_party/blink/public/common/features.h b/third_party/blink/public/common/features.h
index c332d6aa..0bb61b6 100644
--- a/third_party/blink/public/common/features.h
+++ b/third_party/blink/public/common/features.h
@@ -446,6 +446,10 @@
 // Synchronously load web fonts inlined as data urls. See crbug.com/1236283
 BLINK_COMMON_EXPORT extern const base::Feature kSyncLoadDataUrlFonts;
 
+// Makes Persistent quota the same as Temporary quota.
+BLINK_COMMON_EXPORT
+extern const base::Feature kPersistentQuotaIsTemporaryQuota;
+
 }  // namespace features
 }  // namespace blink
 
diff --git a/third_party/blink/public/mojom/BUILD.gn b/third_party/blink/public/mojom/BUILD.gn
index 6fb5282..c0a231f9 100644
--- a/third_party/blink/public/mojom/BUILD.gn
+++ b/third_party/blink/public/mojom/BUILD.gn
@@ -58,6 +58,9 @@
     "devtools/devtools_frontend.mojom",
     "devtools/inspector_issue.mojom",
     "disk_allocator.mojom",
+    "dom_storage/dom_storage.mojom",
+    "dom_storage/session_storage_namespace.mojom",
+    "dom_storage/storage_area.mojom",
     "favicon/favicon_url.mojom",
     "feature_observer/feature_observer.mojom",
     "federated_learning/floc.mojom",
@@ -92,7 +95,6 @@
     "input/touch_event.mojom",
     "interest_group/ad_auction_service.mojom",
     "interest_group/interest_group_types.mojom",
-    "interest_group/restricted_interest_group_store.mojom",
     "keyboard_lock/keyboard_lock.mojom",
     "leak_detector/leak_detector.mojom",
     "link_to_text/link_to_text.mojom",
@@ -253,7 +255,6 @@
     "//services/service_manager/public/mojom",
     "//services/viz/public/mojom",
     "//skia/public/mojom",
-    "//third_party/blink/public/mojom/dom_storage",
     "//third_party/blink/public/mojom/frame",
     "//third_party/blink/public/mojom/gpu",
     "//third_party/blink/public/mojom/service_worker:storage",
diff --git a/third_party/blink/public/mojom/dom_storage/BUILD.gn b/third_party/blink/public/mojom/dom_storage/BUILD.gn
deleted file mode 100644
index baae1310..0000000
--- a/third_party/blink/public/mojom/dom_storage/BUILD.gn
+++ /dev/null
@@ -1,27 +0,0 @@
-# Copyright 2019 The Chromium Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-import("//mojo/public/tools/bindings/mojom.gni")
-
-mojom_component("dom_storage") {
-  output_prefix = "dom_storage_mojom"
-  macro_prefix = "DOM_STORAGE_MOJOM"
-
-  sources = [
-    "dom_storage.mojom",
-    "session_storage_namespace.mojom",
-    "storage_area.mojom",
-  ]
-
-  public_deps = [ "//url/mojom:url_mojom_origin" ]
-
-  # The Blink variant is linked directly into the private platform
-  # implementation and so it requires the corresponding export configuration to
-  # override the normal mojom_component() target exports.
-  export_class_attribute_blink = "PLATFORM_EXPORT"
-  export_define_blink = "BLINK_PLATFORM_IMPLEMENTATION=1"
-  export_header_blink = "third_party/blink/renderer/platform/platform_export.h"
-
-  generate_java = true
-}
diff --git a/third_party/blink/public/mojom/interest_group/ad_auction_service.mojom b/third_party/blink/public/mojom/interest_group/ad_auction_service.mojom
index c52b9ba..ad18b27 100644
--- a/third_party/blink/public/mojom/interest_group/ad_auction_service.mojom
+++ b/third_party/blink/public/mojom/interest_group/ad_auction_service.mojom
@@ -5,10 +5,10 @@
 module blink.mojom;
 
 import "third_party/blink/public/mojom/interest_group/interest_group_types.mojom";
+import "url/mojom/origin.mojom";
 import "url/mojom/url.mojom";
 
-// API to initiate on-device ad auction, using interest groups specified with
-// the RestrictedInterestGroupStore service.
+// Per-frame API to initiate on-device ad auction and add interest groups.
 // https://github.com/WICG/turtledove/blob/main/FLEDGE.md
 interface AdAuctionService {
   // Triggers the ad auction to run in a sandboxed JavaScript process. The
@@ -18,4 +18,26 @@
   // winning ad creative, which the publisher page loads into a page or iframe
   // in the owner's domain. If no ad wins the auction, null is returned.
   RunAdAuction(AuctionAdConfig config) => (url.mojom.Url? ad_display_url);
+
+  // Requests that the browser process create or overwrite persisted interest
+  // group keyed by `owner` and `name` with information from `group`. The
+  // browser verifies that the owner origin matches that of the frame's origin
+  // (in the future, a mechanism will allow delegating to other origins). The
+  // caller cannot observe the status or results of this operation.
+  JoinInterestGroup(InterestGroup group);
+
+  // Deletes the interest group stored in the browser as indicated by the
+  // (`origin`, `name`) tuple. `origin` must use https.
+  LeaveInterestGroup(url.mojom.Origin owner, string name);
+
+  // Requests the browser update stored interest groups owned by the current
+  // frame's origin *only* (interest groups not owned by the frame origin aren't
+  // modified) using the last `update_url` registered for each owned interest
+  // group. JSON is downloaded from each interest group's URL, parsed safely
+  // using //services/data_decoder/public/cpp/data_decoder.h, and the interest
+  // group store is updated. Unlike the JoinInterestGroup() operation, this
+  // operation doesn't clear fields that weren't present in the server JSON
+  // response. The JSON `name`, `owner`, `userBiddingSignals` and other unknown
+  // fields will be ignored.
+  UpdateAdInterestGroups();
 };
diff --git a/third_party/blink/public/mojom/interest_group/restricted_interest_group_store.mojom b/third_party/blink/public/mojom/interest_group/restricted_interest_group_store.mojom
deleted file mode 100644
index eacbb1f..0000000
--- a/third_party/blink/public/mojom/interest_group/restricted_interest_group_store.mojom
+++ /dev/null
@@ -1,36 +0,0 @@
-// Copyright 2021 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-module blink.mojom;
-
-import "third_party/blink/public/mojom/interest_group/interest_group_types.mojom";
-import "url/mojom/origin.mojom";
-import "url/mojom/url.mojom";
-
-// APIs to add/leave interest groups and to initiate on-device auction. Exposed
-// to the renderer on a per-frame basis.
-// https://github.com/WICG/turtledove/blob/main/FLEDGE.md
-interface RestrictedInterestGroupStore {
-  // Requests that the browser process create or overwrite persisted interest
-  // group keyed by `owner` and `name` with information from `group`. The
-  // browser verifies that the owner origin matches that of the frame's origin
-  // (in the future, a mechanism will allow delegating to other origins). The
-  // caller cannot observe the status or results of this operation.
-  JoinInterestGroup(InterestGroup group);
-
-  // Deletes the interest group stored in the browser as indicated by the
-  // (`origin`, `name`) tuple. `origin` must use https.
-  LeaveInterestGroup(url.mojom.Origin owner, string name);
-
-  // Requests the browser update stored interest groups owned by the current
-  // frame's origin *only* (interest groups not owned by the frame origin aren't
-  // modified) using the last `update_url` registered for each owned interest
-  // group. JSON is downloaded from each interest group's URL, parsed safely
-  // using //services/data_decoder/public/cpp/data_decoder.h, and the interest
-  // group store is updated. Unlike the JoinInterestGroup() operation, this
-  // operation doesn't clear fields that weren't present in the server JSON
-  // response. The JSON `name`, `owner`, `userBiddingSignals` and other unknown
-  // fields will be ignored.
-  UpdateAdInterestGroups();
-};
diff --git a/third_party/blink/public/mojom/web_feature/web_feature.mojom b/third_party/blink/public/mojom/web_feature/web_feature.mojom
index 74dd120d..fb231df5 100644
--- a/third_party/blink/public/mojom/web_feature/web_feature.mojom
+++ b/third_party/blink/public/mojom/web_feature/web_feature.mojom
@@ -3293,6 +3293,7 @@
   kV8NavigatorManagedData_GetAnnotatedLocation_Method = 3981,
   kUserDataFieldFilledPreviously = 3982,
   kTableCollapsedBorderDifferentToVisual = 3983,
+  kHighlightAPIRegisterHighlight = 3984,
 
   // Add new features immediately above this line. Don't change assigned
   // numbers of any item, and don't reuse removed slots.
diff --git a/third_party/blink/public/platform/web_media_player.h b/third_party/blink/public/platform/web_media_player.h
index 306f13a..22a7222 100644
--- a/third_party/blink/public/platform/web_media_player.h
+++ b/third_party/blink/public/platform/web_media_player.h
@@ -179,9 +179,6 @@
   // calls are still made periodically.
   virtual void OnTimeUpdate() {}
 
-  virtual void RequestRemotePlayback() {}
-  virtual void RequestRemotePlaybackControl() {}
-  virtual void RequestRemotePlaybackStop() {}
   virtual void RequestRemotePlaybackDisabled(bool disabled) {}
   virtual void FlingingStarted() {}
   virtual void FlingingStopped() {}
diff --git a/third_party/blink/renderer/core/app_history/app_history.cc b/third_party/blink/renderer/core/app_history/app_history.cc
index 06d695b..d151741 100644
--- a/third_party/blink/renderer/core/app_history/app_history.cc
+++ b/third_party/blink/renderer/core/app_history/app_history.cc
@@ -37,15 +37,18 @@
  public:
   AppHistoryApiNavigation(ScriptState* script_state,
                           AppHistoryNavigationOptions* options,
-                          const String& key = String())
+                          const String& key,
+                          scoped_refptr<SerializedScriptValue> state = nullptr)
       : info(options->getInfoOr(
             ScriptValue(script_state->GetIsolate(),
                         v8::Undefined(script_state->GetIsolate())))),
+        serialized_state(std::move(state)),
         resolver(MakeGarbageCollected<ScriptPromiseResolver>(script_state)),
         returned_promise(resolver->Promise()),
         key(key) {}
 
   ScriptValue info;
+  scoped_refptr<SerializedScriptValue> serialized_state;
   Member<ScriptPromiseResolver> resolver;
   ScriptPromise returned_promise;
   String key;
@@ -99,12 +102,13 @@
 
   ScriptValue Call(ScriptValue value) final {
     DCHECK(window_);
-    if (signal_ && signal_->aborted()) {
+    if (signal_->aborted()) {
       window_ = nullptr;
       return ScriptValue();
     }
 
     AppHistory* app_history = AppHistory::appHistory(*window_);
+    app_history->ongoing_navigation_signal_ = nullptr;
     if (type_ == ResolveType::kFulfill) {
       if (navigation_) {
         navigation_->resolver->Resolve();
@@ -200,7 +204,6 @@
   // A same-document navigation (e.g., a document.open()) in a newly created
   // iframe will try to operate on an empty |entries_|. appHistory considers
   // this a no-op.
-  post_navigate_event_ongoing_navigation_signal_ = nullptr;
   if (entries_.IsEmpty())
     return;
 
@@ -330,11 +333,10 @@
          frame_load_type == WebFrameLoadType::kStandard);
 
   AppHistoryApiNavigation* navigation =
-      MakeGarbageCollected<AppHistoryApiNavigation>(script_state, options);
+      MakeGarbageCollected<AppHistoryApiNavigation>(
+          script_state, options, String(), std::move(serialized_state));
   upcoming_non_traversal_navigation_ = navigation;
 
-  to_be_set_serialized_state_ = serialized_state;
-
   GetSupplementable()->GetFrame()->MaybeLogAdClickNavigation();
 
   FrameLoadRequest request(GetSupplementable(), ResourceRequest(url));
@@ -351,9 +353,9 @@
     return ScriptPromise();
   }
 
-  if (to_be_set_serialized_state_) {
+  if (navigation->serialized_state) {
     current()->GetItem()->SetAppHistoryState(
-        std::move(to_be_set_serialized_state_));
+        std::move(navigation->serialized_state));
   }
   return navigation->returned_promise;
 }
@@ -532,8 +534,8 @@
   SerializedScriptValue* destination_state = nullptr;
   if (destination_item)
     destination_state = destination_item->GetAppHistoryState();
-  else if (to_be_set_serialized_state_)
-    destination_state = to_be_set_serialized_state_.get();
+  else if (navigation && navigation->serialized_state)
+    destination_state = navigation->serialized_state.get();
   AppHistoryDestination* destination =
       MakeGarbageCollected<AppHistoryDestination>(
           url, event_type != NavigateEventType::kCrossDocument,
@@ -570,17 +572,20 @@
   navigate_event->SetUrl(url);
 
   DCHECK(!ongoing_navigate_event_);
+  DCHECK(!ongoing_navigation_signal_);
   ongoing_navigate_event_ = navigate_event;
+  ongoing_navigation_signal_ = navigate_event->signal();
   DispatchEvent(*navigate_event);
   ongoing_navigate_event_ = nullptr;
 
-  if (!GetSupplementable()->GetFrame()) {
-    DCHECK(navigate_event->signal()->aborted());
+  if (navigate_event->defaultPrevented()) {
+    if (!navigate_event->signal()->aborted())
+      FinalizeWithAbortedNavigationError(script_state, navigation);
     return DispatchResult::kAbort;
   }
 
   auto promise_list = navigate_event->GetNavigationActionPromisesList();
-  if (!promise_list.IsEmpty() && !navigate_event->defaultPrevented()) {
+  if (!promise_list.IsEmpty()) {
     // The spec says that at this point we should either run the URL and history
     // update steps (for non-traverse cases) or we should do a same-document
     // history traversal. In our implementation it's easier for the caller to do
@@ -593,25 +598,17 @@
           state_object, type);
     }
   }
-  post_navigate_event_ongoing_navigation_signal_ = navigate_event->signal();
-
-  if (navigate_event->defaultPrevented()) {
-    if (!navigate_event->signal()->aborted())
-      FinalizeWithAbortedNavigationError(script_state, navigation);
-    return DispatchResult::kAbort;
-  }
 
   if (!promise_list.IsEmpty() ||
       event_type != NavigateEventType::kCrossDocument) {
     NavigateReaction::React(script_state,
                             ScriptPromise::All(script_state, promise_list),
                             navigation, navigate_event->signal());
-  } else {
-    to_be_set_serialized_state_.reset();
+  } else if (navigation) {
+    navigation->serialized_state.reset();
     // The spec assumes it's ok to leave a promise permanently unresolved, but
     // ScriptPromiseResolver requires either resolution or explicit detach.
-    if (navigation)
-      navigation->resolver->Detach();
+    navigation->resolver->Detach();
   }
 
   return promise_list.IsEmpty() ? DispatchResult::kContinue
@@ -619,13 +616,18 @@
 }
 
 void AppHistory::InformAboutCanceledNavigation() {
-  if (ongoing_navigate_event_ ||
-      post_navigate_event_ongoing_navigation_signal_) {
+  if (ongoing_navigation_signal_) {
     auto* script_state =
         ToScriptStateForMainWorld(GetSupplementable()->GetFrame());
     ScriptState::Scope scope(script_state);
-    FinalizeWithAbortedNavigationError(script_state,
-                                       ongoing_non_traversal_navigation_);
+    AppHistoryApiNavigation* navigation = ongoing_non_traversal_navigation_;
+    if (!navigation && ongoing_navigate_event_) {
+      auto it = ongoing_traversals_.find(
+          ongoing_navigate_event_->destination()->key());
+      if (it != ongoing_traversals_.end())
+        navigation = it->value;
+    }
+    FinalizeWithAbortedNavigationError(script_state, navigation);
   }
 
   // If this function is being called as part of frame detach, also cleanup any
@@ -680,18 +682,17 @@
 void AppHistory::FinalizeWithAbortedNavigationError(
     ScriptState* script_state,
     AppHistoryApiNavigation* navigation) {
-  DCHECK(!ongoing_navigate_event_ ||
-         !post_navigate_event_ongoing_navigation_signal_);
   if (ongoing_navigate_event_) {
     ongoing_navigate_event_->preventDefault();
-    ongoing_navigate_event_->signal()->SignalAbort();
     ongoing_navigate_event_ = nullptr;
-  } else if (post_navigate_event_ongoing_navigation_signal_) {
-    post_navigate_event_ongoing_navigation_signal_->SignalAbort();
-    post_navigate_event_ongoing_navigation_signal_ = nullptr;
+  }
+  if (ongoing_navigation_signal_) {
+    ongoing_navigation_signal_->SignalAbort();
+    ongoing_navigation_signal_ = nullptr;
   }
 
-  to_be_set_serialized_state_ = nullptr;
+  if (navigation)
+    navigation->serialized_state.reset();
   RejectPromiseAndFireNavigateErrorEvent(
       navigation,
       ScriptValue::From(script_state, MakeGarbageCollected<DOMException>(
@@ -718,7 +719,7 @@
   visitor->Trace(ongoing_traversals_);
   visitor->Trace(upcoming_non_traversal_navigation_);
   visitor->Trace(ongoing_navigate_event_);
-  visitor->Trace(post_navigate_event_ongoing_navigation_signal_);
+  visitor->Trace(ongoing_navigation_signal_);
 }
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/app_history/app_history.h b/third_party/blink/renderer/core/app_history/app_history.h
index ac0b39c2..4e1f8ec 100644
--- a/third_party/blink/renderer/core/app_history/app_history.h
+++ b/third_party/blink/renderer/core/app_history/app_history.h
@@ -137,9 +137,7 @@
   Member<AppHistoryApiNavigation> upcoming_non_traversal_navigation_;
 
   Member<AppHistoryNavigateEvent> ongoing_navigate_event_;
-  Member<AbortSignal> post_navigate_event_ongoing_navigation_signal_;
-
-  scoped_refptr<SerializedScriptValue> to_be_set_serialized_state_;
+  Member<AbortSignal> ongoing_navigation_signal_;
 };
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/app_history/app_history_navigate_event.idl b/third_party/blink/renderer/core/app_history/app_history_navigate_event.idl
index 36c7cb8..e2d0910 100644
--- a/third_party/blink/renderer/core/app_history/app_history_navigate_event.idl
+++ b/third_party/blink/renderer/core/app_history/app_history_navigate_event.idl
@@ -7,7 +7,7 @@
   Exposed=Window,
   RuntimeEnabled=AppHistory
 ] interface AppHistoryNavigateEvent : Event {
-  [CallWith=ExecutionContext] constructor(DOMString type, optional AppHistoryNavigateEventInit eventInit = {});
+  [CallWith=ExecutionContext] constructor(DOMString type, AppHistoryNavigateEventInit eventInit);
 
   readonly attribute AppHistoryNavigationType navigationType;
   readonly attribute AppHistoryDestination destination;
diff --git a/third_party/blink/renderer/core/dom/events/event_dispatcher.cc b/third_party/blink/renderer/core/dom/events/event_dispatcher.cc
index 3cfc916..f14a046 100644
--- a/third_party/blink/renderer/core/dom/events/event_dispatcher.cc
+++ b/third_party/blink/renderer/core/dom/events/event_dispatcher.cc
@@ -163,8 +163,8 @@
   LocalFrame* frame = node_->GetDocument().GetFrame();
   if (frame && frame->DomWindow()) {
     eventTiming = EventTiming::Create(frame->DomWindow(), *event_);
-    // TODO(hbsong): Calculate First Input Delay for filtered events.
-    EventTiming::HandleInputDelay(frame->DomWindow(), *event_);
+    if (!base::FeatureList::IsEnabled(kFirstInputDelayWithoutEventListener))
+      EventTiming::HandleInputDelay(frame->DomWindow(), *event_);
   }
 
   if (event_->type() == event_type_names::kChange && event_->isTrusted() &&
diff --git a/third_party/blink/renderer/core/dom/first_letter_pseudo_element.cc b/third_party/blink/renderer/core/dom/first_letter_pseudo_element.cc
index 953acec..24a14fd 100644
--- a/third_party/blink/renderer/core/dom/first_letter_pseudo_element.cc
+++ b/third_party/blink/renderer/core/dom/first_letter_pseudo_element.cc
@@ -138,14 +138,20 @@
             kPseudoIdFirstLetter) {
       first_letter_text_layout_object =
           first_letter_text_layout_object->NextSibling();
-    } else if (first_letter_text_layout_object->IsText()) {
+    } else if (auto* layout_text =
+                   DynamicTo<LayoutText>(first_letter_text_layout_object)) {
+      // Don't apply first letter styling to passwords and other elements
+      // obfuscated by -webkit-text-security. Also, see
+      // ShouldUpdateLayoutByReattaching() in text.cc.
+      if (layout_text->IsSecure())
+        return nullptr;
       // FIXME: If there is leading punctuation in a different LayoutText than
       // the first letter, we'll not apply the correct style to it.
       scoped_refptr<StringImpl> str =
-          To<LayoutText>(first_letter_text_layout_object)->IsTextFragment()
+          layout_text->IsTextFragment()
               ? To<LayoutTextFragment>(first_letter_text_layout_object)
                     ->CompleteText()
-              : To<LayoutText>(first_letter_text_layout_object)->OriginalText();
+              : layout_text->OriginalText();
       if (FirstLetterLength(str.get()) ||
           IsInvalidFirstLetterLayoutObject(first_letter_text_layout_object))
         break;
diff --git a/third_party/blink/renderer/core/dom/text.cc b/third_party/blink/renderer/core/dom/text.cc
index d759e6ea..fe357798 100644
--- a/third_party/blink/renderer/core/dom/text.cc
+++ b/third_party/blink/renderer/core/dom/text.cc
@@ -447,6 +447,17 @@
     return text_fragment_layout_object.GetFirstLetterPseudoElement() ||
            !text_fragment_layout_object.IsRemainingTextLayoutObject();
   }
+  // If we force a re-attach for password inputs and other elements hiding text
+  // input via -webkit-text-security, the last character input will be hidden
+  // immediately, even if the passwordEchoEnabled setting is enabled.
+  // ::first-letter do not seem to apply to text inputs, so for those skipping
+  // the re-attachment should be safe.
+  // We can possibly still cause DCHECKs for mismatch of first letter text in
+  // editing with the combination of -webkit-text-security in author styles on
+  // other elements in combination with ::first-letter.
+  // See crbug.com/1240988
+  if (text_layout_object->IsSecure())
+    return false;
   if (!FirstLetterPseudoElement::FirstLetterLength(
           text_layout_object->GetText()) &&
       FirstLetterPseudoElement::FirstLetterLength(text_node.data())) {
diff --git a/third_party/blink/renderer/core/editing/finder/text_finder.cc b/third_party/blink/renderer/core/editing/finder/text_finder.cc
index cb8e8e22..07f826f 100644
--- a/third_party/blink/renderer/core/editing/finder/text_finder.cc
+++ b/third_party/blink/renderer/core/editing/finder/text_finder.cc
@@ -108,39 +108,14 @@
 
   // If the active match is hidden inside a <details> element, then we should
   // expand it so find-in-page can scroll to it.
-  if (RuntimeEnabledFeatures::AutoExpandDetailsElementEnabled()) {
-    bool details_opened = false;
-
-    for (Node& parent : FlatTreeTraversal::AncestorsOf(first_node)) {
-      if (HTMLDetailsElement* details = DynamicTo<HTMLDetailsElement>(parent)) {
-        // If the active match is inside the <summary> of a <details>, then we
-        // shouldn't expand the <details> because the active match is already
-        // visible.
-        bool inside_summary = false;
-        Element& summary = *details->FindMainSummary();
-        for (Node& ancestor : FlatTreeTraversal::AncestorsOf(first_node)) {
-          if (&ancestor == &summary) {
-            inside_summary = true;
-            break;
-          }
-        }
-
-        if (!inside_summary &&
-            !details->FastHasAttribute(html_names::kOpenAttr)) {
-          details->setAttribute(html_names::kOpenAttr, g_empty_atom);
-          details_opened = true;
-        }
-      }
-    }
-
-    if (details_opened) {
-      // If we opened any details elements, we need to update style and layout
-      // to account for the new content to render inside the now-expanded
-      // details element before we scroll to it. The added open attribute may
-      // also affect style.
-      first_node.GetDocument().UpdateStyleAndLayoutForNode(
-          &first_node, DocumentUpdateReason::kFindInPage);
-    }
+  if (RuntimeEnabledFeatures::AutoExpandDetailsElementEnabled() &&
+      HTMLDetailsElement::ExpandDetailsAncestors(first_node)) {
+    // If we opened any details elements, we need to update style and layout
+    // to account for the new content to render inside the now-expanded
+    // details element before we scroll to it. The added open attribute may
+    // also affect style and have fired mutation events.
+    first_node.GetDocument().UpdateStyleAndLayoutForNode(
+        &first_node, DocumentUpdateReason::kFindInPage);
   }
 
   // We don't always have a LayoutObject for the node we're trying to scroll to
diff --git a/third_party/blink/renderer/core/exported/web_media_player_impl_unittest.cc b/third_party/blink/renderer/core/exported/web_media_player_impl_unittest.cc
index 1cff8b3..52a580c 100644
--- a/third_party/blink/renderer/core/exported/web_media_player_impl_unittest.cc
+++ b/third_party/blink/renderer/core/exported/web_media_player_impl_unittest.cc
@@ -2363,9 +2363,10 @@
 
   // Video is always paused when suspension is on and only if matches the
   // optimization criteria if the optimization is on.
-  bool should_pause = !IsBackgroundVideoPlaybackEnabled() ||
-                      IsMediaSuspendOn() ||
-                      (IsBackgroundPauseOn() && matches_requirements);
+  bool should_pause =
+      (!IsBackgroundVideoPlaybackEnabled() || IsMediaSuspendOn() ||
+       (IsBackgroundPauseOn() && matches_requirements)) &&
+      !(IsPictureInPictureOn() && IsAndroid());
   EXPECT_EQ(should_pause, ShouldPausePlaybackWhenHidden());
 }
 
@@ -2385,8 +2386,9 @@
   // videos is on and background video playback is disabled. Background video
   // playback is enabled by default. Both media suspend and resume background
   // videos are on by default on Android and off on desktop.
-  EXPECT_EQ(!IsBackgroundVideoPlaybackEnabled() ||
-                (IsMediaSuspendOn() && IsResumeBackgroundVideoEnabled()),
+  EXPECT_EQ((!IsBackgroundVideoPlaybackEnabled() ||
+             (IsMediaSuspendOn() && IsResumeBackgroundVideoEnabled())) &&
+                !(IsPictureInPictureOn() && IsAndroid()),
             ShouldPausePlaybackWhenHidden());
 
   if (!matches_requirements || !ShouldDisableVideoWhenHidden() ||
diff --git a/third_party/blink/renderer/core/highlight/highlight_registry.cc b/third_party/blink/renderer/core/highlight/highlight_registry.cc
index 54fb8aba..ed992329 100644
--- a/third_party/blink/renderer/core/highlight/highlight_registry.cc
+++ b/third_party/blink/renderer/core/highlight/highlight_registry.cc
@@ -9,6 +9,7 @@
 #include "third_party/blink/renderer/core/editing/markers/document_marker_controller.h"
 #include "third_party/blink/renderer/core/frame/local_dom_window.h"
 #include "third_party/blink/renderer/core/frame/local_frame.h"
+#include "third_party/blink/renderer/platform/instrumentation/use_counter.h"
 
 namespace blink {
 
@@ -76,6 +77,8 @@
     AtomicString highlight_name,
     Member<Highlight> highlight,
     ExceptionState& exception_state) {
+  UseCounter::Count(ExecutionContext::From(script_state),
+                    WebFeature::kHighlightAPIRegisterHighlight);
   auto highlights_iterator = GetMapIterator(highlight_name);
   if (highlights_iterator != highlights_.end()) {
     highlights_iterator->Get()->highlight->DeregisterFrom(this);
diff --git a/third_party/blink/renderer/core/html/html_details_element.cc b/third_party/blink/renderer/core/html/html_details_element.cc
index 1b8de6b..d11fee4 100644
--- a/third_party/blink/renderer/core/html/html_details_element.cc
+++ b/third_party/blink/renderer/core/html/html_details_element.cc
@@ -26,6 +26,7 @@
 #include "third_party/blink/renderer/core/css_value_keywords.h"
 #include "third_party/blink/renderer/core/dom/element_traversal.h"
 #include "third_party/blink/renderer/core/dom/events/event.h"
+#include "third_party/blink/renderer/core/dom/flat_tree_traversal.h"
 #include "third_party/blink/renderer/core/dom/shadow_root.h"
 #include "third_party/blink/renderer/core/dom/text.h"
 #include "third_party/blink/renderer/core/frame/web_feature.h"
@@ -185,4 +186,39 @@
   return true;
 }
 
+// static
+bool HTMLDetailsElement::ExpandDetailsAncestors(const Node& node) {
+  // Since setting the open attribute fires mutation events which could mess
+  // with the FlatTreeTraversal iterator, we should first iterate details
+  // elements to open and then open them all.
+  VectorOf<HTMLDetailsElement> details_to_open;
+
+  for (Node& parent : FlatTreeTraversal::AncestorsOf(node)) {
+    if (HTMLDetailsElement* details = DynamicTo<HTMLDetailsElement>(parent)) {
+      // If the active match is inside the <summary> of a <details>, then we
+      // shouldn't expand the <details> because the active match is already
+      // visible.
+      bool inside_summary = false;
+      Element& summary = *details->FindMainSummary();
+      for (Node& ancestor : FlatTreeTraversal::AncestorsOf(node)) {
+        if (&ancestor == &summary) {
+          inside_summary = true;
+          break;
+        }
+      }
+
+      if (!inside_summary &&
+          !details->FastHasAttribute(html_names::kOpenAttr)) {
+        details_to_open.push_back(details);
+      }
+    }
+  }
+
+  for (HTMLDetailsElement* details : details_to_open) {
+    details->setAttribute(html_names::kOpenAttr, g_empty_atom);
+  }
+
+  return details_to_open.size();
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/html/html_details_element.h b/third_party/blink/renderer/core/html/html_details_element.h
index 972a4f63..c0724ebf 100644
--- a/third_party/blink/renderer/core/html/html_details_element.h
+++ b/third_party/blink/renderer/core/html/html_details_element.h
@@ -40,6 +40,12 @@
   // Used for slot assignment.
   static bool IsFirstSummary(const Node&);
 
+  // Walks up the ancestor chain and expands all <details> elements found along
+  // the way by setting the open attribute. If any were expanded, returns true.
+  // This method may run script because of the mutation events fired when
+  // setting the open attribute.
+  static bool ExpandDetailsAncestors(const Node&);
+
  private:
   void DispatchPendingEvent(const AttributeModificationReason);
 
diff --git a/third_party/blink/renderer/core/html/media/html_media_element.cc b/third_party/blink/renderer/core/html/media/html_media_element.cc
index 7592c5dd..4fe2c62 100644
--- a/third_party/blink/renderer/core/html/media/html_media_element.cc
+++ b/third_party/blink/renderer/core/html/media/html_media_element.cc
@@ -4644,6 +4644,10 @@
   setCurrentTime(seek_time.InSecondsF());
 }
 
+void HTMLMediaElement::RequestMute(bool mute) {
+  setMuted(mute);
+}
+
 void HTMLMediaElement::SetVolumeMultiplier(double multiplier) {
   if (web_media_player_)
     web_media_player_->SetVolumeMultiplier(multiplier);
diff --git a/third_party/blink/renderer/core/html/media/html_media_element.h b/third_party/blink/renderer/core/html/media/html_media_element.h
index 4af2fe7..6280204 100644
--- a/third_party/blink/renderer/core/html/media/html_media_element.h
+++ b/third_party/blink/renderer/core/html/media/html_media_element.h
@@ -518,6 +518,7 @@
   void RequestSeekTo(base::TimeDelta seek_time) override;
   void RequestEnterPictureInPicture() override {}
   void RequestExitPictureInPicture() override {}
+  void RequestMute(bool mute) override;
   void SetVolumeMultiplier(double multiplier) override;
   void SetPersistentState(bool persistent) override {}
   void SetPowerExperimentState(bool enabled) override;
diff --git a/third_party/blink/renderer/core/inspector/inspector_dom_debugger_agent.cc b/third_party/blink/renderer/core/inspector/inspector_dom_debugger_agent.cc
index 326a8bf..7edecec 100644
--- a/third_party/blink/renderer/core/inspector/inspector_dom_debugger_agent.cc
+++ b/third_party/blink/renderer/core/inspector/inspector_dom_debugger_agent.cc
@@ -286,8 +286,8 @@
 
 void InspectorDOMDebuggerAgent::DidInsertDOMNode(Node* node) {
   if (dom_breakpoints_.size()) {
-    uint32_t mask = dom_breakpoints_.DeprecatedAtOrEmptyValue(
-        InspectorDOMAgent::InnerParentNode(node));
+    uint32_t mask =
+        FindBreakpointMask(InspectorDOMAgent::InnerParentNode(node));
     uint32_t inheritable_types_mask =
         (mask | (mask >> domBreakpointDerivedTypeShift)) &
         inheritableDOMBreakpointTypesMask;
@@ -390,8 +390,7 @@
     return response;
 
   uint32_t root_bit = 1 << type;
-  dom_breakpoints_.Set(
-      node, dom_breakpoints_.DeprecatedAtOrEmptyValue(node) | root_bit);
+  dom_breakpoints_.Set(node, FindBreakpointMask(node) | root_bit);
   if (root_bit & inheritableDOMBreakpointTypesMask) {
     for (Node* child = InspectorDOMAgent::InnerFirstChild(node); child;
          child = InspectorDOMAgent::InnerNextSibling(child))
@@ -415,7 +414,7 @@
     return response;
 
   uint32_t root_bit = 1 << type;
-  uint32_t mask = dom_breakpoints_.DeprecatedAtOrEmptyValue(node) & ~root_bit;
+  uint32_t mask = FindBreakpointMask(node) & ~root_bit;
   if (mask)
     dom_breakpoints_.Set(node, mask);
   else
@@ -567,8 +566,7 @@
     if (!insertion)
       breakpoint_owner = InspectorDOMAgent::InnerParentNode(target);
     DCHECK(breakpoint_owner);
-    while (!(dom_breakpoints_.DeprecatedAtOrEmptyValue(breakpoint_owner) &
-             (1 << breakpoint_type))) {
+    while (!(FindBreakpointMask(breakpoint_owner) & (1 << breakpoint_type))) {
       Node* parent_node = InspectorDOMAgent::InnerParentNode(breakpoint_owner);
       if (!parent_node)
         break;
@@ -591,19 +589,23 @@
       v8_inspector::StringView(json.data(), json.size()));
 }
 
-bool InspectorDOMDebuggerAgent::HasBreakpoint(Node* node, int type) {
+bool InspectorDOMDebuggerAgent::HasBreakpoint(Node* node, int type) const {
   if (!dom_agent_->Enabled())
     return false;
   uint32_t root_bit = 1 << type;
   uint32_t derived_bit = root_bit << domBreakpointDerivedTypeShift;
-  return dom_breakpoints_.DeprecatedAtOrEmptyValue(node) &
-         (root_bit | derived_bit);
+  return FindBreakpointMask(node) & (root_bit | derived_bit);
+}
+
+uint32_t InspectorDOMDebuggerAgent::FindBreakpointMask(Node* node) const {
+  auto it = dom_breakpoints_.find(node);
+  return it != dom_breakpoints_.end() ? it->value : 0;
 }
 
 void InspectorDOMDebuggerAgent::UpdateSubtreeBreakpoints(Node* node,
                                                          uint32_t root_mask,
                                                          bool set) {
-  uint32_t old_mask = dom_breakpoints_.DeprecatedAtOrEmptyValue(node);
+  uint32_t old_mask = FindBreakpointMask(node);
   uint32_t derived_mask = root_mask << domBreakpointDerivedTypeShift;
   uint32_t new_mask = set ? old_mask | derived_mask : old_mask & ~derived_mask;
   if (new_mask)
diff --git a/third_party/blink/renderer/core/inspector/inspector_dom_debugger_agent.h b/third_party/blink/renderer/core/inspector/inspector_dom_debugger_agent.h
index 484c72a9..5f66429 100644
--- a/third_party/blink/renderer/core/inspector/inspector_dom_debugger_agent.h
+++ b/third_party/blink/renderer/core/inspector/inspector_dom_debugger_agent.h
@@ -162,7 +162,9 @@
                               int breakpoint_type,
                               bool insertion);
   void UpdateSubtreeBreakpoints(Node*, uint32_t root_mask, bool set);
-  bool HasBreakpoint(Node*, int type);
+  bool HasBreakpoint(Node*, int type) const;
+  // Returns value if node is in `dom_breakpoints_`, otherwise zero.
+  uint32_t FindBreakpointMask(Node*) const;
   protocol::Response SetBreakpoint(const String& event_name,
                                    const String& target_name);
   protocol::Response RemoveBreakpoint(const String& event_name,
diff --git a/third_party/blink/renderer/core/inspector/inspector_media_context_impl.cc b/third_party/blink/renderer/core/inspector/inspector_media_context_impl.cc
index 4457f0ea..678c11e3 100644
--- a/third_party/blink/renderer/core/inspector/inspector_media_context_impl.cc
+++ b/third_party/blink/renderer/core/inspector/inspector_media_context_impl.cc
@@ -10,6 +10,7 @@
 #include "base/unguessable_token.h"
 #include "third_party/blink/renderer/core/execution_context/execution_context.h"
 #include "third_party/blink/renderer/core/probe/core_probes.h"
+#include "third_party/blink/renderer/platform/wtf/hash_map.h"
 
 namespace blink {
 
@@ -51,8 +52,7 @@
 
 Vector<WebString> MediaInspectorContextImpl::AllPlayerIdsAndMarkSent() {
   Vector<WebString> existing_players;
-  const auto& keys = players_.Keys();
-  existing_players.AppendRange(keys.begin(), keys.end());
+  WTF::CopyKeysToVector(players_, existing_players);
   unsent_players_.clear();
   return existing_players;
 }
@@ -193,9 +193,7 @@
   if (player != players_.end()) {
     for (const auto& property : props)
       player->value->properties.Set(property.name, property);
-
-    properties.AppendRange(player->value->properties.Values().begin(),
-                           player->value->properties.Values().end());
+    WTF::CopyValuesToVector(player->value->properties, properties);
   }
   probe::PlayerPropertiesChanged(GetSupplementable(), playerId, properties);
 }
diff --git a/third_party/blink/renderer/core/inspector/inspector_session_state.h b/third_party/blink/renderer/core/inspector/inspector_session_state.h
index 8ca82b4..ef5fb0c0 100644
--- a/third_party/blink/renderer/core/inspector/inspector_session_state.h
+++ b/third_party/blink/renderer/core/inspector/inspector_session_state.h
@@ -10,6 +10,7 @@
 #include "third_party/blink/public/mojom/devtools/devtools_agent.mojom-blink.h"
 #include "third_party/blink/public/platform/web_vector.h"
 #include "third_party/blink/renderer/core/core_export.h"
+#include "third_party/blink/renderer/platform/wtf/hash_map.h"
 #include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
 #include "third_party/inspector_protocol/crdtp/span.h"
 
@@ -177,8 +178,7 @@
       // TODO(johannes): It'd be nice to avoid copying; unfortunately
       // it didn't seem easy to return map_.Keys().
       Vector<WTF::String> keys;
-      for (const WTF::String& s : map_.Keys())
-        keys.push_back(s);
+      WTF::CopyKeysToVector(map_, keys);
       return keys;
     }
 
diff --git a/third_party/blink/renderer/core/inspector/inspector_session_state_test.cc b/third_party/blink/renderer/core/inspector/inspector_session_state_test.cc
index 99dccbf..d86321a 100644
--- a/third_party/blink/renderer/core/inspector/inspector_session_state_test.cc
+++ b/third_party/blink/renderer/core/inspector/inspector_session_state_test.cc
@@ -7,6 +7,7 @@
 #include "build/build_config.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/renderer/platform/wtf/hash_map.h"
 
 namespace blink {
 using mojom::blink::DevToolsSessionState;
@@ -236,8 +237,7 @@
   // passed to AgentState so that the stored values won't collide.
   DevToolsSessionStatePtr cookie = dev_tools_session.CloneCookie();
   Vector<WTF::String> keys;
-  for (const WTF::String& k : cookie->entries.Keys())
-    keys.push_back(k);
+  WTF::CopyKeysToVector(cookie->entries, keys);
 
   EXPECT_THAT(keys, UnorderedElementsAre("map_agents.1/Pi", "simple_agent.4/"));
 
diff --git a/third_party/blink/renderer/core/layout/layout_object.h b/third_party/blink/renderer/core/layout/layout_object.h
index 25db76c8..23e98cf 100644
--- a/third_party/blink/renderer/core/layout/layout_object.h
+++ b/third_party/blink/renderer/core/layout/layout_object.h
@@ -1473,6 +1473,10 @@
     NOT_DESTROYED();
     return bitfields_.IsAtomicInlineLevel();
   }
+  bool IsBlockInInline() const {
+    return IsAnonymous() && !IsInline() && !IsFloatingOrOutOfFlowPositioned() &&
+           Parent() && Parent()->IsLayoutInline();
+  }
   bool IsHorizontalWritingMode() const {
     NOT_DESTROYED();
     return bitfields_.HorizontalWritingMode();
diff --git a/third_party/blink/renderer/core/layout/ng/inline/ng_fragment_items_builder.cc b/third_party/blink/renderer/core/layout/ng/inline/ng_fragment_items_builder.cc
index 43b8c8d..5e5012e 100644
--- a/third_party/blink/renderer/core/layout/ng/inline/ng_fragment_items_builder.cc
+++ b/third_party/blink/renderer/core/layout/ng/inline/ng_fragment_items_builder.cc
@@ -89,6 +89,17 @@
   return current_line_items_;
 }
 
+const NGLogicalLineItems& NGFragmentItemsBuilder::LogicalLineItems(
+    const NGPhysicalLineBoxFragment& line_fragment) const {
+  if (&line_fragment == current_line_fragment_) {
+    DCHECK(current_line_items_);
+    return *current_line_items_;
+  }
+  const NGLogicalLineItems* items = line_items_map_.at(&line_fragment);
+  DCHECK(items);
+  return *items;
+}
+
 void NGFragmentItemsBuilder::AssociateLogicalLineItems(
     NGLogicalLineItems* line_items,
     const NGPhysicalFragment& line_fragment) {
diff --git a/third_party/blink/renderer/core/layout/ng/inline/ng_fragment_items_builder.h b/third_party/blink/renderer/core/layout/ng/inline/ng_fragment_items_builder.h
index 17596d09..cde3196 100644
--- a/third_party/blink/renderer/core/layout/ng/inline/ng_fragment_items_builder.h
+++ b/third_party/blink/renderer/core/layout/ng/inline/ng_fragment_items_builder.h
@@ -71,6 +71,8 @@
   // them alive until |AddLine|.
   NGLogicalLineItems* AcquireLogicalLineItems();
   void ReleaseCurrentLogicalLineItems();
+  const NGLogicalLineItems& LogicalLineItems(
+      const NGPhysicalLineBoxFragment&) const;
   void AssociateLogicalLineItems(NGLogicalLineItems* line_items,
                                  const NGPhysicalFragment& line_fragment);
   void AddLine(const NGPhysicalLineBoxFragment& line,
diff --git a/third_party/blink/renderer/core/layout/ng/inline/ng_inline_layout_algorithm.cc b/third_party/blink/renderer/core/layout/ng/inline/ng_inline_layout_algorithm.cc
index 0feb74d..d771d70 100644
--- a/third_party/blink/renderer/core/layout/ng/inline/ng_inline_layout_algorithm.cc
+++ b/third_party/blink/renderer/core/layout/ng/inline/ng_inline_layout_algorithm.cc
@@ -612,6 +612,7 @@
   // Setup |container_builder_|. Set it up here instead of in |CreateLine|,
   // because there should be only one block-in-inline, and we need data from the
   // |NGLayoutResult|.
+  container_builder_.SetIsBlockInInline();
   container_builder_.SetBaseDirection(line_info.BaseDirection());
   if (absl::optional<LayoutUnit> block_offset = result.BfcBlockOffset()) {
     container_builder_.SetBfcBlockOffset(*block_offset);
diff --git a/third_party/blink/renderer/core/layout/ng/inline/ng_logical_line_item.cc b/third_party/blink/renderer/core/layout/ng/inline/ng_logical_line_item.cc
index c1c0546..d05d1b2 100644
--- a/third_party/blink/renderer/core/layout/ng/inline/ng_logical_line_item.cc
+++ b/third_party/blink/renderer/core/layout/ng/inline/ng_logical_line_item.cc
@@ -71,6 +71,15 @@
   return nullptr;
 }
 
+const NGLayoutResult* NGLogicalLineItems::BlockInInlineLayoutResult() const {
+  for (const NGLogicalLineItem& item : *this) {
+    if (item.layout_result &&
+        item.layout_result->PhysicalFragment().IsBlockInInline())
+      return item.layout_result.get();
+  }
+  return nullptr;
+}
+
 void NGLogicalLineItems::WillInsertChild(unsigned insert_before) {
   unsigned index = 0;
   for (NGLogicalLineItem& child : children_) {
diff --git a/third_party/blink/renderer/core/layout/ng/inline/ng_logical_line_item.h b/third_party/blink/renderer/core/layout/ng/inline/ng_logical_line_item.h
index 0d4b2554..59d0e30 100644
--- a/third_party/blink/renderer/core/layout/ng/inline/ng_logical_line_item.h
+++ b/third_party/blink/renderer/core/layout/ng/inline/ng_logical_line_item.h
@@ -16,6 +16,7 @@
 namespace blink {
 
 class LayoutObject;
+class NGLayoutResult;
 
 // This class represents an item in a line, after line break, but still mutable
 // and in the logical coordinate system.
@@ -287,6 +288,8 @@
   NGLogicalLineItem* FirstInFlowChild();
   NGLogicalLineItem* LastInFlowChild();
 
+  const NGLayoutResult* BlockInInlineLayoutResult() const;
+
   // Add a child. Accepts all constructor arguments for |NGLogicalLineItem|.
   template <class... Args>
   void AddChild(Args&&... args) {
diff --git a/third_party/blink/renderer/core/layout/ng/ng_box_fragment_builder.cc b/third_party/blink/renderer/core/layout/ng/ng_box_fragment_builder.cc
index f8beaa4e..d4ee6ed 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_box_fragment_builder.cc
+++ b/third_party/blink/renderer/core/layout/ng/ng_box_fragment_builder.cc
@@ -97,9 +97,21 @@
     absl::optional<LogicalOffset> relative_offset,
     bool propagate_oof_descendants) {
   const auto& fragment = child_layout_result.PhysicalFragment();
-  if (items_builder_) {
+  const NGLayoutResult* child_box_layout_result = nullptr;
+  if (fragment.IsBox()) {
+    child_box_layout_result = &child_layout_result;
+  } else if (items_builder_) {
     if (const NGPhysicalLineBoxFragment* line =
             DynamicTo<NGPhysicalLineBoxFragment>(&fragment)) {
+      if (UNLIKELY(line->IsBlockInInline() && has_block_fragmentation_)) {
+        // If this line box contains a block-in-inline, propagate break data
+        // from the block-in-inline.
+        const NGLogicalLineItems& line_items =
+            items_builder_->LogicalLineItems(*line);
+        child_box_layout_result = line_items.BlockInInlineLayoutResult();
+        DCHECK(child_box_layout_result);
+      }
+
       items_builder_->AddLine(*line, offset);
       // TODO(kojii): We probably don't need to AddChild this line, but there
       // maybe OOF objects. Investigate how to handle them.
@@ -117,8 +129,9 @@
   AddChild(fragment, offset, /* inline_container */ nullptr, &end_margin_strut,
            child_layout_result.IsSelfCollapsing(), relative_offset,
            adjustment_for_oof_propagation);
-  if (fragment.IsBox())
-    PropagateBreak(child_layout_result);
+
+  if (child_box_layout_result)
+    PropagateBreak(*child_box_layout_result);
 }
 
 void NGBoxFragmentBuilder::AddChild(
@@ -386,6 +399,10 @@
   }
 #endif
 
+  if (UNLIKELY(box_type_ == NGPhysicalFragment::kNormalBox && node_ &&
+               node_.IsBlockInInline()))
+    SetIsBlockInInline();
+
   if (UNLIKELY(has_block_fragmentation_ && !break_token_ && node_)) {
     if (last_inline_break_token_)
       child_break_tokens_.push_back(std::move(last_inline_break_token_));
diff --git a/third_party/blink/renderer/core/layout/ng/ng_container_fragment_builder.h b/third_party/blink/renderer/core/layout/ng/ng_container_fragment_builder.h
index 985fc950..248e648 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_container_fragment_builder.h
+++ b/third_party/blink/renderer/core/layout/ng/ng_container_fragment_builder.h
@@ -245,6 +245,8 @@
     return adjoining_object_types_;
   }
 
+  void SetIsBlockInInline() { is_block_in_inline_ = true; }
+
   void SetHasBlockFragmentation() { has_block_fragmentation_ = true; }
 
   // Set for any node that establishes a fragmentation context, such as multicol
@@ -371,6 +373,7 @@
   bool is_self_collapsing_ = false;
   bool is_pushed_by_floats_ = false;
   bool is_legacy_layout_root_ = false;
+  bool is_block_in_inline_ = false;
 
   bool has_floating_descendants_for_paint_ = false;
   bool has_descendant_that_depends_on_percentage_block_size_ = false;
diff --git a/third_party/blink/renderer/core/layout/ng/ng_layout_input_node.h b/third_party/blink/renderer/core/layout/ng/ng_layout_input_node.h
index c511de7..a9a01e65 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_layout_input_node.h
+++ b/third_party/blink/renderer/core/layout/ng/ng_layout_input_node.h
@@ -77,6 +77,7 @@
   bool IsBlock() const { return type_ == kBlock; }
 
   bool IsBlockFlow() const { return IsBlock() && box_->IsLayoutBlockFlow(); }
+  bool IsBlockInInline() const { return box_->IsBlockInInline(); }
   bool IsLayoutNGCustom() const {
     return IsBlock() && box_->IsLayoutNGCustom();
   }
diff --git a/third_party/blink/renderer/core/layout/ng/ng_physical_box_fragment.cc b/third_party/blink/renderer/core/layout/ng/ng_physical_box_fragment.cc
index 2721b9cc..970608f 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_physical_box_fragment.cc
+++ b/third_party/blink/renderer/core/layout/ng/ng_physical_box_fragment.cc
@@ -1592,6 +1592,8 @@
   DCHECK_EQ(sub_type_, other.sub_type_);
   DCHECK_EQ(style_variant_, other.style_variant_);
   DCHECK_EQ(is_hidden_for_paint_, other.is_hidden_for_paint_);
+  DCHECK_EQ(is_opaque_, other.is_opaque_);
+  DCHECK_EQ(is_block_in_inline_, other.is_block_in_inline_);
   DCHECK_EQ(is_math_fraction_, other.is_math_fraction_);
   DCHECK_EQ(is_math_operator_, other.is_math_operator_);
 
diff --git a/third_party/blink/renderer/core/layout/ng/ng_physical_fragment.cc b/third_party/blink/renderer/core/layout/ng/ng_physical_fragment.cc
index 03887b1..d30d4a86 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_physical_fragment.cc
+++ b/third_party/blink/renderer/core/layout/ng/ng_physical_fragment.cc
@@ -343,6 +343,7 @@
       style_variant_((unsigned)builder->style_variant_),
       is_hidden_for_paint_(builder->is_hidden_for_paint_),
       is_opaque_(builder->is_opaque_),
+      is_block_in_inline_(builder->is_block_in_inline_),
       is_fieldset_container_(false),
       is_table_ng_part_(false),
       is_legacy_layout_root_(false),
@@ -401,6 +402,7 @@
       style_variant_(other.style_variant_),
       is_hidden_for_paint_(other.is_hidden_for_paint_),
       is_opaque_(other.is_opaque_),
+      is_block_in_inline_(other.is_block_in_inline_),
       is_math_fraction_(other.is_math_fraction_),
       is_math_operator_(other.is_math_operator_),
       may_have_descendant_above_block_start_(
diff --git a/third_party/blink/renderer/core/layout/ng/ng_physical_fragment.h b/third_party/blink/renderer/core/layout/ng/ng_physical_fragment.h
index 18081ed..c73ea6a 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_physical_fragment.h
+++ b/third_party/blink/renderer/core/layout/ng/ng_physical_fragment.h
@@ -126,10 +126,9 @@
   bool IsAtomicInline() const {
     return IsBox() && BoxType() == NGBoxType::kAtomicInline;
   }
-  bool IsBlockInInline() const {
-    return IsBox() && BoxType() == NGBoxType::kNormalBox && GetLayoutObject() &&
-           IsA<LayoutInline>(GetLayoutObject()->Parent());
-  }
+  // True if this box is a block-in-inline, or if this line contains a
+  // block-in-inline.
+  bool IsBlockInInline() const { return is_block_in_inline_; }
   // True if this fragment is in-flow in an inline formatting context.
   bool IsInline() const { return IsInlineBox() || IsAtomicInline(); }
   bool IsFloating() const {
@@ -651,6 +650,7 @@
   const unsigned style_variant_ : 2;  // NGStyleVariant
   const unsigned is_hidden_for_paint_ : 1;
   unsigned is_opaque_ : 1;
+  unsigned is_block_in_inline_ : 1;
   unsigned is_math_fraction_ : 1;
   unsigned is_math_operator_ : 1;
   unsigned may_have_descendant_above_block_start_ : 1;
diff --git a/third_party/blink/renderer/core/page/scrolling/element_fragment_anchor.cc b/third_party/blink/renderer/core/page/scrolling/element_fragment_anchor.cc
index 65c9a1b4..d5f752b 100644
--- a/third_party/blink/renderer/core/page/scrolling/element_fragment_anchor.cc
+++ b/third_party/blink/renderer/core/page/scrolling/element_fragment_anchor.cc
@@ -14,6 +14,7 @@
 #include "third_party/blink/renderer/core/editing/frame_selection.h"
 #include "third_party/blink/renderer/core/execution_context/execution_context.h"
 #include "third_party/blink/renderer/core/frame/local_frame.h"
+#include "third_party/blink/renderer/core/html/html_details_element.h"
 #include "third_party/blink/renderer/core/svg/svg_svg_element.h"
 #include "third_party/blink/renderer/platform/bindings/script_forbidden_scope.h"
 #include "third_party/blink/renderer/platform/weborigin/kurl.h"
@@ -102,11 +103,22 @@
     boundary_local_frame->View()->SetSafeToPropagateScrollToParent(false);
   }
 
-  auto* element_to_scroll = DynamicTo<Element>(anchor_node_.Get());
+  Member<Element> element_to_scroll = DynamicTo<Element>(anchor_node_.Get());
   if (!element_to_scroll)
     element_to_scroll = doc.documentElement();
 
   if (element_to_scroll) {
+    // Expand <details> elements so we can make |element_to_scroll| visible.
+    if (RuntimeEnabledFeatures::AutoExpandDetailsElementEnabled() &&
+        HTMLDetailsElement::ExpandDetailsAncestors(*element_to_scroll)) {
+      // If we opened any details elements, we need to update style and layout
+      // to account for the new content to render inside the now-expanded
+      // details element before we scroll to it. The added open attribute may
+      // also affect style.
+      doc.UpdateStyleAndLayoutForNode(element_to_scroll,
+                                      DocumentUpdateReason::kFindInPage);
+    }
+
     ScrollIntoViewOptions* options = ScrollIntoViewOptions::Create();
     options->setBlock("start");
     options->setInlinePosition("nearest");
diff --git a/third_party/blink/renderer/core/timing/event_timing.cc b/third_party/blink/renderer/core/timing/event_timing.cc
index 95143ba..cd2487b 100644
--- a/third_party/blink/renderer/core/timing/event_timing.cc
+++ b/third_party/blink/renderer/core/timing/event_timing.cc
@@ -43,6 +43,10 @@
 
 }  // namespace
 
+// Record FID even when there's no event listener.
+const base::Feature kFirstInputDelayWithoutEventListener{
+    "FirstInputDelayWithoutEventListener", base::FEATURE_DISABLED_BY_DEFAULT};
+
 EventTiming::EventTiming(base::TimeTicks processing_start,
                          WindowPerformance* performance,
                          const Event& event)
@@ -112,6 +116,9 @@
   if (!should_report_for_event_timing && !should_log_event)
     return nullptr;
 
+  if (base::FeatureList::IsEnabled(kFirstInputDelayWithoutEventListener))
+    HandleInputDelay(window, event);
+
   base::TimeTicks processing_start = Now();
   return should_report_for_event_timing
              ? std::make_unique<EventTiming>(processing_start, performance,
diff --git a/third_party/blink/renderer/core/timing/event_timing.h b/third_party/blink/renderer/core/timing/event_timing.h
index 59a8627..aa47db44 100644
--- a/third_party/blink/renderer/core/timing/event_timing.h
+++ b/third_party/blink/renderer/core/timing/event_timing.h
@@ -13,6 +13,8 @@
 
 namespace blink {
 
+CORE_EXPORT extern const base::Feature kFirstInputDelayWithoutEventListener;
+
 class Event;
 
 // Event timing collects and records the event start time, processing start time
diff --git a/third_party/blink/renderer/modules/ad_auction/navigator_auction.cc b/third_party/blink/renderer/modules/ad_auction/navigator_auction.cc
index 956a70b..5b4910d49 100644
--- a/third_party/blink/renderer/modules/ad_auction/navigator_auction.cc
+++ b/third_party/blink/renderer/modules/ad_auction/navigator_auction.cc
@@ -373,16 +373,11 @@
 
 NavigatorAuction::NavigatorAuction(Navigator& navigator)
     : Supplement(navigator),
-      ad_auction_service_(navigator.GetExecutionContext()),
-      interest_group_store_(navigator.GetExecutionContext()) {
+      ad_auction_service_(navigator.GetExecutionContext()) {
   navigator.GetExecutionContext()->GetBrowserInterfaceBroker().GetInterface(
       ad_auction_service_.BindNewPipeAndPassReceiver(
           navigator.GetExecutionContext()->GetTaskRunner(
               TaskType::kMiscPlatformAPI)));
-  navigator.GetExecutionContext()->GetBrowserInterfaceBroker().GetInterface(
-      interest_group_store_.BindNewPipeAndPassReceiver(
-          navigator.GetExecutionContext()->GetTaskRunner(
-              TaskType::kMiscPlatformAPI)));
 }
 
 NavigatorAuction& NavigatorAuction::From(ExecutionContext* context,
@@ -442,7 +437,7 @@
     return;
   }
 
-  interest_group_store_->JoinInterestGroup(std::move(mojo_group));
+  ad_auction_service_->JoinInterestGroup(std::move(mojo_group));
 }
 
 /* static */
@@ -467,7 +462,7 @@
                                    "' must be a valid https origin.");
     return;
   }
-  interest_group_store_->LeaveInterestGroup(owner, group->name());
+  ad_auction_service_->LeaveInterestGroup(owner, group->name());
 }
 
 /* static */
@@ -480,7 +475,7 @@
 }
 
 void NavigatorAuction::updateAdInterestGroups() {
-  interest_group_store_->UpdateAdInterestGroups();
+  ad_auction_service_->UpdateAdInterestGroups();
 }
 
 /* static */
diff --git a/third_party/blink/renderer/modules/ad_auction/navigator_auction.h b/third_party/blink/renderer/modules/ad_auction/navigator_auction.h
index ee8d74d..b7f40635 100644
--- a/third_party/blink/renderer/modules/ad_auction/navigator_auction.h
+++ b/third_party/blink/renderer/modules/ad_auction/navigator_auction.h
@@ -6,7 +6,6 @@
 #define THIRD_PARTY_BLINK_RENDERER_MODULES_AD_AUCTION_NAVIGATOR_AUCTION_H_
 
 #include "third_party/blink/public/mojom/interest_group/ad_auction_service.mojom-blink.h"
-#include "third_party/blink/public/mojom/interest_group/restricted_interest_group_store.mojom-blink.h"
 #include "third_party/blink/renderer/core/frame/navigator.h"
 #include "third_party/blink/renderer/modules/modules_export.h"
 #include "third_party/blink/renderer/platform/heap/handle.h"
@@ -58,7 +57,6 @@
                                     ExceptionState&);
 
   void Trace(Visitor* visitor) const override {
-    visitor->Trace(interest_group_store_);
     visitor->Trace(ad_auction_service_);
     Supplement<Navigator>::Trace(visitor);
   }
@@ -68,8 +66,6 @@
   void AuctionComplete(ScriptPromiseResolver*, const absl::optional<KURL>&);
 
   HeapMojoRemote<mojom::blink::AdAuctionService> ad_auction_service_;
-  HeapMojoRemote<mojom::blink::RestrictedInterestGroupStore>
-      interest_group_store_;
 };
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/modules/canvas/canvas2d/canvas_rendering_context_2d_api_test.cc b/third_party/blink/renderer/modules/canvas/canvas2d/canvas_rendering_context_2d_api_test.cc
index 4f08b2afd..3af96849 100644
--- a/third_party/blink/renderer/modules/canvas/canvas2d/canvas_rendering_context_2d_api_test.cc
+++ b/third_party/blink/renderer/modules/canvas/canvas2d/canvas_rendering_context_2d_api_test.cc
@@ -6,6 +6,7 @@
 
 #include <memory>
 
+#include "build/build_config.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/blink/public/common/privacy_budget/identifiability_study_settings.h"
@@ -590,8 +591,17 @@
   EXPECT_FALSE(Context2D()->IdentifiabilityEncounteredPartiallyDigestedImage());
 }
 
+// TODO(crbug.com/1239374): Fix test on Android L and re-enable.
+#if defined(OS_ANDROID)
+#define MAYBE_IdentifiabilityStudyDigest_putImageData \
+  DISABLED_IdentifiabilityStudyDigest_putImageData
+#else
+#define MAYBE_IdentifiabilityStudyDigest_putImageData \
+  IdentifiabilityStudyDigest_putImageData
+#endif  // defined(OS_ANDROID)
+
 TEST_F(CanvasRenderingContext2DAPITest,
-       IdentifiabilityStudyDigest_putImageData) {
+       MAYBE_IdentifiabilityStudyDigest_putImageData) {
   StudyParticipationRaii study_participation_raii;
   CreateContext(kNonOpaque);
   NonThrowableExceptionState exception_state;
diff --git a/third_party/blink/renderer/modules/presentation/presentation_connection_close_event.cc b/third_party/blink/renderer/modules/presentation/presentation_connection_close_event.cc
index c1355d0f..467dc6e3 100644
--- a/third_party/blink/renderer/modules/presentation/presentation_connection_close_event.cc
+++ b/third_party/blink/renderer/modules/presentation/presentation_connection_close_event.cc
@@ -21,7 +21,8 @@
     const PresentationConnectionCloseEventInit* initializer)
     : Event(event_type, initializer),
       reason_(initializer->reason()),
-      message_(initializer->message()) {}
+      message_(initializer->hasMessage() ? initializer->message()
+                                         : g_empty_string) {}
 
 const AtomicString& PresentationConnectionCloseEvent::InterfaceName() const {
   return event_interface_names::kPresentationConnectionCloseEvent;
diff --git a/third_party/blink/renderer/platform/loader/fetch/response_body_loader_test.cc b/third_party/blink/renderer/platform/loader/fetch/response_body_loader_test.cc
index 0f7db968..88e4a9f 100644
--- a/third_party/blink/renderer/platform/loader/fetch/response_body_loader_test.cc
+++ b/third_party/blink/renderer/platform/loader/fetch/response_body_loader_test.cc
@@ -7,6 +7,7 @@
 #include <memory>
 #include <string>
 #include <utility>
+#include "base/strings/string_number_conversions.h"
 #include "base/test/scoped_feature_list.h"
 #include "services/network/public/cpp/features.h"
 #include "testing/gtest/include/gtest/gtest.h"
diff --git a/third_party/blink/renderer/platform/media/web_media_player_impl.cc b/third_party/blink/renderer/platform/media/web_media_player_impl.cc
index 4e02831..ce567284 100644
--- a/third_party/blink/renderer/platform/media/web_media_player_impl.cc
+++ b/third_party/blink/renderer/platform/media/web_media_player_impl.cc
@@ -3498,6 +3498,11 @@
     return false;
   }
 
+#if defined(OS_ANDROID)
+  if (IsInPictureInPicture())
+    return false;
+#endif
+
   if (!is_background_video_playback_enabled_)
     return true;
 
diff --git a/third_party/blink/web_tests/WebDriverExpectations b/third_party/blink/web_tests/WebDriverExpectations
index 1e704f2d..d7600b5 100644
--- a/third_party/blink/web_tests/WebDriverExpectations
+++ b/third_party/blink/web_tests/WebDriverExpectations
@@ -77,12 +77,7 @@
 crbug.com/1020018 [ Linux ] external/wpt/webdriver/tests/get_active_element/get.py>>test_sucess_input [ Failure ]
 crbug.com/1020018 [ Linux ] external/wpt/webdriver/tests/get_active_element/get.py>>test_sucess_input_non_interactable [ Failure ]
 
-crbug.com/1167321 [ Linux ] external/wpt/webdriver/tests/find_elements_from_shadow_root/find.py>>test_no_browsing_context [ Failure ]
-crbug.com/1167321 [ Linux ] external/wpt/webdriver/tests/find_elements_from_shadow_root/find.py>>test_find_elements[tag-name-a] [ Failure ]
-crbug.com/1167321 [ Linux ] external/wpt/webdriver/tests/find_elements_from_shadow_root/find.py>>test_find_elements[xpath-//a] [ Failure ]
-crbug.com/1167321 [ Linux ] external/wpt/webdriver/tests/find_element_from_shadow_root/find.py>>test_no_browsing_context [ Failure ]
-crbug.com/1167321 [ Linux ] external/wpt/webdriver/tests/find_element_from_shadow_root/find.py>>test_find_element[tag-name-a] [ Failure ]
-crbug.com/1167321 [ Linux ] external/wpt/webdriver/tests/find_element_from_shadow_root/find.py>>test_find_element[xpath-//a] [ Failure ]
+crbug.com/1167321 [ Linux ] external/wpt/webdriver/tests/find_elements_from_shadow_root/find.py [ Failure ]
 
 crbug.com/1240985 [ Linux ] external/wpt/webdriver/tests/find_elements_from_shadow_root/user_prompts.py>>test_accept_and_notify[capabilities0-alert-None] [ Failure ]
 crbug.com/1240985 [ Linux ] external/wpt/webdriver/tests/find_elements_from_shadow_root/user_prompts.py>>test_accept_and_notify[capabilities0-confirm-True] [ Failure ]
@@ -114,7 +109,29 @@
 crbug.com/626703 [ Linux ] external/wpt/webdriver/tests/forward/forward.py>>test_cross_origin[capabilities0] [ Failure ]
 crbug.com/626703 [ Linux ] external/wpt/webdriver/tests/element_click/navigate.py>>test_link_cross_origin[capabilities0] [ Failure ]
 crbug.com/626703 [ Linux ] external/wpt/webdriver/tests/new_session/create_alwaysMatch.py>>test_valid[timeouts-value12] [ Failure ]
+crbug.com/626703 [ Linux ] external/wpt/webdriver/tests/find_elements_from_shadow_root/user_prompts.py>>test_accept[capabilities0-prompt-] [ Failure ]
+crbug.com/626703 [ Linux ] external/wpt/webdriver/tests/find_elements_from_shadow_root/user_prompts.py>>test_accept[capabilities0-alert-None] [ Failure ]
+crbug.com/626703 [ Linux ] external/wpt/webdriver/tests/find_elements_from_shadow_root/user_prompts.py>>test_dismiss[capabilities0-alert-None] [ Failure ]
+crbug.com/626703 [ Linux ] external/wpt/webdriver/tests/find_elements_from_shadow_root/user_prompts.py>>test_accept[capabilities0-confirm-True] [ Failure ]
+crbug.com/626703 [ Linux ] external/wpt/webdriver/tests/find_elements_from_shadow_root/user_prompts.py>>test_dismiss_and_notify[capabilities0-prompt-None] [ Failure ]
+crbug.com/626703 [ Linux ] external/wpt/webdriver/tests/find_elements_from_shadow_root/user_prompts.py>>test_accept_and_notify[capabilities0-prompt-] [ Failure ]
+crbug.com/626703 [ Linux ] external/wpt/webdriver/tests/find_elements_from_shadow_root/user_prompts.py>>test_ignore[capabilities0-prompt] [ Failure ]
+crbug.com/626703 [ Linux ] external/wpt/webdriver/tests/find_elements_from_shadow_root/user_prompts.py>>test_ignore[capabilities0-alert] [ Failure ]
+crbug.com/626703 [ Linux ] external/wpt/webdriver/tests/find_elements_from_shadow_root/user_prompts.py>>test_default[alert-None] [ Failure ]
+crbug.com/626703 [ Linux ] external/wpt/webdriver/tests/find_elements_from_shadow_root/user_prompts.py>>test_dismiss_and_notify[capabilities0-alert-None] [ Failure ]
+crbug.com/626703 [ Linux ] external/wpt/webdriver/tests/find_elements_from_shadow_root/user_prompts.py>>test_accept_and_notify[capabilities0-alert-None] [ Failure ]
+crbug.com/626703 [ Linux ] external/wpt/webdriver/tests/find_elements_from_shadow_root/user_prompts.py>>test_dismiss[capabilities0-prompt-None] [ Failure ]
+crbug.com/626703 [ Linux ] external/wpt/webdriver/tests/find_elements_from_shadow_root/user_prompts.py>>test_dismiss[capabilities0-confirm-False] [ Failure ]
+crbug.com/626703 [ Linux ] external/wpt/webdriver/tests/find_elements_from_shadow_root/user_prompts.py>>test_accept_and_notify[capabilities0-confirm-True] [ Failure ]
+crbug.com/626703 [ Linux ] external/wpt/webdriver/tests/find_elements_from_shadow_root/user_prompts.py>>test_default[prompt-None] [ Failure ]
+crbug.com/626703 [ Linux ] external/wpt/webdriver/tests/find_elements_from_shadow_root/user_prompts.py>>test_dismiss_and_notify[capabilities0-confirm-False] [ Failure ]
 crbug.com/626703 [ Linux ] external/wpt/webdriver/tests/get_element_shadow_root/get.py>>test_no_top_browsing_context [ Failure ]
+crbug.com/626703 [ Linux ] external/wpt/webdriver/tests/find_elements_from_shadow_root/user_prompts.py>>test_default[confirm-False] [ Failure ]
+crbug.com/626703 [ Linux ] external/wpt/webdriver/tests/get_element_shadow_root/get.py>>test_element_not_found [ Failure ]
+crbug.com/626703 [ Linux ] external/wpt/webdriver/tests/find_elements_from_shadow_root/user_prompts.py>>test_ignore[capabilities0-confirm] [ Failure ]
+crbug.com/626703 [ Linux ] external/wpt/webdriver/tests/get_element_shadow_root/get.py>>test_no_shadow_root [ Failure ]
+crbug.com/626703 [ Linux ] external/wpt/webdriver/tests/get_element_shadow_root/get.py>>test_get_shadow_root [ Failure ]
+crbug.com/626703 [ Linux ] external/wpt/webdriver/tests/get_element_shadow_root/get.py>>test_element_stale [ Failure ]
 crbug.com/626703 [ Linux ] external/wpt/webdriver/tests/get_element_shadow_root/get.py>>test_no_browsing_context [ Failure ]
 crbug.com/626703 [ Linux ] external/wpt/webdriver/tests/switch_to_frame/switch.py>>test_frame_id_null [ Failure ]
 crbug.com/626703 [ Linux ] external/wpt/webdriver/tests/switch_to_parent_frame/switch.py>>test_switch_from_iframe [ Failure ]
@@ -291,3 +308,40 @@
 crbug.com/1123907 [ Linux ] external/wpt/webdriver/tests/fullscreen_window/stress.py>>test_stress[4] [ Failure Pass ]
 crbug.com/1123907 [ Linux ] external/wpt/webdriver/tests/fullscreen_window/stress.py>>test_stress [ Failure Pass ]
 crbug.com/1128104 [ Linux ] external/wpt/webdriver/tests/release_actions/sequence.py>>test_no_release_mouse_sequence_keeps_dblclick_state [ Failure ]
+crbug.com/1168659 [ Linux ] external/wpt/webdriver/tests/get_element_shadow_root/user_prompts.py>>test_accept[capabilities0-alert-None] [ Failure ]
+crbug.com/1168659 [ Linux ] external/wpt/webdriver/tests/get_element_shadow_root/user_prompts.py>>test_accept[capabilities0-confirm-True] [ Failure ]
+crbug.com/1168659 [ Linux ] external/wpt/webdriver/tests/get_element_shadow_root/user_prompts.py>>test_accept[capabilities0-prompt-] [ Failure ]
+crbug.com/1168659 [ Linux ] external/wpt/webdriver/tests/get_element_shadow_root/user_prompts.py>>test_accept_and_notify[capabilities0-alert-None] [ Failure ]
+crbug.com/1168659 [ Linux ] external/wpt/webdriver/tests/get_element_shadow_root/user_prompts.py>>test_accept_and_notify[capabilities0-confirm-True] [ Failure ]
+crbug.com/1168659 [ Linux ] external/wpt/webdriver/tests/get_element_shadow_root/user_prompts.py>>test_accept_and_notify[capabilities0-prompt-] [ Failure ]
+crbug.com/1168659 [ Linux ] external/wpt/webdriver/tests/get_element_shadow_root/user_prompts.py>>test_default[alert-None] [ Failure ]
+crbug.com/1168659 [ Linux ] external/wpt/webdriver/tests/get_element_shadow_root/user_prompts.py>>test_default[confirm-False] [ Failure ]
+crbug.com/1168659 [ Linux ] external/wpt/webdriver/tests/get_element_shadow_root/user_prompts.py>>test_default[prompt-None] [ Failure ]
+crbug.com/1168659 [ Linux ] external/wpt/webdriver/tests/get_element_shadow_root/user_prompts.py>>test_dismiss[capabilities0-alert-None] [ Failure ]
+crbug.com/1168659 [ Linux ] external/wpt/webdriver/tests/get_element_shadow_root/user_prompts.py>>test_dismiss[capabilities0-confirm-False] [ Failure ]
+crbug.com/1168659 [ Linux ] external/wpt/webdriver/tests/get_element_shadow_root/user_prompts.py>>test_dismiss[capabilities0-prompt-None] [ Failure ]
+crbug.com/1168659 [ Linux ] external/wpt/webdriver/tests/get_element_shadow_root/user_prompts.py>>test_dismiss_and_notify[capabilities0-alert-None] [ Failure ]
+crbug.com/1168659 [ Linux ] external/wpt/webdriver/tests/get_element_shadow_root/user_prompts.py>>test_dismiss_and_notify[capabilities0-confirm-False] [ Failure ]
+crbug.com/1168659 [ Linux ] external/wpt/webdriver/tests/get_element_shadow_root/user_prompts.py>>test_dismiss_and_notify[capabilities0-prompt-None] [ Failure ]
+crbug.com/1168659 [ Linux ] external/wpt/webdriver/tests/get_element_shadow_root/user_prompts.py>>test_ignore[capabilities0-alert] [ Failure ]
+crbug.com/1168659 [ Linux ] external/wpt/webdriver/tests/get_element_shadow_root/user_prompts.py>>test_ignore[capabilities0-confirm] [ Failure ]
+crbug.com/1168659 [ Linux ] external/wpt/webdriver/tests/get_element_shadow_root/user_prompts.py>>test_ignore[capabilities0-prompt] [ Failure ]
+
+crbug.com/1189610 [ Linux ] external/wpt/webdriver/tests/find_element_from_shadow_root/user_prompts.py>>test_accept[capabilities0-alert-None] [ Failure ]
+crbug.com/1189610 [ Linux ] external/wpt/webdriver/tests/find_element_from_shadow_root/user_prompts.py>>test_accept[capabilities0-confirm-True] [ Failure ]
+crbug.com/1189610 [ Linux ] external/wpt/webdriver/tests/find_element_from_shadow_root/user_prompts.py>>test_accept[capabilities0-prompt-] [ Failure ]
+crbug.com/1189610 [ Linux ] external/wpt/webdriver/tests/find_element_from_shadow_root/user_prompts.py>>test_accept_and_notify[capabilities0-alert-None] [ Failure ]
+crbug.com/1189610 [ Linux ] external/wpt/webdriver/tests/find_element_from_shadow_root/user_prompts.py>>test_accept_and_notify[capabilities0-confirm-True] [ Failure ]
+crbug.com/1189610 [ Linux ] external/wpt/webdriver/tests/find_element_from_shadow_root/user_prompts.py>>test_accept_and_notify[capabilities0-prompt-] [ Failure ]
+crbug.com/1189610 [ Linux ] external/wpt/webdriver/tests/find_element_from_shadow_root/user_prompts.py>>test_default[alert-None] [ Failure ]
+crbug.com/1189610 [ Linux ] external/wpt/webdriver/tests/find_element_from_shadow_root/user_prompts.py>>test_default[confirm-False] [ Failure ]
+crbug.com/1189610 [ Linux ] external/wpt/webdriver/tests/find_element_from_shadow_root/user_prompts.py>>test_default[prompt-None] [ Failure ]
+crbug.com/1189610 [ Linux ] external/wpt/webdriver/tests/find_element_from_shadow_root/user_prompts.py>>test_dismiss[capabilities0-alert-None] [ Failure ]
+crbug.com/1189610 [ Linux ] external/wpt/webdriver/tests/find_element_from_shadow_root/user_prompts.py>>test_dismiss[capabilities0-confirm-False] [ Failure ]
+crbug.com/1189610 [ Linux ] external/wpt/webdriver/tests/find_element_from_shadow_root/user_prompts.py>>test_dismiss[capabilities0-prompt-None] [ Failure ]
+crbug.com/1189610 [ Linux ] external/wpt/webdriver/tests/find_element_from_shadow_root/user_prompts.py>>test_dismiss_and_notify[capabilities0-alert-None] [ Failure ]
+crbug.com/1189610 [ Linux ] external/wpt/webdriver/tests/find_element_from_shadow_root/user_prompts.py>>test_dismiss_and_notify[capabilities0-confirm-False] [ Failure ]
+crbug.com/1189610 [ Linux ] external/wpt/webdriver/tests/find_element_from_shadow_root/user_prompts.py>>test_dismiss_and_notify[capabilities0-prompt-None] [ Failure ]
+crbug.com/1189610 [ Linux ] external/wpt/webdriver/tests/find_element_from_shadow_root/user_prompts.py>>test_ignore[capabilities0-alert] [ Failure ]
+crbug.com/1189610 [ Linux ] external/wpt/webdriver/tests/find_element_from_shadow_root/user_prompts.py>>test_ignore[capabilities0-confirm] [ Failure ]
+crbug.com/1189610 [ Linux ] external/wpt/webdriver/tests/find_element_from_shadow_root/user_prompts.py>>test_ignore[capabilities0-prompt] [ Failure ]
diff --git a/third_party/blink/web_tests/editing/input/password-echo-passnode-expected.txt b/third_party/blink/web_tests/editing/input/password-echo-passnode-expected.txt
index c2f53df..e517912 100644
--- a/third_party/blink/web_tests/editing/input/password-echo-passnode-expected.txt
+++ b/third_party/blink/web_tests/editing/input/password-echo-passnode-expected.txt
@@ -1,6 +1,6 @@
 Tests if input chars are secured correctly 
 
-Success: secured right after. expected=true, actual=true
+Success: secured right after. expected=false, actual=false
 Success: secured after delay. expected=true, actual=true
-Success: secured right after. expected=true, actual=true
+Success: secured right after. expected=false, actual=false
 Success: secured after delay. expected=true, actual=true
diff --git a/third_party/blink/web_tests/editing/input/password-echo-passnode.html b/third_party/blink/web_tests/editing/input/password-echo-passnode.html
index 7f703040..c9141f4d 100644
--- a/third_party/blink/web_tests/editing/input/password-echo-passnode.html
+++ b/third_party/blink/web_tests/editing/input/password-echo-passnode.html
@@ -3,8 +3,8 @@
 <script language="javascript" type="text/javascript">
 var mytests = [
         //format: [preedit1, preedit2,...,commit_text], secured_right_after?, secured_after_delay? check?
-        [['a'], true, true, true],         // test password (when only 1 char) is always secured(regular).
-        [['2','2','b'], true, true, true], // test password (when only 1 char) is always secured(ime).
+        [['a'], false, true, true],         // test password (when only 1 char) is only secured after a delay(regular).
+        [['2','2','b'], false, true, true], // test password (when only 1 char) is only secured after a delay(ime).
     ];
 </script>
 <body onload=init(mytests)>
diff --git a/third_party/blink/web_tests/editing/input/password-echo-passnode2-expected.txt b/third_party/blink/web_tests/editing/input/password-echo-passnode2-expected.txt
index c2f53df..e517912 100644
--- a/third_party/blink/web_tests/editing/input/password-echo-passnode2-expected.txt
+++ b/third_party/blink/web_tests/editing/input/password-echo-passnode2-expected.txt
@@ -1,6 +1,6 @@
 Tests if input chars are secured correctly 
 
-Success: secured right after. expected=true, actual=true
+Success: secured right after. expected=false, actual=false
 Success: secured after delay. expected=true, actual=true
-Success: secured right after. expected=true, actual=true
+Success: secured right after. expected=false, actual=false
 Success: secured after delay. expected=true, actual=true
diff --git a/third_party/blink/web_tests/editing/input/password-echo-passnode2.html b/third_party/blink/web_tests/editing/input/password-echo-passnode2.html
index 5ed6d07a..d20a20b 100644
--- a/third_party/blink/web_tests/editing/input/password-echo-passnode2.html
+++ b/third_party/blink/web_tests/editing/input/password-echo-passnode2.html
@@ -3,9 +3,9 @@
 <script language="javascript" type="text/javascript">
 var mytests = [
         //format: [preedit1, preedit2,...,commit_text], secured_right_after?, secured_after_delay? check?
-        [['a'], true, true, false],
-        [['f'], true, true, true],             // test password (when more than 1 char) is always secured(regular).
-        [['3','3','3','f'], true, true, true],  // test password (when more than 1 char) is always secured(ime).
+        [['a'], false, true, false],
+        [['f'], false, true, true],             // test password (when more than 1 char) is only secured after a delay(regular).
+        [['3','3','3','f'], false, true, true],  // test password (when more than 1 char) is only secured after a delay(ime).
     ];
 </script>
 <body onload=init(mytests)>
diff --git a/third_party/blink/web_tests/external/wpt/app-history/navigate-event/event-constructor.html b/third_party/blink/web_tests/external/wpt/app-history/navigate-event/event-constructor.html
index f580e77..7a8803fa 100644
--- a/third_party/blink/web_tests/external/wpt/app-history/navigate-event/event-constructor.html
+++ b/third_party/blink/web_tests/external/wpt/app-history/navigate-event/event-constructor.html
@@ -4,6 +4,12 @@
 <script>
 test(() => {
   assert_throws_js(TypeError, () => {
+    new AppHistoryNavigateEvent("navigate");
+  });
+}, "can't bypass required members by omitting the dictionary entirely");
+
+test(() => {
+  assert_throws_js(TypeError, () => {
     new AppHistoryNavigateEvent("navigate", {
       navigationType: "push",
       canTransition: false,
diff --git a/third_party/blink/web_tests/external/wpt/app-history/navigate-event/transitionWhile-on-synthetic-event.html b/third_party/blink/web_tests/external/wpt/app-history/navigate-event/transitionWhile-on-synthetic-event.html
index d912e38..a3bb80f 100644
--- a/third_party/blink/web_tests/external/wpt/app-history/navigate-event/transitionWhile-on-synthetic-event.html
+++ b/third_party/blink/web_tests/external/wpt/app-history/navigate-event/transitionWhile-on-synthetic-event.html
@@ -2,7 +2,16 @@
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
 <script>
-test(() => {
-  assert_throws_dom("SecurityError", () => new AppHistoryNavigateEvent("navigate").transitionWhile(Promise.resolve()));
+async_test(t => {
+  // We need to grab an AppHistoryDestination to construct the event.
+  appHistory.onnavigate = t.step_func_done(e => {
+    const event = new AppHistoryNavigateEvent("navigate", {
+      destination: e.destination,
+      signal: (new AbortController()).signal
+    });
+
+    assert_throws_dom("SecurityError", () => event.transitionWhile(Promise.resolve()));
+  });
+  history.pushState(1, null, "#1");
 }, "AppHistoryNavigateEvent's transitionWhile() throws if invoked on a synthetic event");
 </script>
diff --git a/third_party/blink/web_tests/external/wpt/app-history/navigate/disambigaute-goto-forward-multiple.html b/third_party/blink/web_tests/external/wpt/app-history/navigate/disambigaute-goto-forward-multiple.html
index b1378b66..213b717 100644
--- a/third_party/blink/web_tests/external/wpt/app-history/navigate/disambigaute-goto-forward-multiple.html
+++ b/third_party/blink/web_tests/external/wpt/app-history/navigate/disambigaute-goto-forward-multiple.html
@@ -3,41 +3,37 @@
 <script src="/resources/testharnessreport.js"></script>
 <iframe id="i" src="/common/blank.html"></iframe>
 <script>
-async_test(t => {
+promise_test(async t => {
   // Wait for after the load event so that the navigation doesn't get converted
   // into a replace navigation.
-  window.onload = () => t.step_timeout(() => {
-    assert_equals(appHistory.entries().length, 1);
-    assert_equals(i.contentWindow.appHistory.entries().length, 1);
-    let initial_key = appHistory.current.key;
-    appHistory.navigate("#top1");
-    appHistory.navigate("#top2");
-    assert_equals(appHistory.entries().length, 3);
-    assert_equals(i.contentWindow.appHistory.entries().length, 1);
-    i.contentWindow.appHistory.navigate("#1");
-    assert_equals(appHistory.entries().length, 3);
-    assert_equals(i.contentWindow.appHistory.entries().length, 2);
-    assert_equals(appHistory.current.index, 2);
-    assert_equals(i.contentWindow.appHistory.current.index, 1);
-    assert_true(appHistory.canGoBack);
-    assert_true(i.contentWindow.appHistory.canGoBack);
-    let final_key = appHistory.current.key;
+  await new Promise(resolve => window.onload = () => t.step_timeout(resolve, 0));
+  assert_equals(appHistory.entries().length, 1);
+  assert_equals(i.contentWindow.appHistory.entries().length, 1);
+  let initial_key = appHistory.current.key;
+  await appHistory.navigate("#top1");
+  await appHistory.navigate("#top2");
+  assert_equals(appHistory.entries().length, 3);
+  assert_equals(i.contentWindow.appHistory.entries().length, 1);
+  await i.contentWindow.appHistory.navigate("#1");
+  assert_equals(appHistory.entries().length, 3);
+  assert_equals(i.contentWindow.appHistory.entries().length, 2);
+  assert_equals(appHistory.current.index, 2);
+  assert_equals(i.contentWindow.appHistory.current.index, 1);
+  assert_true(appHistory.canGoBack);
+  assert_true(i.contentWindow.appHistory.canGoBack);
+  let final_key = appHistory.current.key;
 
-    i.contentWindow.appHistory.back().then(t.step_func(() => {
-      assert_equals(appHistory.current.index, 2);
-      assert_equals(i.contentWindow.appHistory.current.index, 0);
-      appHistory.goTo(initial_key).then(t.step_func(() => {
-        assert_equals(appHistory.current.index, 0);
-        assert_equals(i.contentWindow.appHistory.current.index, 0);
-        // There are 2 joint session history entries containing the top window's
-        // final key. Navigate to the nearest one (which navigates only the
-        // top window).
-        appHistory.goTo(final_key).then(t.step_func_done(() => {
-          assert_equals(appHistory.current.index, 2);
-          assert_equals(i.contentWindow.appHistory.current.index, 0);
-        }));
-      }));
-    }));
-  }, 0);
+  await i.contentWindow.appHistory.back();
+  assert_equals(appHistory.current.index, 2);
+  assert_equals(i.contentWindow.appHistory.current.index, 0);
+  await appHistory.goTo(initial_key)
+  assert_equals(appHistory.current.index, 0);
+  assert_equals(i.contentWindow.appHistory.current.index, 0);
+  // There are 2 joint session history entries containing the top window's
+  // final key. Navigate to the nearest one (which navigates only the
+  // top window).
+  await appHistory.goTo(final_key);
+  assert_equals(appHistory.current.index, 2);
+  assert_equals(i.contentWindow.appHistory.current.index, 0);
 }, "appHistory.goTo() goes to the nearest entry when going forward");
 </script>
diff --git a/third_party/blink/web_tests/external/wpt/app-history/navigate/goTo-detach-in-onnavigate.html b/third_party/blink/web_tests/external/wpt/app-history/navigate/goTo-detach-in-onnavigate.html
index 34bebca..c245da9 100644
--- a/third_party/blink/web_tests/external/wpt/app-history/navigate/goTo-detach-in-onnavigate.html
+++ b/third_party/blink/web_tests/external/wpt/app-history/navigate/goTo-detach-in-onnavigate.html
@@ -7,7 +7,7 @@
   await new Promise(resolve => window.onload = resolve);
   let iframe_constructor = i.contentWindow.DOMException;
   let i_win = i.contentWindow;
-  i.contentWindow.history.pushState(1, "", "#1");
+  await i.contentWindow.appHistory.navigate("#1");
   assert_equals(i.contentWindow.appHistory.entries().length, 2);
   let key = i.contentWindow.appHistory.entries()[0].key;
 
@@ -15,6 +15,7 @@
   let onnavigateerror_error;
   i.contentWindow.appHistory.onnavigate = t.step_func(() => i.remove());
   i.contentWindow.appHistory.onnavigateerror = t.step_func(e => {
+    assert_false(onnavigateerror_called);
     onnavigateerror_called = true;
     onnavigateerror_error = e.error;
     assert_equals(e.filename, location.href);
diff --git a/third_party/blink/web_tests/external/wpt/app-history/navigate/goTo-multiple-steps.html b/third_party/blink/web_tests/external/wpt/app-history/navigate/goTo-multiple-steps.html
index c827fef0..9b8c5ac9 100644
--- a/third_party/blink/web_tests/external/wpt/app-history/navigate/goTo-multiple-steps.html
+++ b/third_party/blink/web_tests/external/wpt/app-history/navigate/goTo-multiple-steps.html
@@ -2,29 +2,24 @@
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
 <script>
-async_test(t => {
+promise_test(async t => {
   // Wait for after the load event so that the navigation doesn't get converted
   // into a replace navigation.
-  window.onload = () => t.step_timeout(() => {
-    assert_equals(appHistory.entries().length, 1);
-    let key0 = appHistory.current.key;
-    appHistory.navigate("#1");
-    appHistory.navigate("#2");
-    let key2 = appHistory.current.key;
-    assert_equals(appHistory.entries().length, 3);
+  await new Promise(resolve => window.onload = () => t.step_timeout(resolve, 0));
+  assert_equals(appHistory.entries().length, 1);
+  let key0 = appHistory.current.key;
+  await appHistory.navigate("#1");
+  await appHistory.navigate("#2");
+  let key2 = appHistory.current.key;
+  assert_equals(appHistory.entries().length, 3);
 
-    appHistory.goTo(key0)
-      .then(t.step_func(() => {
-        assert_equals(appHistory.entries().length, 3);
-        assert_equals(appHistory.current, appHistory.entries()[0]);
-        assert_equals(key0, appHistory.current.key);
-        return appHistory.goTo(key2);
-      }))
-      .then(t.step_func_done(() => {
-        assert_equals(appHistory.entries().length, 3);
-        assert_equals(appHistory.current, appHistory.entries()[2]);
-        assert_equals(key2, appHistory.current.key);
-      }));
-  }, 0);
+  await appHistory.goTo(key0);
+  assert_equals(appHistory.entries().length, 3);
+  assert_equals(appHistory.current, appHistory.entries()[0]);
+  assert_equals(key0, appHistory.current.key);
+  await appHistory.goTo(key2);
+  assert_equals(appHistory.entries().length, 3);
+  assert_equals(appHistory.current, appHistory.entries()[2]);
+  assert_equals(key2, appHistory.current.key);
 }, "goto() can precisely traverse multiple steps in the joint session history");
 </script>
diff --git a/third_party/blink/web_tests/external/wpt/app-history/navigate/navigate-state-repeated-await.html b/third_party/blink/web_tests/external/wpt/app-history/navigate/navigate-state-repeated-await.html
new file mode 100644
index 0000000..6b647492
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/app-history/navigate/navigate-state-repeated-await.html
@@ -0,0 +1,13 @@
+<!doctype html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+promise_test(async t => {
+  appHistory.onnavigate = e => e.transitionWhile(Promise.resolve());
+
+  await appHistory.navigate('/foo', {state: {foo: 1}});
+  assert_equals(appHistory.current.getState().foo, 1);
+  await appHistory.navigate('/foo', {state: {foo: 2}});
+  assert_equals(appHistory.current.getState().foo, 2);
+}, "navigate() with state should work correctly when called repeatedly - with awaits");
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/app-history/navigate/navigate-state-repeated.html b/third_party/blink/web_tests/external/wpt/app-history/navigate/navigate-state-repeated.html
new file mode 100644
index 0000000..8d33860
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/app-history/navigate/navigate-state-repeated.html
@@ -0,0 +1,15 @@
+<!doctype html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+promise_test(async t => {
+  appHistory.onnavigate = e => e.transitionWhile(Promise.resolve());
+
+  let p1 = promise_rejects_dom(t, 'AbortError', appHistory.navigate('/foo', {state: {foo: 1}}));
+  assert_equals(appHistory.current.getState().foo, 1);
+  let p2 = appHistory.navigate('/foo', {state: {foo: 2}});
+  assert_equals(appHistory.current.getState().foo, 2);
+  await p1;
+  await p2;
+}, "navigate() with state should work correctly when called repeatedly");
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-contain/contain-paint-026.html b/third_party/blink/web_tests/external/wpt/css/css-contain/contain-paint-026.html
new file mode 100644
index 0000000..f6da867
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-contain/contain-paint-026.html
@@ -0,0 +1,25 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>CSS Containment Test: Paint containment and box-shadow</title>
+<link rel="help" href="https://drafts.csswg.org/css-contain-1/#containment-paint">
+<link rel="match" href="../reference/ref-filled-green-100px-square.xht">
+<meta name=assert content="Paint containment should clip box-shadow effect on child.">
+<style>
+#contain-paint {
+  contain: paint;
+  width: 100px;
+  height: 100px;
+  background: red;
+}
+
+#child {
+  background: green;
+  box-shadow: 0 0 100px 100px red;
+  width: 100px;
+  height: 100px;
+}
+</style>
+<p>Test passes if there is a filled green square and <strong>no red</strong>.</p>
+<div id="contain-paint">
+  <div id="child"></div>
+</div>
diff --git a/third_party/blink/web_tests/external/wpt/html/semantics/interactive-elements/the-details-element/auto-expand-details-element-fragment-expected.txt b/third_party/blink/web_tests/external/wpt/html/semantics/interactive-elements/the-details-element/auto-expand-details-element-fragment-expected.txt
new file mode 100644
index 0000000..23533fd
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/html/semantics/interactive-elements/the-details-element/auto-expand-details-element-fragment-expected.txt
@@ -0,0 +1,4 @@
+This is a testharness.js-based test.
+FAIL auto-expand-details-element-fragment assert_true: <details> should be opened by navigating to an element inside it. expected true got false
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/external/wpt/html/semantics/interactive-elements/the-details-element/auto-expand-details-element-fragment.html b/third_party/blink/web_tests/external/wpt/html/semantics/interactive-elements/the-details-element/auto-expand-details-element-fragment.html
new file mode 100644
index 0000000..d3d04f0
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/html/semantics/interactive-elements/the-details-element/auto-expand-details-element-fragment.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<link rel="author" title="Joey Arhar" href="mailto:jarhar@chromium.org">
+<link rel="help" href="https://github.com/whatwg/html/pull/6466">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<div style="height:2000px">spacer</div>
+
+<details id=details>
+  <div id=target>target</div>
+</details>
+
+<script>
+async_test(t => {
+  assert_false(details.hasAttribute('open'),
+    `The <details> should be closed at the start of the test.`);
+  assert_equals(window.pageYOffset, 0,
+    `The page should be scrolled to the top at the start of the test.`);
+
+  window.location.hash = '#target';
+
+  requestAnimationFrame(t.step_func_done(() => {
+    assert_true(details.hasAttribute('open'),
+      `<details> should be opened by navigating to an element inside it.`);
+    assert_not_equals(window.pageYOffset, 0,
+      `The page should be scrolled down to the <details> element.`);
+  }));
+});
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/media-capabilities/decodingInfo.webrtc.html b/third_party/blink/web_tests/external/wpt/media-capabilities/decodingInfo.webrtc.html
index d5399d7..f2839561 100644
--- a/third_party/blink/web_tests/external/wpt/media-capabilities/decodingInfo.webrtc.html
+++ b/third_party/blink/web_tests/external/wpt/media-capabilities/decodingInfo.webrtc.html
@@ -6,7 +6,7 @@
 
 // Minimal VideoConfiguration that will be allowed per spec. All optional
 // properties are missing.
-var minimalVideoConfiguration = {
+const minimalVideoConfiguration = {
   contentType: 'video/VP9; profile-level="0"',
   width: 800,
   height: 600,
@@ -16,7 +16,7 @@
 
 // Minimal AudioConfiguration that will be allowed per spec. All optional
 // properties are missing.
-var minimalAudioConfiguration = {
+const minimalAudioConfiguration = {
   contentType: 'audio/opus',
 };
 
@@ -149,11 +149,11 @@
   });
 }, "Test that decodingInfo returns supported, smooth, and powerEfficient set to false for non-webrtc audio content type.");
 
-var validAudioCodecs = (() => {
+const validAudioCodecs = (() => {
   // Some codecs that are returned by getCapabilities() are not real codecs,
   // exclude these from the test.
-  var excludeList = [ 'audio/CN', 'audio/telephone-event', 'audio/red' ];
-  var audioCodecs = [];
+  const excludeList = [ 'audio/CN', 'audio/telephone-event', 'audio/red' ];
+  const audioCodecs = [];
   RTCRtpReceiver.getCapabilities("audio")['codecs'].forEach(codec => {
     if (excludeList.indexOf(codec.mimeType) < 0 &&
         audioCodecs.indexOf(codec.mimeType) < 0) {
@@ -176,16 +176,16 @@
 }, "Test that decodingInfo returns supported true for the codec " + codec + " returned by RTCRtpReceiver.getCapabilities()")}
 );
 
-var validVideoCodecs = (() => {
+const validVideoCodecs = (() => {
   // Some codecs that are returned by getCapabilities() are not real codecs but
   // only used for error correction, exclude these from the test.
-  var excludeList = [ 'video/rtx', 'video/red', 'video/ulpfec',
+  const excludeList = [ 'video/rtx', 'video/red', 'video/ulpfec',
                       'video/flexfec-03' ];
-  var videoCodecs = [];
+  const videoCodecs = [];
 
   RTCRtpReceiver.getCapabilities("video")['codecs'].forEach(codec => {
     if (excludeList.indexOf(codec.mimeType) < 0) {
-      var mimeType = codec.mimeType;
+      let mimeType = codec.mimeType;
       if ('sdpFmtpLine' in codec) {
         mimeType += "; " + codec.sdpFmtpLine;
       }
diff --git a/third_party/blink/web_tests/external/wpt/media-capabilities/encodingInfo.webrtc.html b/third_party/blink/web_tests/external/wpt/media-capabilities/encodingInfo.webrtc.html
index 3f29919..414b794 100644
--- a/third_party/blink/web_tests/external/wpt/media-capabilities/encodingInfo.webrtc.html
+++ b/third_party/blink/web_tests/external/wpt/media-capabilities/encodingInfo.webrtc.html
@@ -6,7 +6,7 @@
 
 // Minimal VideoConfiguration that will be allowed per spec. All optional
 // properties are missing.
-var minimalVideoConfiguration = {
+const minimalVideoConfiguration = {
   contentType: 'video/VP9; profile-level="0"',
   width: 800,
   height: 600,
@@ -16,7 +16,7 @@
 
 // Minimal AudioConfiguration that will be allowed per spec. All optional
 // properties are missing.
-var minimalAudioConfiguration = {
+const minimalAudioConfiguration = {
   contentType: 'audio/opus',
 };
 
@@ -149,11 +149,11 @@
   });
 }, "Test that encodingInfo returns supported, smooth, and powerEfficient set to false for non-webrtc audio content type.");
 
-var validAudioCodecs = (() => {
+const validAudioCodecs = (() => {
   // Some codecs that are returned by getCapabilities() are not real codecs,
   // exclude these from the test.
-  var excludeList = [ 'audio/CN', 'audio/telephone-event', 'audio/red' ];
-  var audioCodecs = [];
+  const excludeList = [ 'audio/CN', 'audio/telephone-event', 'audio/red' ];
+  const audioCodecs = [];
   RTCRtpSender.getCapabilities("audio")['codecs'].forEach(codec => {
     if (excludeList.indexOf(codec.mimeType) < 0 &&
         audioCodecs.indexOf(codec.mimeType) < 0) {
@@ -176,16 +176,16 @@
 }, "Test that encodingInfo returns supported true for the codec " + codec + " returned by RTCRtpSender.getCapabilities()")}
 );
 
-var validVideoCodecs = (() => {
+const validVideoCodecs = (() => {
   // Some codecs that are returned by getCapabilities() are not real codecs but
   // only used for error correction, exclude these from the test.
-  var excludeList = [ 'video/rtx', 'video/red', 'video/ulpfec',
+  const excludeList = [ 'video/rtx', 'video/red', 'video/ulpfec',
                       'video/flexfec-03' ];
-  var videoCodecs = [];
+  const videoCodecs = [];
 
   RTCRtpSender.getCapabilities("video")['codecs'].forEach(codec => {
     if (excludeList.indexOf(codec.mimeType) < 0) {
-      var mimeType = codec.mimeType;
+      let mimeType = codec.mimeType;
       if ('sdpFmtpLine' in codec) {
         mimeType += "; " + codec.sdpFmtpLine;
       }
diff --git a/third_party/blink/web_tests/external/wpt/presentation-api/controlling-ua/PresentationConnectionCloseEvent.https.html b/third_party/blink/web_tests/external/wpt/presentation-api/controlling-ua/PresentationConnectionCloseEvent.https.html
new file mode 100644
index 0000000..34c935a6
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/presentation-api/controlling-ua/PresentationConnectionCloseEvent.https.html
@@ -0,0 +1,24 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Constructing a PresentationConnectionCloseEvent</title>
+<link rel="author" title="mark a. foltz" href="https://github.com/mfoltzgoogle">
+<link rel="help" href="http://w3c.github.io/presentation-api/#controlling-user-agent">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<script>
+  test(() => {
+    let eventWithMessage, eventWithoutMessage;
+    for (let reason of ["error", "closed", "wentaway"]) {
+      eventWithMessage = new PresentationConnectionCloseEvent("close", {reason: reason, message: "A message" });
+      assert_equals(eventWithMessage.type, "close");
+      assert_equals(eventWithMessage.reason, reason);
+      assert_equals(eventWithMessage.message, "A message");
+
+      eventWithoutMessage = new PresentationConnectionCloseEvent("close", {reason: reason});
+      assert_equals(eventWithoutMessage.type, "close");
+      assert_equals(eventWithoutMessage.reason, reason);
+      assert_equals(eventWithoutMessage.message, "");
+    }
+  });
+</script>
diff --git a/third_party/blink/web_tests/fast/css/containment/paint-containment-with-box-shadow.html b/third_party/blink/web_tests/fast/css/containment/paint-containment-with-box-shadow.html
deleted file mode 100644
index ffa0f31..0000000
--- a/third_party/blink/web_tests/fast/css/containment/paint-containment-with-box-shadow.html
+++ /dev/null
@@ -1,16 +0,0 @@
-<!DOCTYPE html>
-<style>
-div {
-    width: 100px;
-    height: 100px;
-}
-#container {
-    contain: paint;
-}
-#child {
-    background-color: green;
-    -webkit-box-shadow: 0 0 100px 100px red;
-}
-</style>
-<body>
-<div id="container"><div id="child"></div></div>
\ No newline at end of file
diff --git a/third_party/blink/web_tests/wpt_internal/css/webkit-text-security/first-letter-ref.html b/third_party/blink/web_tests/wpt_internal/css/webkit-text-security/first-letter-ref.html
new file mode 100644
index 0000000..3cc2a849
--- /dev/null
+++ b/third_party/blink/web_tests/wpt_internal/css/webkit-text-security/first-letter-ref.html
@@ -0,0 +1,2 @@
+<!doctype html>
+<div style="-webkit-text-security:circle">PASS</div>
diff --git a/third_party/blink/web_tests/wpt_internal/css/webkit-text-security/first-letter.html b/third_party/blink/web_tests/wpt_internal/css/webkit-text-security/first-letter.html
new file mode 100644
index 0000000..ca18a2d
--- /dev/null
+++ b/third_party/blink/web_tests/wpt_internal/css/webkit-text-security/first-letter.html
@@ -0,0 +1,8 @@
+<!doctype html>
+<title>Do not apply ::first-letter to -webkit-text-security</title>
+<link rel="match" href="first-letter-ref.html">
+<style>
+  #obfuscated { -webkit-text-security: circle; }
+  #obfuscated::first-letter { font-size: 100px; }
+</style>
+<div id="obfuscated">PASS</div>
diff --git a/third_party/closure_compiler/externs/automation.js b/third_party/closure_compiler/externs/automation.js
index 832952b..5ec8918 100644
--- a/third_party/closure_compiler/externs/automation.js
+++ b/third_party/closure_compiler/externs/automation.js
@@ -1264,6 +1264,13 @@
 chrome.automation.AutomationNode.prototype.hierarchicalLevel;
 
 /**
+ * An array of ints of the current caret bounds in screen coordinates. (x, y, width, height).
+ * @type {(!Array<number>|undefined)}
+ * @see https://developer.chrome.com/extensions/automation#type-caretBounds
+ */
+chrome.automation.AutomationNode.prototype.caretBounds;
+
+/**
  * The start and end index of each word in an inline text box.
  * @type {(!Array<number>|undefined)}
  * @see https://developer.chrome.com/extensions/automation#type-wordStarts
diff --git a/third_party/freetype/README.chromium b/third_party/freetype/README.chromium
index 2eb00391..12fbeba 100644
--- a/third_party/freetype/README.chromium
+++ b/third_party/freetype/README.chromium
@@ -1,7 +1,7 @@
 Name: FreeType
 URL: http://www.freetype.org/
-Version: VER-2-11-0-26-gf44c2d586
-Revision: f44c2d586064bcccdb504bf098b9dc78e660269e
+Version: VER-2-11-0-27-ge2cceed85
+Revision: e2cceed857f0b4f3f3fd48681d2f3009e62d1194
 CPEPrefix: cpe:/a:freetype:freetype:2.10.4
 License: Custom license "inspired by the BSD, Artistic, and IJG (Independent
          JPEG Group) licenses"
diff --git a/third_party/jinja2/OWNERS b/third_party/jinja2/OWNERS
index 05a4a96..c4b81d8 100644
--- a/third_party/jinja2/OWNERS
+++ b/third_party/jinja2/OWNERS
@@ -1,3 +1,4 @@
 timloh@chromium.org
 haraken@chromium.org
+wnwen@chromium.org
 yukishiino@chromium.org
diff --git a/third_party/jinja2/lexer.py b/third_party/jinja2/lexer.py
index 6fd135d..1f79025 100644
--- a/third_party/jinja2/lexer.py
+++ b/third_party/jinja2/lexer.py
@@ -45,12 +45,6 @@
     from jinja2 import _identifier
     name_re = re.compile(r'[\w{0}]+'.format(_identifier.pattern))
     check_ident = True
-    # remove the pattern from memory after building the regex
-    import sys
-    del sys.modules['jinja2._identifier']
-    import jinja2
-    del jinja2._identifier
-    del _identifier
 
 float_re = re.compile(r'(?<!\.)\d+\.\d+')
 newline_re = re.compile(r'(\r\n|\r|\n)')
diff --git a/third_party/jinja2/patches/0002-jinja2-add-_identifier-to-pydeps-for-py3.patch b/third_party/jinja2/patches/0002-jinja2-add-_identifier-to-pydeps-for-py3.patch
new file mode 100644
index 0000000..a979bed7
--- /dev/null
+++ b/third_party/jinja2/patches/0002-jinja2-add-_identifier-to-pydeps-for-py3.patch
@@ -0,0 +1,34 @@
+From c07882fb6ea8a02869c84fd79e48855229ca5985 Mon Sep 17 00:00:00 2001
+From: Peter Wen <wnwen@chromium.org>
+Date: Wed, 18 Aug 2021 09:53:11 -0400
+Subject: [PATCH] [PATCH] jinja2: add _identifier to pydeps for py3
+
+If _identifier is deleted then build/print_python_deps.py fails to
+detect _identifier.py as a pydep for jinja, resulting in the isolate
+files missing third_party/jinja2/_identifier.py as a necessary file.
+
+Bug: 1228231
+---
+ third_party/jinja2/lexer.py | 6 ------
+ 1 file changed, 6 deletions(-)
+
+diff --git a/third_party/jinja2/lexer.py b/third_party/jinja2/lexer.py
+index 6fd135dd5b0a..1f790259d671 100644
+--- a/third_party/jinja2/lexer.py
++++ b/third_party/jinja2/lexer.py
+@@ -45,12 +45,6 @@ else:
+     from jinja2 import _identifier
+     name_re = re.compile(r'[\w{0}]+'.format(_identifier.pattern))
+     check_ident = True
+-    # remove the pattern from memory after building the regex
+-    import sys
+-    del sys.modules['jinja2._identifier']
+-    import jinja2
+-    del jinja2._identifier
+-    del _identifier
+
+ float_re = re.compile(r'(?<!\.)\d+\.\d+')
+ newline_re = re.compile(r'(\r\n|\r|\n)')
+--
+2.33.0.rc1.237.g0d66db33f3-goog
+
diff --git a/third_party/r8/3pp/3pp.pb b/third_party/r8/3pp/3pp.pb
index ef9c4ba..b1962ba 100644
--- a/third_party/r8/3pp/3pp.pb
+++ b/third_party/r8/3pp/3pp.pb
@@ -3,8 +3,13 @@
     git {
       repo: "https://r8.googlesource.com/r8"
       tag_pattern: "%s-dev"
+      version_restriction {
+          op: EQ
+          val: "3.1.16"
+      }
     }
     patch_dir: "patches"
+    patch_version: "crbug1214915"
   }
 
   build {
diff --git a/third_party/r8/3pp/patches/0004-Disable-useDexPcAsDebugInformation.patch b/third_party/r8/3pp/patches/0004-Disable-useDexPcAsDebugInformation.patch
deleted file mode 100644
index 0781a3e..0000000
--- a/third_party/r8/3pp/patches/0004-Disable-useDexPcAsDebugInformation.patch
+++ /dev/null
@@ -1,28 +0,0 @@
-From ca66a211ce82c330257d9ca94dcca4c644cb4674 Mon Sep 17 00:00:00 2001
-From: Andrew Grieve <agrieve@chromium.org>
-Date: Wed, 23 Jun 2021 21:53:35 -0400
-Subject: [PATCH 4/4] Disable useDexPcAsDebugInformation
-
-useDexPcAsDebugInformation breaks deobfuscation, but would be great to turn on.
-
-See crbug.com/1214915 for more information.
----
- src/main/java/com/android/tools/r8/utils/InternalOptions.java | 2 +-
- 1 file changed, 1 insertion(+), 1 deletion(-)
-
-diff --git a/src/main/java/com/android/tools/r8/utils/InternalOptions.java b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
-index e82e04d90..03bab9255 100644
---- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
-+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
-@@ -1704,7 +1704,7 @@ public class InternalOptions implements GlobalKeepInfoConfiguration {
-   }
- 
-   public boolean canUseDexPcAsDebugInformation() {
--    return lineNumberOptimization == LineNumberOptimization.ON && hasMinApi(AndroidApiLevel.O);
-+    return false && lineNumberOptimization == LineNumberOptimization.ON && hasMinApi(AndroidApiLevel.O);
-   }
- 
-   public boolean isInterfaceMethodDesugaringEnabled() {
--- 
-2.32.0.288.g62a8d224e6-goog
-
diff --git a/third_party/r8/README.chromium b/third_party/r8/README.chromium
index dc99df65..ee05f7e 100644
--- a/third_party/r8/README.chromium
+++ b/third_party/r8/README.chromium
@@ -1,6 +1,6 @@
 Name: R8
 URL: https://r8.googlesource.com/r8
-Version: 3.1.13.alpha
+Version: 3.1.16-dev
 License: BSD 3-Clause
 License File: NOT_SHIPPED
 Security Critical: no
diff --git a/tools/android/adb_reboot/adb_reboot.c b/tools/android/adb_reboot/adb_reboot.c
index d414dd5..f382391c 100644
--- a/tools/android/adb_reboot/adb_reboot.c
+++ b/tools/android/adb_reboot/adb_reboot.c
@@ -38,6 +38,4 @@
       system("su -c reboot");
     }
   }
-
-  return 0;
 }
diff --git a/tools/json_schema_compiler/cc_generator.py b/tools/json_schema_compiler/cc_generator.py
index 7da1896b..42184ca5 100644
--- a/tools/json_schema_compiler/cc_generator.py
+++ b/tools/json_schema_compiler/cc_generator.py
@@ -854,24 +854,12 @@
 
   def _GenerateFunctionParamsCreate(self, function):
     """Generate function to create an instance of Params. The generated
-    function takes a base::Value::ConstListView of arguments. A deprecated
-    function that takes a base::ListValue is also provided.
+    function takes a base::Value::ConstListView of arguments.
 
     E.g for function "Bar", generate Bar::Params::Create()
     """
     c = Code()
 
-    # Temporary wrapper for base::ListValue
-    # TODO(crbug.com/1187001): Remove once all callers have been converted over.
-    (c.Append('// static')
-      .Sblock('std::unique_ptr<Params> Params::Create(%s) {' %
-                  self._GenerateParams(['const base::ListValue& args']))
-      .Append('return Params::Create(%s);' %
-                  self._GenerateArgs(['args.GetList()']))
-      .Eblock('}')
-      .Append()
-    )
-
     (c.Append('// static')
       .Sblock('std::unique_ptr<Params> Params::Create(%s) {' %
                   self._GenerateParams([
diff --git a/tools/json_schema_compiler/h_generator.py b/tools/json_schema_compiler/h_generator.py
index eff6471..97edbfe3 100644
--- a/tools/json_schema_compiler/h_generator.py
+++ b/tools/json_schema_compiler/h_generator.py
@@ -344,10 +344,6 @@
       .Append('static std::unique_ptr<Params> Create(%s);' %
                   self._GenerateParams(
                       ('const base::Value::ConstListView& args',)))
-      .Append()
-      .Append('// Deprecated')
-      .Append('static std::unique_ptr<Params> Create(%s);' %
-                  self._GenerateParams(('const base::ListValue& args',)))
       .Append('Params(const Params&) = delete;')
       .Append('Params& operator=(const Params&) = delete;')
       .Append('~Params();')
diff --git a/tools/json_schema_compiler/test/additional_properties_unittest.cc b/tools/json_schema_compiler/test/additional_properties_unittest.cc
index 94d3ded..15df873f 100644
--- a/tools/json_schema_compiler/test/additional_properties_unittest.cc
+++ b/tools/json_schema_compiler/test/additional_properties_unittest.cc
@@ -40,10 +40,10 @@
   auto param_object_value = std::make_unique<base::DictionaryValue>();
   param_object_value->SetString("str", "a");
   param_object_value->SetInteger("num", 1);
-  auto params_value = std::make_unique<base::ListValue>();
-  params_value->Append(param_object_value->CreateDeepCopy());
+  std::vector<base::Value> params_value;
+  params_value.push_back(param_object_value->Clone());
   std::unique_ptr<ap::AdditionalProperties::Params> params(
-      ap::AdditionalProperties::Params::Create(*params_value));
+      ap::AdditionalProperties::Params::Create(params_value));
   EXPECT_TRUE(params.get());
   EXPECT_TRUE(params->param_object.additional_properties.Equals(
       param_object_value.get()));
diff --git a/tools/json_schema_compiler/test/any_unittest.cc b/tools/json_schema_compiler/test/any_unittest.cc
index 668966b..169f7e55 100644
--- a/tools/json_schema_compiler/test/any_unittest.cc
+++ b/tools/json_schema_compiler/test/any_unittest.cc
@@ -2,6 +2,10 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include <utility>
+#include <vector>
+
+#include "base/values.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "tools/json_schema_compiler/test/any.h"
 
@@ -26,30 +30,30 @@
 
 TEST(JsonSchemaCompilerAnyTest, OptionalAnyParamsCreate) {
   {
-    auto params_value = std::make_unique<base::ListValue>();
+    std::vector<base::Value> params_value;
     std::unique_ptr<test::api::any::OptionalAny::Params> params(
-        test::api::any::OptionalAny::Params::Create(*params_value));
+        test::api::any::OptionalAny::Params::Create(params_value));
     EXPECT_TRUE(params.get());
     EXPECT_FALSE(params->any_name.get());
   }
   {
-    auto params_value = std::make_unique<base::ListValue>();
-    auto param = std::make_unique<base::Value>("asdf");
-    params_value->Append(param->CreateDeepCopy());
+    std::vector<base::Value> params_value;
+    base::Value param("asdf");
+    params_value.push_back(param.Clone());
     std::unique_ptr<test::api::any::OptionalAny::Params> params(
-        test::api::any::OptionalAny::Params::Create(*params_value));
+        test::api::any::OptionalAny::Params::Create(params_value));
     ASSERT_TRUE(params);
     ASSERT_TRUE(params->any_name);
-    EXPECT_TRUE(params->any_name->Equals(param.get()));
+    EXPECT_TRUE(params->any_name->Equals(&param));
   }
   {
-    auto params_value = std::make_unique<base::ListValue>();
-    auto param = std::make_unique<base::Value>(true);
-    params_value->Append(param->CreateDeepCopy());
+    std::vector<base::Value> params_value;
+    base::Value param(true);
+    params_value.push_back(param.Clone());
     std::unique_ptr<test::api::any::OptionalAny::Params> params(
-        test::api::any::OptionalAny::Params::Create(*params_value));
+        test::api::any::OptionalAny::Params::Create(params_value));
     ASSERT_TRUE(params);
     ASSERT_TRUE(params->any_name);
-    EXPECT_TRUE(params->any_name->Equals(param.get()));
+    EXPECT_TRUE(params->any_name->Equals(&param));
   }
 }
diff --git a/tools/json_schema_compiler/test/arrays_unittest.cc b/tools/json_schema_compiler/test/arrays_unittest.cc
index c063421b..fb357168 100644
--- a/tools/json_schema_compiler/test/arrays_unittest.cc
+++ b/tools/json_schema_compiler/test/arrays_unittest.cc
@@ -194,8 +194,7 @@
   integer_array.Append(8);
   params_value.Append(std::move(integer_array));
   std::unique_ptr<arrays::IntegerArray::Params> params(
-      arrays::IntegerArray::Params::Create(
-          base::Value::AsListValue(params_value)));
+      arrays::IntegerArray::Params::Create(params_value.GetList()));
   EXPECT_TRUE(params.get());
   ASSERT_EQ(3u, params->nums.size());
   EXPECT_EQ(2, params->nums[0]);
@@ -211,7 +210,7 @@
   any_array.Append(CreateItemValue(2));
   params_value.Append(std::move(any_array));
   std::unique_ptr<arrays::AnyArray::Params> params(
-      arrays::AnyArray::Params::Create(base::Value::AsListValue(params_value)));
+      arrays::AnyArray::Params::Create(params_value.GetList()));
   EXPECT_TRUE(params.get());
   ASSERT_EQ(3u, params->anys.size());
   ASSERT_TRUE(params->anys[0]->is_int());
@@ -225,8 +224,7 @@
   item_array.Append(CreateItemValue(2));
   params_value.Append(std::move(item_array));
   std::unique_ptr<arrays::ObjectArray::Params> params(
-      arrays::ObjectArray::Params::Create(
-          base::Value::AsListValue(params_value)));
+      arrays::ObjectArray::Params::Create(params_value.GetList()));
   EXPECT_TRUE(params.get());
   ASSERT_EQ(2u, params->objects.size());
   EXPECT_EQ(1, params->objects[0].additional_properties["val"]);
@@ -240,7 +238,7 @@
   item_array.Append(CreateItemValue(2));
   params_value.Append(std::move(item_array));
   std::unique_ptr<arrays::RefArray::Params> params(
-      arrays::RefArray::Params::Create(base::Value::AsListValue(params_value)));
+      arrays::RefArray::Params::Create(params_value.GetList()));
   EXPECT_TRUE(params.get());
   ASSERT_EQ(2u, params->refs.size());
   EXPECT_EQ(1, params->refs[0].val);
diff --git a/tools/json_schema_compiler/test/choices_unittest.cc b/tools/json_schema_compiler/test/choices_unittest.cc
index a4772ef..4f95d23 100644
--- a/tools/json_schema_compiler/test/choices_unittest.cc
+++ b/tools/json_schema_compiler/test/choices_unittest.cc
@@ -26,21 +26,22 @@
 TEST(JsonSchemaCompilerChoicesTest, TakesIntegersParamsCreate) {
   {
     std::unique_ptr<TakesIntegers::Params> params(TakesIntegers::Params::Create(
-        *List(std::make_unique<base::Value>(true))));
+        List(std::make_unique<base::Value>(true))->GetList()));
     EXPECT_FALSE(params);
   }
   {
-    std::unique_ptr<TakesIntegers::Params> params(
-        TakesIntegers::Params::Create(*List(std::make_unique<base::Value>(6))));
+    std::unique_ptr<TakesIntegers::Params> params(TakesIntegers::Params::Create(
+        List(std::make_unique<base::Value>(6))->GetList()));
     ASSERT_TRUE(params);
     EXPECT_FALSE(params->nums.as_integers);
     EXPECT_EQ(6, *params->nums.as_integer);
   }
   {
-    std::unique_ptr<TakesIntegers::Params> params(
-        TakesIntegers::Params::Create(*List(List(
-            std::make_unique<base::Value>(2), std::make_unique<base::Value>(6),
-            std::make_unique<base::Value>(8)))));
+    std::unique_ptr<TakesIntegers::Params> params(TakesIntegers::Params::Create(
+        List(List(std::make_unique<base::Value>(2),
+                  std::make_unique<base::Value>(6),
+                  std::make_unique<base::Value>(8)))
+            ->GetList()));
     ASSERT_TRUE(params);
     ASSERT_TRUE(params->nums.as_integers);
     EXPECT_EQ(Vector(2, 6, 8), *params->nums.as_integers);
@@ -50,8 +51,9 @@
 TEST(JsonSchemaCompilerChoicesTest, ObjectWithChoicesParamsCreate) {
   {
     std::unique_ptr<choices::ObjectWithChoices::Params> params(
-        choices::ObjectWithChoices::Params::Create(*List(
-            Dictionary("strings", std::make_unique<base::Value>("asdf")))));
+        choices::ObjectWithChoices::Params::Create(
+            List(Dictionary("strings", std::make_unique<base::Value>("asdf")))
+                ->GetList()));
     ASSERT_TRUE(params);
     EXPECT_FALSE(params->string_info.strings.as_strings);
     EXPECT_EQ("asdf", *params->string_info.strings.as_string);
@@ -60,8 +62,9 @@
   {
     std::unique_ptr<choices::ObjectWithChoices::Params> params(
         choices::ObjectWithChoices::Params::Create(
-            *List(Dictionary("strings", std::make_unique<base::Value>("asdf"),
-                             "integers", std::make_unique<base::Value>(6)))));
+            List(Dictionary("strings", std::make_unique<base::Value>("asdf"),
+                            "integers", std::make_unique<base::Value>(6)))
+                ->GetList()));
     ASSERT_TRUE(params);
     EXPECT_FALSE(params->string_info.strings.as_strings);
     EXPECT_EQ("asdf", *params->string_info.strings.as_string);
@@ -81,7 +84,7 @@
     std::unique_ptr<base::ListValue> params_value(new base::ListValue());
     params_value->Append(std::move(object_param));
     std::unique_ptr<choices::ObjectWithChoices::Params> params(
-        choices::ObjectWithChoices::Params::Create(*params_value));
+        choices::ObjectWithChoices::Params::Create(params_value->GetList()));
     EXPECT_FALSE(params.get());
   }
   {
@@ -91,7 +94,7 @@
     std::unique_ptr<base::ListValue> params_value(new base::ListValue());
     params_value->Append(std::move(object_param));
     std::unique_ptr<choices::ObjectWithChoices::Params> params(
-        choices::ObjectWithChoices::Params::Create(*params_value));
+        choices::ObjectWithChoices::Params::Create(params_value->GetList()));
     EXPECT_FALSE(params.get());
   }
   {
@@ -100,7 +103,7 @@
     std::unique_ptr<base::ListValue> params_value(new base::ListValue());
     params_value->Append(std::move(object_param));
     std::unique_ptr<choices::ObjectWithChoices::Params> params(
-        choices::ObjectWithChoices::Params::Create(*params_value));
+        choices::ObjectWithChoices::Params::Create(params_value->GetList()));
     EXPECT_FALSE(params.get());
   }
 }
diff --git a/tools/json_schema_compiler/test/crossref_unittest.cc b/tools/json_schema_compiler/test/crossref_unittest.cc
index e8df02e..96aaab4 100644
--- a/tools/json_schema_compiler/test/crossref_unittest.cc
+++ b/tools/json_schema_compiler/test/crossref_unittest.cc
@@ -57,7 +57,7 @@
   auto params_value = std::make_unique<base::ListValue>();
   params_value->Append(CreateTestTypeValue());
   std::unique_ptr<crossref::TestTypeOptionalParam::Params> params(
-      crossref::TestTypeOptionalParam::Params::Create(*params_value));
+      crossref::TestTypeOptionalParam::Params::Create(params_value->GetList()));
   EXPECT_TRUE(params.get());
   EXPECT_TRUE(params->test_type.get());
   EXPECT_TRUE(
@@ -71,7 +71,7 @@
   test_type_value->RemoveKey("number");
   params_value->Append(std::move(test_type_value));
   std::unique_ptr<crossref::TestTypeOptionalParam::Params> params(
-      crossref::TestTypeOptionalParam::Params::Create(*params_value));
+      crossref::TestTypeOptionalParam::Params::Create(params_value->GetList()));
   EXPECT_FALSE(params.get());
 }
 
@@ -95,7 +95,7 @@
     param_object_value->SetBoolean("boolean", true);
     params_value->Append(std::move(param_object_value));
     std::unique_ptr<crossref::TestTypeInObject::Params> params(
-        crossref::TestTypeInObject::Params::Create(*params_value));
+        crossref::TestTypeInObject::Params::Create(params_value->GetList()));
     EXPECT_TRUE(params.get());
     EXPECT_TRUE(params->param_object.test_type.get());
     EXPECT_TRUE(params->param_object.boolean);
@@ -108,7 +108,7 @@
     param_object_value->SetBoolean("boolean", true);
     params_value->Append(std::move(param_object_value));
     std::unique_ptr<crossref::TestTypeInObject::Params> params(
-        crossref::TestTypeInObject::Params::Create(*params_value));
+        crossref::TestTypeInObject::Params::Create(params_value->GetList()));
     EXPECT_TRUE(params.get());
     EXPECT_FALSE(params->param_object.test_type.get());
     EXPECT_TRUE(params->param_object.boolean);
@@ -120,7 +120,7 @@
     param_object_value->SetBoolean("boolean", true);
     params_value->Append(std::move(param_object_value));
     std::unique_ptr<crossref::TestTypeInObject::Params> params(
-        crossref::TestTypeInObject::Params::Create(*params_value));
+        crossref::TestTypeInObject::Params::Create(params_value->GetList()));
     EXPECT_FALSE(params.get());
   }
   {
@@ -130,7 +130,7 @@
         "testType", base::Value::FromUniquePtrValue(CreateTestTypeValue()));
     params_value->Append(std::move(param_object_value));
     std::unique_ptr<crossref::TestTypeInObject::Params> params(
-        crossref::TestTypeInObject::Params::Create(*params_value));
+        crossref::TestTypeInObject::Params::Create(params_value->GetList()));
     EXPECT_FALSE(params.get());
   }
 }
diff --git a/tools/json_schema_compiler/test/enums_unittest.cc b/tools/json_schema_compiler/test/enums_unittest.cc
index 5507b42..8a014ce4 100644
--- a/tools/json_schema_compiler/test/enums_unittest.cc
+++ b/tools/json_schema_compiler/test/enums_unittest.cc
@@ -4,6 +4,8 @@
 
 #include "tools/json_schema_compiler/test/enums.h"
 
+#include <vector>
+
 #include "base/values.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "tools/json_schema_compiler/test/test_util.h"
@@ -30,16 +32,16 @@
 
 TEST(JsonSchemaCompilerEnumsTest, EnumsAsTypes) {
   {
-    base::ListValue args;
-    args.Append("one");
+    std::vector<base::Value> args;
+    args.emplace_back("one");
 
     std::unique_ptr<enums::TakesEnumAsType::Params> params(
         enums::TakesEnumAsType::Params::Create(args));
     ASSERT_TRUE(params.get());
     EXPECT_EQ(enums::ENUMERATION_ONE, params->enumeration);
 
-    EXPECT_EQ(args, base::Value(enums::ReturnsEnumAsType::Results::Create(
-                        enums::ENUMERATION_ONE)));
+    EXPECT_EQ(args, enums::ReturnsEnumAsType::Results::Create(
+                        enums::ENUMERATION_ONE));
   }
   {
     enums::HasEnumeration enumeration;
@@ -72,9 +74,10 @@
 
 TEST(JsonSchemaCompilerEnumsTest, EnumsArrayAsType) {
   {
-    base::ListValue params_value;
-    params_value.Append(List(std::make_unique<base::Value>("one"),
-                             std::make_unique<base::Value>("two")));
+    std::vector<base::Value> params_value;
+    params_value.push_back(base::Value::FromUniquePtrValue(
+        List(std::make_unique<base::Value>("one"),
+             std::make_unique<base::Value>("two"))));
     std::unique_ptr<enums::TakesEnumArrayAsType::Params> params(
         enums::TakesEnumArrayAsType::Params::Create(params_value));
     ASSERT_TRUE(params);
@@ -83,8 +86,9 @@
     EXPECT_EQ(enums::ENUMERATION_TWO, params->values[1]);
   }
   {
-    base::ListValue params_value;
-    params_value.Append(List(std::make_unique<base::Value>("invalid")));
+    std::vector<base::Value> params_value;
+    params_value.push_back(base::Value::FromUniquePtrValue(
+        List(std::make_unique<base::Value>("invalid"))));
     std::unique_ptr<enums::TakesEnumArrayAsType::Params> params(
         enums::TakesEnumArrayAsType::Params::Create(params_value));
     EXPECT_FALSE(params);
@@ -144,16 +148,16 @@
 
 TEST(JsonSchemaCompilerEnumsTest, TakesEnumParamsCreate) {
   {
-    base::ListValue params_value;
-    params_value.Append("two");
+    std::vector<base::Value> params_value;
+    params_value.emplace_back("two");
     std::unique_ptr<enums::TakesEnum::Params> params(
         enums::TakesEnum::Params::Create(params_value));
     EXPECT_TRUE(params.get());
     EXPECT_EQ(enums::ENUMERATION_TWO, params->state);
   }
   {
-    base::ListValue params_value;
-    params_value.Append("invalid");
+    std::vector<base::Value> params_value;
+    params_value.emplace_back("invalid");
     std::unique_ptr<enums::TakesEnum::Params> params(
         enums::TakesEnum::Params::Create(params_value));
     EXPECT_FALSE(params.get());
@@ -162,9 +166,10 @@
 
 TEST(JsonSchemaCompilerEnumsTest, TakesEnumArrayParamsCreate) {
   {
-    base::ListValue params_value;
-    params_value.Append(List(std::make_unique<base::Value>("one"),
-                             std::make_unique<base::Value>("two")));
+    std::vector<base::Value> params_value;
+    params_value.push_back(base::Value::FromUniquePtrValue(
+        List(std::make_unique<base::Value>("one"),
+             std::make_unique<base::Value>("two"))));
     std::unique_ptr<enums::TakesEnumArray::Params> params(
         enums::TakesEnumArray::Params::Create(params_value));
     ASSERT_TRUE(params);
@@ -173,8 +178,9 @@
     EXPECT_EQ(enums::ENUMERATION_TWO, params->values[1]);
   }
   {
-    base::ListValue params_value;
-    params_value.Append(List(std::make_unique<base::Value>("invalid")));
+    std::vector<base::Value> params_value;
+    params_value.push_back(base::Value::FromUniquePtrValue(
+        List(std::make_unique<base::Value>("invalid"))));
     std::unique_ptr<enums::TakesEnumArray::Params> params(
         enums::TakesEnumArray::Params::Create(params_value));
     EXPECT_FALSE(params);
@@ -183,23 +189,23 @@
 
 TEST(JsonSchemaCompilerEnumsTest, TakesOptionalEnumParamsCreate) {
   {
-    base::ListValue params_value;
-    params_value.Append("three");
+    std::vector<base::Value> params_value;
+    params_value.emplace_back("three");
     std::unique_ptr<enums::TakesOptionalEnum::Params> params(
         enums::TakesOptionalEnum::Params::Create(params_value));
     EXPECT_TRUE(params.get());
     EXPECT_EQ(enums::ENUMERATION_THREE, params->state);
   }
   {
-    base::ListValue params_value;
+    std::vector<base::Value> params_value;
     std::unique_ptr<enums::TakesOptionalEnum::Params> params(
         enums::TakesOptionalEnum::Params::Create(params_value));
     EXPECT_TRUE(params.get());
     EXPECT_EQ(enums::ENUMERATION_NONE, params->state);
   }
   {
-    base::ListValue params_value;
-    params_value.Append("invalid");
+    std::vector<base::Value> params_value;
+    params_value.emplace_back("invalid");
     std::unique_ptr<enums::TakesOptionalEnum::Params> params(
         enums::TakesOptionalEnum::Params::Create(params_value));
     EXPECT_FALSE(params.get());
@@ -208,9 +214,9 @@
 
 TEST(JsonSchemaCompilerEnumsTest, TakesMultipleOptionalEnumsParamsCreate) {
   {
-    base::ListValue params_value;
-    params_value.Append("one");
-    params_value.Append("ham");
+    std::vector<base::Value> params_value;
+    params_value.emplace_back("one");
+    params_value.emplace_back("ham");
     std::unique_ptr<enums::TakesMultipleOptionalEnums::Params> params(
         enums::TakesMultipleOptionalEnums::Params::Create(params_value));
     EXPECT_TRUE(params.get());
@@ -218,8 +224,8 @@
     EXPECT_EQ(enums::OTHER_ENUMERATION_HAM, params->type);
   }
   {
-    base::ListValue params_value;
-    params_value.Append("one");
+    std::vector<base::Value> params_value;
+    params_value.emplace_back("one");
     std::unique_ptr<enums::TakesMultipleOptionalEnums::Params> params(
         enums::TakesMultipleOptionalEnums::Params::Create(params_value));
     EXPECT_TRUE(params.get());
@@ -227,7 +233,7 @@
     EXPECT_EQ(enums::OTHER_ENUMERATION_NONE, params->type);
   }
   {
-    base::ListValue params_value;
+    std::vector<base::Value> params_value;
     std::unique_ptr<enums::TakesMultipleOptionalEnums::Params> params(
         enums::TakesMultipleOptionalEnums::Params::Create(params_value));
     EXPECT_TRUE(params.get());
@@ -235,9 +241,9 @@
     EXPECT_EQ(enums::OTHER_ENUMERATION_NONE, params->type);
   }
   {
-    base::ListValue params_value;
-    params_value.Append("three");
-    params_value.Append("invalid");
+    std::vector<base::Value> params_value;
+    params_value.emplace_back("three");
+    params_value.emplace_back("invalid");
     std::unique_ptr<enums::TakesMultipleOptionalEnums::Params> params(
         enums::TakesMultipleOptionalEnums::Params::Create(params_value));
     EXPECT_FALSE(params.get());
diff --git a/tools/json_schema_compiler/test/error_generation_unittest.cc b/tools/json_schema_compiler/test/error_generation_unittest.cc
index 41fd18d..cd5ffe9c 100644
--- a/tools/json_schema_compiler/test/error_generation_unittest.cc
+++ b/tools/json_schema_compiler/test/error_generation_unittest.cc
@@ -5,6 +5,7 @@
 #include "tools/json_schema_compiler/test/error_generation.h"
 
 #include <memory>
+#include <vector>
 
 #include "base/json/json_writer.h"
 #include "base/logging.h"
@@ -81,16 +82,17 @@
 
 TEST(JsonSchemaCompilerErrorTest, TooManyParameters) {
   {
-    std::unique_ptr<base::ListValue> params_value =
-        List(std::make_unique<Value>(5));
+    std::vector<Value> params_value;
+    params_value.emplace_back(5);
     std::u16string error;
-    EXPECT_TRUE(errors::TestFunction::Params::Create(*params_value, &error));
+    EXPECT_TRUE(errors::TestFunction::Params::Create(params_value, &error));
   }
   {
-    std::unique_ptr<base::ListValue> params_value =
-        List(std::make_unique<Value>(5), std::make_unique<Value>(5));
+    std::vector<Value> params_value;
+    params_value.emplace_back(5);
+    params_value.emplace_back(5);
     std::u16string error;
-    EXPECT_FALSE(errors::TestFunction::Params::Create(*params_value, &error));
+    EXPECT_FALSE(errors::TestFunction::Params::Create(params_value, &error));
     EXPECT_TRUE(EqualsUtf16("expected 1 arguments, got 2", error));
   }
 }
@@ -99,16 +101,16 @@
 
 TEST(JsonSchemaCompilerErrorTest, ParamIsRequired) {
   {
-    std::unique_ptr<base::ListValue> params_value =
-        List(std::make_unique<Value>(5));
+    std::vector<Value> params_value;
+    params_value.emplace_back(5);
     std::u16string error;
-    EXPECT_TRUE(errors::TestFunction::Params::Create(*params_value, &error));
+    EXPECT_TRUE(errors::TestFunction::Params::Create(params_value, &error));
   }
   {
-    std::unique_ptr<base::ListValue> params_value =
-        List(std::make_unique<Value>());
+    std::vector<Value> params_value;
+    params_value.emplace_back();
     std::u16string error;
-    EXPECT_FALSE(errors::TestFunction::Params::Create(*params_value, &error));
+    EXPECT_FALSE(errors::TestFunction::Params::Create(params_value, &error));
     EXPECT_TRUE(EqualsUtf16("'num' is required", error));
   }
 }
@@ -132,16 +134,16 @@
 TEST(JsonSchemaCompilerErrorTest, WrongParameterCreationType) {
   {
     std::u16string error;
-    std::unique_ptr<base::ListValue> params_value =
-        List(std::make_unique<Value>("Yeah!"));
-    EXPECT_TRUE(errors::TestString::Params::Create(*params_value, &error));
+    std::vector<Value> params_value;
+    params_value.emplace_back("Yeah!");
+    EXPECT_TRUE(errors::TestString::Params::Create(params_value, &error));
   }
   {
-    std::unique_ptr<base::ListValue> params_value =
-        List(std::make_unique<Value>(5));
+    std::vector<Value> params_value;
+    params_value.emplace_back(5);
     std::u16string error;
     EXPECT_FALSE(
-        errors::TestTypeInObject::Params::Create(*params_value, &error));
+        errors::TestTypeInObject::Params::Create(params_value, &error));
     EXPECT_TRUE(EqualsUtf16("'paramObject': expected dictionary, got integer",
         error));
   }
diff --git a/tools/json_schema_compiler/test/functions_on_types_unittest.cc b/tools/json_schema_compiler/test/functions_on_types_unittest.cc
index 321bc69..9029f19 100644
--- a/tools/json_schema_compiler/test/functions_on_types_unittest.cc
+++ b/tools/json_schema_compiler/test/functions_on_types_unittest.cc
@@ -5,6 +5,7 @@
 #include "tools/json_schema_compiler/test/functions_on_types.h"
 
 #include <utility>
+#include <vector>
 
 #include "base/values.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -13,40 +14,40 @@
 
 TEST(JsonSchemaCompilerFunctionsOnTypesTest, StorageAreaGetParamsCreate) {
   {
-    auto params_value = std::make_unique<base::ListValue>();
+    std::vector<base::Value> params_value;
     std::unique_ptr<functions_on_types::StorageArea::Get::Params> params(
-        functions_on_types::StorageArea::Get::Params::Create(*params_value));
+        functions_on_types::StorageArea::Get::Params::Create(params_value));
     ASSERT_TRUE(params);
     EXPECT_FALSE(params->keys);
   }
   {
-    auto params_value = std::make_unique<base::ListValue>();
-    params_value->Append(9);
+    std::vector<base::Value> params_value;
+    params_value.emplace_back(9);
     std::unique_ptr<functions_on_types::StorageArea::Get::Params> params(
-        functions_on_types::StorageArea::Get::Params::Create(*params_value));
+        functions_on_types::StorageArea::Get::Params::Create(params_value));
     EXPECT_FALSE(params);
   }
   {
-    auto params_value = std::make_unique<base::ListValue>();
-    params_value->Append("test");
+    std::vector<base::Value> params_value;
+    params_value.emplace_back("test");
     std::unique_ptr<functions_on_types::StorageArea::Get::Params> params(
-        functions_on_types::StorageArea::Get::Params::Create(*params_value));
+        functions_on_types::StorageArea::Get::Params::Create(params_value));
     ASSERT_TRUE(params);
     ASSERT_TRUE(params->keys);
     EXPECT_EQ("test", *params->keys->as_string);
   }
   {
-    auto keys_object_value = std::make_unique<base::DictionaryValue>();
-    keys_object_value->SetInteger("integer", 5);
-    keys_object_value->SetString("string", "string");
-    auto params_value = std::make_unique<base::ListValue>();
-    params_value->Append(keys_object_value->CreateDeepCopy());
+    base::Value keys_object_value(base::Value::Type::DICTIONARY);
+    keys_object_value.SetIntKey("integer", 5);
+    keys_object_value.SetStringKey("string", "string");
+    std::vector<base::Value> params_value;
+    params_value.push_back(keys_object_value.Clone());
     std::unique_ptr<functions_on_types::StorageArea::Get::Params> params(
-        functions_on_types::StorageArea::Get::Params::Create(*params_value));
+        functions_on_types::StorageArea::Get::Params::Create(params_value));
     ASSERT_TRUE(params);
     ASSERT_TRUE(params->keys);
-    EXPECT_TRUE(keys_object_value->Equals(
-        &params->keys->as_object->additional_properties));
+    EXPECT_EQ(keys_object_value,
+              params->keys->as_object->additional_properties);
   }
 }
 
@@ -62,12 +63,12 @@
 }
 
 TEST(JsonSchemaCompilerFunctionsOnTypesTest, ChromeSettingGetParamsCreate) {
-  auto details_value = std::make_unique<base::DictionaryValue>();
-  details_value->SetBoolean("incognito", true);
-  auto params_value = std::make_unique<base::ListValue>();
-  params_value->Append(std::move(details_value));
+  base::Value details_value(base::Value::Type::DICTIONARY);
+  details_value.SetBoolKey("incognito", true);
+  std::vector<base::Value> params_value;
+  params_value.push_back(std::move(details_value));
   std::unique_ptr<functions_on_types::ChromeSetting::Get::Params> params(
-      functions_on_types::ChromeSetting::Get::Params::Create(*params_value));
+      functions_on_types::ChromeSetting::Get::Params::Create(params_value));
   EXPECT_TRUE(params.get());
   EXPECT_TRUE(*params->details.incognito);
 }
diff --git a/tools/json_schema_compiler/test/idl_schemas_unittest.cc b/tools/json_schema_compiler/test/idl_schemas_unittest.cc
index f58be79..5f5ecdfa 100644
--- a/tools/json_schema_compiler/test/idl_schemas_unittest.cc
+++ b/tools/json_schema_compiler/test/idl_schemas_unittest.cc
@@ -4,6 +4,7 @@
 
 #include <memory>
 #include <utility>
+#include <vector>
 
 #include "base/values.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -39,22 +40,22 @@
   EXPECT_EQ(a.y, b.y);
 
   // Test Function2, which takes an integer parameter.
-  base::ListValue list;
-  list.Append(5);
+  std::vector<base::Value> list;
+  list.emplace_back(5);
   std::unique_ptr<Function2::Params> f2_params =
       Function2::Params::Create(list);
   EXPECT_EQ(5, f2_params->x);
 
   // Test Function3, which takes a MyType1 parameter.
-  list.ClearList();
-  std::unique_ptr<base::DictionaryValue> tmp(new base::DictionaryValue());
-  tmp->SetInteger("x", 17);
-  tmp->SetString("y", "hello");
-  tmp->SetString("z", "zstring");
-  tmp->SetString("a", "astring");
-  tmp->SetString("b", "bstring");
-  tmp->SetString("c", "cstring");
-  list.Append(std::move(tmp));
+  list.clear();
+  base::DictionaryValue tmp;
+  tmp.SetInteger("x", 17);
+  tmp.SetString("y", "hello");
+  tmp.SetString("z", "zstring");
+  tmp.SetString("a", "astring");
+  tmp.SetString("b", "bstring");
+  tmp.SetString("c", "cstring");
+  list.emplace_back(std::move(tmp));
   std::unique_ptr<Function3::Params> f3_params =
       Function3::Params::Create(list);
   EXPECT_EQ(17, f3_params->arg.x);
@@ -62,20 +63,18 @@
 
   // Test functions that take a callback function as a parameter, with varying
   // callback signatures.
-  base::Value f4_results(Function4::Results::Create());
-  base::ListValue expected;
+  std::vector<base::Value> f4_results(Function4::Results::Create());
+  std::vector<base::Value> expected;
   EXPECT_EQ(expected, f4_results);
 
-  base::Value f5_results(Function5::Results::Create(13));
-  ASSERT_TRUE(f5_results.is_list());
-  ASSERT_EQ(1u, f5_results.GetList().size());
-  EXPECT_TRUE(f5_results.GetList()[0].is_int());
+  std::vector<base::Value> f5_results(Function5::Results::Create(13));
+  ASSERT_EQ(1u, f5_results.size());
+  EXPECT_TRUE(f5_results[0].is_int());
 
-  base::Value f6_results(Function6::Results::Create(a));
-  ASSERT_TRUE(f6_results.is_list());
-  ASSERT_EQ(1u, f6_results.GetList().size());
+  std::vector<base::Value> f6_results(Function6::Results::Create(a));
+  ASSERT_EQ(1u, f6_results.size());
   MyType1 c;
-  EXPECT_TRUE(MyType1::Populate(f6_results.GetList()[0], &c));
+  EXPECT_TRUE(MyType1::Populate(f6_results[0], &c));
   EXPECT_EQ(a.x, c.x);
   EXPECT_EQ(a.y, c.y);
 }
@@ -83,41 +82,41 @@
 TEST(IdlCompiler, OptionalArguments) {
   // Test a function that takes one optional argument, both without and with
   // that argument.
-  base::ListValue list;
+  std::vector<base::Value> list;
   std::unique_ptr<Function7::Params> f7_params =
       Function7::Params::Create(list);
   EXPECT_EQ(nullptr, f7_params->arg.get());
-  list.Append(7);
+  list.emplace_back(7);
   f7_params = Function7::Params::Create(list);
   EXPECT_EQ(7, *(f7_params->arg));
 
   // Similar to above, but a function with one required and one optional
   // argument.
-  list.ClearList();
-  list.Append(8);
+  list.clear();
+  list.emplace_back(8);
   std::unique_ptr<Function8::Params> f8_params =
       Function8::Params::Create(list);
   EXPECT_EQ(8, f8_params->arg1);
   EXPECT_EQ(nullptr, f8_params->arg2.get());
-  list.Append("foo");
+  list.emplace_back("foo");
   f8_params = Function8::Params::Create(list);
   EXPECT_EQ(8, f8_params->arg1);
   EXPECT_EQ("foo", *(f8_params->arg2));
 
   // Test a function with an optional argument of custom type.
-  list.ClearList();
+  list.clear();
   std::unique_ptr<Function9::Params> f9_params =
       Function9::Params::Create(list);
   EXPECT_EQ(nullptr, f9_params->arg.get());
-  list.ClearList();
-  std::unique_ptr<base::DictionaryValue> tmp(new base::DictionaryValue());
-  tmp->SetInteger("x", 17);
-  tmp->SetString("y", "hello");
-  tmp->SetString("z", "zstring");
-  tmp->SetString("a", "astring");
-  tmp->SetString("b", "bstring");
-  tmp->SetString("c", "cstring");
-  list.Append(std::move(tmp));
+  list.clear();
+  base::DictionaryValue tmp;
+  tmp.SetInteger("x", 17);
+  tmp.SetString("y", "hello");
+  tmp.SetString("z", "zstring");
+  tmp.SetString("a", "astring");
+  tmp.SetString("b", "bstring");
+  tmp.SetString("c", "cstring");
+  list.emplace_back(std::move(tmp));
   f9_params = Function9::Params::Create(list);
   ASSERT_TRUE(f9_params->arg.get() != nullptr);
   MyType1* t1 = f9_params->arg.get();
@@ -128,9 +127,9 @@
 TEST(IdlCompiler, ArrayTypes) {
   // Tests of a function that takes an integer and an array of integers. First
   // use an empty array.
-  base::ListValue list;
-  list.Append(33);
-  list.Append(std::make_unique<base::ListValue>());
+  std::vector<base::Value> list;
+  list.emplace_back(33);
+  list.emplace_back(std::vector<base::Value>());
   std::unique_ptr<Function10::Params> f10_params =
       Function10::Params::Create(list);
   ASSERT_TRUE(f10_params != nullptr);
@@ -138,12 +137,12 @@
   EXPECT_TRUE(f10_params->y.empty());
 
   // Same function, but this time with 2 values in the array.
-  list.ClearList();
-  list.Append(33);
-  std::unique_ptr<base::ListValue> sublist(new base::ListValue);
-  sublist->Append(34);
-  sublist->Append(35);
-  list.Append(std::move(sublist));
+  list.clear();
+  list.emplace_back(33);
+  std::vector<base::Value> sublist;
+  sublist.emplace_back(34);
+  sublist.emplace_back(35);
+  list.emplace_back(std::move(sublist));
   f10_params = Function10::Params::Create(list);
   ASSERT_TRUE(f10_params != nullptr);
   EXPECT_EQ(33, f10_params->x);
@@ -152,17 +151,17 @@
   EXPECT_EQ(35, f10_params->y[1]);
 
   // Now test a function which takes an array of a defined type.
-  list.ClearList();
+  list.clear();
   MyType1 a;
   MyType1 b;
   a.x = 5;
   b.x = 6;
   a.y = std::string("foo");
   b.y = std::string("bar");
-  std::unique_ptr<base::ListValue> sublist2(new base::ListValue);
-  sublist2->Append(a.ToValue());
-  sublist2->Append(b.ToValue());
-  list.Append(std::move(sublist2));
+  std::vector<base::Value> sublist2;
+  sublist2.push_back(base::Value::FromUniquePtrValue(a.ToValue()));
+  sublist2.push_back(base::Value::FromUniquePtrValue(b.ToValue()));
+  list.emplace_back(std::move(sublist2));
   std::unique_ptr<Function11::Params> f11_params =
       Function11::Params::Create(list);
   ASSERT_TRUE(f11_params != nullptr);
@@ -192,14 +191,12 @@
   EXPECT_EQ(7, b2.x->GetInt());
 
   // Test the params to the ObjectFunction1 function.
-  std::unique_ptr<base::DictionaryValue> icon_props(
-      new base::DictionaryValue());
-  icon_props->SetString("hello", "world");
+  base::DictionaryValue icon_props;
+  icon_props.SetString("hello", "world");
   ObjectFunction1::Params::Icon icon;
-  EXPECT_TRUE(ObjectFunction1::Params::Icon::Populate(*(icon_props.get()),
-                                                      &icon));
-  base::ListValue list;
-  list.Append(std::move(icon_props));
+  EXPECT_TRUE(ObjectFunction1::Params::Icon::Populate(icon_props, &icon));
+  std::vector<base::Value> list;
+  list.push_back(std::move(icon_props));
   std::unique_ptr<ObjectFunction1::Params> params =
       ObjectFunction1::Params::Create(list);
   ASSERT_TRUE(params.get() != nullptr);
diff --git a/tools/json_schema_compiler/test/objects_unittest.cc b/tools/json_schema_compiler/test/objects_unittest.cc
index f6f4b9c..cbd332a 100644
--- a/tools/json_schema_compiler/test/objects_unittest.cc
+++ b/tools/json_schema_compiler/test/objects_unittest.cc
@@ -8,6 +8,7 @@
 
 #include <memory>
 #include <utility>
+#include <vector>
 
 #include "base/json/json_writer.h"
 #include "base/values.h"
@@ -27,11 +28,10 @@
     info_value.SetIntPath("integer", 5);
     info_value.SetBoolPath("boolean", true);
 
-    base::Value params_value(base::Value::Type::LIST);
-    params_value.Append(std::move(info_value));
+    std::vector<base::Value> params_value;
+    params_value.push_back(std::move(info_value));
     std::unique_ptr<test::api::objects::ObjectParam::Params> params(
-        test::api::objects::ObjectParam::Params::Create(
-            base::Value::AsListValue(params_value)));
+        test::api::objects::ObjectParam::Params::Create(params_value));
     EXPECT_TRUE(params.get());
     EXPECT_EQ((size_t) 2, params->info.strings.size());
     EXPECT_EQ("one", params->info.strings[0]);
@@ -47,11 +47,10 @@
     info_value.SetKey("strings", std::move(strings));
     info_value.SetIntPath("integer", 5);
 
-    base::Value params_value(base::Value::Type::LIST);
-    params_value.Append(std::move(info_value));
+    std::vector<base::Value> params_value;
+    params_value.push_back(std::move(info_value));
     std::unique_ptr<test::api::objects::ObjectParam::Params> params(
-        test::api::objects::ObjectParam::Params::Create(
-            base::Value::AsListValue(params_value)));
+        test::api::objects::ObjectParam::Params::Create(params_value));
     EXPECT_FALSE(params.get());
   }
 }
diff --git a/tools/json_schema_compiler/test/simple_api_unittest.cc b/tools/json_schema_compiler/test/simple_api_unittest.cc
index 44cea060..41212b4 100644
--- a/tools/json_schema_compiler/test/simple_api_unittest.cc
+++ b/tools/json_schema_compiler/test/simple_api_unittest.cc
@@ -6,6 +6,7 @@
 
 #include <memory>
 #include <string>
+#include <vector>
 
 #include "base/json/json_reader.h"
 #include "base/logging.h"
@@ -66,44 +67,44 @@
 }
 
 TEST(JsonSchemaCompilerSimpleTest, IncrementIntegerParamsCreate) {
-  auto params_value = std::make_unique<base::ListValue>();
-  params_value->Append(6);
+  std::vector<base::Value> params_value;
+  params_value.emplace_back(6);
   std::unique_ptr<simple_api::IncrementInteger::Params> params(
-      simple_api::IncrementInteger::Params::Create(*params_value));
+      simple_api::IncrementInteger::Params::Create(params_value));
   EXPECT_TRUE(params.get());
   EXPECT_EQ(6, params->num);
 }
 
 TEST(JsonSchemaCompilerSimpleTest, NumberOfParams) {
   {
-    auto params_value = std::make_unique<base::ListValue>();
-    params_value->Append("text");
-    params_value->Append("text");
+    std::vector<base::Value> params_value;
+    params_value.emplace_back("text");
+    params_value.emplace_back("text");
     std::unique_ptr<simple_api::OptionalString::Params> params(
-        simple_api::OptionalString::Params::Create(*params_value));
+        simple_api::OptionalString::Params::Create(params_value));
     EXPECT_FALSE(params.get());
   }
   {
-    auto params_value = std::make_unique<base::ListValue>();
+    std::vector<base::Value> params_value;
     std::unique_ptr<simple_api::IncrementInteger::Params> params(
-        simple_api::IncrementInteger::Params::Create(*params_value));
+        simple_api::IncrementInteger::Params::Create(params_value));
     EXPECT_FALSE(params.get());
   }
 }
 
 TEST(JsonSchemaCompilerSimpleTest, OptionalStringParamsCreate) {
   {
-    auto params_value = std::make_unique<base::ListValue>();
+    std::vector<base::Value> params_value;
     std::unique_ptr<simple_api::OptionalString::Params> params(
-        simple_api::OptionalString::Params::Create(*params_value));
+        simple_api::OptionalString::Params::Create(params_value));
     EXPECT_TRUE(params.get());
     EXPECT_FALSE(params->str.get());
   }
   {
-    auto params_value = std::make_unique<base::ListValue>();
-    params_value->Append("asdf");
+    std::vector<base::Value> params_value;
+    params_value.emplace_back("asdf");
     std::unique_ptr<simple_api::OptionalString::Params> params(
-        simple_api::OptionalString::Params::Create(*params_value));
+        simple_api::OptionalString::Params::Create(params_value));
     EXPECT_TRUE(params.get());
     EXPECT_TRUE(params->str.get());
     EXPECT_EQ("asdf", *params->str);
@@ -112,10 +113,10 @@
 
 TEST(JsonSchemaCompilerSimpleTest, OptionalParamsTakingNull) {
   {
-    auto params_value = std::make_unique<base::ListValue>();
-    params_value->Append(std::make_unique<base::Value>());
+    std::vector<base::Value> params_value;
+    params_value.emplace_back();
     std::unique_ptr<simple_api::OptionalString::Params> params(
-        simple_api::OptionalString::Params::Create(*params_value));
+        simple_api::OptionalString::Params::Create(params_value));
     EXPECT_TRUE(params.get());
     EXPECT_FALSE(params->str.get());
   }
@@ -123,21 +124,21 @@
 
 TEST(JsonSchemaCompilerSimpleTest, OptionalStringParamsWrongType) {
   {
-    auto params_value = std::make_unique<base::ListValue>();
-    params_value->Append(5);
+    std::vector<base::Value> params_value;
+    params_value.emplace_back(5);
     std::unique_ptr<simple_api::OptionalString::Params> params(
-        simple_api::OptionalString::Params::Create(*params_value));
+        simple_api::OptionalString::Params::Create(params_value));
     EXPECT_FALSE(params.get());
   }
 }
 
 TEST(JsonSchemaCompilerSimpleTest, OptionalBeforeRequired) {
   {
-    auto params_value = std::make_unique<base::ListValue>();
-    params_value->Append(std::make_unique<base::Value>());
-    params_value->Append("asdf");
+    std::vector<base::Value> params_value;
+    params_value.emplace_back();
+    params_value.emplace_back("asdf");
     std::unique_ptr<simple_api::OptionalBeforeRequired::Params> params(
-        simple_api::OptionalBeforeRequired::Params::Create(*params_value));
+        simple_api::OptionalBeforeRequired::Params::Create(params_value));
     EXPECT_TRUE(params.get());
     EXPECT_FALSE(params->first.get());
     EXPECT_EQ("asdf", params->second);
diff --git a/tools/mb/mb_config.pyl b/tools/mb/mb_config.pyl
index cd44693d..ec9eb246 100644
--- a/tools/mb/mb_config.pyl
+++ b/tools/mb/mb_config.pyl
@@ -738,6 +738,7 @@
       'mac-autofill-captured-sites-rel': 'release_bot',
       'win-autofill-captured-sites-rel': 'release_bot',
       'win-celab-rel': 'official_celab_release_bot',
+      'win-chrome-finch-fyi': 'official_goma',
       'win-password-manager-captured-sites-rel': 'release_bot',
     },
 
@@ -913,6 +914,7 @@
       'win-celab-try-rel': 'official_celab_release_bot',
       'win-chrome': 'official_goma_x86',
       'win-chrome-beta': 'official_goma_x86',
+      'win-chrome-finch-fyi': 'official_goma',
       'win-chrome-stable': 'official_goma_x86',
       'win64-chrome': 'official_goma_x64',
       'win64-chrome-beta': 'official_goma_x64',
diff --git a/tools/mb/mb_config_expectations/internal.chrome.fyi.json b/tools/mb/mb_config_expectations/internal.chrome.fyi.json
index 847079e..d0953a5 100644
--- a/tools/mb/mb_config_expectations/internal.chrome.fyi.json
+++ b/tools/mb/mb_config_expectations/internal.chrome.fyi.json
@@ -75,6 +75,13 @@
       "use_goma": true
     }
   },
+  "win-chrome-finch-fyi": {
+    "gn_args": {
+      "is_chrome_branded": true,
+      "is_official_build": true,
+      "use_goma": true
+    }
+  },
   "win-password-manager-captured-sites-rel": {
     "gn_args": {
       "dcheck_always_on": false,
diff --git a/tools/mb/mb_config_expectations/tryserver.chrome.json b/tools/mb/mb_config_expectations/tryserver.chrome.json
index 382514cd..4896e35 100644
--- a/tools/mb/mb_config_expectations/tryserver.chrome.json
+++ b/tools/mb/mb_config_expectations/tryserver.chrome.json
@@ -252,6 +252,13 @@
       "use_goma": true
     }
   },
+  "win-chrome-finch-fyi": {
+    "gn_args": {
+      "is_chrome_branded": true,
+      "is_official_build": true,
+      "use_goma": true
+    }
+  },
   "win-chrome-stable": {
     "gn_args": {
       "is_chrome_branded": true,
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
index ab21932..12eb5b1e 100644
--- a/tools/metrics/histograms/enums.xml
+++ b/tools/metrics/histograms/enums.xml
@@ -2528,7 +2528,8 @@
 
 <enum name="AndroidWebViewSafeModeResult">
   <int value="0" label="Success"/>
-  <int value="1" label="Unknown error"/>
+  <int value="1" label="Unknown error (Java Exception)"/>
+  <int value="2" label="At least one action indiciates failure"/>
 </enum>
 
 <enum name="AndroidWebViewSingleOrMultiProcess">
@@ -14650,6 +14651,18 @@
   <int value="2" label="External non-200 HTTP error"/>
 </enum>
 
+<enum name="ConversionStorageCreateReportStatus">
+  <int value="0" label="Success"/>
+  <int value="1" label="Success, but dropped lower-priority report"/>
+  <int value="2" label="Internal error"/>
+  <int value="3" label="No capacity for conversion destination"/>
+  <int value="4" label="No matching impressions"/>
+  <int value="5" label="Deduplicated against existing report"/>
+  <int value="6" label="Rate-limited"/>
+  <int value="7" label="Priority lower than existing reports"/>
+  <int value="8" label="Dropped for noise"/>
+</enum>
+
 <enum name="ConversionStorageSqlInitStatus">
   <int value="0" label="Success"/>
   <int value="1" label="Failed to open DB in memory"/>
@@ -34191,6 +34204,7 @@
   <int value="3981" label="V8NavigatorManagedData_GetAnnotatedLocation_Method"/>
   <int value="3982" label="UserDataFieldFilledPreviously"/>
   <int value="3983" label="TableCollapsedBorderDifferentToVisual"/>
+  <int value="3984" label="HighlightAPIRegisterHighlight"/>
 </enum>
 
 <enum name="FeaturePolicyAllowlistType">
@@ -48435,6 +48449,8 @@
   <int value="-832561975" label="enable-picture-in-picture"/>
   <int value="-831066457" label="IncognitoBrandConsistencyForDesktop:disabled"/>
   <int value="-825942229" label="tab-management-experiment-type-elderberry"/>
+  <int value="-825747847"
+      label="AutofillEnableSupportForMoreStructureInAddresses:enabled"/>
   <int value="-824199802" label="ContextualSearchSimplifiedServer:enabled"/>
   <int value="-823394398" label="TargetEmbeddingLookalikes:enabled"/>
   <int value="-823165021" label="MaterialDesignUserMenu:enabled"/>
@@ -48604,6 +48620,8 @@
   <int value="-695687521" label="double-buffer-compositing"/>
   <int value="-694622753" label="VizHitTest:disabled"/>
   <int value="-694187898" label="MashOopViz:disabled"/>
+  <int value="-693881124"
+      label="AutofillEnableSupportForMoreStructureInNames:disabled"/>
   <int value="-688003116" label="FocusFollowsCursor:enabled"/>
   <int value="-687302378" label="BluetoothFixA2dpPacketSize:enabled"/>
   <int value="-686788480" label="OsSettingsAppNotificationsPage:enabled"/>
@@ -49104,6 +49122,7 @@
   <int value="-270261701" label="WebRtcEnableCaptureMultiChannelApm:disabled"/>
   <int value="-269440655"
       label="AutofillDownstreamCvcPromptUseGooglePayLogo:disabled"/>
+  <int value="-269227049" label="PersistentQuotaIsTemporaryQuota:enabled"/>
   <int value="-268897347" label="DisableCameraFrameRotationAtSource:disabled"/>
   <int value="-268549184"
       label="AutofillSaveCreditCardUsesStrikeSystemV2:enabled"/>
@@ -49714,6 +49733,7 @@
   <int value="246106068" label="HandwritingLegacyRecognition:disabled"/>
   <int value="247200195" label="EnhancedProtectionPromoCard:enabled"/>
   <int value="250855010" label="WebAssemblyBaseline:disabled"/>
+  <int value="251367892" label="PersistentQuotaIsTemporaryQuota:disabled"/>
   <int value="254497185" label="VideoPlayerJsModules:enabled"/>
   <int value="255375615" label="stop-non-timers-in-background:enabled"/>
   <int value="258621334"
@@ -50741,6 +50761,8 @@
   <int value="1070449228" label="ContextualSuggestionsCarousel:enabled"/>
   <int value="1070840417" label="ChromeColors:enabled"/>
   <int value="1071520357" label="WebAuthenticationTouchId:enabled"/>
+  <int value="1071962531"
+      label="AutofillEnableSupportForMoreStructureInAddresses:disabled"/>
   <int value="1072010558" label="NTPModernLayout:disabled"/>
   <int value="1072517479" label="ImprovedCookieControls:enabled"/>
   <int value="1074359194" label="UseSuggestionsEvenIfFew:enabled"/>
@@ -51215,6 +51237,7 @@
   <int value="1434515920" label="ReaderModeInCCT:enabled"/>
   <int value="1435251818" label="AutofillNoLocalSaveOnUploadSuccess:enabled"/>
   <int value="1436454450" label="InterestFeedV2:disabled"/>
+  <int value="1436693426" label="document-transition-slowdown-factor"/>
   <int value="1437413720" label="CooperativeScheduling:disabled"/>
   <int value="1438417722" label="MessagesForAndroidReaderMode:enabled"/>
   <int value="1440618113" label="EnableNetworkingInDiagnosticsApp:disabled"/>
@@ -51545,6 +51568,8 @@
   <int value="1707873180"
       label="OmniboxOnFocusSuggestionsContextualWebAllowSRP:disabled"/>
   <int value="1708118086" label="TextFragmentAnchor:disabled"/>
+  <int value="1710630380"
+      label="AutofillEnableSupportForMoreStructureInNames:enabled"/>
   <int value="1711286384" label="ContextMenuCopyImage:disabled"/>
   <int value="1712622545" label="Memories:disabled"/>
   <int value="1712697097" label="QuickAnswersV2:disabled"/>
diff --git a/tools/metrics/histograms/metadata/android/histograms.xml b/tools/metrics/histograms/metadata/android/histograms.xml
index c03c950..86c4dc67 100644
--- a/tools/metrics/histograms/metadata/android/histograms.xml
+++ b/tools/metrics/histograms/metadata/android/histograms.xml
@@ -198,7 +198,10 @@
 </histogram>
 
 <histogram name="Android.AppNotificationStatus" enum="NotificationAppStatus"
-    expires_after="2020-11-15">
+    expires_after="never">
+<!-- expires-never: Indicates whether Chrome has notification permission. -->
+
+  <owner>dtrainor@chromium.org</owner>
   <owner>peter@chromium.org</owner>
   <summary>
     Records whether notifications are enabled for Chrome, as the Android app,
diff --git a/tools/metrics/histograms/metadata/chromeos/histograms.xml b/tools/metrics/histograms/metadata/chromeos/histograms.xml
index 6bc1ae6..1cbdde6 100644
--- a/tools/metrics/histograms/metadata/chromeos/histograms.xml
+++ b/tools/metrics/histograms/metadata/chromeos/histograms.xml
@@ -1122,10 +1122,10 @@
 </histogram>
 
 <histogram name="ChromeOS.Settings.Device.KeyboardFunctionKeys"
-    enum="BooleanToggled" expires_after="2021-09-30">
-  <owner>khorimoto@chromium.org</owner>
-  <owner>hsuregan@chromium.org</owner>
-  <owner>cros-customization@google.com</owner>
+    enum="BooleanToggled" expires_after="2022-03-15">
+  <owner>jimmyxgong@chromium.org</owner>
+  <owner>zentaro@chromium.org</owner>
+  <owner>cros-peripherals@google.com</owner>
   <summary>
     Records when a user changes the kKeyboardFunctionKeys setting on the Device
     page.
@@ -1321,7 +1321,7 @@
 </histogram>
 
 <histogram name="ChromeOS.Settings.People.AddAccountCount" units="accounts"
-    expires_after="2021-09-30">
+    expires_after="2022-03-15">
   <owner>khorimoto@chromium.org</owner>
   <owner>hsuregan@chromium.org</owner>
   <owner>cros-customization@google.com</owner>
diff --git a/tools/metrics/histograms/metadata/holding_space/histograms.xml b/tools/metrics/histograms/metadata/holding_space/histograms.xml
index 218dd335..9fac9e1 100644
--- a/tools/metrics/histograms/metadata/holding_space/histograms.xml
+++ b/tools/metrics/histograms/metadata/holding_space/histograms.xml
@@ -67,7 +67,7 @@
 </histogram>
 
 <histogram name="HoldingSpace.Animation.PodResize.Smoothness" units="%"
-    expires_after="2021-09-24">
+    expires_after="2022-01-30">
   <owner>dmblack@google.com</owner>
   <owner>gzadina@google.com</owner>
   <summary>
@@ -109,7 +109,7 @@
 </histogram>
 
 <histogram name="HoldingSpace.Item.Action.{action}" enum="HoldingSpaceItemType"
-    expires_after="2021-09-24">
+    expires_after="2022-01-30">
   <owner>dmblack@google.com</owner>
   <owner>gzadina@google.com</owner>
   <summary>
@@ -120,7 +120,7 @@
 </histogram>
 
 <histogram name="HoldingSpace.Item.Action.{action}.Extension"
-    enum="HoldingSpaceExtension" expires_after="2021-09-24">
+    enum="HoldingSpaceExtension" expires_after="2022-01-30">
   <owner>dmblack@google.com</owner>
   <owner>gzadina@google.com</owner>
   <summary>
@@ -131,7 +131,7 @@
 </histogram>
 
 <histogram name="HoldingSpace.Item.Count.{type}" units="items"
-    expires_after="2021-09-24">
+    expires_after="2022-01-30">
   <owner>dmblack@google.com</owner>
   <owner>gzadina@google.com</owner>
   <summary>
@@ -142,7 +142,7 @@
 </histogram>
 
 <histogram name="HoldingSpace.Item.FailureToLaunch" enum="HoldingSpaceItemType"
-    expires_after="2021-09-24">
+    expires_after="2022-01-30">
   <owner>dmblack@google.com</owner>
   <owner>gzadina@google.com</owner>
   <summary>
@@ -152,7 +152,7 @@
 </histogram>
 
 <histogram name="HoldingSpace.Item.FailureToLaunch.Extension"
-    enum="HoldingSpaceExtension" expires_after="2021-09-24">
+    enum="HoldingSpaceExtension" expires_after="2022-01-30">
   <owner>dmblack@google.com</owner>
   <owner>gzadina@google.com</owner>
   <summary>
@@ -172,7 +172,7 @@
 </histogram>
 
 <histogram name="HoldingSpace.TimeFromFirstAvailabilityToFirstAdd" units="ms"
-    expires_after="2021-09-24">
+    expires_after="2022-01-30">
   <owner>dmblack@google.com</owner>
   <owner>gzadina@google.com</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/others/histograms.xml b/tools/metrics/histograms/metadata/others/histograms.xml
index 44457f6..db453125 100644
--- a/tools/metrics/histograms/metadata/others/histograms.xml
+++ b/tools/metrics/histograms/metadata/others/histograms.xml
@@ -3703,6 +3703,18 @@
   </summary>
 </histogram>
 
+<histogram name="Conversions.CreateReportStatus"
+    enum="ConversionStorageCreateReportStatus" expires_after="M98">
+  <owner>apaseltiner@chromium.org</owner>
+  <owner>johnidel@chromium.org</owner>
+  <owner>measurement-api-dev+metrics@google.com</owner>
+  <summary>
+    Measures how often conversions are stored successfully or rejected and why.
+    Recorded once for each conversion event processed by the attribution storage
+    layer.
+  </summary>
+</histogram>
+
 <histogram name="Conversions.ExtraReportDelay" units="ms"
     expires_after="2021-12-19">
   <obsolete>
diff --git a/tools/metrics/histograms/metadata/stability/histograms.xml b/tools/metrics/histograms/metadata/stability/histograms.xml
index 61e7bf54..89964fa 100644
--- a/tools/metrics/histograms/metadata/stability/histograms.xml
+++ b/tools/metrics/histograms/metadata/stability/histograms.xml
@@ -228,7 +228,7 @@
 </histogram>
 
 <histogram name="Stability.ChildFrameCrash.ShownAfterCrashingReason"
-    enum="ShownAfterCrashingReason" expires_after="2021-12-05">
+    enum="ShownAfterCrashingReason" expires_after="2022-08-18">
   <owner>alexmos@chromium.org</owner>
   <owner>boliu@chromium.org</owner>
   <owner>lukasza@chromium.org</owner>
@@ -240,7 +240,7 @@
 </histogram>
 
 <histogram name="Stability.ChildFrameCrash.TabMarkedForReload"
-    enum="BooleanMarkedForReload" expires_after="2021-09-30">
+    enum="BooleanMarkedForReload" expires_after="2022-08-18">
   <owner>alexmos@chromium.org</owner>
   <owner>boliu@chromium.org</owner>
   <summary>
@@ -250,7 +250,7 @@
 </histogram>
 
 <histogram name="Stability.ChildFrameCrash.TabMarkedForReload.Visibility"
-    enum="FrameVisibility" expires_after="2021-09-30">
+    enum="FrameVisibility" expires_after="2022-08-18">
   <owner>alexmos@chromium.org</owner>
   <owner>boliu@chromium.org</owner>
   <summary>
@@ -261,7 +261,7 @@
 </histogram>
 
 <histogram name="Stability.ChildFrameCrash.Visibility" enum="CrashVisibility"
-    expires_after="2022-02-06">
+    expires_after="2022-08-18">
   <owner>alexmos@chromium.org</owner>
   <owner>boliu@chromium.org</owner>
   <owner>lfg@chromium.org</owner>
diff --git a/tools/metrics/ukm/ukm.xml b/tools/metrics/ukm/ukm.xml
index f3c510a..06f57221 100644
--- a/tools/metrics/ukm/ukm.xml
+++ b/tools/metrics/ukm/ukm.xml
@@ -2031,6 +2031,25 @@
   </metric>
 </event>
 
+<event name="AutofillAssistant.Timing">
+  <owner>arbesser@google.com</owner>
+  <owner>hluca@google.com</owner>
+  <owner>sandromaggi@google.com</owner>
+  <metric name="TriggerConditionEvaluationMs">
+    <summary>
+      The time it took to evaluate the trigger conditions, in milliseconds. Only
+      successful evaluations are recorded.
+    </summary>
+    <aggregation>
+      <history>
+        <statistics>
+          <quantiles type="std-percentiles"/>
+        </statistics>
+      </history>
+    </aggregation>
+  </metric>
+</event>
+
 <event name="BackForwardCacheDisabledForRenderFrameHostReason">
   <owner>altimin@chromium.org</owner>
   <owner>hajimehoshi@chromium.org</owner>
diff --git a/ui/accessibility/ax_enum_util.cc b/ui/accessibility/ax_enum_util.cc
index f4dfc6df..ef74ff4f 100644
--- a/ui/accessibility/ax_enum_util.cc
+++ b/ui/accessibility/ax_enum_util.cc
@@ -1046,6 +1046,8 @@
       return "characterOffsets";
     case ax::mojom::IntListAttribute::kCachedLineStarts:
       return "cachedLineStarts";
+    case ax::mojom::IntListAttribute::kCaretBounds:
+      return "caretBounds";
     case ax::mojom::IntListAttribute::kWordStarts:
       return "wordStarts";
     case ax::mojom::IntListAttribute::kWordEnds:
diff --git a/ui/accessibility/ax_enums.mojom b/ui/accessibility/ax_enums.mojom
index 5ae7a04..79a5322 100644
--- a/ui/accessibility/ax_enums.mojom
+++ b/ui/accessibility/ax_enums.mojom
@@ -810,7 +810,8 @@
   kTouchPassthrough = 20,
 };
 
-// Next value: 16
+// Next version: 2
+// Next value: 17
 [Extensible, Stable, Uuid="6d1f823f-28a9-4263-bc4a-69fb19a4ef46"]
 enum IntListAttribute {
   [Default]kNone = 0,
@@ -859,6 +860,9 @@
   // items. Developer can expose those actions as custom actions. Currently
   // custom actions are used only in Android window.
   kCustomActionIds = 15,
+
+  // Caret bounds in screen coordinates - [left, top, width, height].
+  [MinVersion=1] kCaretBounds = 16,
 };
 
 // Next value: 2
diff --git a/ui/accessibility/ax_event_generator.cc b/ui/accessibility/ax_event_generator.cc
index d890e03..179c165d 100644
--- a/ui/accessibility/ax_event_generator.cc
+++ b/ui/accessibility/ax_event_generator.cc
@@ -688,6 +688,9 @@
         AddEvent(node, Event::TEXT_ATTRIBUTE_CHANGED);
       }
       break;
+    case ax::mojom::IntListAttribute::kCaretBounds:
+      AddEvent(node, Event::CARET_BOUNDS_CHANGED);
+      break;
     default:
       AddEvent(node, Event::OTHER_ATTRIBUTE_CHANGED);
       break;
@@ -1177,6 +1180,8 @@
       return "autoCompleteChanged";
     case AXEventGenerator::Event::BUSY_CHANGED:
       return "busyChanged";
+    case AXEventGenerator::Event::CARET_BOUNDS_CHANGED:
+      return "caretBoundsChanged";
     case AXEventGenerator::Event::CHECKED_STATE_CHANGED:
       return "checkedStateChanged";
     case AXEventGenerator::Event::CHECKED_STATE_DESCRIPTION_CHANGED:
diff --git a/ui/accessibility/ax_event_generator.h b/ui/accessibility/ax_event_generator.h
index 026ecdf..6464b4a 100644
--- a/ui/accessibility/ax_event_generator.h
+++ b/ui/accessibility/ax_event_generator.h
@@ -42,6 +42,7 @@
     ATOMIC_CHANGED,
     AUTO_COMPLETE_CHANGED,
     BUSY_CHANGED,
+    CARET_BOUNDS_CHANGED,
     CHECKED_STATE_CHANGED,
     CHECKED_STATE_DESCRIPTION_CHANGED,
     CHILDREN_CHANGED,
diff --git a/ui/accessibility/ax_node_data.cc b/ui/accessibility/ax_node_data.cc
index 6dc61bf..7ca7e69 100644
--- a/ui/accessibility/ax_node_data.cc
+++ b/ui/accessibility/ax_node_data.cc
@@ -200,6 +200,7 @@
     case ax::mojom::IntListAttribute::kMarkerEnds:
     case ax::mojom::IntListAttribute::kCharacterOffsets:
     case ax::mojom::IntListAttribute::kCachedLineStarts:
+    case ax::mojom::IntListAttribute::kCaretBounds:
     case ax::mojom::IntListAttribute::kWordStarts:
     case ax::mojom::IntListAttribute::kWordEnds:
     case ax::mojom::IntListAttribute::kCustomActionIds:
@@ -1731,6 +1732,9 @@
       case ax::mojom::IntListAttribute::kCachedLineStarts:
         result += " cached_line_start_offsets=" + IntVectorToString(values);
         break;
+      case ax::mojom::IntListAttribute::kCaretBounds:
+        result += " caret_bounds=" + IntVectorToString(values);
+        break;
       case ax::mojom::IntListAttribute::kWordStarts:
         result += " word_starts=" + IntVectorToString(values);
         break;
diff --git a/ui/android/java/src/org/chromium/ui/modaldialog/ModalDialogProperties.java b/ui/android/java/src/org/chromium/ui/modaldialog/ModalDialogProperties.java
index 25104a3..ae79e2b 100644
--- a/ui/android/java/src/org/chromium/ui/modaldialog/ModalDialogProperties.java
+++ b/ui/android/java/src/org/chromium/ui/modaldialog/ModalDialogProperties.java
@@ -71,7 +71,7 @@
             new WritableObjectPropertyKey<>();
 
     /** The message of the dialog. */
-    public static final WritableObjectPropertyKey<String> MESSAGE =
+    public static final WritableObjectPropertyKey<CharSequence> MESSAGE =
             new WritableObjectPropertyKey<>();
 
     /** The customized content view of the dialog. */
diff --git a/ui/base/cocoa/controls/button_utils.h b/ui/base/cocoa/controls/button_utils.h
index d9b039f..f7df9c51 100644
--- a/ui/base/cocoa/controls/button_utils.h
+++ b/ui/base/cocoa/controls/button_utils.h
@@ -14,8 +14,7 @@
 
 // These methods are a polyfill for convenience constructors that exist on
 // NSButton in macOS 10.12+.
-// TODO(ellyjones): once we target only 10.12+, delete these and migrate callers
-// over to NSButton directly.
+// TODO(https://crbug.com/1241080): once we target only 10.12+, delete these.
 + (NSButton*)buttonWithTitle:(NSString*)title
                       action:(SEL)action
                       target:(id)target;
diff --git a/ui/base/cocoa/controls/textfield_utils.h b/ui/base/cocoa/controls/textfield_utils.h
index 4fd54c6..ff684b1 100644
--- a/ui/base/cocoa/controls/textfield_utils.h
+++ b/ui/base/cocoa/controls/textfield_utils.h
@@ -13,8 +13,7 @@
 @interface TextFieldUtils : NSObject
 
 // This method is a polyfill for a method on NSTextField on macOS 10.12+.
-// TODO(ellyjones): Once we target only 10.12+, delete this and convert uses
-// over to NSTextField.
+// TODO(https://crbug.com/1241080): Once we only support 10.12+, delete this.
 + (NSTextField*)labelWithString:(NSString*)text;
 
 @end
diff --git a/ui/compositor/test/layer_animator_test_controller.cc b/ui/compositor/test/layer_animator_test_controller.cc
index d271f08..344068c 100644
--- a/ui/compositor/test/layer_animator_test_controller.cc
+++ b/ui/compositor/test/layer_animator_test_controller.cc
@@ -28,7 +28,8 @@
     return NULL;
 }
 
-void LayerAnimatorTestController::StartThreadedAnimationsIfNeeded() {
+void LayerAnimatorTestController::StartThreadedAnimationsIfNeeded(
+    base::TimeTicks started_time) {
   std::vector<cc::TargetProperty::Type> threaded_properties;
   threaded_properties.push_back(cc::TargetProperty::OPACITY);
   threaded_properties.push_back(cc::TargetProperty::TRANSFORM);
@@ -48,8 +49,7 @@
         element->effective_start_time() != base::TimeTicks())
       continue;
 
-    animator_->OnThreadedAnimationStarted(base::TimeTicks::Now(),
-                                          threaded_properties[i],
+    animator_->OnThreadedAnimationStarted(started_time, threaded_properties[i],
                                           element->animation_group_id());
   }
 }
diff --git a/ui/compositor/test/layer_animator_test_controller.h b/ui/compositor/test/layer_animator_test_controller.h
index 92798f3..ca64b08 100644
--- a/ui/compositor/test/layer_animator_test_controller.h
+++ b/ui/compositor/test/layer_animator_test_controller.h
@@ -23,7 +23,8 @@
       LayerAnimationElement::AnimatableProperty property);
 
   // Starts threaded animations that are waiting for an effective start time.
-  void StartThreadedAnimationsIfNeeded();
+  void StartThreadedAnimationsIfNeeded(
+      base::TimeTicks started_time = base::TimeTicks::Now());
 
   // Progresses all running animations by the given |duration|.
   void Step(const base::TimeDelta& duration);
diff --git a/ui/events/platform/platform_event_source.cc b/ui/events/platform/platform_event_source.cc
index c80a92e..b3dc00c 100644
--- a/ui/events/platform/platform_event_source.cc
+++ b/ui/events/platform/platform_event_source.cc
@@ -5,6 +5,7 @@
 #include "ui/events/platform/platform_event_source.h"
 
 #include <algorithm>
+#include <ostream>
 
 #include "base/lazy_instance.h"
 #include "base/threading/thread_local.h"
diff --git a/ui/gfx/font_list.cc b/ui/gfx/font_list.cc
index 502b808..4155b8aba4 100644
--- a/ui/gfx/font_list.cc
+++ b/ui/gfx/font_list.cc
@@ -4,6 +4,8 @@
 
 #include "ui/gfx/font_list.h"
 
+#include <ostream>
+
 #include "base/lazy_instance.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/string_split.h"
diff --git a/ui/message_center/notification_view_controller.cc b/ui/message_center/notification_view_controller.cc
index f4038ef..cfab706c 100644
--- a/ui/message_center/notification_view_controller.cc
+++ b/ui/message_center/notification_view_controller.cc
@@ -3,8 +3,12 @@
 // found in the LICENSE file.
 
 #include "ui/message_center/notification_view_controller.h"
+#include <iterator>
+#include <memory>
 
 #include "ash/constants/ash_features.h"
+#include "base/debug/stack_trace.h"
+#include "base/no_destructor.h"
 #include "base/time/time.h"
 #include "ui/message_center/public/cpp/message_center_constants.h"
 #include "ui/message_center/public/cpp/notification.h"
@@ -12,7 +16,111 @@
 
 namespace message_center {
 
-NotificationViewController::NotificationViewController() {
+namespace {
+
+class GroupedNotificationList {
+ public:
+  GroupedNotificationList() = default;
+  GroupedNotificationList(const GroupedNotificationList& other) = delete;
+  GroupedNotificationList& operator=(const GroupedNotificationList& other) =
+      delete;
+  ~GroupedNotificationList() = default;
+
+  void AddGroupedNotification(const std::string& notification_id,
+                              const std::string& parent_id) {
+    if (notifications_in_parent_map_.find(parent_id) ==
+        notifications_in_parent_map_.end()) {
+      notifications_in_parent_map_[parent_id] = {};
+    }
+
+    child_parent_map_[notification_id] = parent_id;
+
+    if (notification_id != parent_id)
+      notifications_in_parent_map_[parent_id].insert(notification_id);
+  }
+
+  // Remove a single child notification from a grouped notification.
+  void RemoveGroupedChildNotification(const std::string& notification_id) {
+    std::string& parent_id = child_parent_map_[notification_id];
+    notifications_in_parent_map_[parent_id].erase(notification_id);
+    child_parent_map_.erase(notification_id);
+  }
+
+  // Clear the entire grouped notification with `parent_id`
+  void ClearGroupedNotification(const std::string& parent_id) {
+    notifications_in_parent_map_.erase(parent_id);
+    std::vector<std::string> to_be_deleted;
+    for (const auto& it : child_parent_map_) {
+      if (it.second == parent_id)
+        to_be_deleted.push_back(it.first);
+    }
+    for (const auto& child : to_be_deleted)
+      child_parent_map_.erase(child);
+  }
+
+  const std::string& GetParentForChild(const std::string& child_id) {
+    return child_parent_map_[child_id];
+  }
+
+  std::set<std::string>& GetGroupedNotificationsForParent(
+      const std::string& parent_id) {
+    return notifications_in_parent_map_[parent_id];
+  }
+
+  bool GroupedChildNotificationExists(const std::string& child_id) {
+    return child_parent_map_.find(child_id) != child_parent_map_.end();
+  }
+
+  bool ParentNotificationExists(const std::string& parent_id) {
+    return notifications_in_parent_map_.find(parent_id) !=
+           notifications_in_parent_map_.end();
+  }
+
+  // Replaces all instances of `old_parent_id` with `new_parent_id` in
+  // the `child_parent_map_`.
+  void ReplaceParentId(const std::string& new_parent_id,
+                       const std::string& old_parent_id) {
+    // Remove entry with `new_parent_id` as a child id and replace with
+    // `old_parent_id` as a child id.
+    child_parent_map_.erase(new_parent_id);
+    child_parent_map_[old_parent_id] = new_parent_id;
+
+    // Replace all occurrences of `old_parent_id` with `new_parent_id`.
+    std::vector<std::string> to_be_updated;
+    for (const auto& child : child_parent_map_) {
+      if (child.second == old_parent_id)
+        to_be_updated.push_back(child.first);
+    }
+    for (const auto& id : to_be_updated) {
+      child_parent_map_.erase(child_parent_map_.find(id));
+      child_parent_map_[id] = new_parent_id;
+    }
+  }
+
+ private:
+  // Map for looking up the parent `notification_id` for any given notification
+  // id.
+  std::map<std::string, std::string> child_parent_map_;
+
+  // Map containing a list of child notification ids per each group parent id.
+  // Used to keep track of grouped notifications which already have a parent
+  // notification view.
+  std::map<std::string, std::set<std::string>> notifications_in_parent_map_;
+};
+
+// Needs to be a static instance because
+// we need a single instance to be shared across MessagePopupCollection and
+// UnifiedMessageListView. TODO(crbug/1239033) Refactor
+// NotificationViewController so we won't have to do this.
+GroupedNotificationList& GetGroupedNotificationListInstance() {
+  static base::NoDestructor<GroupedNotificationList> instance;
+  return *instance;
+}
+
+}  // namespace
+
+NotificationViewController::NotificationViewController()
+    : grouped_notification_list_(&GetGroupedNotificationListInstance()) {
   observer_.Observe(MessageCenter::Get());
 }
 
@@ -30,12 +138,18 @@
   for (auto* notification : MessageCenter::Get()->GetNotifications()) {
     if (notification->notifier_id() == parent_view->notifier_id() &&
         notification->id() != parent_view->notification_id()) {
-      child_parent_map_[notification->id()] = parent_view->notification_id();
+      grouped_notification_list_->AddGroupedNotification(notification->id(),
+                                                         notification_id);
       parent_view->AddGroupNotification(*notification, /*newest_first=*/true);
     }
   }
 }
 
+const std::string& NotificationViewController::GetParentIdForChildForTest(
+    const std::string& notification_id) {
+  return grouped_notification_list_->GetParentForChild(notification_id);
+}
+
 void NotificationViewController::SetupParentNotification(
     std::string* parent_id) {
   Notification* parent_notification =
@@ -47,7 +161,8 @@
   std::string old_parent_id = *parent_id;
   *parent_id = new_parent_id;
 
-  parent_grouped_notification_id_set_.insert(new_parent_id);
+  grouped_notification_list_->AddGroupedNotification(old_parent_id,
+                                                     new_parent_id);
 
   Notification* new_parent_notification = notification_copy.get();
   MessageCenter::Get()->AddNotification(std::move(notification_copy));
@@ -55,8 +170,10 @@
   ConvertNotificationViewToGroupedNotificationView(
       /*ungrouped_notification_id=*/old_parent_id,
       /*new_grouped_notification_id=*/new_parent_id);
-  UpdateChildParentMap(/*new_parent_id=*/new_parent_id,
-                       /*old_parent_id=*/old_parent_id);
+
+  grouped_notification_list_->ReplaceParentId(
+      /*new_parent_id=*/new_parent_id,
+      /*old_parent_id=*/old_parent_id);
 
   // Add the old parent notification as a group child to the
   // newly created parent notification which will act as a
@@ -64,8 +181,33 @@
   new_parent_notification->SetGroupParent();
   parent_notification->SetGroupChild();
 
-  GetMessageViewForNotificationId(new_parent_id)
-      ->AddGroupNotification(*parent_notification, /*newest_first=*/false);
+  auto* parent_view = GetMessageViewForNotificationId(new_parent_id);
+  if (parent_view)
+    parent_view->AddGroupNotification(*parent_notification,
+                                      /*newest_first=*/false);
+}
+
+void NotificationViewController::SetupSingleNotificationFromGroupedNotification(
+    const std::string& group_parent_id,
+    const std::string& new_single_notification_id) {
+  auto* message_center = MessageCenter::Get();
+  MessageView* parent_view = GetMessageViewForNotificationId(group_parent_id);
+  auto* new_single_notification =
+      message_center->FindNotificationById(new_single_notification_id);
+
+  parent_view->RemoveGroupNotification(new_single_notification_id);
+  parent_view->UpdateWithNotification(*new_single_notification);
+
+  ConvertGroupedNotificationViewToNotificationView(
+      /*grouped_notification_id=*/group_parent_id,
+      /*new_single_notification_id=*/new_single_notification_id);
+
+  message_center->FindNotificationById(group_parent_id)->ClearGroupParent();
+  new_single_notification->ClearGroupChild();
+
+  grouped_notification_list_->ClearGroupedNotification(group_parent_id);
+
+  message_center->RemoveNotification(group_parent_id, /*by_user=*/false);
 }
 
 std::unique_ptr<Notification>
@@ -88,31 +230,40 @@
   return child_copy;
 }
 
-void NotificationViewController::UpdateChildParentMap(
-    const std::string& new_parent_id,
-    const std::string& old_parent_id) {
-  // Remove entry with `new_parent_id` as a child id and replace with
-  // `old_parent_id` as a child id.
-  DCHECK(child_parent_map_.find(new_parent_id) != child_parent_map_.end());
-  child_parent_map_.erase(child_parent_map_.find(new_parent_id));
-  child_parent_map_[old_parent_id] = new_parent_id;
-
-  // Replace all occurrences of `old_parent_id` with `new_parent_id`.
-  std::vector<std::string> to_be_updated;
-  for (auto& child : child_parent_map_) {
-    if (child.second == old_parent_id)
-      to_be_updated.push_back(child.first);
+void NotificationViewController::RemoveGroupedChild(
+    const std::string& notification_id) {
+  if (!grouped_notification_list_->GroupedChildNotificationExists(
+          notification_id)) {
+    return;
   }
-  for (auto id : to_be_updated) {
-    child_parent_map_.erase(child_parent_map_.find(id));
-    child_parent_map_[id] = new_parent_id;
+
+  const std::string parent_id =
+      grouped_notification_list_->GetParentForChild(notification_id);
+
+  MessageView* parent_view = GetMessageViewForNotificationId(parent_id);
+  if (parent_view)
+    parent_view->RemoveGroupNotification(notification_id);
+
+  grouped_notification_list_->RemoveGroupedChildNotification(notification_id);
+
+  // Convert back to a single notification if there is only one
+  // group child left in the group notification.
+  auto grouped_notifications =
+      grouped_notification_list_->GetGroupedNotificationsForParent(parent_id);
+  if (GetMessageViewForNotificationId(parent_id) &&
+      grouped_notifications.size() == 1) {
+    SetupSingleNotificationFromGroupedNotification(
+        /*group_parent_id=*/parent_id,
+        /*new_single_notification_id=*/*grouped_notifications.begin());
   }
 }
 
 void NotificationViewController::OnNotificationAdded(
     const std::string& notification_id) {
+  auto* message_center = MessageCenter::Get();
   Notification* notification =
-      MessageCenter::Get()->FindNotificationById(notification_id);
+      message_center->FindNotificationById(notification_id);
+
   // We only need to process notifications that are children of an
   // existing group. So do nothing otherwise.
   if (!notification)
@@ -122,7 +273,7 @@
     return;
 
   Notification* parent_notification =
-      MessageCenter::Get()->FindOldestNotificationByNotiferId(
+      message_center->FindOldestNotificationByNotiferId(
           notification->notifier_id());
   std::string parent_id = parent_notification->id();
 
@@ -130,40 +281,37 @@
   // we must create a copy of the designated parent notification and
   // use it to set up a container notification which will hold all
   // notifications for this group.
-  if (!parent_grouped_notification_id_set_.count(parent_id))
+  if (!grouped_notification_list_->ParentNotificationExists(parent_id))
     SetupParentNotification(&parent_id);
 
-  child_parent_map_[notification_id] = parent_id;
+  grouped_notification_list_->AddGroupedNotification(notification_id,
+                                                     parent_id);
 
   MessageView* parent_view = GetMessageViewForNotificationId(parent_id);
   if (parent_view)
     parent_view->AddGroupNotification(*notification, /*newest_first=*/false);
   else
-    MessageCenter::Get()->ResetSinglePopup(parent_id);
+    message_center->ResetSinglePopup(parent_id);
 }
 
 void NotificationViewController::OnNotificationRemoved(
     const std::string& notification_id,
     bool by_user) {
-  auto child_it = child_parent_map_.find(notification_id);
-  if (child_it != child_parent_map_.end()) {
-    MessageView* parent_view =
-        GetMessageViewForNotificationId(child_parent_map_[notification_id]);
-
-    child_parent_map_.erase(child_it);
-
-    if (parent_view)
-      parent_view->RemoveGroupNotification(notification_id);
+  if (grouped_notification_list_->GroupedChildNotificationExists(
+          notification_id)) {
+    RemoveGroupedChild(notification_id);
   }
-  auto parent = parent_grouped_notification_id_set_.find(notification_id);
-  if (parent != parent_grouped_notification_id_set_.end()) {
-    std::vector<std::string> to_be_deleted;
-    for (auto& child : child_parent_map_) {
-      if (child.second == notification_id)
-        to_be_deleted.push_back(child.first);
-    }
 
-    for (auto id : to_be_deleted)
+  if (grouped_notification_list_->ParentNotificationExists(notification_id)) {
+    std::vector<std::string> to_be_deleted;
+    auto grouped_notifications =
+        grouped_notification_list_->GetGroupedNotificationsForParent(
+            notification_id);
+    std::copy(grouped_notifications.begin(), grouped_notifications.end(),
+              std::back_inserter(to_be_deleted));
+    grouped_notification_list_->ClearGroupedNotification(notification_id);
+
+    for (const auto& id : to_be_deleted)
       MessageCenter::Get()->RemoveNotification(id, by_user);
   }
 }
diff --git a/ui/message_center/notification_view_controller.h b/ui/message_center/notification_view_controller.h
index f273ba54..177526a 100644
--- a/ui/message_center/notification_view_controller.h
+++ b/ui/message_center/notification_view_controller.h
@@ -12,6 +12,10 @@
 
 namespace message_center {
 
+namespace {
+class GroupedNotificationList;
+}  // namespace
+
 class MessageView;
 
 // A controller class to manage adding, removing and updating group
@@ -35,11 +39,16 @@
   // view.
   void PopulateGroupParent(const std::string& notification_id);
 
+  const std::string& GetParentIdForChildForTest(
+      const std::string& notification_id);
+
  private:
+  friend class MockNotificationViewController;
+
   virtual MessageView* GetMessageViewForNotificationId(
       const std::string& notification_id) = 0;
 
-  // Updates the notification id associated with a message center view and
+  // Updates the notification id associated with a `MessageCenterView` and
   // popup if required. We do this to covert an existing message view into
   // a message view that acts as a container for grouped notifications.
   // Creating a new view for this would make the code simpler but we need
@@ -49,29 +58,39 @@
       const std::string& ungrouped_notification_id,
       const std::string& new_grouped_notification_id) = 0;
 
+  // Updates the notification id associated with a `MessageCenterView` and
+  // popup if needed. This is done to convert an existing grouped notification
+  // view back into a single notification view.
+  virtual void ConvertGroupedNotificationViewToNotificationView(
+      const std::string& grouped_notification_id,
+      const std::string& new_single_notification_id) = 0;
+
   // Sets up a parent view to hold all message views for
   // a grouped notification. Does this by creating a copy of the
   // parent notification and switching the notification_ids of the
   // current message view associated with the parent notification.
   void SetupParentNotification(std::string* parent_id);
 
+  // Clears all group data for `group_parent_id` and converts
+  // the existing message view for `group_parent_id` to a single
+  // ungrouped notification view representing `new_single_notification_id`.
+  void SetupSingleNotificationFromGroupedNotification(
+      const std::string& group_parent_id,
+      const std::string& new_single_notification_id);
+
   // Creates a copy notification that will act as a parent notification
   // for its group.
   std::unique_ptr<Notification> CreateCopyForParentNotification(
       const Notification& parent_notification);
 
-  // Replaces all instances of `old_parent_id` with `new_parent_id` in
-  // the `child_parent_map_`.
-  void UpdateChildParentMap(const std::string& new_parent_id,
-                            const std::string& old_parent_id);
+  // Remove `notification_id` from `child_parent_map` and
+  // `notifications_in_parent_map` Also remove from it's parent notification's
+  // view if if the view currently exists.
+  void RemoveGroupedChild(const std::string& notification_id);
 
-  // Map for looking up the parent notification_id for any given notification
-  // id.
-  std::map<std::string, std::string> child_parent_map_;
-
-  // Set of current parent notification ids. Used to keep track of grouped
-  // notifications which already have a parent notification view.
-  std::set<std::string> parent_grouped_notification_id_set_;
+  // A data structure that holds all grouped notifications along with their
+  // associations with their parent notifications.
+  GroupedNotificationList* const grouped_notification_list_;
 
   base::ScopedObservation<MessageCenter, MessageCenterObserver> observer_{this};
 };
diff --git a/ui/message_center/notification_view_controller_unittest.cc b/ui/message_center/notification_view_controller_unittest.cc
index 4b29990..f593bc15 100644
--- a/ui/message_center/notification_view_controller_unittest.cc
+++ b/ui/message_center/notification_view_controller_unittest.cc
@@ -30,7 +30,7 @@
   MessageView* GetMessageViewForNotificationId(
       const std::string& notification_id) override {
     auto it = std::find_if(fake_message_views_.begin(),
-                           fake_message_views_.end(), [&](auto& child) {
+                           fake_message_views_.end(), [&](const auto& child) {
                              return child->notification_id() == notification_id;
                            });
 
@@ -40,13 +40,31 @@
     return it->get();
   }
   void ConvertNotificationViewToGroupedNotificationView(
-      const std::string& ungrouped_popup_id,
-      const std::string& new_grouped_popup_id) override {}
+      const std::string& ungrouped_notification_id,
+      const std::string& new_grouped_notification_id) override {}
+
+  void ConvertGroupedNotificationViewToNotificationView(
+      const std::string& ungrouped_notification_id,
+      const std::string& new_grouped_notification_id) override {}
 
   void OnNotificationAdded(const std::string& notification_id) override {
-    fake_message_views_.push_back(std::make_unique<MockMessageView>(
-        *MessageCenter::Get()->FindNotificationById(notification_id)));
     NotificationViewController::OnNotificationAdded(notification_id);
+    auto* notification =
+        MessageCenter::Get()->FindNotificationById(notification_id);
+    fake_message_views_.push_back(
+        std::make_unique<MockMessageView>(*notification));
+
+    if (!notification->group_child())
+      return;
+
+    std::string parent_id = GetParentIdForChildForTest(notification_id);
+    auto it = std::find_if(
+        fake_message_views_.begin(), fake_message_views_.end(),
+        [&](const auto& view) { return view->notification_id() == parent_id; });
+    if (it == fake_message_views_.end()) {
+      fake_message_views_.push_back(std::make_unique<MockMessageView>(
+          *MessageCenter::Get()->FindNotificationById(parent_id)));
+    }
   }
 
   void OnNotificationRemoved(const std::string& notification_id,
@@ -58,7 +76,7 @@
   void RemoveNotificationViewById(const std::string& id) {
     auto it = std::find_if(
         fake_message_views_.begin(), fake_message_views_.end(),
-        [&](auto& child) { return child->notification_id() == id; });
+        [&](const auto& view) { return view->notification_id() == id; });
     if (it != fake_message_views_.end())
       fake_message_views_.erase(it);
   }
@@ -98,8 +116,9 @@
 
   Notification* GetPopupNotification(const std::string& id) {
     auto popups = MessageCenter::Get()->GetPopupNotifications();
-    auto it = std::find_if(popups.begin(), popups.end(),
-                           [&](auto popup) { return popup->id() == id; });
+    auto it =
+        std::find_if(popups.begin(), popups.end(),
+                     [&](const auto& popup) { return popup->id() == id; });
     if (it == popups.end())
       return nullptr;
 
@@ -138,17 +157,41 @@
 const char NotificationViewControllerTest::kIdFormat[] = "id%ld";
 
 TEST_F(NotificationViewControllerTest, BasicGrouping) {
+  auto* message_center = MessageCenter::Get();
   std::string id0, id1, id2;
   const char group_id[] = "group";
   id0 = AddNotificationWithNotifierId(group_id);
   id1 = AddNotificationWithNotifierId(group_id);
   id2 = AddNotificationWithNotifierId(group_id);
 
-  EXPECT_TRUE(MessageCenter::Get()->FindNotificationById(id0)->group_child());
-  EXPECT_TRUE(MessageCenter::Get()->FindNotificationById(id1)->group_child());
-  EXPECT_TRUE(MessageCenter::Get()->FindNotificationById(id2)->group_child());
+  EXPECT_TRUE(message_center->FindNotificationById(id0)->group_child());
+  EXPECT_TRUE(message_center->FindNotificationById(id1)->group_child());
+  EXPECT_TRUE(message_center->FindNotificationById(id2)->group_child());
 
   std::string id_parent = id0 + kIdSuffixForGroupContainerNotification;
+  EXPECT_TRUE(message_center->FindNotificationById(id_parent)->group_parent());
+}
+
+TEST_F(NotificationViewControllerTest, BasicRemoval) {
+  std::string id0, id1, id2;
+  const char group_id[] = "group";
+  id0 = AddNotificationWithNotifierId(group_id);
+  id1 = AddNotificationWithNotifierId(group_id);
+  id2 = AddNotificationWithNotifierId(group_id);
+
+  std::string id_parent = id0 + kIdSuffixForGroupContainerNotification;
+  // Group notification should stay intact if a single group notification is
+  // removed.
+  MessageCenter::Get()->RemoveNotification(id1, true);
+  EXPECT_TRUE(
+      MessageCenter::Get()->FindNotificationById(id_parent)->group_parent());
+
+  // Adding and removing a non group notification should have no impact.
+  std::string tmp = AddNotificationWithNotifierId("tmp");
+  MessageCenter::Get()->RemoveNotification(tmp, true);
+
+  EXPECT_TRUE(MessageCenter::Get()->FindNotificationById(id0)->group_child());
+  EXPECT_TRUE(MessageCenter::Get()->FindNotificationById(id2)->group_child());
   EXPECT_TRUE(
       MessageCenter::Get()->FindNotificationById(id_parent)->group_parent());
 }
@@ -190,8 +233,62 @@
   ASSERT_FALSE(MessageCenter::Get()->HasPopupNotifications());
 }
 
-// TODO(crbug/1223697) Tests TBA:
-//  * Expected behavior on removal
-//  * All but 1 child notification removed from group
+TEST_F(NotificationViewControllerTest,
+       ConvertingGroupedNotificationToSingleNotificationAndBack) {
+  auto* message_center = MessageCenter::Get();
+  std::string id0, id1, id2;
+  const char group_id[] = "group";
+  id0 = AddNotificationWithNotifierId(group_id);
+  id1 = AddNotificationWithNotifierId(group_id);
+  id2 = AddNotificationWithNotifierId(group_id);
+
+  std::string parent_id = id0 + kIdSuffixForGroupContainerNotification;
+  EXPECT_TRUE(
+      MessageCenter::Get()->FindNotificationById(parent_id)->group_parent());
+
+  // Removing all but 1 notification should convert it back to a single
+  // notification and result in the removal of the parent notification.
+  message_center->RemoveNotification(id0, true);
+  message_center->RemoveNotification(id1, true);
+
+  auto* single_notification = message_center->FindNotificationById(id2);
+  EXPECT_FALSE(single_notification->group_child() ||
+               single_notification->group_parent());
+  EXPECT_FALSE(message_center->FindNotificationById(parent_id));
+
+  // Adding further notifications should create a new group with the parent id
+  // being derived from `id2`.
+  id0 = AddNotificationWithNotifierId(group_id);
+  id1 = AddNotificationWithNotifierId(group_id);
+
+  parent_id = id2 + kIdSuffixForGroupContainerNotification;
+  EXPECT_TRUE(message_center->FindNotificationById(parent_id));
+}
+
+TEST_F(NotificationViewControllerTest,
+       ConvertingRepopulatedParentToSingleNotification) {
+  auto* message_center = MessageCenter::Get();
+  std::string id0, id1, id2, id3, id4;
+  const char group_id[] = "group";
+  id0 = AddNotificationWithNotifierId(group_id);
+  id1 = AddNotificationWithNotifierId(group_id);
+  id2 = AddNotificationWithNotifierId(group_id);
+  id3 = AddNotificationWithNotifierId(group_id);
+
+  std::string parent_id = id0 + kIdSuffixForGroupContainerNotification;
+  RemoveAndMarkPopupAsShown(parent_id);
+
+  id4 = AddNotificationWithNotifierId(group_id);
+
+  message_center->RemoveNotification(id0, true);
+  message_center->RemoveNotification(id1, true);
+  message_center->RemoveNotification(id2, true);
+  message_center->RemoveNotification(id3, true);
+
+  auto* single_notification = MessageCenter::Get()->FindNotificationById(id4);
+  EXPECT_FALSE(single_notification->group_child() ||
+               single_notification->group_parent());
+  EXPECT_FALSE(MessageCenter::Get()->FindNotificationById(parent_id));
+}
 
 }  // namespace message_center
diff --git a/ui/message_center/public/cpp/notification.cc b/ui/message_center/public/cpp/notification.cc
index 571a40a..e37ac84d 100644
--- a/ui/message_center/public/cpp/notification.cc
+++ b/ui/message_center/public/cpp/notification.cc
@@ -139,6 +139,16 @@
   group_parent_ = true;
 }
 
+void Notification::ClearGroupChild() {
+  DCHECK(!group_parent_);
+  group_child_ = false;
+}
+
+void Notification::ClearGroupParent() {
+  DCHECK(!group_child_);
+  group_parent_ = false;
+}
+
 bool Notification::UseOriginAsContextMessage() const {
   return optional_fields_.context_message.empty() && origin_url_.is_valid() &&
          origin_url_.SchemeIsHTTPOrHTTPS();
diff --git a/ui/message_center/public/cpp/notification.h b/ui/message_center/public/cpp/notification.h
index 8dbc01f..5a19480 100644
--- a/ui/message_center/public/cpp/notification.h
+++ b/ui/message_center/public/cpp/notification.h
@@ -475,6 +475,14 @@
   // notifications that are part of its group.
   void SetGroupParent();
 
+  // Set `group_child_` to false so it's back to it's
+  // default state.
+  void ClearGroupChild();
+
+  // Set `group_parent_` to false so it's back to it's
+  // default state.
+  void ClearGroupParent();
+
 #if BUILDFLAG(IS_CHROMEOS_ASH)
   void set_system_notification_warning_level(
       SystemNotificationWarningLevel warning_level) {
diff --git a/ui/message_center/views/message_popup_collection.cc b/ui/message_center/views/message_popup_collection.cc
index 57f265c..e421adb 100644
--- a/ui/message_center/views/message_popup_collection.cc
+++ b/ui/message_center/views/message_popup_collection.cc
@@ -129,8 +129,8 @@
 
 MessageView* MessagePopupCollection::GetMessageViewForNotificationId(
     const std::string& notification_id) {
-  auto it =
-      std::find_if(popup_items_.begin(), popup_items_.end(), [&](auto child) {
+  auto it = std::find_if(
+      popup_items_.begin(), popup_items_.end(), [&](const auto& child) {
         return child.popup->message_view()->notification_id() ==
                notification_id;
       });
@@ -146,7 +146,7 @@
     const std::string& new_grouped_notification_id) {
   auto it = std::find_if(
       popup_items_.begin(), popup_items_.end(),
-      [&](auto popup) { return popup.id == ungrouped_notification_id; });
+      [&](const auto& popup) { return popup.id == ungrouped_notification_id; });
   if (it == popup_items_.end())
     return;
 
@@ -154,11 +154,26 @@
   it->popup->message_view()->set_notification_id(new_grouped_notification_id);
 }
 
+void MessagePopupCollection::ConvertGroupedNotificationViewToNotificationView(
+    const std::string& grouped_notification_id,
+    const std::string& new_single_notification_id) {
+  auto it = std::find_if(
+      popup_items_.begin(), popup_items_.end(),
+      [&](const auto& popup) { return popup.id == grouped_notification_id; });
+  if (it == popup_items_.end())
+    return;
+
+  it->id = new_single_notification_id;
+  it->popup->message_view()->set_notification_id(new_single_notification_id);
+}
+
 void MessagePopupCollection::OnNotificationAdded(
     const std::string& notification_id) {
 #if BUILDFLAG(IS_CHROMEOS_ASH)
-  if (ash::features::IsNotificationsRefreshEnabled())
+  if (ash::features::IsNotificationsRefreshEnabled() &&
+      !MessageCenter::Get()->IsMessageCenterVisible()) {
     NotificationViewController::OnNotificationAdded(notification_id);
+  }
 #endif
   // Should not call MessagePopupCollection::Update here. Because notification
   // may be removed before animation which is triggered by the previous
@@ -173,8 +188,10 @@
     const std::string& notification_id,
     bool by_user) {
 #if BUILDFLAG(IS_CHROMEOS_ASH)
-  if (ash::features::IsNotificationsRefreshEnabled())
+  if (ash::features::IsNotificationsRefreshEnabled() &&
+      !MessageCenter::Get()->IsMessageCenterVisible()) {
     NotificationViewController::OnNotificationRemoved(notification_id, by_user);
+  }
 #endif
 
   if (by_user) {
diff --git a/ui/message_center/views/message_popup_collection.h b/ui/message_center/views/message_popup_collection.h
index 1661b28..c78f0ec 100644
--- a/ui/message_center/views/message_popup_collection.h
+++ b/ui/message_center/views/message_popup_collection.h
@@ -63,6 +63,9 @@
   void ConvertNotificationViewToGroupedNotificationView(
       const std::string& ungrouped_notification_id,
       const std::string& new_grouped_notification_id) override;
+  void ConvertGroupedNotificationViewToNotificationView(
+      const std::string& grouped_notification_id,
+      const std::string& new_single_notification_id) override;
   void OnNotificationAdded(const std::string& notification_id) override;
   void OnNotificationRemoved(const std::string& notification_id,
                              bool by_user) override;
diff --git a/ui/views/accessibility/ax_window_obj_wrapper.cc b/ui/views/accessibility/ax_window_obj_wrapper.cc
index b1bc358..6607365 100644
--- a/ui/views/accessibility/ax_window_obj_wrapper.cc
+++ b/ui/views/accessibility/ax_window_obj_wrapper.cc
@@ -18,6 +18,7 @@
 #include "ui/aura/client/focus_client.h"
 #include "ui/aura/window_tree_host.h"
 #include "ui/aura/window_tree_host_platform.h"
+#include "ui/base/ime/text_input_client.h"
 #include "ui/compositor/layer.h"
 #include "ui/platform_window/platform_window.h"
 #include "ui/views/accessibility/ax_aura_obj_cache.h"
@@ -89,6 +90,14 @@
 
   if (is_root_window_)
     aura_obj_cache_->OnRootWindowObjCreated(window);
+
+  // This is a top level root window.
+  if (window->IsRootWindow() && !window->parent()) {
+    // On desktop aura there is one WindowTreeHost per top-level window.
+    aura::WindowTreeHost* window_tree_host = window->GetHost();
+    if (window_tree_host)
+      ime_observation_.Observe(window_tree_host->GetInputMethod());
+  }
 }
 
 AXWindowObjWrapper::~AXWindowObjWrapper() = default;
@@ -159,6 +168,18 @@
     }
   }
 #endif  // BUILDFLAG(IS_CHROMEOS_LACROS)
+  if (window_->IsRootWindow() && !window_->parent() && window_->GetHost()) {
+    ui::TextInputClient* client =
+        window_->GetHost()->GetInputMethod()->GetTextInputClient();
+    // Only set caret bounds if input caret is in an editable node.
+    if (client && client->GetTextInputType() != ui::TEXT_INPUT_TYPE_NONE) {
+      gfx::Rect caret_bounds_in_screen = client->GetCaretBounds();
+      out_node_data->AddIntListAttribute(
+          ax::mojom::IntListAttribute::kCaretBounds,
+          {caret_bounds_in_screen.x(), caret_bounds_in_screen.y(),
+           caret_bounds_in_screen.width(), caret_bounds_in_screen.height()});
+    }
+  }
 
   out_node_data->id = GetUniqueId();
   ax::mojom::Role role = window_->GetProperty(ui::kAXRoleOverride);
@@ -218,6 +239,11 @@
   return GetWindowName(window_);
 }
 
+void AXWindowObjWrapper::OnCaretBoundsChanged(
+    const ui::TextInputClient* client) {
+  FireEvent(ax::mojom::Event::kTreeChanged);
+}
+
 void AXWindowObjWrapper::OnWindowDestroyed(aura::Window* window) {
   aura_obj_cache_->Remove(window, nullptr);
 }
diff --git a/ui/views/accessibility/ax_window_obj_wrapper.h b/ui/views/accessibility/ax_window_obj_wrapper.h
index dda56e3..a866bed 100644
--- a/ui/views/accessibility/ax_window_obj_wrapper.h
+++ b/ui/views/accessibility/ax_window_obj_wrapper.h
@@ -15,13 +15,20 @@
 #include "ui/accessibility/platform/ax_unique_id.h"
 #include "ui/aura/window.h"
 #include "ui/aura/window_observer.h"
+#include "ui/base/ime/input_method.h"
+#include "ui/base/ime/input_method_observer.h"
 #include "ui/views/accessibility/ax_aura_obj_wrapper.h"
 
+namespace ui {
+class InputMethod;
+}
+
 namespace views {
 class AXAuraObjCache;
 
 // Describes a |Window| for use with other AX classes.
 class AXWindowObjWrapper : public AXAuraObjWrapper,
+                           public ui::InputMethodObserver,
                            public aura::WindowObserver {
  public:
   // |aura_obj_cache| and |window| must outlive this object.
@@ -38,6 +45,14 @@
   ui::AXNodeID GetUniqueId() const final;
   std::string ToString() const override;
 
+  // InputMethodObserver overrides.
+  void OnFocus() override {}
+  void OnBlur() override {}
+  void OnInputMethodDestroyed(const ui::InputMethod* input_method) override {}
+  void OnShowVirtualKeyboardIfEnabled() override {}
+  void OnTextInputStateChanged(const ui::TextInputClient* client) override {}
+  void OnCaretBoundsChanged(const ui::TextInputClient* client) override;
+
   // WindowObserver overrides.
   void OnWindowDestroyed(aura::Window* window) override;
   void OnWindowDestroying(aura::Window* window) override;
@@ -58,6 +73,8 @@
   // Fires an accessibility event.
   void FireEvent(ax::mojom::Event event_type);
 
+  gfx::Rect GetCaretBounds(const ui::TextInputClient* client);
+
   aura::Window* const window_;
 
   const bool is_root_window_;
@@ -71,6 +88,9 @@
 
   base::ScopedObservation<aura::Window, aura::WindowObserver> observation_{
       this};
+
+  base::ScopedObservation<ui::InputMethod, ui::InputMethodObserver>
+      ime_observation_{this};
 };
 
 }  // namespace views
diff --git a/ui/views/animation/animation_builder.cc b/ui/views/animation/animation_builder.cc
index 6089c11..a91c67b 100644
--- a/ui/views/animation/animation_builder.cc
+++ b/ui/views/animation/animation_builder.cc
@@ -46,6 +46,9 @@
   void OnLayerAnimationAborted(ui::LayerAnimationSequence* sequence) override;
   void OnLayerAnimationScheduled(ui::LayerAnimationSequence* sequence) override;
 
+ protected:
+  void OnDetachedFromSequence(ui::LayerAnimationSequence* sequence) override;
+
  private:
   using RepeatMap = base::flat_map<ui::LayerAnimationSequence*, int>;
   RepeatMap repeat_map_;
@@ -97,7 +100,6 @@
   if (running <= 1) {
     if (on_ended_)
       std::move(on_ended_).Run();
-    delete this;
   }
 }
 
@@ -132,6 +134,12 @@
     std::move(on_scheduled_).Run();
 }
 
+void AnimationBuilder::Observer::OnDetachedFromSequence(
+    ui::LayerAnimationSequence* sequence) {
+  if (attached_sequences().empty())
+    delete this;
+}
+
 struct AnimationBuilder::Value {
   base::TimeDelta start;
   std::unique_ptr<ui::LayerAnimationElement> element;
diff --git a/ui/views/animation/animation_builder_unittest.cc b/ui/views/animation/animation_builder_unittest.cc
index 0d90cdec..d32fd21c 100644
--- a/ui/views/animation/animation_builder_unittest.cc
+++ b/ui/views/animation/animation_builder_unittest.cc
@@ -9,8 +9,6 @@
 #include "ui/compositor/layer.h"
 #include "ui/compositor/layer_animator.h"
 #include "ui/compositor/layer_owner.h"
-#include "ui/compositor/property_change_reason.h"
-#include "ui/compositor/scoped_animation_duration_scale_mode.h"
 #include "ui/compositor/test/layer_animator_test_controller.h"
 #include "ui/compositor/test/test_layer_animation_delegate.h"
 #include "ui/gfx/geometry/rounded_corners_f.h"
@@ -46,12 +44,8 @@
   void Step(const base::TimeDelta& duration) {
     DCHECK_GT(duration, base::TimeDelta());
     for (const auto& controller : animator_controllers_) {
-      if (elapsed_.is_zero()) {
-        controller->StartThreadedAnimationsIfNeeded();
-        // We need this because StartThreadedAnimationsIfNeeded() sets sequence
-        // start time to Now() but does not update animator's step time.
-        controller->animator()->set_last_step_time(base::TimeTicks::Now());
-      }
+      controller->StartThreadedAnimationsIfNeeded(
+          controller->animator()->last_step_time());
       controller->Step(duration);
     }
     elapsed_ += duration;
@@ -159,4 +153,54 @@
   EXPECT_TRUE(ended);
 }
 
+TEST_F(AnimationBuilderTest, DelayedStart) {
+  TestAnimatibleLayerOwner* view = CreateTestLayerOwner();
+  ui::LayerAnimationDelegate* delegate = view->delegate();
+
+  constexpr auto kDelay = base::TimeDelta::FromSeconds(1);
+  constexpr auto kDuration = base::TimeDelta::FromSeconds(1);
+
+  {
+    // clang-format off
+    AnimationBuilder()
+      .Once()
+      .At(kDelay)
+      .SetDuration(kDuration)
+      .SetOpacity(view, 0.4f);
+    // clang-format on
+  }
+
+  // Original value before the animation steps.
+  EXPECT_FLOAT_EQ(delegate->GetOpacityForAnimation(), 1.0);
+  Step(kDelay);
+  // The animation on opacity is not yet started.
+  EXPECT_FLOAT_EQ(delegate->GetOpacityForAnimation(), 1.0);
+  Step(kDuration);
+  EXPECT_FLOAT_EQ(delegate->GetOpacityForAnimation(), 0.4f);
+}
+
+TEST_F(AnimationBuilderTest, TwoKeyFrame) {
+  TestAnimatibleLayerOwner* view = CreateTestLayerOwner();
+  ui::LayerAnimationDelegate* delegate = view->delegate();
+
+  constexpr auto kDuration = base::TimeDelta::FromSeconds(1);
+
+  {
+    AnimationBuilder()
+        .Once()
+        .SetDuration(kDuration)
+        .SetOpacity(view, 0.4f)
+        .Then()
+        .SetDuration(kDuration)
+        .SetOpacity(view, 0.9f);
+  }
+
+  // The animation on opacity is not yet started.
+  EXPECT_FLOAT_EQ(delegate->GetOpacityForAnimation(), 1.0);
+  Step(kDuration);
+  EXPECT_FLOAT_EQ(delegate->GetOpacityForAnimation(), 0.4f);
+  Step(kDuration);
+  EXPECT_FLOAT_EQ(delegate->GetOpacityForAnimation(), 0.9f);
+}
+
 }  // namespace views
diff --git a/ui/webui/resources/js/BUILD.gn b/ui/webui/resources/js/BUILD.gn
index 2d398138..aa316098 100644
--- a/ui/webui/resources/js/BUILD.gn
+++ b/ui/webui/resources/js/BUILD.gn
@@ -48,6 +48,8 @@
     "icon.js",
     "load_time_data.m.js",
     "plural_string_proxy.js",
+    "post_message_api_server.js",
+    "post_message_api_client.js",
     "search_highlight_utils.js",
     "test_loader.js",
     "test_loader_util.js",
@@ -98,6 +100,8 @@
     "event_tracker.m.js",
     "load_time_data.js",
     "parse_html_subset.m.js",
+    "post_message_api_server.m.js",
+    "post_message_api_client.m.js",
     "promise_resolver.m.js",
     "util.m.js",
   ]
@@ -131,6 +135,8 @@
     ":list_property_update_behavior",
     ":load_time_data",
     ":parse_html_subset",
+    ":post_message_api_client",
+    ":post_message_api_server",
     ":promise_resolver",
     ":util",
     ":web_ui_listener_behavior",
@@ -188,6 +194,12 @@
 js_library("parse_html_subset") {
 }
 
+js_library("post_message_api_server") {
+}
+
+js_library("post_message_api_client") {
+}
+
 js_library("promise_resolver") {
   deps = [ ":assert" ]
 }
@@ -221,6 +233,8 @@
     "i18n_behavior.js",
     "list_property_update_behavior.js",
     "parse_html_subset.js",
+    "post_message_api_server.js",
+    "post_message_api_client.js",
     "promise_resolver.js",
     "util.js",
     "web_ui_listener_behavior.js",
@@ -256,6 +270,8 @@
     ":load_time_data.m",
     ":parse_html_subset.m",
     ":plural_string_proxy",
+    ":post_message_api_client.m",
+    ":post_message_api_server.m",
     ":promise_resolver.m",
     ":search_highlight_utils",
     ":util.m",
@@ -319,6 +335,18 @@
   deps = [ ":cr.m" ]
 }
 
+js_library("post_message_api_server.m") {
+  sources =
+      [ "$root_gen_dir/ui/webui/resources/js/post_message_api_server.m.js" ]
+  extra_deps = [ ":modulize_local" ]
+}
+
+js_library("post_message_api_client.m") {
+  sources =
+      [ "$root_gen_dir/ui/webui/resources/js/post_message_api_client.m.js" ]
+  extra_deps = [ ":modulize_local" ]
+}
+
 js_library("promise_resolver.m") {
   sources = [ "$root_gen_dir/ui/webui/resources/js/promise_resolver.m.js" ]
   deps = [ ":assert.m" ]
diff --git a/ui/webui/resources/js/post_message_api_client.js b/ui/webui/resources/js/post_message_api_client.js
new file mode 100644
index 0000000..b8c2627
--- /dev/null
+++ b/ui/webui/resources/js/post_message_api_client.js
@@ -0,0 +1,148 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+/**
+ * Class that provides the functionality for talking to a PostMessageAPIServer
+ * over the postMessage API.  This should be subclassed and the methods in the
+ * server that the client needs to access should be provided in methodList.
+ *
+ */
+export class PostMessageAPIClient {
+  /**
+   * @param {!Array<string>} methodList The list of methods accessible via the
+   *     client.
+   * @param {!string} serverOriginURLFilter  Only messages from this origin
+   *     will be accepted.
+   */
+  constructor(methodList, serverOriginURLFilter) {
+    /**
+     * @private @const {!string} Filter to use to validate
+     * the origin of received messages.  The origin of messages
+     * must exactly match this value.
+     */
+    this.serverOriginURLFilter_ = serverOriginURLFilter;
+
+    /**
+     * The parent window.
+     * @private {Window}
+     */
+    this.parentWindow_ = null;
+    /*
+     * @private {number}
+     */
+    this.nextMethodId_ = 0;
+    /**
+     * Map of methods awaiting a response.
+     * @private {!Map}
+     */
+    this.methodsAwaitingResponse_ = new Map;
+    /**
+     * Function property that tracks whether client has
+     * been initialized by the server.
+     * @private {Function}
+     */
+    this.boundOnInitialize_ = this.onInitialize_.bind(this);
+
+    // Wait for an init message from the server.
+    window.addEventListener('message', this.boundOnInitialize_);
+  }
+
+  /**
+   * Virtual method called when the client is initialized and it knows the
+   * server that it is communicating with. This method should be overwritten by
+   * subclasses which would like to know when initialization is done.
+   */
+  onInitialized() {}
+
+  //
+  // Private implementation:
+  //
+
+  /**
+   * Handles initialization event sent from the server to establish
+   * communication.
+   * @private
+   * @param {!Event} event  An event received when the initialization message is
+   *     sent from the server.
+   */
+  onInitialize_(event) {
+    if (!this.originMatchesFilter(event.origin)) {
+      console.error(
+          'Initialization event received from non-authorized origin: ' +
+          event.origin);
+      return;
+    }
+    this.parentWindow_ = event.source;
+    this.parentWindow_.postMessage('init', this.serverOriginURLFilter_);
+    window.removeEventListener('message', this.boundOnInitialize_);
+    this.boundOnInitialize_ = null;
+    window.addEventListener('message', this.onMessage_.bind(this));
+    this.onInitialized();
+  }
+
+  /**
+   * Determine if the specified server origin URL matches the origin filter.
+   * @param {!string} origin The origin URL to match with the filter.
+   * @return {boolean}  whether the specified origin matches the filter.
+   */
+  originMatchesFilter(origin) {
+    return (new URL(origin)).toString() === this.serverOriginURLFilter_;
+  }
+
+  /**
+   * Handles postMessage events sent from the server.
+   * @param {Event} event  An event received from the server via the postMessage
+   *     API.
+   */
+  onMessage_(event) {
+    if (!this.originMatchesFilter(event.origin)) {
+      console.error(
+          'Message received from non-authorized origin: ' + event.origin);
+      return;
+    }
+    if (event.source !== this.parentWindow_) {
+      console.error('discarding event whose source is not the parent window');
+      return;
+    }
+    if (!this.methodsAwaitingResponse_.has(event.data.methodId)) {
+      if (event.data === 'init') {
+        console.log('Received init message after initialization is complete.');
+        this.parentWindow_.postMessage('init', this.serverOriginURLFilter_);
+        return;
+      } else {
+        console.error('discarding event method is not waiting for a response');
+        return;
+      }
+    }
+    const method = this.methodsAwaitingResponse_.get(event.data.methodId);
+    this.methodsAwaitingResponse_.delete(event.data.methodId);
+    method(event.data.result);
+  }
+
+  /**
+   * Converts a function call with arguments into a postMessage event
+   * and sends it to the server via the postMessage API.
+   * @param {string} fn  The function to call.
+   * @param {!Array<Object>} args The arguments to pass to the function.
+   * @return {!Promise} A promise capturing the executing of the function.
+   */
+  callApiFn(fn, args) {
+    const newMethodId = this.nextMethodId_++;
+    const promise = new Promise((resolve, reject) => {
+      if (!this.parentWindow_) {
+        reject('No parent window defined');
+      }
+      this.parentWindow_.postMessage(
+          {
+            methodId: newMethodId,
+            fn: fn,
+            args: args,
+          },
+          this.serverOriginURLFilter_);
+
+      this.methodsAwaitingResponse_.set(newMethodId, resolve);
+    });
+    return promise;
+  }
+}
diff --git a/ui/webui/resources/js/post_message_api_server.js b/ui/webui/resources/js/post_message_api_server.js
new file mode 100644
index 0000000..ffe1159
--- /dev/null
+++ b/ui/webui/resources/js/post_message_api_server.js
@@ -0,0 +1,231 @@
+// 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.
+
+/**
+ *  Initialization retry wait in milliseconds (subject to exponential backoff).
+ */
+const INITIALIZATION_ATTEMPT_RETRY_WAIT_MS = 100;
+
+/**
+ * Maximum number of initialization attempts before resetting the
+ * initialization attempt cycle.  With exponential backoff, this works out
+ * a maximum wait of 25 seconds on the 8th attempt before restarting.
+ */
+const MAX_INITIALIZATION_ATTEMPTS = 8;
+
+/**
+ * Class that provides the functionality for talking to a client
+ * over the PostMessageAPI.  This should be subclassed and the
+ * methods provided in methodList should be implemented as methods
+ * of the subclass.
+ */
+export class PostMessageAPIServer {
+  constructor(clientElement, methodList, targetURL, messageOriginURLFilter) {
+    /**
+     * The Window type element to which this server will listen for messages,
+     * probably a <webview>, but also could be an <iframe> or a browser window
+     * object.
+     * @private @const {!Element}
+     */
+    this.clientElement_ = clientElement;
+
+    /**
+     * The guest URL embedded in the element above. Used for message targeting.
+     * This should be same as the URL loaded in the clientElement, i.e. the
+     * "src" attribute of a <webview>.
+     * @private @const {!URL}
+     */
+    this.targetURL_ = new URL(targetURL);
+
+    /**
+     * Incoming messages received from origin URLs without this prefix
+     * will not be accepted. This should be used to restrict the API access
+     * to the intended guest content.
+     * @private @const {!URL}
+     */
+    this.messageOriginURLFilter_ = new URL(messageOriginURLFilter);
+
+    /**
+     * Map that stores references to the methods implemented by the API.
+     * @private {!Map<string, function(!Array):?>}
+     */
+    this.apiFns_ = new Map();
+
+    /**
+     *  The ID of the timeout set before checking whether initialization has
+     * taken place yet.
+     * @private {number}
+     */
+    this.initialization_timeout_id_ = 0;
+
+    /**
+     * Indicates how many attempts have been made to initialize the channel.
+     * @private {number}
+     */
+    this.numInitializationAttempts_ = 0;
+
+    /**
+     * Indicates whether the communication channel between this server and the
+     * WebView has been established.
+     * @private {boolean}
+     */
+    this.isInitialized_ = false;
+
+    if (this.clientElement_.tagName === 'IFRAME') {
+      this.clientElement_.onload = this.onLoad_.bind(this);
+    } else {
+      this.clientElement_.addEventListener(
+          'contentload', this.onLoad_.bind(this));
+    }
+
+    // Listen for events.
+    window.addEventListener('message', (event) => {
+      this.onMessage_(event);
+    });
+  }
+
+  /**
+   * Registers the specified method name with the specified
+   * function.
+   *
+   * @param {!string} methodName name of the method to register.
+   * @param {!function(!Array):?} method The function to associate with the
+   *     name.
+   */
+  registerMethod(methodName, method) {
+    this.apiFns_.set(methodName, method);
+  }
+
+  /**
+   * Send initialization message to client element.
+   */
+  initialize() {
+    if (this.isInitialized_ ||
+        !this.originMatchesFilter(this.clientElement_.src)) {
+      return;
+    }
+
+    if (this.numInitializationAttempts_ < MAX_INITIALIZATION_ATTEMPTS) {
+      // Tell the embedded webviews whose src matches our origin to initialize
+      // by sending it a message, which will include a handle for it to use to
+      // send messages back.
+      console.log(
+          'Sending init message to guest content,  attempt # :' +
+          this.numInitializationAttempts_);
+
+      this.clientElement_.contentWindow.postMessage(
+          'init', this.targetURL_.toString());
+
+      // Set timeout to check if initialization message has been received using
+      // exponential backoff.
+      this.initialization_timeout_id_ = setTimeout(
+          () => {
+            // If the timeout id is non-zero, that indicates that initialization
+            // hasn't succeeded yet, so  try to initialize again.
+            this.initialize();
+          },
+          INITIALIZATION_ATTEMPT_RETRY_WAIT_MS *
+              (2 ** this.numInitializationAttempts_));
+
+      this.numInitializationAttempts_++;
+    } else {
+      // Exponential backoff has maxed out. Show error page if present.
+      this.onInitializationError(this.clientElement_.src);
+    }
+  }
+
+  /**
+   *  Virtual method to be overridden by implementations of this class to notify
+   * them that we were unable to initialize communication channel with the
+   * `clientElement_`.
+   *
+   * @param {!string} origin The origin URL that was not able to initialize
+   *     communication.
+   */
+  onInitializationError(origin) {}
+
+  /**
+   * Determines if the specified origin matches the origin filter.
+   * @param {!string} origin The origin URL to match with the filter.
+   * @return {boolean}  whether the specified origin matches the filter.
+   */
+  originMatchesFilter(origin) {
+    const originURL = new URL(origin);
+
+    // We allow the pathname portion of the URL to be a prefix filter,
+    // to permit for different paths communicating with this server.
+    return originURL.protocol === this.messageOriginURLFilter_.protocol &&
+        originURL.host === this.messageOriginURLFilter_.host &&
+        originURL.pathname.startsWith(this.messageOriginURLFilter_.pathname);
+  }
+
+  /**
+   * Handles postMessage events from the client.
+   * @private
+   * @param {Event} event  The postMessage event to handle.
+   */
+  onMessage_(event) {
+    if (!this.originMatchesFilter(event.origin)) {
+      console.log('Message received from unauthorized origin: ' + event.origin);
+      return;
+    }
+
+    if (event.data === 'init') {
+      if (this.initialization_timeout_id_) {
+        // Cancel the current init timeout, and signal to the initialization
+        // polling process that we have received an init message from the guest
+        // content, so it doesn't reschedule the timer.
+        clearTimeout(this.initialization_timeout_id_);
+        this.initialization_timeout_id_ = 0;
+      }
+
+      this.isInitialized_ = true;
+      return;
+    }
+    // If we have gotten this far, we have received a message from a trusted
+    // origin, and we should try to process it.  We can't gate this on whether
+    // the channel is initialized, because we can receive events out of order,
+    // and method calls can be received before the init event. Essentially, we
+    // should treat the channel as being potentially as soon as we send 'init'
+    // to the guest content.
+    const methodId = event.data.methodId;
+    const fn = event.data.fn;
+    const args = event.data.args || [];
+
+    if (!this.apiFns_.has(fn)) {
+      console.error('Unknown function requested: ' + fn);
+      return;
+    }
+
+    const sendMessage = (methodId, result) => {
+      this.clientElement_.contentWindow.postMessage(
+          {
+            methodId: methodId,
+            result: result,
+          },
+          this.targetURL_.toString());
+    };
+
+    // Some methods return a promise and some don't. If we have a promise,
+    // we resolve it first, otherwise we send the result directly (e.g., for
+    // void functions we send 'undefined').
+    const result = this.apiFns_.get(fn)(args);
+    if (result instanceof Promise) {
+      result.then((result) => sendMessage(methodId, result));
+    } else {
+      sendMessage(methodId, result);
+    }
+  }
+
+  /**
+   * Reinitiates the connection when the content inside the clientElement
+   * reloads.
+   * @private
+   */
+  onLoad_() {
+    this.numInitializationAttempts_ = 0;
+    this.isInitialized_ = false;
+    this.initialize();
+  }
+}
diff --git a/weblayer/browser/android/javatests/skew/expectations.txt b/weblayer/browser/android/javatests/skew/expectations.txt
index f984a1ef..ab44db6 100644
--- a/weblayer/browser/android/javatests/skew/expectations.txt
+++ b/weblayer/browser/android/javatests/skew/expectations.txt
@@ -5,22 +5,17 @@
 # with versions less than or equal to $VERSION of the implementation.
 #
 # These lines are not comments! They define the set of known tags and other information.
-# tags: [ client_lte_94 ]
+# tags: [ client_lte_91 client_lte_94 ]
 # 'all' disables the test from any skew test.
 # tags: [ all ]
 # results: [ Skip ]
 # conflicts_allowed: True
 
-crbug.com/1238481 [ all ] org.chromium.weblayer.test.TabTest#testRotationDoesntChangeVisibility [ Skip ]
-crbug.com/1239026 [ all ] org.chromium.weblayer.test.TabCallbackTest#testDismissTransientUi [ Skip ]
-crbug.com/1239028 [ all ] org.chromium.weblayer.test.MediaSessionTest#basic [ Skip ]
-crbug.com/1239034 [ all ] org.chromium.weblayer.test.TabCallbackTest#testTabModalOverlay [ Skip ]
-crbug.com/1225662 [ all ] org.chromium.weblayer.test.NavigationTest#testDestroyTabWithModalDialog [ Skip ]
-crbug.com/1239002 [ all ] org.chromium.weblayer.test.FullscreenSizeTest#testOsk [ Skip ]
+# No real changes to weblayer APIs, changes should only affect chrome-internal behavior around
+# navigation which the test depends on.
+crbug.com/1196803 [ client_lte_91 ] org.chromium.weblayer.test.ExternalNavigationTest#testExternalIntentWithNoRedirectInBrowserStartupInIncognitoBlockedWhenBackgroundLaunchesAllowedAndUserForbids [ Skip ]
 
-# This test was flaky, the fix was in the associated html file which landed in
-# 94.
-crbug.com/1222694 [ client_lte_94 ] org.chromium.weblayer.test.FullscreenCallbackTest* [ Skip ]
+crbug.com/1239002 [ all ] org.chromium.weblayer.test.FullscreenSizeTest#testOsk [ Skip ]
 
 # These tests were flaky, the fix was in the associated html files which landed
 # in 94.
@@ -33,10 +28,15 @@
 crbug.com/1191751 [ client_lte_94 ] org.chromium.weblayer.test.TabListCallbackTest#testDestroyTab [ Skip ]
 crbug.com/1191751 [ client_lte_94 ] org.chromium.weblayer.test.TabListCallbackTest#testActiveTabChanged [ Skip ]
 crbug.com/1191751 [ client_lte_94 ] org.chromium.weblayer.test.FindInPageTest#testHideOnNewTab [ Skip ]
+crbug.com/1239034 [ client_lte_94 ] org.chromium.weblayer.test.TabCallbackTest#testTabModalOverlay [ Skip ]
+crbug.com/1239026 [ client_lte_94 ] org.chromium.weblayer.test.TabCallbackTest#testDismissTransientUi [ Skip ]
+crbug.com/1191751 [ client_lte_94 ] org.chromium.weblayer.test.NavigationTest#testRepostConfirmation [ Skip ]
+crbug.com/1225662 [ client_lte_94 ] org.chromium.weblayer.test.NavigationTest#testDestroyTabWithModalDialog [ Skip ]
+crbug.com/1238481 [ client_lte_94 ] org.chromium.weblayer.test.TabTest#testRotationDoesntChangeVisibility [ Skip ]
+crbug.com/1239028 [ client_lte_94 ] org.chromium.weblayer.test.MediaSessionTest#basic [ Skip ]
 
 # Bulk disable to get bot green.
 crbug.com/1191751 [ all ] org.chromium.weblayer.test.InputTypesTest* [ Skip ]
-crbug.com/1191751 [ all ] org.chromium.weblayer.test.NavigationTest#testRepostConfirmation [ Skip ]
 crbug.com/1191751 [ all ] org.chromium.weblayer.test.TabCallbackTest#testScrollNotificationDirectionChange [ Skip ]
 crbug.com/1191751 [ all ] org.chromium.weblayer.test.TabTest#testBeforeUnload [ Skip ]
 crbug.com/1191751 [ all ] org.chromium.weblayer.test.DownloadCallbackTest#testBasic [ Skip ]
diff --git a/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/MediaSessionTest.java b/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/MediaSessionTest.java
index d187ca4..30f6cc6 100644
--- a/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/MediaSessionTest.java
+++ b/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/MediaSessionTest.java
@@ -23,7 +23,6 @@
 import org.chromium.base.test.util.CommandLineFlags;
 import org.chromium.base.test.util.Criteria;
 import org.chromium.base.test.util.CriteriaHelper;
-import org.chromium.base.test.util.DisabledTest;
 import org.chromium.base.test.util.MinAndroidSdkLevel;
 import org.chromium.weblayer.shell.InstrumentationActivity;
 
@@ -42,7 +41,6 @@
     @Test
     @MediumTest
     @MinAndroidSdkLevel(Build.VERSION_CODES.M)
-    @DisabledTest(message = "crbug.com/1239028")
     public void basic() throws Throwable {
         mActivity = mActivityTestRule.launchShellWithUrl(
                 mActivityTestRule.getTestDataURL("media_session.html"));
diff --git a/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/NavigationTest.java b/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/NavigationTest.java
index 4df8bde..86317ab7 100644
--- a/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/NavigationTest.java
+++ b/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/NavigationTest.java
@@ -1169,7 +1169,6 @@
     // This is a regression test for https://crbug.com/1121388.
     @Test
     @SmallTest
-    @DisabledTest(message = "crbug.com/1225662")
     public void testDestroyTabWithModalDialog() throws Exception {
         // Load a page with a form.
         InstrumentationActivity activity =
diff --git a/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/TabCallbackTest.java b/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/TabCallbackTest.java
index cf66eb1..605c730 100644
--- a/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/TabCallbackTest.java
+++ b/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/TabCallbackTest.java
@@ -20,7 +20,6 @@
 import org.chromium.base.test.util.Criteria;
 import org.chromium.base.test.util.CriteriaHelper;
 import org.chromium.base.test.util.DisableIf;
-import org.chromium.base.test.util.DisabledTest;
 import org.chromium.content_public.browser.test.util.TestThreadUtils;
 import org.chromium.content_public.browser.test.util.TestTouchUtils;
 import org.chromium.weblayer.ContextMenuParams;
@@ -154,7 +153,6 @@
         file.delete();
     }
 
-    @MinWebLayerVersion(88)
     @Test
     @SmallTest
     @DisableIf.
@@ -174,7 +172,6 @@
         waitForFileExist(tempDownloadDirectory, "lorem_ipsum.txt");
     }
 
-    @MinWebLayerVersion(88)
     @Test
     @SmallTest
     @DisableIf.
@@ -196,7 +193,6 @@
 
     @Test
     @SmallTest
-    @DisabledTest(message = "crbug.com/1239034")
     public void testTabModalOverlay() throws TimeoutException {
         String pageUrl = mActivityTestRule.getTestDataURL("alert.html");
         InstrumentationActivity activity = mActivityTestRule.launchShellWithUrl(pageUrl);
@@ -230,7 +226,6 @@
 
     @Test
     @SmallTest
-    @DisabledTest(message = "crbug.com/1239026")
     public void testDismissTransientUi() throws TimeoutException {
         String pageUrl = mActivityTestRule.getTestDataURL("alert.html");
         InstrumentationActivity activity = mActivityTestRule.launchShellWithUrl(pageUrl);
diff --git a/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/TabListCallbackTest.java b/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/TabListCallbackTest.java
index 5791810..e5ead80 100644
--- a/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/TabListCallbackTest.java
+++ b/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/TabListCallbackTest.java
@@ -12,7 +12,6 @@
 import org.junit.runner.RunWith;
 
 import org.chromium.base.test.util.CallbackHelper;
-import org.chromium.base.test.util.DisabledTest;
 import org.chromium.content_public.browser.test.util.TestThreadUtils;
 import org.chromium.weblayer.Browser;
 import org.chromium.weblayer.Tab;
@@ -161,7 +160,6 @@
 
     @Test
     @SmallTest
-    @DisabledTest(message = "crbug.com/1239032")
     public void testCallbackInvokedWhenTabClosedViaWebContents() {
         initialize("new_tab_then_close.html");
 
diff --git a/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/TabTest.java b/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/TabTest.java
index 2636353..6789c8e 100644
--- a/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/TabTest.java
+++ b/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/TabTest.java
@@ -21,7 +21,6 @@
 import org.chromium.base.test.util.CallbackHelper;
 import org.chromium.base.test.util.Criteria;
 import org.chromium.base.test.util.CriteriaHelper;
-import org.chromium.base.test.util.DisabledTest;
 import org.chromium.content_public.browser.test.util.TestThreadUtils;
 import org.chromium.weblayer.ActionModeCallback;
 import org.chromium.weblayer.ActionModeItemType;
@@ -266,8 +265,6 @@
 
     @Test
     @SmallTest
-    @MinWebLayerVersion(88) // Bug fix in 88.
-    @DisabledTest(message = "crbug.com/1238481")
     // This is a regression test for https://crbug.com/1075744 .
     public void testRotationDoesntChangeVisibility() throws Exception {
         String url = mActivityTestRule.getTestDataURL("rotation.html");
@@ -289,7 +286,6 @@
 
     @Test
     @SmallTest
-    @MinWebLayerVersion(88)
     public void setFloatingActionModeOverride() throws Exception {
         mActivity = mActivityTestRule.launchShellWithUrl("about:blank");
         TestThreadUtils.runOnUiThreadBlocking(() -> {
@@ -306,7 +302,6 @@
 
     @Test
     @SmallTest
-    @MinWebLayerVersion(88) // New API added in 88.
     public void testWillAutomaticallyReloadAfterCrash() throws Exception {
         mActivity = mActivityTestRule.launchShellWithUrl("about:blank");
         Browser browser2 = TestThreadUtils.runOnUiThreadBlocking(() -> {
diff --git a/weblayer/browser/java/org/chromium/weblayer_private/BrowserViewController.java b/weblayer/browser/java/org/chromium/weblayer_private/BrowserViewController.java
index d83b2c3..e2e1000 100644
--- a/weblayer/browser/java/org/chromium/weblayer_private/BrowserViewController.java
+++ b/weblayer/browser/java/org/chromium/weblayer_private/BrowserViewController.java
@@ -512,7 +512,8 @@
                         .with(ModalDialogProperties.CONTROLLER, dialogController)
                         .with(ModalDialogProperties.TITLE, resources,
                                 R.string.http_post_warning_title)
-                        .with(ModalDialogProperties.MESSAGE, resources, R.string.http_post_warning)
+                        .with(ModalDialogProperties.MESSAGE,
+                                resources.getString(R.string.http_post_warning))
                         .with(ModalDialogProperties.POSITIVE_BUTTON_TEXT, resources,
                                 R.string.http_post_warning_resend)
                         .with(ModalDialogProperties.NEGATIVE_BUTTON_TEXT, resources,
diff --git a/weblayer/test/data/alert.html b/weblayer/test/data/alert.html
index bd547ce..ce622e6 100644
--- a/weblayer/test/data/alert.html
+++ b/weblayer/test/data/alert.html
@@ -1,10 +1,5 @@
 <html>
-  <body>
+  <body onclick="window.alert('tab modal overlay');">
     <p id='x'>XXXX</p>
   </body>
-  <script>
-    document.addEventListener('touchend', function(e) {
-      window.alert('tab modal overlay');
-    }, false);
-  </script>
 </html>
diff --git a/weblayer/test/data/form.html b/weblayer/test/data/form.html
index 4511dcf..a957bd1 100644
--- a/weblayer/test/data/form.html
+++ b/weblayer/test/data/form.html
@@ -1,14 +1,9 @@
 <html>
-  <body>
+  <body onclick="document.forms['form1'].submit();">
     <form id="form1" action="simple_page.html" method="post">
       <input type="text" name="name" value="Name"><br>
       <input type="text" name="address" value="Address"><br>
       <input type="text" name="city" value="City"><br>
     </form>
   </body>
-  <script>
-    document.addEventListener('touchend', function(e) {
-      document.forms['form1'].submit();
-    }, false);
-  </script>
 </html>
diff --git a/weblayer/test/data/media_session.html b/weblayer/test/data/media_session.html
index 565b383..23433240 100644
--- a/weblayer/test/data/media_session.html
+++ b/weblayer/test/data/media_session.html
@@ -1,13 +1,12 @@
 <html>
-  <body>
-    <p>Some text so the document is ready for input.</p>
-  </body>
   <script>
-    let audio = document.createElement('audio');
     function playAudio() {
+      let audio = document.createElement('audio');
       audio.src = 'test_audio.ogg';
       audio.play();
     }
-    document.addEventListener('touchend', function(e) { playAudio(); }, false);
   </script>
+  <body onclick="playAudio();">
+    <p>Some text so the document is ready for input.</p>
+  </body>
 </html>
diff --git a/weblayer/test/data/new_tab_then_close.html b/weblayer/test/data/new_tab_then_close.html
index f5c30a00..bc22379 100644
--- a/weblayer/test/data/new_tab_then_close.html
+++ b/weblayer/test/data/new_tab_then_close.html
@@ -1,7 +1,4 @@
 <html>
-  <body>
-    <p>some content</p>
-  </body>
   <script>
     var openedWindow = null;
     function openNewWindowOrClose() {
@@ -12,8 +9,8 @@
         openedWindow = null;
       }
     }
-
-    document.addEventListener('touchend',
-                              function(e) { openNewWindowOrClose(); }, false);
   </script>
+  <body onclick="openNewWindowOrClose();">
+    <p>some content</p>
+  </body>
 </html>
diff --git a/weblayer/test/data/rotation.html b/weblayer/test/data/rotation.html
index 4dbbade6..fc8ab98 100644
--- a/weblayer/test/data/rotation.html
+++ b/weblayer/test/data/rotation.html
@@ -1,14 +1,23 @@
 <html>
-  <body style="height:5000px">
-    <p>A (mostly) empty page.</p>
-  </body>
   <script>
     var gotHide = false;
     var gotOrientationChange = false;
+    var attachedListeners = false;
     async function doRequestFullscreen() {
         return document.documentElement.requestFullscreen();
     }
     async function toggleFullscreen() {
+      if (!attachedListeners) {
+        attachedListeners = true;
+        document.addEventListener("visibilitychange", function() {
+          if (document.visibilityState !== 'visible') {
+            gotHide = true;
+          }
+        });
+        window.addEventListener("orientationchange", function() {
+          gotOrientationChange = true;
+        });
+      }
       if (!document.fullscreenElement) {
           await doRequestFullscreen();
           await screen.orientation.lock("landscape");
@@ -16,14 +25,8 @@
         document.exitFullscreen();
       }
     }
-    document.addEventListener("visibilitychange", function() {
-      if (document.visibilityState !== 'visible') {
-        gotHide = true;
-      }
-    });
-    window.addEventListener("orientationchange", function() {
-      gotOrientationChange = true;
-    });
-    document.addEventListener('touchend', function(e) { toggleFullscreen(); }, false);
   </script>
+  <body style="height:5000px" onclick="toggleFullscreen();">
+    <p>A (mostly) empty page.</p>
+  </body>
 </html>