diff --git a/DEPS b/DEPS
index a6b111a5..5a46372 100644
--- a/DEPS
+++ b/DEPS
@@ -300,7 +300,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': '31f895f28df4179dda94325f1f967a3b0a3e0105',
+  'skia_revision': 'a91c70dac27ecebd6365f90e7098a977f6878686',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling V8
   # and whatever else without interference from each other.
@@ -308,7 +308,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling ANGLE
   # and whatever else without interference from each other.
-  'angle_revision': '383783de3e689fe0b9976d1e2cf5439a0ff972a2',
+  'angle_revision': 'c04ad8e025ee06b64b52ff7009a19fb7c1e720a4',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling SwiftShader
   # and whatever else without interference from each other.
@@ -316,7 +316,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling PDFium
   # and whatever else without interference from each other.
-  'pdfium_revision': '448f3994dc496591d1ba8bf8e75bcd225ba1aab5',
+  'pdfium_revision': '4a5e28a78c2dda8033481a0b351953dceb8116fb',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling BoringSSL
   # and whatever else without interference from each other.
@@ -327,7 +327,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling Fuchsia sdk
   # and whatever else without interference from each other.
-  'fuchsia_version': 'version:12.20230519.1.1',
+  'fuchsia_version': 'version:12.20230519.3.1',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling google-toolbox-for-mac
   # and whatever else without interference from each other.
@@ -351,7 +351,7 @@
   # 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': '115e927540dba128980dd734dadeb06aa7b0f4d8',
+  'freetype_revision': '2342a03a9d3e58a82e698fc4074dc2e5f95c4e26',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling freetype
   # and whatever else without interference from each other.
@@ -387,7 +387,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': '16ecfae54bf4f46c05de9a0dd3b47ed5d7916ae6',
+  'devtools_frontend_revision': '90b0454e4a7f015fe198ea740588e0f9c4ebd217',
   # 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.
@@ -427,7 +427,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': '74dcafc17b116a0a551cbb6cd54db74e996797fa',
+  'dawn_revision': '3d3584793b226989b8addb5fbac9db6cf67482c0',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
@@ -803,7 +803,7 @@
 
   'src/clank': {
     'url': Var('chrome_git') + '/clank/internal/apps.git' + '@' +
-    'a8f5ec6bcc79fe929145d05327eca1ed6d43df27',
+    '6c538a09a151061ecb4ad4bc21eeb51194785875',
     'condition': 'checkout_android and checkout_src_internal',
   },
 
@@ -1213,7 +1213,7 @@
 
   # For Linux and Chromium OS.
   'src/third_party/cros_system_api': {
-      'url': Var('chromium_git') + '/chromiumos/platform2/system_api.git' + '@' + '627c0fcc633d7abf63c302482ad4c094c9a8f757',
+      'url': Var('chromium_git') + '/chromiumos/platform2/system_api.git' + '@' + 'c5ca42c6955b899f41974c668069c1b63a179276',
       'condition': 'checkout_linux',
   },
 
@@ -1227,13 +1227,13 @@
   },
 
   'src/third_party/depot_tools':
-    Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + '9c32c9240c80469faed85dcfd481db29b89329af',
+    Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + '4d73c057d82ac5994d992f0b1f57ca6d513c3554',
 
   'src/third_party/devtools-frontend/src':
     Var('chromium_git') + '/devtools/devtools-frontend' + '@' + Var('devtools_frontend_revision'),
 
   'src/third_party/devtools-frontend-internal': {
-      'url': Var('chrome_git') + '/devtools/devtools-internal.git' + '@' + '3666f7fff0ccb83f18b353fb732a08434fd86a31',
+      'url': Var('chrome_git') + '/devtools/devtools-internal.git' + '@' + '05a1a955936a44fd72b886e7bb80113d390b34bf',
     'condition': 'checkout_src_internal',
   },
 
@@ -1700,7 +1700,7 @@
     Var('pdfium_git') + '/pdfium.git' + '@' +  Var('pdfium_revision'),
 
   'src/third_party/perfetto':
-    Var('android_git') + '/platform/external/perfetto.git' + '@' + '2a96a77e3dee79a45a95765c63a8986d75fe39ca',
+    Var('android_git') + '/platform/external/perfetto.git' + '@' + '87d4934194957d4d3073209cd450c197843fe25a',
 
   'src/third_party/perl': {
       'url': Var('chromium_git') + '/chromium/deps/perl.git' + '@' + '6f3e5028eb65d0b4c5fdd792106ac4c84eee1eb3',
@@ -1845,7 +1845,7 @@
       'dep_type': 'cipd',
   },
 
-  'src/third_party/vulkan-deps': '{chromium_git}/vulkan-deps@68bc448fa9011489559a06e303a90ea9fbbf28d2',
+  'src/third_party/vulkan-deps': '{chromium_git}/vulkan-deps@303074feac1525ccb32137047cb4bdb6b21d1b7e',
 
   'src/third_party/vulkan_memory_allocator':
     Var('chromium_git') + '/external/github.com/GPUOpen-LibrariesAndSDKs/VulkanMemoryAllocator.git' + '@' + 'ebe84bec02c041d28f902da0214bf442743fc907',
@@ -1885,7 +1885,7 @@
     Var('chromium_git') + '/external/github.com/gpuweb/cts.git' + '@' + 'b033a4f1ae4a0e19ae4d5563fae023001bbf570f',
 
   'src/third_party/webrtc':
-    Var('webrtc_git') + '/src.git' + '@' + 'cc1ee35a692d6d3c247be70934e598f272db01af',
+    Var('webrtc_git') + '/src.git' + '@' + '447fc3f555be32e2e12bc5c505d470fac29b5284',
 
   # Wuffs' canonical repository is at github.com/google/wuffs, but we use
   # Skia's mirror of Wuffs, the same as in upstream Skia's DEPS file.
@@ -1975,7 +1975,7 @@
     Var('chromium_git') + '/v8/v8.git' + '@' +  Var('v8_revision'),
 
   'src-internal': {
-    'url': Var('chrome_git') + '/chrome/src-internal.git@ca755af32e93ba6c2b5d7c6677665364fe7ae015',
+    'url': Var('chrome_git') + '/chrome/src-internal.git@d4f788516cd28d43db025029155eae995f51431a',
     'condition': 'checkout_src_internal',
   },
 
diff --git a/android_webview/BUILD.gn b/android_webview/BUILD.gn
index fcef866..7fe85b1 100644
--- a/android_webview/BUILD.gn
+++ b/android_webview/BUILD.gn
@@ -513,6 +513,7 @@
     "java/src/org/chromium/android_webview/AwAutofillClient.java",
     "java/src/org/chromium/android_webview/AwBrowserContext.java",
     "java/src/org/chromium/android_webview/AwBrowserProcess.java",
+    "java/src/org/chromium/android_webview/AwComputedFlags.java",
     "java/src/org/chromium/android_webview/AwConsoleMessage.java",
     "java/src/org/chromium/android_webview/AwContents.java",
     "java/src/org/chromium/android_webview/AwContentsBackgroundThreadClient.java",
@@ -529,7 +530,6 @@
     "java/src/org/chromium/android_webview/AwDevToolsServer.java",
     "java/src/org/chromium/android_webview/AwDisplayCutoutController.java",
     "java/src/org/chromium/android_webview/AwDisplayModeController.java",
-    "java/src/org/chromium/android_webview/AwFeatureList.java",
     "java/src/org/chromium/android_webview/AwFeatureMap.java",
     "java/src/org/chromium/android_webview/AwFormDatabase.java",
     "java/src/org/chromium/android_webview/AwGeolocationPermissions.java",
diff --git a/android_webview/java/src/org/chromium/android_webview/AwComputedFlags.java b/android_webview/java/src/org/chromium/android_webview/AwComputedFlags.java
new file mode 100644
index 0000000..51a6c09
--- /dev/null
+++ b/android_webview/java/src/org/chromium/android_webview/AwComputedFlags.java
@@ -0,0 +1,39 @@
+// Copyright 2018 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.android_webview;
+
+import org.chromium.base.ContextUtils;
+import org.chromium.base.PackageUtils;
+
+/**
+ * Feature flags computed from system capabilities.
+ */
+public final class AwComputedFlags {
+    // Do not instantiate this class.
+    private AwComputedFlags() {}
+
+    private static final String GMS_PACKAGE = "com.google.android.gms";
+
+    private static Boolean sPageStartedOnCommitForBrowserNavigations;
+
+    private static boolean computePageStartedOnCommitForBrowserNavigations() {
+        if (GMS_PACKAGE.equals(ContextUtils.getApplicationContext().getPackageName())) {
+            int gmsPackageVersion = PackageUtils.getPackageVersion(GMS_PACKAGE);
+            return gmsPackageVersion >= 15000000;
+        }
+        return true;
+    }
+
+    public static boolean pageStartedOnCommitEnabled(boolean isRendererInitiated) {
+        // Always enable for renderer-initiated navigations.
+        if (isRendererInitiated) return true;
+        if (sPageStartedOnCommitForBrowserNavigations != null) {
+            return sPageStartedOnCommitForBrowserNavigations;
+        }
+        sPageStartedOnCommitForBrowserNavigations =
+                computePageStartedOnCommitForBrowserNavigations();
+        return sPageStartedOnCommitForBrowserNavigations;
+    }
+}
diff --git a/android_webview/java/src/org/chromium/android_webview/AwContents.java b/android_webview/java/src/org/chromium/android_webview/AwContents.java
index e97f739a..44085c9 100644
--- a/android_webview/java/src/org/chromium/android_webview/AwContents.java
+++ b/android_webview/java/src/org/chromium/android_webview/AwContents.java
@@ -692,7 +692,8 @@
             // The shouldOverrideUrlLoading call might have resulted in posting messages to the
             // UI thread. Using sendMessage here (instead of calling onPageStarted directly)
             // will allow those to run in order.
-            if (!AwFeatureList.pageStartedOnCommitEnabled(navigationHandle.isRendererInitiated())) {
+            if (!AwComputedFlags.pageStartedOnCommitEnabled(
+                        navigationHandle.isRendererInitiated())) {
                 GURL url = navigationHandle.getBaseUrlForDataUrl().isEmpty()
                         ? navigationHandle.getUrl()
                         : navigationHandle.getBaseUrlForDataUrl();
diff --git a/android_webview/java/src/org/chromium/android_webview/AwContentsClientBridge.java b/android_webview/java/src/org/chromium/android_webview/AwContentsClientBridge.java
index 8a0f892..ed396c3 100644
--- a/android_webview/java/src/org/chromium/android_webview/AwContentsClientBridge.java
+++ b/android_webview/java/src/org/chromium/android_webview/AwContentsClientBridge.java
@@ -340,7 +340,7 @@
                 }
             }
             if (request.isOutermostMainFrame
-                    && AwFeatureList.pageStartedOnCommitEnabled(isRendererInitiated)) {
+                    && AwComputedFlags.pageStartedOnCommitEnabled(isRendererInitiated)) {
                 mClient.getCallbackHelper().postOnPageStarted(request.url);
             }
             mClient.getCallbackHelper().postOnReceivedError(request, error);
diff --git a/android_webview/java/src/org/chromium/android_webview/AwFeatureList.java b/android_webview/java/src/org/chromium/android_webview/AwFeatureList.java
deleted file mode 100644
index 9ce1927..0000000
--- a/android_webview/java/src/org/chromium/android_webview/AwFeatureList.java
+++ /dev/null
@@ -1,75 +0,0 @@
-// Copyright 2018 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-package org.chromium.android_webview;
-
-import org.chromium.base.ContextUtils;
-import org.chromium.base.PackageUtils;
-
-/**
- * Java accessor for base/feature_list.h state.
- */
-public final class AwFeatureList {
-    // Do not instantiate this class.
-    private AwFeatureList() {}
-
-    private static final String GMS_PACKAGE = "com.google.android.gms";
-
-    private static Boolean sPageStartedOnCommitForBrowserNavigations;
-
-    private static boolean computePageStartedOnCommitForBrowserNavigations() {
-        if (GMS_PACKAGE.equals(ContextUtils.getApplicationContext().getPackageName())) {
-            int gmsPackageVersion = PackageUtils.getPackageVersion(GMS_PACKAGE);
-            return gmsPackageVersion >= 15000000;
-        }
-        return true;
-    }
-
-    public static boolean pageStartedOnCommitEnabled(boolean isRendererInitiated) {
-        // Always enable for renderer-initiated navigations.
-        if (isRendererInitiated) return true;
-        if (sPageStartedOnCommitForBrowserNavigations != null) {
-            return sPageStartedOnCommitForBrowserNavigations;
-        }
-        sPageStartedOnCommitForBrowserNavigations =
-                computePageStartedOnCommitForBrowserNavigations();
-        return sPageStartedOnCommitForBrowserNavigations;
-    }
-
-    /**
-     * Returns whether the specified feature is enabled or not.
-     *
-     * Note: Features queried through this API must be added to the array
-     * |kFeaturesExposedToJava| in android_webview/browser/aw_feature_map.cc
-     *
-     * @param featureName The name of the feature to query.
-     * @return Whether the feature is enabled or not.
-     */
-    public static boolean isEnabled(String featureName) {
-        return AwFeatureMap.getInstance().isEnabled(featureName);
-    }
-
-    /**
-     * Returns the configured feature parameter value as an integer.
-     *
-     * If the feature is not enabled or the parameter does not exist, this method
-     * will return the |defaultValue|.
-     *
-     * Calling this method will mark the field trial as active. See details
-     * in base/metrics/field_trial_params.h
-     *
-     * Note: Features queried through this API must be added to the array
-     * |kFeaturesExposedToJava| in android_webview/browser/aw_feature_map.cc
-     *
-     * @param featureName The name of the feature to query.
-     * @param paramName The name of the feature parameter to query.
-     * @param defaultValue The default value to return if the feature or parameter is not found.
-     * @return The configured parameter value as an integer.
-     */
-    public static int getFeatureParamValueAsInt(
-            String featureName, String paramName, int defaultValue) {
-        return AwFeatureMap.getInstance().getFieldTrialParamByFeatureAsInt(
-                featureName, paramName, defaultValue);
-    }
-}
diff --git a/android_webview/java/src/org/chromium/android_webview/AwWebContentsObserver.java b/android_webview/java/src/org/chromium/android_webview/AwWebContentsObserver.java
index 6b6b160..3c9db25 100644
--- a/android_webview/java/src/org/chromium/android_webview/AwWebContentsObserver.java
+++ b/android_webview/java/src/org/chromium/android_webview/AwWebContentsObserver.java
@@ -136,7 +136,8 @@
             // navigations and navigation from history.push/replaceState.
             // Error page is handled by AwContentsClientBridge.onReceivedError.
             if (!navigation.isSameDocument() && !navigation.isErrorPage()
-                    && AwFeatureList.pageStartedOnCommitEnabled(navigation.isRendererInitiated())) {
+                    && AwComputedFlags.pageStartedOnCommitEnabled(
+                            navigation.isRendererInitiated())) {
                 client.getCallbackHelper().postOnPageStarted(url);
             }
 
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/SafeBrowsingTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/SafeBrowsingTest.java
index f257ab4..05b20a5 100644
--- a/android_webview/javatests/src/org/chromium/android_webview/test/SafeBrowsingTest.java
+++ b/android_webview/javatests/src/org/chromium/android_webview/test/SafeBrowsingTest.java
@@ -55,7 +55,7 @@
 import org.chromium.base.test.util.Feature;
 import org.chromium.base.test.util.InMemorySharedPreferences;
 import org.chromium.components.safe_browsing.SafeBrowsingApiBridge;
-import org.chromium.components.safe_browsing.SafeBrowsingApiHandler;
+import org.chromium.components.safe_browsing.SafetyNetApiHandler;
 import org.chromium.content_public.browser.test.util.TestThreadUtils;
 import org.chromium.content_public.common.ContentUrlConstants;
 import org.chromium.net.test.EmbeddedTestServer;
@@ -131,10 +131,10 @@
     private static final String WEB_UI_HOST = "safe-browsing";
 
     /**
-     * A fake SafeBrowsingApiHandler which treats URLs ending in MALWARE_HTML_PATH as malicious URLs
+     * A fake SafetyNetApiHandler which treats URLs ending in MALWARE_HTML_PATH as malicious URLs
      * that should be blocked.
      */
-    public static class MockSafeBrowsingApiHandler implements SafeBrowsingApiHandler {
+    public static class MockSafetyNetApiHandler implements SafetyNetApiHandler {
         private Observer mObserver;
         private static final String SAFE_METADATA = "{}";
 
@@ -192,12 +192,12 @@
     }
 
     /**
-     * A fake AwBrowserContext which loads the MockSafeBrowsingApiHandler instead of the real one.
+     * A fake AwBrowserContext which loads the MockSafetyNetApiHandler instead of the real one.
      */
     private static class MockAwBrowserContext extends AwBrowserContext {
         public MockAwBrowserContext(SharedPreferences sharedPreferences) {
             super(sharedPreferences, 0, true);
-            SafeBrowsingApiBridge.setHandler(new MockSafeBrowsingApiHandler());
+            SafeBrowsingApiBridge.setHandler(new MockSafetyNetApiHandler());
         }
     }
 
diff --git a/ash/constants/ash_features.cc b/ash/constants/ash_features.cc
index 737672d..c0917f9e 100644
--- a/ash/constants/ash_features.cc
+++ b/ash/constants/ash_features.cc
@@ -822,7 +822,7 @@
 // or not (=decides using heuristics based on key code etc.).
 BASE_FEATURE(kExoConsumedByImeByFlag,
              "ExoConsumedByImeByFlag",
-             base::FEATURE_ENABLED_BY_DEFAULT);
+             base::FEATURE_DISABLED_BY_DEFAULT);
 
 // Enables to check KeyEvent flag to see if the event is consumed by IME
 // or not (=decides using heuristics based on key code etc.).
diff --git a/ash/system/eche/eche_tray.cc b/ash/system/eche/eche_tray.cc
index c4607598..47c3ec81 100644
--- a/ash/system/eche/eche_tray.cc
+++ b/ash/system/eche/eche_tray.cc
@@ -611,8 +611,8 @@
     eche_app::mojom::ConnectionStatus last_connection_status,
     eche_app::mojom::AppStreamLaunchEntryPoint entry_point) {
   if (features::IsEcheNetworkConnectionStateEnabled() &&
-      last_connection_status ==
-          eche_app::mojom::ConnectionStatus::kConnectionStatusFailed &&
+      last_connection_status !=
+          eche_app::mojom::ConnectionStatus::kConnectionStatusConnected &&
       entry_point == eche_app::mojom::AppStreamLaunchEntryPoint::NOTIFICATION) {
     base::UmaHistogramEnumeration(
         "Eche.StreamEvent.FromNotification.PreviousNetworkCheckFailed.Result",
diff --git a/ash/system/privacy/privacy_indicators_tray_item_view.cc b/ash/system/privacy/privacy_indicators_tray_item_view.cc
index 60080016..9a67bd6 100644
--- a/ash/system/privacy/privacy_indicators_tray_item_view.cc
+++ b/ash/system/privacy/privacy_indicators_tray_item_view.cc
@@ -446,6 +446,12 @@
   UpdateBoundsInset();
 }
 
+void PrivacyIndicatorsTrayItemView::ImmediatelyUpdateVisibility() {
+  // Normally there is work to do here, but this view implements custom
+  // visibility animations that do not adhere to the `TrayItemView` animations
+  // contract. See b/283493232 for details.
+}
+
 void PrivacyIndicatorsTrayItemView::PerformAnimation() {
   // End all previous animations before starting a new sequence of animations.
   EndAllAnimations();
diff --git a/ash/system/privacy/privacy_indicators_tray_item_view.h b/ash/system/privacy/privacy_indicators_tray_item_view.h
index b74d5bee..98a9474 100644
--- a/ash/system/privacy/privacy_indicators_tray_item_view.h
+++ b/ash/system/privacy/privacy_indicators_tray_item_view.h
@@ -116,6 +116,7 @@
   void AnimationProgressed(const gfx::Animation* animation) override;
   void AnimationEnded(const gfx::Animation* animation) override;
   void AnimationCanceled(const gfx::Animation* animation) override;
+  void ImmediatelyUpdateVisibility() override;
 
   // Performs a sequence of expand, dwell, and then shrink animations to notify
   // users about the usage of camera, microphone, and screen sharing.
diff --git a/ash/system/privacy/privacy_indicators_tray_item_view_unittest.cc b/ash/system/privacy/privacy_indicators_tray_item_view_unittest.cc
index 569159b..9c3048996 100644
--- a/ash/system/privacy/privacy_indicators_tray_item_view_unittest.cc
+++ b/ash/system/privacy/privacy_indicators_tray_item_view_unittest.cc
@@ -22,6 +22,7 @@
 #include "base/test/task_environment.h"
 #include "base/time/time.h"
 #include "ui/base/l10n/l10n_util.h"
+#include "ui/compositor/layer.h"
 #include "ui/gfx/animation/linear_animation.h"
 #include "ui/views/controls/image_view.h"
 #include "ui/views/layout/box_layout.h"
@@ -389,6 +390,44 @@
             GetLayoutManager(view)->GetOrientation());
 }
 
+// Tests that the privacy indicators tray item is visible when its show
+// animation finishes running after the notification center tray has been
+// hidden. This test was added in response to b/283091001.
+TEST_P(PrivacyIndicatorsTrayItemViewTest,
+       ShowAnimationAfterNotificationCenterTrayHidden) {
+  // The notification center tray only exists when the QS revamp is enabled.
+  if (!IsQsRevampEnabled()) {
+    return;
+  }
+
+  // Verify that the privacy indicators are hidden and not animating.
+  ASSERT_FALSE(privacy_indicators_view()->GetVisible());
+  ASSERT_EQ(PrivacyIndicatorsTrayItemView::AnimationState::kIdle,
+            animation_state());
+
+  // Show the notification center tray.
+  GetPrimaryNotificationCenterTray()->SetVisiblePreferred(true);
+  ASSERT_TRUE(GetPrimaryNotificationCenterTray()->IsDrawn());
+  ASSERT_EQ(GetPrimaryNotificationCenterTray()->layer()->opacity(), 1.0f);
+
+  // Hide the notification center tray.
+  GetPrimaryNotificationCenterTray()->SetVisiblePreferred(false);
+  ASSERT_FALSE(GetPrimaryNotificationCenterTray()->IsDrawn());
+  ASSERT_EQ(GetPrimaryNotificationCenterTray()->layer()->opacity(), 0.0f);
+
+  // Show privacy indicators and let the animation end.
+  UpdateCameraAndMicrophoneUsage(
+      /*is_camera_used=*/true,
+      /*is_microphone_used=*/true);
+  SimulateAnimationEnded();
+  ASSERT_EQ(PrivacyIndicatorsTrayItemView::AnimationState::kIdle,
+            animation_state());
+
+  // Verify that the privacy indicators tray item is visible.
+  EXPECT_TRUE(privacy_indicators_view()->IsDrawn());
+  EXPECT_EQ(privacy_indicators_view()->layer()->opacity(), 1.0f);
+}
+
 TEST_P(PrivacyIndicatorsTrayItemViewTest, VisibilityAnimation) {
   GetPrimaryShelf()->SetAlignment(ShelfAlignment::kBottom);
 
diff --git a/ash/system/tray/tray_item_view.cc b/ash/system/tray/tray_item_view.cc
index cf91069..86fdbe8 100644
--- a/ash/system/tray/tray_item_view.cc
+++ b/ash/system/tray/tray_item_view.cc
@@ -172,7 +172,7 @@
   }
 
   // Immediately progress to the end of the animation if animation is disabled.
-  if (!IsAnimationEnabled()) {
+  if (!ShouldVisibilityChangeBeAnimated()) {
     // Tray items need to stay visible during the notification center tray's
     // hide animation, so don't do anything here.
     // `StatusAreaAnimationController` will call `ImmediatelyUpdateVisibility()`
diff --git a/ash/system/tray/tray_item_view.h b/ash/system/tray/tray_item_view.h
index bc47477..1cf9b50 100644
--- a/ash/system/tray/tray_item_view.h
+++ b/ash/system/tray/tray_item_view.h
@@ -50,6 +50,17 @@
 // Base-class for items in the tray. It makes sure the widget is updated
 // correctly when the visibility/size of the tray item changes. It also adds
 // animation when showing/hiding the item in the tray.
+//
+// A derived class can implement its own custom visibility animations by
+// overriding `PerformVisibilityAnimation()`. If the QS revamp is enabled, then
+// it is also important to override `ImmediatelyUpdateVisibility()`, which will
+// be called in certain scenarios like at the end of the
+// `NotificationCenterTray`'s hide animation or when the
+// `NotificationCenterTray`'s hide animation is interrupted by its show
+// animation. Also note that `IsAnimationEnabled()` should be checked whenever
+// attempting to perform the custom animations, as there are times when a
+// `TrayItemView`'s visibility should change but that change should not be
+// animated (for instance, when the `NotificationCenterTray` is hidden).
 class ASH_EXPORT TrayItemView : public views::View,
                                 public views::AnimationDelegateViews {
  public:
@@ -98,8 +109,12 @@
   bool IsAnimating();
 
   // Updates this `TrayItemView`'s visibility according to `target_visible_`
-  // without animating.
-  void ImmediatelyUpdateVisibility();
+  // without animating. Only called when the QS revamp is enabled. This is
+  // called in certain scenarios like at the end of the
+  // `NotificationCenterTray`'s hide animation or when the
+  // `NotificationCenterTray`'s hide animation is interrupted by its show
+  // animation.
+  virtual void ImmediatelyUpdateVisibility();
 
   // Returns the target visibility. For testing only.
   bool target_visible_for_testing() const { return target_visible_; }
@@ -127,9 +142,15 @@
   bool IsHorizontalAlignment() const;
 
   // Perform visibility animation for this view. This function can be overridden
-  // so that the visibility animation can be customized.
+  // so that the visibility animation can be customized. If the QS revamp is
+  // enabled then `ImmediatelyUpdateVisibility()` should also be overridden.
   virtual void PerformVisibilityAnimation(bool visible);
 
+  // Checks if we should use animation on visibility changes.
+  bool ShouldVisibilityChangeBeAnimated() const {
+    return disable_animation_count_ == 0u;
+  }
+
  private:
   // views::View.
   void ChildPreferredSizeChanged(View* child) override;
@@ -152,9 +173,6 @@
   double GetItemScaleProgressFromAnimationProgress(
       double animation_value) const;
 
-  // Checks if we should use animation on visibility changes.
-  bool IsAnimationEnabled() const { return disable_animation_count_ == 0u; }
-
   const raw_ptr<Shelf, ExperimentalAsh> shelf_;
 
   // When showing the item in tray, the animation is executed with 2 stages:
diff --git a/ash/webui/eche_app_ui/apps_launch_info_provider.cc b/ash/webui/eche_app_ui/apps_launch_info_provider.cc
index 13b657f4..d2a5da24 100644
--- a/ash/webui/eche_app_ui/apps_launch_info_provider.cc
+++ b/ash/webui/eche_app_ui/apps_launch_info_provider.cc
@@ -6,28 +6,17 @@
 
 #include "ash/webui/eche_app_ui/mojom/eche_app.mojom-shared.h"
 
-namespace ash {
-namespace eche_app {
+namespace ash::eche_app {
 
 AppsLaunchInfoProvider::AppsLaunchInfoProvider(
     EcheConnectionStatusHandler* connection_handler)
-    : eche_connection_status_handler_(connection_handler) {
-  eche_connection_status_handler_->AddObserver(this);
-}
+    : eche_connection_status_handler_(connection_handler) {}
 
-AppsLaunchInfoProvider::~AppsLaunchInfoProvider() {
-  eche_connection_status_handler_->RemoveObserver(this);
-}
-
-void AppsLaunchInfoProvider::OnConnectionStatusForUiChanged(
-    mojom::ConnectionStatus connection_status) {
-  last_connection_ = connection_status;
-}
-
-void AppsLaunchInfoProvider::SetEntryPoint(
+void AppsLaunchInfoProvider::SetAppLaunchInfo(
     mojom::AppStreamLaunchEntryPoint entry_point) {
   entry_point_ = entry_point;
+  last_connection_ =
+      eche_connection_status_handler_->connection_status_for_ui();
 }
 
-}  // namespace eche_app
-}  // namespace ash
\ No newline at end of file
+}  // namespace ash::eche_app
diff --git a/ash/webui/eche_app_ui/apps_launch_info_provider.h b/ash/webui/eche_app_ui/apps_launch_info_provider.h
index ba3818ea..22ae1f4 100644
--- a/ash/webui/eche_app_ui/apps_launch_info_provider.h
+++ b/ash/webui/eche_app_ui/apps_launch_info_provider.h
@@ -15,21 +15,17 @@
 namespace eche_app {
 
 // A class to store app stream entry point and last connection status.
-class AppsLaunchInfoProvider : public EcheConnectionStatusHandler::Observer {
+class AppsLaunchInfoProvider {
  public:
   explicit AppsLaunchInfoProvider(EcheConnectionStatusHandler*);
-  ~AppsLaunchInfoProvider() override;
+  ~AppsLaunchInfoProvider() = default;
 
   AppsLaunchInfoProvider(const AppsLaunchInfoProvider&) = delete;
   AppsLaunchInfoProvider& operator=(const AppsLaunchInfoProvider&) = delete;
 
-  // EcheConnectionStatusHandler::Observer:
-  void OnConnectionStatusForUiChanged(
-      mojom::ConnectionStatus connection_status) override;
+  void SetAppLaunchInfo(mojom::AppStreamLaunchEntryPoint entry_point);
 
-  void SetEntryPoint(mojom::AppStreamLaunchEntryPoint entry_point);
-
-  mojom::ConnectionStatus GetConnectionStatusForUi() {
+  mojom::ConnectionStatus GetConnectionStatusFromLastAttempt() {
     return last_connection_;
   }
 
diff --git a/ash/webui/eche_app_ui/apps_launch_info_provider_unittest.cc b/ash/webui/eche_app_ui/apps_launch_info_provider_unittest.cc
index 7b0adf5..91e1c45a 100644
--- a/ash/webui/eche_app_ui/apps_launch_info_provider_unittest.cc
+++ b/ash/webui/eche_app_ui/apps_launch_info_provider_unittest.cc
@@ -37,16 +37,14 @@
     handler_.reset();
   }
 
-  void NotifyConnectionStatusForUiChanged(mojom::ConnectionStatus status) {
+  mojom::ConnectionStatus GetConnectionStatusFromLastAttempt() {
+    return provider_->GetConnectionStatusFromLastAttempt();
+  }
+
+  void SetAppLaunchInfo(mojom::AppStreamLaunchEntryPoint entry_point,
+                        mojom::ConnectionStatus status) {
     handler_->SetConnectionStatusForUi(status);
-  }
-
-  mojom::ConnectionStatus GetLastConnectionStatus() {
-    return provider_->GetConnectionStatusForUi();
-  }
-
-  void SetEntryPoint(mojom::AppStreamLaunchEntryPoint entry_point) {
-    provider_->SetEntryPoint(entry_point);
+    provider_->SetAppLaunchInfo(entry_point);
   }
 
   mojom::AppStreamLaunchEntryPoint GetEntryPoint() {
@@ -60,42 +58,28 @@
   std::unique_ptr<AppsLaunchInfoProvider> provider_;
 };
 
-TEST_F(AppsLaunchInfoProviderTest, OnConnectionStatusForUiChanged) {
-  EXPECT_EQ(GetLastConnectionStatus(),
-            mojom::ConnectionStatus::kConnectionStatusDisconnected);
-
-  NotifyConnectionStatusForUiChanged(
-      mojom::ConnectionStatus::kConnectionStatusConnecting);
-  EXPECT_EQ(GetLastConnectionStatus(),
-            mojom::ConnectionStatus::kConnectionStatusConnecting);
-
-  NotifyConnectionStatusForUiChanged(
-      mojom::ConnectionStatus::kConnectionStatusConnected);
-  EXPECT_EQ(GetLastConnectionStatus(),
-            mojom::ConnectionStatus::kConnectionStatusConnected);
-
-  NotifyConnectionStatusForUiChanged(
-      mojom::ConnectionStatus::kConnectionStatusFailed);
-  EXPECT_EQ(GetLastConnectionStatus(),
-            mojom::ConnectionStatus::kConnectionStatusFailed);
-
-  NotifyConnectionStatusForUiChanged(
-      mojom::ConnectionStatus::kConnectionStatusDisconnected);
-  EXPECT_EQ(GetLastConnectionStatus(),
-            mojom::ConnectionStatus::kConnectionStatusDisconnected);
-}
-
 TEST_F(AppsLaunchInfoProviderTest, SetEntryPoint) {
   EXPECT_EQ(GetEntryPoint(), mojom::AppStreamLaunchEntryPoint::UNKNOWN);
+  EXPECT_EQ(GetConnectionStatusFromLastAttempt(),
+            mojom::ConnectionStatus::kConnectionStatusDisconnected);
 
-  SetEntryPoint(mojom::AppStreamLaunchEntryPoint::NOTIFICATION);
+  SetAppLaunchInfo(mojom::AppStreamLaunchEntryPoint::NOTIFICATION,
+                   mojom::ConnectionStatus::kConnectionStatusConnecting);
   EXPECT_EQ(GetEntryPoint(), mojom::AppStreamLaunchEntryPoint::NOTIFICATION);
+  EXPECT_EQ(GetConnectionStatusFromLastAttempt(),
+            mojom::ConnectionStatus::kConnectionStatusConnecting);
 
-  SetEntryPoint(mojom::AppStreamLaunchEntryPoint::APPS_LIST);
+  SetAppLaunchInfo(mojom::AppStreamLaunchEntryPoint::APPS_LIST,
+                   mojom::ConnectionStatus::kConnectionStatusConnected);
   EXPECT_EQ(GetEntryPoint(), mojom::AppStreamLaunchEntryPoint::APPS_LIST);
+  EXPECT_EQ(GetConnectionStatusFromLastAttempt(),
+            mojom::ConnectionStatus::kConnectionStatusConnected);
 
-  SetEntryPoint(mojom::AppStreamLaunchEntryPoint::RECENT_APPS);
+  SetAppLaunchInfo(mojom::AppStreamLaunchEntryPoint::RECENT_APPS,
+                   mojom::ConnectionStatus::kConnectionStatusFailed);
   EXPECT_EQ(GetEntryPoint(), mojom::AppStreamLaunchEntryPoint::RECENT_APPS);
+  EXPECT_EQ(GetConnectionStatusFromLastAttempt(),
+            mojom::ConnectionStatus::kConnectionStatusFailed);
 }
 
 }  // namespace ash::eche_app
\ No newline at end of file
diff --git a/ash/webui/eche_app_ui/eche_connection_status_handler.h b/ash/webui/eche_app_ui/eche_connection_status_handler.h
index 419f3d9..bc32e21 100644
--- a/ash/webui/eche_app_ui/eche_connection_status_handler.h
+++ b/ash/webui/eche_app_ui/eche_connection_status_handler.h
@@ -66,6 +66,10 @@
 
   void Bind(mojo::PendingReceiver<mojom::ConnectionStatusObserver> receiver);
 
+  mojom::ConnectionStatus connection_status_for_ui() const {
+    return connection_status_for_ui_;
+  }
+
  private:
   friend class EcheConnectionStatusHandlerTest;
 
@@ -79,9 +83,6 @@
   void set_feature_status_for_test(FeatureStatus feature_status) {
     feature_status_ = feature_status;
   }
-  mojom::ConnectionStatus get_connection_status_for_ui_for_test() const {
-    return connection_status_for_ui_;
-  }
 
   mojom::ConnectionStatus connection_status_for_ui_ =
       mojom::ConnectionStatus::kConnectionStatusDisconnected;
diff --git a/ash/webui/eche_app_ui/eche_connection_status_handler_unittest.cc b/ash/webui/eche_app_ui/eche_connection_status_handler_unittest.cc
index d65f843..438b64d 100644
--- a/ash/webui/eche_app_ui/eche_connection_status_handler_unittest.cc
+++ b/ash/webui/eche_app_ui/eche_connection_status_handler_unittest.cc
@@ -137,7 +137,7 @@
   }
 
   mojom::ConnectionStatus GetConnectionStatusForUi() const {
-    return handler_->get_connection_status_for_ui_for_test();
+    return handler_->connection_status_for_ui();
   }
 
   base::test::ScopedFeatureList scoped_feature_list_;
diff --git a/ash/webui/eche_app_ui/eche_notification_click_handler.cc b/ash/webui/eche_app_ui/eche_notification_click_handler.cc
index ed0a567..0910f3ef 100644
--- a/ash/webui/eche_app_ui/eche_notification_click_handler.cc
+++ b/ash/webui/eche_app_ui/eche_notification_click_handler.cc
@@ -52,7 +52,7 @@
       base::UmaHistogramEnumeration(
           "Eche.AppStream.LaunchAttempt",
           mojom::AppStreamLaunchEntryPoint::NOTIFICATION);
-      apps_launch_info_provider_->SetEntryPoint(
+      apps_launch_info_provider_->SetAppLaunchInfo(
           mojom::AppStreamLaunchEntryPoint::NOTIFICATION);
       launch_app_helper_->LaunchEcheApp(
           notification_id, app_metadata.package_name,
diff --git a/ash/webui/eche_app_ui/eche_recent_app_click_handler.cc b/ash/webui/eche_app_ui/eche_recent_app_click_handler.cc
index 172f9f83..972c633 100644
--- a/ash/webui/eche_app_ui/eche_recent_app_click_handler.cc
+++ b/ash/webui/eche_app_ui/eche_recent_app_click_handler.cc
@@ -87,7 +87,7 @@
       base::UmaHistogramEnumeration("Eche.AppStream.LaunchAttempt", entrypoint);
 
       to_stream_apps_.emplace_back(app_metadata);
-      apps_launch_info_provider_->SetEntryPoint(entrypoint);
+      apps_launch_info_provider_->SetAppLaunchInfo(entrypoint);
       launch_app_helper_->LaunchEcheApp(
           /*notification_id=*/absl::nullopt, app_metadata.package_name,
           app_metadata.visible_app_name, app_metadata.user_id,
diff --git a/ash/webui/eche_app_ui/eche_signaler.cc b/ash/webui/eche_app_ui/eche_signaler.cc
index a9fb035f..1c68d51 100644
--- a/ash/webui/eche_app_ui/eche_signaler.cc
+++ b/ash/webui/eche_app_ui/eche_signaler.cc
@@ -198,7 +198,7 @@
   if (eche_tray && eche_tray->IsBackgroundConnectionAttemptInProgress()) {
     base::UmaHistogramEnumeration("Eche.NetworkCheck.FailureReason",
                                   probably_connection_failed_reason_);
-  } else if (apps_launch_info_provider_->GetConnectionStatusForUi() ==
+  } else if (apps_launch_info_provider_->GetConnectionStatusFromLastAttempt() ==
                  mojom::ConnectionStatus::kConnectionStatusFailed &&
              apps_launch_info_provider_->entry_point() ==
                  mojom::AppStreamLaunchEntryPoint::NOTIFICATION) {
diff --git a/ash/webui/eche_app_ui/eche_stream_status_change_handler.cc b/ash/webui/eche_app_ui/eche_stream_status_change_handler.cc
index b6b2b04..9a13055 100644
--- a/ash/webui/eche_app_ui/eche_stream_status_change_handler.cc
+++ b/ash/webui/eche_app_ui/eche_stream_status_change_handler.cc
@@ -37,10 +37,18 @@
                << status;
   NotifyStreamStatusChanged(status);
 
+  // Note that the stream status and connection status from
+  // |apps_launch_info_provider_| can be out of sync. This is because the
+  // pre-warm connection may not havbe been established (e.g. launched from a
+  // notification when Phone Hub just get connected) and the value is stored in
+  // |apps_launch_info_provider_| when app streaming gets initialized. This is
+  // very likely to fail to start actual streaming and we are recording these
+  // events to separate bucket to prevent it from polluting our real success
+  // metrics.
   if (status == mojom::StreamStatus::kStreamStatusStarted) {
     if (features::IsEcheNetworkConnectionStateEnabled() &&
-        apps_launch_info_provider_->GetConnectionStatusForUi() ==
-            mojom::ConnectionStatus::kConnectionStatusFailed &&
+        apps_launch_info_provider_->GetConnectionStatusFromLastAttempt() !=
+            mojom::ConnectionStatus::kConnectionStatusConnected &&
         apps_launch_info_provider_->entry_point() ==
             mojom::AppStreamLaunchEntryPoint::NOTIFICATION) {
       base::UmaHistogramEnumeration(
diff --git a/ash/webui/print_management/print_management_ui.cc b/ash/webui/print_management/print_management_ui.cc
index b787076..8860b1c 100644
--- a/ash/webui/print_management/print_management_ui.cc
+++ b/ash/webui/print_management/print_management_ui.cc
@@ -111,7 +111,13 @@
        IDS_PRINT_MANAGEMENT_CANCEL_PRINT_JOB_BUTTON_LABEL},
       {"cancelledPrintJob",
        IDS_PRINT_MANAGEMENT_CANCELED_PRINT_JOB_ARIA_ANNOUNCEMENT},
-      {"collapsedPrintingText", IDS_PRINT_MANAGEMENT_COLLAPSE_PRINTING_STATUS}};
+      {"collapsedPrintingText", IDS_PRINT_MANAGEMENT_COLLAPSE_PRINTING_STATUS},
+      {"emptyStateNoJobsMessage",
+       IDS_PRINT_MANAGEMENT_EMPTY_STATE_NO_JOBS_MESSAGE},
+      {"emptyStatePrinterSettingsMessage",
+       IDS_PRINT_MANAGEMENT_EMPTY_STATE_PRINTER_SETTINGS_MESSAGE},
+      {"managePrintersButtonLabel",
+       IDS_PRINT_MANAGEMENT_EMPTY_STATE_MANAGE_PRINTERS_LABEL}};
 
   html_source->AddLocalizedStrings(kLocalizedStrings);
   html_source->UseStringsJs();
diff --git a/ash/webui/print_management/resources/printer_setup_info.html b/ash/webui/print_management/resources/printer_setup_info.html
index 0231642f..cee6bb4 100644
--- a/ash/webui/print_management/resources/printer_setup_info.html
+++ b/ash/webui/print_management/resources/printer_setup_info.html
@@ -10,7 +10,9 @@
 </style>
 <div class="container">
   <iron-icon icon="print-management:file-generic"></iron-icon>
-  <h2 class="message-heading">No printer jobs</h2>
-  <p class="message-detail">Go to Printer settings to manage your printers</p>
-  <cr-button class="action-button">Manage</cr-button>
+  <h2 class="message-heading">[[i18n('emptyStateNoJobsMessage')]]</h2>
+  <p class="message-detail">[[i18n('emptyStatePrinterSettingsMessage')]]</p>
+  <cr-button class="action-button">
+    [[i18n('managePrintersButtonLabel')]]
+  </cr-button>
 </div>
diff --git a/ash/webui/print_management/resources/printer_setup_info.ts b/ash/webui/print_management/resources/printer_setup_info.ts
index bb8ab5e..d34e0fe 100644
--- a/ash/webui/print_management/resources/printer_setup_info.ts
+++ b/ash/webui/print_management/resources/printer_setup_info.ts
@@ -8,6 +8,7 @@
 import './print_management_fonts.css.js';
 import './print_management_shared.css.js';
 
+import {I18nMixin} from 'chrome://resources/cr_elements/i18n_mixin.js';
 import {PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 
 import {getTemplate} from './printer_setup_info.html.js';
@@ -20,7 +21,9 @@
 
 const PrinterSetupInfoElementIs = 'printer-setup-info';
 
-export class PrinterSetupInfoElement extends PolymerElement {
+const PrinterSetupInfoElementBase = I18nMixin(PolymerElement);
+
+export class PrinterSetupInfoElement extends PrinterSetupInfoElementBase {
   static get is(): string {
     return PrinterSetupInfoElementIs;
   }
diff --git a/ash/webui/shortcut_customization_ui/resources/js/shortcuts_page.ts b/ash/webui/shortcut_customization_ui/resources/js/shortcuts_page.ts
index 4ce64de..5f58cdf 100644
--- a/ash/webui/shortcut_customization_ui/resources/js/shortcuts_page.ts
+++ b/ash/webui/shortcut_customization_ui/resources/js/shortcuts_page.ts
@@ -138,7 +138,12 @@
    * a search result.
    */
   onRouteChanged(url: URL): void {
-    this.maybeScrollToAcceleratorRowBasedOnUrl(url);
+    const didScroll = this.maybeScrollToAcceleratorRowBasedOnUrl(url);
+    if (didScroll) {
+      // Reset the route after scrolling so the app doesn't re-scroll when
+      // the user manually changes pages.
+      Router.getInstance().resetRoute();
+    }
   }
 
   /**
diff --git a/base/logging_unittest.cc b/base/logging_unittest.cc
index 3b17939..75b2fe2 100644
--- a/base/logging_unittest.cc
+++ b/base/logging_unittest.cc
@@ -91,7 +91,7 @@
   // 4 base logs: LOG, LOG_IF, PLOG, and PLOG_IF
   int expected_logs = 4;
 
-  // 4 verbose logs: VLOG, VLOG_IF, PVLOG, PVLOG_IF.
+  // 4 verbose logs: VLOG, VLOG_IF, VPLOG, VPLOG_IF.
   if (VLOG_IS_ON(0))
     expected_logs += 4;
 
diff --git a/build/fuchsia/linux_internal.sdk.sha1 b/build/fuchsia/linux_internal.sdk.sha1
index 2f4e14ee..3368264b 100644
--- a/build/fuchsia/linux_internal.sdk.sha1
+++ b/build/fuchsia/linux_internal.sdk.sha1
@@ -1 +1 @@
-12.20230519.2.1
+12.20230519.3.1
diff --git a/chrome/VERSION b/chrome/VERSION
index ea01df6..efa2db5 100644
--- a/chrome/VERSION
+++ b/chrome/VERSION
@@ -1,4 +1,4 @@
 MAJOR=115
 MINOR=0
-BUILD=5782
+BUILD=5783
 PATCH=0
diff --git a/chrome/android/chrome_test_java_sources.gni b/chrome/android/chrome_test_java_sources.gni
index a3f9673c..fa70685 100644
--- a/chrome/android/chrome_test_java_sources.gni
+++ b/chrome/android/chrome_test_java_sources.gni
@@ -26,7 +26,7 @@
   "javatests/src/org/chromium/chrome/browser/JavaScriptEvalChromeTest.java",
   "javatests/src/org/chromium/chrome/browser/LauncherShortcutTest.java",
   "javatests/src/org/chromium/chrome/browser/MainActivityWithURLTest.java",
-  "javatests/src/org/chromium/chrome/browser/MockSafeBrowsingApiHandler.java",
+  "javatests/src/org/chromium/chrome/browser/MockSafetyNetApiHandler.java",
   "javatests/src/org/chromium/chrome/browser/NavigateTest.java",
   "javatests/src/org/chromium/chrome/browser/OmahaServiceStartDelayerIntegrationTest.java",
   "javatests/src/org/chromium/chrome/browser/PopularUrlsTest.java",
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/WarmupManager.java b/chrome/android/java/src/org/chromium/chrome/browser/WarmupManager.java
index c3bf4a31..70982fb 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/WarmupManager.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/WarmupManager.java
@@ -262,6 +262,18 @@
     }
 
     /**
+     * @param tab Tab to compare with SpareTab with.
+     *
+     * @return Returns true if tab is same as spare tab.
+     */
+    public boolean isSpareTab(Tab tab) {
+        if (mSpareTab == null) return false;
+
+        assert mSpareTab.isHidden() : "Spare tab is not hidden";
+        return mSpareTab == tab;
+    }
+
+    /**
      * Various conditions are checked to determine whether the spare tab can be used to load a URL.
      * In order to load a URL, the tab properties must match the tab that is being used.
      *
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/infobar/TranslateCompactInfoBar.java b/chrome/android/java/src/org/chromium/chrome/browser/infobar/TranslateCompactInfoBar.java
index fe75f0f..ed9e293 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/infobar/TranslateCompactInfoBar.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/infobar/TranslateCompactInfoBar.java
@@ -66,9 +66,6 @@
     private long mNativeTranslateInfoBarPtr;
     private TranslateTabLayout mTabLayout;
 
-    // Metric to track the total number of translations in a page, including reverts to original.
-    private int mTotalTranslationCount;
-
     private static final String INFOBAR_HISTOGRAM = "Translate.CompactInfobar.Event";
 
     // Need 2 instances of TranslateMenuHelper to prevent a race condition bug which happens when
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tab/TabImpl.java b/chrome/android/java/src/org/chromium/chrome/browser/tab/TabImpl.java
index d6ed11ec..cfbdd33c 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/tab/TabImpl.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/tab/TabImpl.java
@@ -1288,7 +1288,14 @@
         mIsLoading = false;
 
         RewindableIterator<TabObserver> observers = getTabObservers();
-        while (observers.hasNext()) observers.next().onCrash(this);
+        // When the renderer crashes for a hidden spare tab, we can skip notifying the observers to
+        // crash the underlying tab. This is because it is safe to keep the spare tab around without
+        // a renderer process, and since the tab is hidden, we don't need to show a sad tab. When
+        // the spare tab is used for navigation it will create a new renderer process.
+        // TODO(crbug.com/1447250): Make this logic more robust for all hidden tab cases.
+        if (!WarmupManager.getInstance().isSpareTab(this)) {
+            while (observers.hasNext()) observers.next().onCrash(this);
+        }
         mIsBeingRestored = false;
     }
 
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/MockSafeBrowsingApiHandler.java b/chrome/android/javatests/src/org/chromium/chrome/browser/MockSafetyNetApiHandler.java
similarity index 92%
rename from chrome/android/javatests/src/org/chromium/chrome/browser/MockSafeBrowsingApiHandler.java
rename to chrome/android/javatests/src/org/chromium/chrome/browser/MockSafetyNetApiHandler.java
index 1e0f0fe..1910696 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/MockSafeBrowsingApiHandler.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/MockSafetyNetApiHandler.java
@@ -10,21 +10,21 @@
 
 import org.chromium.base.task.PostTask;
 import org.chromium.base.task.TaskTraits;
-import org.chromium.components.safe_browsing.SafeBrowsingApiHandler;
+import org.chromium.components.safe_browsing.SafetyNetApiHandler;
 
 import java.util.HashMap;
 import java.util.Map;
 
 /**
- * SafeBrowsingApiHandler that vends fake responses.
+ * SafetyNetApiHandler that vends fake responses.
  */
-public class MockSafeBrowsingApiHandler implements SafeBrowsingApiHandler {
+public class MockSafetyNetApiHandler implements SafetyNetApiHandler {
     private Observer mObserver;
     // Mock time it takes for a lookup request to complete.
     private static final long DEFAULT_CHECK_DELTA_US = 10;
     private static final String SAFE_METADATA = "{}";
 
-    // Global url -> metadataResponse map. In practice there is only one SafeBrowsingApiHandler, but
+    // Global url -> metadataResponse map. In practice there is only one SafetyNetApiHandler, but
     // it is cumbersome for tests to reach into the singleton instance directly. So just make this
     // static and modifiable from java tests using a static method.
     private static final Map<String, String> sResponseMap = new HashMap<>();
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/PopupTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/PopupTest.java
index ae2be768..0217790c 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/PopupTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/PopupTest.java
@@ -64,7 +64,7 @@
 
     @Before
     public void setUp() throws Exception {
-        SafeBrowsingApiBridge.setHandler(new MockSafeBrowsingApiHandler());
+        SafeBrowsingApiBridge.setHandler(new MockSafetyNetApiHandler());
         mActivityTestRule.startMainActivityOnBlankPage();
 
         PostTask.runOrPostTask(
@@ -78,7 +78,7 @@
     @After
     public void tearDown() {
         mTestServer.stopAndDestroyServer();
-        MockSafeBrowsingApiHandler.clearMockResponses();
+        MockSafetyNetApiHandler.clearMockResponses();
     }
 
     @Test
@@ -114,7 +114,7 @@
         final TabModelSelector selector = mActivityTestRule.getActivity().getTabModelSelector();
 
         String url = mTestServer.getURL("/chrome/test/data/android/popup_on_click.html");
-        MockSafeBrowsingApiHandler.addMockResponse(url, METADATA_FOR_ABUSIVE_ENFORCEMENT);
+        MockSafetyNetApiHandler.addMockResponse(url, METADATA_FOR_ABUSIVE_ENFORCEMENT);
 
         mActivityTestRule.loadUrl(url);
         CriteriaHelper.pollUiThread(
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/SafeBrowsingTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/SafeBrowsingTest.java
index bc8e523f..d1cf4d35c 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/SafeBrowsingTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/SafeBrowsingTest.java
@@ -31,7 +31,7 @@
 import org.chromium.ui.base.PageTransition;
 
 /**
- * Test integration with the SafeBrowsingApiHandler.
+ * Test integration with the SafetyNetApiHandler.
  */
 @RunWith(ChromeJUnit4ClassRunner.class)
 @CommandLineFlags.Add({ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE})
@@ -79,7 +79,7 @@
 
     @Before
     public void setUp() {
-        SafeBrowsingApiBridge.setHandler(new MockSafeBrowsingApiHandler());
+        SafeBrowsingApiBridge.setHandler(new MockSafetyNetApiHandler());
     }
 
     @After
@@ -87,7 +87,7 @@
         if (mTestServer != null) {
             mTestServer.stopAndDestroyServer();
         }
-        MockSafeBrowsingApiHandler.clearMockResponses();
+        MockSafetyNetApiHandler.clearMockResponses();
     }
 
     @Test
@@ -108,7 +108,7 @@
         mTestServer = EmbeddedTestServer.createAndStartServer(
                 ApplicationProvider.getApplicationContext());
         String url = mTestServer.getURL("/chrome/test/data/android/about.html");
-        MockSafeBrowsingApiHandler.addMockResponse(url, "{\"matches\":[{\"threat_type\":\"5\"}]}");
+        MockSafetyNetApiHandler.addMockResponse(url, "{\"matches\":[{\"threat_type\":\"5\"}]}");
         mActivityTestRule.startMainActivityOnBlankPage();
 
         loadUrlNonBlocking(url);
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/WarmupManagerTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/WarmupManagerTest.java
index 3849979..54e33d6 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/WarmupManagerTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/WarmupManagerTest.java
@@ -436,26 +436,26 @@
         histogramWatcher.assertExpected();
     }
 
-    /** Tests that when SpareTab is destroyed when the renderer is killed. */
+    /** Tests that when SpareTab is not destroyed when the renderer is killed. */
     @Test
     @MediumTest
     @Feature({"SpareTab"})
     @EnableFeatures(ChromeFeatureList.SPARE_TAB)
     @UiThreadTest
-    public void testDestroySpareTabWhenRendererKilled() {
+    public void testDontDestroySpareTabWhenRendererKilled() {
         // Set the param to true allowing renderer initialization.
         WarmupManager.SPARE_TAB_INITIALIZE_RENDERER.setForTesting(true);
 
         var histogramWatcher = HistogramWatcher.newSingleRecordWatcher(
-                HISTOGRAM_SPARE_TAB_FINAL_STATUS, SpareTabFinalStatus.TAB_CRASHED);
+                HISTOGRAM_SPARE_TAB_FINAL_STATUS, SpareTabFinalStatus.TAB_USED);
 
         mWarmupManager.createSpareTab(mActivityTestRule.getActivity().getCurrentTabCreator(),
                 TabLaunchType.FROM_START_SURFACE);
 
-        // Kill the renderer process, this should kill the associated spare tab and record
-        // TAB_CRASHED status.
+        // Kill the renderer process, this shouldn't kill the associated spare tab and record
+        // TAB_CREATED status.
         WebContentsUtils.simulateRendererKilled(mWarmupManager.mSpareTab.getWebContents());
-        Assert.assertNull(mWarmupManager.takeSpareTab(false, TabLaunchType.FROM_START_SURFACE));
+        Assert.assertNotNull(mWarmupManager.takeSpareTab(false, TabLaunchType.FROM_START_SURFACE));
 
         histogramWatcher.assertExpected();
     }
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/DetachedResourceRequestTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/DetachedResourceRequestTest.java
index 02f2936..b2a2af8 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/DetachedResourceRequestTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/DetachedResourceRequestTest.java
@@ -33,7 +33,7 @@
 import org.chromium.base.test.util.CriteriaHelper;
 import org.chromium.base.test.util.DisabledTest;
 import org.chromium.base.test.util.HistogramWatcher;
-import org.chromium.chrome.browser.MockSafeBrowsingApiHandler;
+import org.chromium.chrome.browser.MockSafetyNetApiHandler;
 import org.chromium.chrome.browser.browserservices.verification.ChromeOriginVerifier;
 import org.chromium.chrome.browser.document.ChromeLauncherActivity;
 import org.chromium.chrome.browser.firstrun.FirstRunStatus;
@@ -578,7 +578,7 @@
 
     private void testSafeBrowsingMainResource(boolean afterNative, boolean splitCacheEnabled)
             throws Exception {
-        SafeBrowsingApiBridge.setHandler(new MockSafeBrowsingApiHandler());
+        SafeBrowsingApiBridge.setHandler(new MockSafetyNetApiHandler());
         CustomTabsSessionToken session = prepareSession();
 
         String cacheable = "/cachetime";
@@ -587,7 +587,7 @@
         Uri url = Uri.parse(mServer.getURL(cacheable));
 
         try {
-            MockSafeBrowsingApiHandler.addMockResponse(
+            MockSafetyNetApiHandler.addMockResponse(
                     url.toString(), "{\"matches\":[{\"threat_type\":\"5\"}]}");
 
             Intent intent = CustomTabsIntentTestUtils.createMinimalCustomTabIntent(
@@ -616,19 +616,19 @@
                 Assert.assertEquals(1, readFromSocketCallback.getCallCount());
             }
         } finally {
-            MockSafeBrowsingApiHandler.clearMockResponses();
+            MockSafetyNetApiHandler.clearMockResponses();
         }
     }
 
     private void testSafeBrowsingSubresource(boolean afterNative) throws Exception {
-        SafeBrowsingApiBridge.setHandler(new MockSafeBrowsingApiHandler());
+        SafeBrowsingApiBridge.setHandler(new MockSafetyNetApiHandler());
         CustomTabsSessionToken session = prepareSession();
         String cacheable = "/cachetime";
         waitForDetachedRequest(session, cacheable, afterNative);
         Uri url = Uri.parse(mServer.getURL(cacheable));
 
         try {
-            MockSafeBrowsingApiHandler.addMockResponse(
+            MockSafetyNetApiHandler.addMockResponse(
                     url.toString(), "{\"matches\":[{\"threat_type\":\"5\"}]}");
 
             String pageUrl = mServer.getURL("/chrome/test/data/android/cacheable_subresource.html");
@@ -644,7 +644,7 @@
             // robust check.
             CriteriaHelper.pollUiThread(() -> webContents.getTitle().equals("Security error"));
         } finally {
-            MockSafeBrowsingApiHandler.clearMockResponses();
+            MockSafetyNetApiHandler.clearMockResponses();
         }
     }
 
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/subresource_filter/SubresourceFilterTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/subresource_filter/SubresourceFilterTest.java
index d809350..2b723130 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/subresource_filter/SubresourceFilterTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/subresource_filter/SubresourceFilterTest.java
@@ -23,7 +23,7 @@
 import org.chromium.base.test.util.CommandLineFlags;
 import org.chromium.base.test.util.CriteriaHelper;
 import org.chromium.base.test.util.Restriction;
-import org.chromium.chrome.browser.MockSafeBrowsingApiHandler;
+import org.chromium.chrome.browser.MockSafetyNetApiHandler;
 import org.chromium.chrome.browser.flags.ChromeFeatureList;
 import org.chromium.chrome.browser.flags.ChromeSwitches;
 import org.chromium.chrome.browser.infobar.InfoBarContainer;
@@ -100,7 +100,7 @@
     @Before
     public void setUp() throws Exception {
         mTestServer = mTestServerRule.getServer();
-        SafeBrowsingApiBridge.setHandler(new MockSafeBrowsingApiHandler());
+        SafeBrowsingApiBridge.setHandler(new MockSafetyNetApiHandler());
         mActivityTestRule.startMainActivityOnBlankPage();
 
         // Disallow all jpgs.
@@ -109,7 +109,7 @@
 
     @After
     public void tearDown() {
-        MockSafeBrowsingApiHandler.clearMockResponses();
+        MockSafetyNetApiHandler.clearMockResponses();
     }
 
     @Test
@@ -322,7 +322,7 @@
 
     private boolean loadPageWithBlockableContentAndTestIfBlocked(String url, String metadata)
             throws TimeoutException {
-        MockSafeBrowsingApiHandler.addMockResponse(url, metadata);
+        MockSafetyNetApiHandler.addMockResponse(url, metadata);
         mActivityTestRule.loadUrl(url);
         return Boolean.parseBoolean(mActivityTestRule.runJavaScriptCodeInCurrentTab("imgLoaded"));
     }
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/usage_stats/TabSuspensionTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/usage_stats/TabSuspensionTest.java
index 979061f..97e11c4 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/usage_stats/TabSuspensionTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/usage_stats/TabSuspensionTest.java
@@ -34,7 +34,7 @@
 import org.chromium.base.test.util.MinAndroidSdkLevel;
 import org.chromium.chrome.browser.ChromeTabbedActivity;
 import org.chromium.chrome.browser.ChromeTabbedActivity2;
-import org.chromium.chrome.browser.MockSafeBrowsingApiHandler;
+import org.chromium.chrome.browser.MockSafetyNetApiHandler;
 import org.chromium.chrome.browser.customtabs.CustomTabActivityTestRule;
 import org.chromium.chrome.browser.customtabs.CustomTabsIntentTestUtils;
 import org.chromium.chrome.browser.flags.ChromeSwitches;
@@ -330,8 +330,8 @@
         startLoadingUrl(mTab, mStartingUrl);
         waitForSuspendedTabToShow(mTab, STARTING_FQDN);
 
-        SafeBrowsingApiBridge.setHandler(new MockSafeBrowsingApiHandler());
-        MockSafeBrowsingApiHandler.addMockResponse(
+        SafeBrowsingApiBridge.setHandler(new MockSafetyNetApiHandler());
+        MockSafetyNetApiHandler.addMockResponse(
                 mDifferentUrl, "{\"matches\":[{\"threat_type\":\"5\"}]}");
         startLoadingUrl(mTab, mDifferentUrl);
 
diff --git a/chrome/app/settings_strings.grdp b/chrome/app/settings_strings.grdp
index 3a4cf77..8290eb4d 100644
--- a/chrome/app/settings_strings.grdp
+++ b/chrome/app/settings_strings.grdp
@@ -2803,9 +2803,6 @@
   <message name="IDS_SETTINGS_SEARCH_ENGINES_DELETE_CONFIRMATION_DESCRIPTION" desc="Description text for a dialog that confirms that a user wants to delete an existing search engine.">
     Are you sure you want to delete this search engine?
   </message>
-  <message name="IDS_SETTINGS_SEARCH_ENGINES_DEFAULT_ENGINES" desc="Label for 'default' Search Engines section">
-    Default search engines
-  </message>
   <message name="IDS_SETTINGS_SEARCH_ENGINES_SEARCH_ENGINES" desc="Label for 'Search Engines' section">
     Search engines
   </message>
@@ -2827,9 +2824,6 @@
   <message name="IDS_SETTINGS_SEARCH_ENGINES_INACTIVE_SHORTCUTS" desc="Label for 'Inactive shortcuts' section">
     Inactive shortcuts
   </message>
-  <message name="IDS_SETTINGS_SEARCH_ENGINES_NO_INACTIVE_SHORTCUTS" desc="Text shown when the 'Inactive Shortcuts' section is empty">
-    No inactive shortcuts
-  </message>
   <message name="IDS_SETTINGS_SEARCH_ENGINES_NO_OTHER_ENGINES" desc="Label shown when the 'other' Search engines section is empty">
     Other saved search engines will appear here
   </message>
@@ -2845,9 +2839,6 @@
   <message name="IDS_SETTINGS_SEARCH_ENGINES_SITE_OR_PAGE" desc="Label for a table column that holds the name of a site or page in the 'Site search' section">
     Site or page
   </message>
-  <message name="IDS_SETTINGS_SEARCH_ENGINES_INACTIVE_SITE" desc="Label for a table column that holds the name of a site or page in the 'Inactive shortcuts' section">
-    Inactive sites
-  </message>
   <message name="IDS_SETTINGS_SEARCH_ENGINES_SHORTCUT" desc="Label for Shortcut column header for a search engine or site search entry">
     Shortcut
   </message>
@@ -2866,9 +2857,6 @@
   <message name="IDS_SETTINGS_SEARCH_ENGINES_DEACTIVATE" desc="Text of the button that deactivates a search engine">
     Deactivate
   </message>
-  <message name="IDS_SETTINGS_SEARCH_ENGINES_REMOVE_FROM_LIST" desc="Label for a button that removes a search engine from the list of search engines">
-    Remove from list
-  </message>
   <message name="IDS_SETTINGS_SEARCH_ENGINES_MANAGE_EXTENSION" desc="Text displayed for a button that allows the user to manage a Chrome extension">
     Manage
   </message>
@@ -2893,9 +2881,6 @@
   <message name="IDS_SETTINGS_SEARCH_ENGINES_ADDITIONAL_INACTIVE_SITES" desc="Label for button that expands the Inactive Sites list of search engines to show all entries">
     Additional inactive sites
   </message>
-  <message name="IDS_SETTINGS_SEARCH_ENGINES_ADDITIONAL_EXTENSIONS" desc="Label for button that expands the list of extensions to show all entries">
-    Additional extensions
-  </message>
 
   <!-- Site Settings Page -->
   <message name="IDS_SETTINGS_EXCEPTIONS_EMBEDDED_ON_HOST" desc="Template text for a child row in the content Exceptions page view. Controls the permission setting for the parent page when embedded on the specified site.">
diff --git a/chrome/app/settings_strings_grdp/IDS_SETTINGS_SEARCH_ENGINES_ADDITIONAL_EXTENSIONS.png.sha1 b/chrome/app/settings_strings_grdp/IDS_SETTINGS_SEARCH_ENGINES_ADDITIONAL_EXTENSIONS.png.sha1
deleted file mode 100644
index 8ea3184..0000000
--- a/chrome/app/settings_strings_grdp/IDS_SETTINGS_SEARCH_ENGINES_ADDITIONAL_EXTENSIONS.png.sha1
+++ /dev/null
@@ -1 +0,0 @@
-9088779be9f0c794376464e1e9307d0731830cfa
\ No newline at end of file
diff --git a/chrome/app/settings_strings_grdp/IDS_SETTINGS_SEARCH_ENGINES_INACTIVE_SITE.png.sha1 b/chrome/app/settings_strings_grdp/IDS_SETTINGS_SEARCH_ENGINES_INACTIVE_SITE.png.sha1
deleted file mode 100644
index b3fac27..0000000
--- a/chrome/app/settings_strings_grdp/IDS_SETTINGS_SEARCH_ENGINES_INACTIVE_SITE.png.sha1
+++ /dev/null
@@ -1 +0,0 @@
-1d8bd33239fca3a230cd1bc406e9e82ff65f1e5b
\ No newline at end of file
diff --git a/chrome/app/settings_strings_grdp/IDS_SETTINGS_SEARCH_ENGINES_NO_INACTIVE_SHORTCUTS.png.sha1 b/chrome/app/settings_strings_grdp/IDS_SETTINGS_SEARCH_ENGINES_NO_INACTIVE_SHORTCUTS.png.sha1
deleted file mode 100644
index 271a2148..0000000
--- a/chrome/app/settings_strings_grdp/IDS_SETTINGS_SEARCH_ENGINES_NO_INACTIVE_SHORTCUTS.png.sha1
+++ /dev/null
@@ -1 +0,0 @@
-e4ea52a31b2dafc0d7f09240892b0e70ce041fbe
\ No newline at end of file
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn
index e5f9474..b50bf31 100644
--- a/chrome/browser/BUILD.gn
+++ b/chrome/browser/BUILD.gn
@@ -7616,7 +7616,7 @@
     ]
 
     if (is_android) {
-      if (enable_arcore || enable_cardboard) {
+      if (enable_arcore || enable_cardboard || enable_openxr) {
         deps += [ "//components/webxr/android" ]
       }
 
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc
index 81a3e67..2eec5ba3 100644
--- a/chrome/browser/about_flags.cc
+++ b/chrome/browser/about_flags.cc
@@ -1527,6 +1527,9 @@
     {"MlUrlScoringRerankFinalMatchesOnly", "true"},
     {"MlUrlScoringPreserveDefault", "true"},
 };
+const FeatureEntry::FeatureParam kOmniboxMlBatchUrlScoring[] = {
+    {"MlBatchUrlScoring", "true"},
+};
 
 const FeatureEntry::FeatureVariation kOmniboxMlUrlScoringVariations[] = {
     {"Run the model but do not rescore or rerank the matches (counterfactual)",
@@ -1540,6 +1543,8 @@
      "match",
      kOmniboxMlUrlScoringPreserveDefault,
      std::size(kOmniboxMlUrlScoringPreserveDefault), nullptr},
+    {"Run the model on a batch of matches", kOmniboxMlBatchUrlScoring,
+     std::size(kOmniboxMlBatchUrlScoring), nullptr},
 };
 const FeatureEntry::FeatureParam kRealboxTwoPreviousSearchRelatedSuggestions[] =
     {
@@ -6776,6 +6781,10 @@
                                     kStartSurfaceReturnTimeVariations,
                                     "StartSurfaceReturnTime")},
 
+    {"tab-drag-drop", flag_descriptions::kTabDragDropName,
+     flag_descriptions::kTabDragDropDescription, kOsAndroid,
+     FEATURE_VALUE_TYPE(chrome::android::kTabDragDropAndroid)},
+
     {"enable-tab-engagement-reporting",
      flag_descriptions::kTabEngagementReportingName,
      flag_descriptions::kTabEngagementReportingDescription, kOsAndroid,
diff --git a/chrome/browser/ash/arc/policy/arc_policy_bridge.cc b/chrome/browser/ash/arc/policy/arc_policy_bridge.cc
index 645106e..ae11c9b 100644
--- a/chrome/browser/ash/arc/policy/arc_policy_bridge.cc
+++ b/chrome/browser/ash/arc/policy/arc_policy_bridge.cc
@@ -7,6 +7,7 @@
 #include <string>
 #include <utility>
 
+#include "arc_policy_util.h"
 #include "ash/components/arc/arc_browser_context_keyed_service_factory_base.h"
 #include "ash/components/arc/arc_prefs.h"
 #include "ash/components/arc/session/arc_bridge_service.h"
@@ -47,13 +48,14 @@
 
 namespace {
 
-constexpr char kArcCaCerts[] = "caCerts";
 constexpr char kPolicyCompliantJson[] = "{ \"policyCompliant\": true }";
-constexpr char kArcRequiredKeyPairs[] = "requiredKeyPairs";
-constexpr char kPrivateKeySelectionEnabled[] = "privateKeySelectionEnabled";
-constexpr char kChoosePrivateKeyRules[] = "choosePrivateKeyRules";
 constexpr char kPolicyAppInstallType[] = "installType";
 constexpr char kPolicyAppInstallTypeForceInstalled[] = "FORCE_INSTALLED";
+constexpr char kPolicyCaCertType[] = "X509";
+constexpr char kPolicyRequiredKeyAlias[] = "alias";
+constexpr char kPolicyPrivateKeyAlias[] = "privateKeyAlias";
+constexpr char kPolicyPrivateKeyPackageNames[] = "packageNames";
+constexpr char kPolicyPlayStoreModeSupervised[] = "SUPERVISED";
 
 // invert_bool_value: If the Chrome policy and the ARC policy with boolean value
 // have opposite semantics, set this to true so the bool is inverted before
@@ -221,13 +223,15 @@
     }
 
     base::Value::Dict data;
-    data.Set("X509", *x509_data);
+    data.Set(kPolicyCaCertType, *x509_data);
     ca_certs.Append(std::move(data));
   }
   if (!ca_certs.empty()) {
-    filtered_policies->Set("credentialsConfigDisabled", base::Value(true));
+    filtered_policies->Set(policy_util::kArcPolicyKeyCredentialsConfigDisabled,
+                           base::Value(true));
   }
-  filtered_policies->Set(kArcCaCerts, std::move(ca_certs));
+  filtered_policies->Set(policy_util::kArcPolicyKeyCaCerts,
+                         std::move(ca_certs));
 }
 
 void AddRequiredKeyPairs(const CertStoreService* cert_store_service,
@@ -238,10 +242,11 @@
   base::Value::List cert_names;
   for (const auto& name : cert_store_service->get_required_cert_names()) {
     base::Value::Dict value;
-    value.Set("alias", name);
+    value.Set(kPolicyRequiredKeyAlias, name);
     cert_names.Append(std::move(value));
   }
-  filtered_policies->Set(kArcRequiredKeyPairs, std::move(cert_names));
+  filtered_policies->Set(policy_util::kArcPolicyKeyRequiredKeyPairs,
+                         std::move(cert_names));
 }
 
 bool LooksLikeAndroidPackageName(const std::string& name) {
@@ -272,13 +277,15 @@
   base::Value::List rules;
   for (const auto& name : cert_store_service->get_required_cert_names()) {
     base::Value::Dict value;
-    value.Set("privateKeyAlias", name);
-    value.Set("packageNames", arc_app_ids.Clone());
+    value.Set(kPolicyPrivateKeyAlias, name);
+    value.Set(kPolicyPrivateKeyPackageNames, arc_app_ids.Clone());
     rules.Append(std::move(value));
   }
 
-  filtered_policies->Set(kPrivateKeySelectionEnabled, true);
-  filtered_policies->Set(kChoosePrivateKeyRules, std::move(rules));
+  filtered_policies->Set(policy_util::kArcPolicyKeyPrivateKeySelectionEnabled,
+                         true);
+  filtered_policies->Set(policy_util::kArcPolicyKeyChoosePrivateKeyRules,
+                         std::move(rules));
 }
 
 // Finds managed configurations of applications in |arc_policy| and replace
@@ -335,28 +342,35 @@
   const PrefService* profile_prefs = profile->GetPrefs();
 
   // Keep them sorted by the ARC policy names.
-  MapBoolToBool("cameraDisabled", policy::key::kVideoCaptureAllowed, policy_map,
+  MapBoolToBool(policy_util::kArcPolicyKeyCameraDisabled,
+                policy::key::kVideoCaptureAllowed, policy_map,
                 /* invert_bool_value */ true, &filtered_policies);
   // Use the pref for "debuggingFeaturesDisabled" to avoid duplicating the
   // logic of handling DeveloperToolsDisabled / DeveloperToolsAvailability
   // policies.
   MapManagedIntPrefToBool(
-      "debuggingFeaturesDisabled", ::prefs::kDevToolsAvailability,
-      profile_prefs,
+      policy_util::kArcPolicyKeyDebuggingFeaturesDisabled,
+      ::prefs::kDevToolsAvailability, profile_prefs,
       static_cast<int>(
           policy::DeveloperToolsPolicyHandler::Availability::kDisallowed),
       &filtered_policies);
-  MapBoolToBool("printingDisabled", policy::key::kPrintingEnabled, policy_map,
+  MapBoolToBool(policy_util::kArcPolicyKeyPrintingDisabled,
+                policy::key::kPrintingEnabled, policy_map,
                 /* invert_bool_value */ true, &filtered_policies);
-  MapBoolToBool("screenCaptureDisabled", policy::key::kDisableScreenshots,
-                policy_map, false, &filtered_policies);
-  MapIntToBool("shareLocationDisabled", policy::key::kDefaultGeolocationSetting,
-               policy_map, 2 /*BlockGeolocation*/, &filtered_policies);
-  MapBoolToBool("unmuteMicrophoneDisabled", policy::key::kAudioCaptureAllowed,
-                policy_map, /* invert_bool_value */ true, &filtered_policies);
-  MapObjectToPresenceBool("setWallpaperDisabled", policy::key::kWallpaperImage,
-                          policy_map, &filtered_policies, {"url", "hash"});
-  MapBoolToBool("vpnConfigDisabled", policy::key::kVpnConfigAllowed, policy_map,
+  MapBoolToBool(policy_util::kArcPolicyKeyScreenCaptureDisabled,
+                policy::key::kDisableScreenshots, policy_map, false,
+                &filtered_policies);
+  MapIntToBool(policy_util::kArcPolicyKeyShareLocationDisabled,
+               policy::key::kDefaultGeolocationSetting, policy_map,
+               2 /*BlockGeolocation*/, &filtered_policies);
+  MapBoolToBool(policy_util::kArcPolicyKeyUnmuteMicrophoneDisabled,
+                policy::key::kAudioCaptureAllowed, policy_map,
+                /* invert_bool_value */ true, &filtered_policies);
+  MapObjectToPresenceBool(policy_util::kArcPolicyKeySetWallpaperDisabled,
+                          policy::key::kWallpaperImage, policy_map,
+                          &filtered_policies, {"url", "hash"});
+  MapBoolToBool(policy_util::kArcPolicyKeyVpnConfigDisabled,
+                policy::key::kVpnConfigAllowed, policy_map,
                 /* invert_bool_value */ true, &filtered_policies);
 }
 
@@ -372,14 +386,16 @@
   // "debuggingFeaturesDisabled".
   if (base::CommandLine::ForCurrentProcess()->HasSwitch(
           ash::switches::kForceDevToolsAvailable)) {
-    filtered_policies.Set("debuggingFeaturesDisabled", false);
+    filtered_policies.Set(policy_util::kArcPolicyKeyDebuggingFeaturesDisabled,
+                          false);
   }
 
   // Always enable APK Cache for affiliated users, and always disable it for
   // not affiliated ones.
-  filtered_policies.Set("apkCacheEnabled", is_affiliated);
+  filtered_policies.Set(policy_util::kArcPolicyKeyApkCacheEnabled,
+                        is_affiliated);
 
-  filtered_policies.Set("guid", guid);
+  filtered_policies.Set(policy_util::kArcPolicyKeyGuid, guid);
 
   // Always allow mounting physical media because mounts are controlled
   // outside of ARC based on policy in file_manager::VolumeManager. Since this
@@ -387,14 +403,15 @@
   // policy::key::kExternalStoragePolicy before, we hard-code it to false to
   // ensure that the old policy setting does not remain on the ARC side.
   // See b/217531658 for details.
-  filtered_policies.Set("mountPhysicalMediaDisabled", false);
+  filtered_policies.Set(policy_util::kArcPolicyKeyMountPhysicalMediaDisabled,
+                        false);
 
   if (profile->IsChild() &&
       ash::ProfileHelper::Get()->IsPrimaryProfile(profile)) {
     // Adds "playStoreMode" policy. The policy value is used to restrict the
     // user from being able to toggle between different accounts in ARC++.
     filtered_policies.Set(policy_util::kArcPolicyKeyPlayStoreMode,
-                          "SUPERVISED");
+                          kPolicyPlayStoreModeSupervised);
   }
 }
 
diff --git a/chrome/browser/ash/arc/policy/arc_policy_util.cc b/chrome/browser/ash/arc/policy/arc_policy_util.cc
index f630357..1f3b390 100644
--- a/chrome/browser/ash/arc/policy/arc_policy_util.cc
+++ b/chrome/browser/ash/arc/policy/arc_policy_util.cc
@@ -72,8 +72,10 @@
   }
 
   for (const auto it : dict.value().GetDict()) {
-    UMA_HISTOGRAM_ENUMERATION("Arc.Policy.Keys",
-                              GetPolicyKeyFromString(it.first));
+    absl::optional<ArcPolicyKey> key = GetPolicyKeyFromString(it.first);
+    if (key.has_value()) {
+      UMA_HISTOGRAM_ENUMERATION("Arc.Policy.Keys", key.value());
+    }
   }
 
   std::map<std::string, std::set<std::string>> install_type_map =
@@ -119,35 +121,69 @@
   return install_type_map;
 }
 
-ArcPolicyKey GetPolicyKeyFromString(const std::string& policy_key) {
-  if (policy_key == "accountTypesWithManagementDisabled") {
+absl::optional<ArcPolicyKey> GetPolicyKeyFromString(
+    const std::string& policy_key) {
+  if (policy_key == kArcPolicyKeyAvailableAppSetPolicyDeprecated ||
+      policy_key == kArcPolicyKeyWorkAccountAppWhitelistDeprecated ||
+      policy_key == kArcPolicyKeyGuid ||
+      policy_key == kArcPolicyKeyMountPhysicalMediaDisabled ||
+      policy_key == kArcPolicyKeyDpsInteractionsDisabled) {
+    // Ignore keys that are always set or represent server side flags.
+    return absl::nullopt;
+  }
+
+  if (policy_key == kArcPolicyKeyAccountTypesWithManagementDisabled) {
     return ArcPolicyKey::kAccountTypesWithManagementDisabled;
-  } else if (policy_key == "alwaysOnVpnPackage") {
+  } else if (policy_key == kArcPolicyKeyAlwaysOnVpnPackage) {
     return ArcPolicyKey::kAlwaysOnVpnPackage;
-  } else if (policy_key == "applications") {
+  } else if (policy_key == kArcPolicyKeyApplications) {
     return ArcPolicyKey::kApplications;
-  } else if (policy_key == "availableAppSetPolicy") {
-    return ArcPolicyKey::kAvailableAppSetPolicy;
-  } else if (policy_key == "complianceRules") {
+  } else if (policy_key == kArcPolicyKeyComplianceRules) {
     return ArcPolicyKey::kComplianceRules;
-  } else if (policy_key == "installUnknownSourcesDisabled") {
+  } else if (policy_key == kArcPolicyKeyInstallUnknownSourcesDisabled) {
     return ArcPolicyKey::kInstallUnknownSourcesDisabled;
-  } else if (policy_key == "maintenanceWindow") {
+  } else if (policy_key == kArcPolicyKeyMaintenanceWindow) {
     return ArcPolicyKey::kMaintenanceWindow;
-  } else if (policy_key == "modifyAccountsDisabled") {
+  } else if (policy_key == kArcPolicyKeyModifyAccountsDisabled) {
     return ArcPolicyKey::kModifyAccountsDisabled;
-  } else if (policy_key == "permissionGrants") {
+  } else if (policy_key == kArcPolicyKeyPermissionGrants) {
     return ArcPolicyKey::kPermissionGrants;
-  } else if (policy_key == "permittedAccessibilityServices") {
+  } else if (policy_key == kArcPolicyKeyPermittedAccessibilityServices) {
     return ArcPolicyKey::kPermittedAccessibilityServices;
-  } else if (policy_key == "playStoreMode") {
+  } else if (policy_key == kArcPolicyKeyPlayStoreMode) {
     return ArcPolicyKey::kPlayStoreMode;
-  } else if (policy_key == "shortSupportMessage") {
+  } else if (policy_key == kArcPolicyKeyShortSupportMessage) {
     return ArcPolicyKey::kShortSupportMessage;
-  } else if (policy_key == "statusReportingSettings") {
+  } else if (policy_key == kArcPolicyKeyStatusReportingSettings) {
     return ArcPolicyKey::kStatusReportingSettings;
-  } else if (policy_key == "workAccountAppWhitelist") {
-    return ArcPolicyKey::kWorkAccountAppWhitelist;
+  } else if (policy_key == kArcPolicyKeyApkCacheEnabled) {
+    return ArcPolicyKey::kApkCacheEnabled;
+  } else if (policy_key == kArcPolicyKeyDebuggingFeaturesDisabled) {
+    return ArcPolicyKey::kDebuggingFeaturesDisabled;
+  } else if (policy_key == kArcPolicyKeyCameraDisabled) {
+    return ArcPolicyKey::kCameraDisabled;
+  } else if (policy_key == kArcPolicyKeyPrintingDisabled) {
+    return ArcPolicyKey::kPrintingDisabled;
+  } else if (policy_key == kArcPolicyKeyScreenCaptureDisabled) {
+    return ArcPolicyKey::kScreenCaptureDisabled;
+  } else if (policy_key == kArcPolicyKeyShareLocationDisabled) {
+    return ArcPolicyKey::kShareLocationDisabled;
+  } else if (policy_key == kArcPolicyKeyUnmuteMicrophoneDisabled) {
+    return ArcPolicyKey::kUnmuteMicrophoneDisabled;
+  } else if (policy_key == kArcPolicyKeySetWallpaperDisabled) {
+    return ArcPolicyKey::kSetWallpaperDisabled;
+  } else if (policy_key == kArcPolicyKeyVpnConfigDisabled) {
+    return ArcPolicyKey::kVpnConfigDisabled;
+  } else if (policy_key == kArcPolicyKeyPrivateKeySelectionEnabled) {
+    return ArcPolicyKey::kPrivateKeySelectionEnabled;
+  } else if (policy_key == kArcPolicyKeyChoosePrivateKeyRules) {
+    return ArcPolicyKey::kChoosePrivateKeyRules;
+  } else if (policy_key == kArcPolicyKeyCredentialsConfigDisabled) {
+    return ArcPolicyKey::kCredentialsConfigDisabled;
+  } else if (policy_key == kArcPolicyKeyCaCerts) {
+    return ArcPolicyKey::kCaCerts;
+  } else if (policy_key == kArcPolicyKeyRequiredKeyPairs) {
+    return ArcPolicyKey::kRequiredKeyPairs;
   }
 
   LOG(WARNING) << "Unknown policy key: " << policy_key;
diff --git a/chrome/browser/ash/arc/policy/arc_policy_util.h b/chrome/browser/ash/arc/policy/arc_policy_util.h
index dc15e34..806be51 100644
--- a/chrome/browser/ash/arc/policy/arc_policy_util.h
+++ b/chrome/browser/ash/arc/policy/arc_policy_util.h
@@ -22,7 +22,8 @@
     "accountTypesWithManagementDisabled";
 constexpr char kArcPolicyKeyAlwaysOnVpnPackage[] = "alwaysOnVpnPackage";
 constexpr char kArcPolicyKeyApplications[] = "applications";
-constexpr char kArcPolicyKeyAvailableAppSetPolicy[] = "availableAppSetPolicy";
+constexpr char kArcPolicyKeyAvailableAppSetPolicyDeprecated[] =
+    "availableAppSetPolicy";
 constexpr char kArcPolicyKeyComplianceRules[] = "complianceRules";
 constexpr char kArcPolicyKeyInstallUnknownSourcesDisabled[] =
     "installUnknownSourcesDisabled";
@@ -35,8 +36,31 @@
 constexpr char kArcPolicyKeyShortSupportMessage[] = "shortSupportMessage";
 constexpr char kArcPolicyKeyStatusReportingSettings[] =
     "statusReportingSettings";
-constexpr char kArcPolicyKeyWorkAccountAppWhitelist[] =
+constexpr char kArcPolicyKeyWorkAccountAppWhitelistDeprecated[] =
     "workAccountAppWhitelist";
+constexpr char kArcPolicyKeyGuid[] = "guid";
+constexpr char kArcPolicyKeyApkCacheEnabled[] = "apkCacheEnabled";
+constexpr char kArcPolicyKeyMountPhysicalMediaDisabled[] =
+    "mountPhysicalMediaDisabled";
+constexpr char kArcPolicyKeyDebuggingFeaturesDisabled[] =
+    "debuggingFeaturesDisabled";
+constexpr char kArcPolicyKeyCameraDisabled[] = "cameraDisabled";
+constexpr char kArcPolicyKeyPrintingDisabled[] = "printingDisabled";
+constexpr char kArcPolicyKeyScreenCaptureDisabled[] = "screenCaptureDisabled";
+constexpr char kArcPolicyKeyShareLocationDisabled[] = "shareLocationDisabled";
+constexpr char kArcPolicyKeyUnmuteMicrophoneDisabled[] =
+    "unmuteMicrophoneDisabled";
+constexpr char kArcPolicyKeySetWallpaperDisabled[] = "setWallpaperDisabled";
+constexpr char kArcPolicyKeyVpnConfigDisabled[] = "vpnConfigDisabled";
+constexpr char kArcPolicyKeyPrivateKeySelectionEnabled[] =
+    "privateKeySelectionEnabled";
+constexpr char kArcPolicyKeyChoosePrivateKeyRules[] = "choosePrivateKeyRules";
+constexpr char kArcPolicyKeyCredentialsConfigDisabled[] =
+    "credentialsConfigDisabled";
+constexpr char kArcPolicyKeyCaCerts[] = "caCerts";
+constexpr char kArcPolicyKeyRequiredKeyPairs[] = "requiredKeyPairs";
+constexpr char kArcPolicyKeyDpsInteractionsDisabled[] =
+    "dpsInteractionsDisabled";
 
 // An app's install type specified by the policy.
 // See google3/wireless/android/enterprise/clouddps/proto/schema.proto.
@@ -65,7 +89,7 @@
   kAccountTypesWithManagementDisabled = 1,
   kAlwaysOnVpnPackage = 2,
   kApplications = 3,
-  kAvailableAppSetPolicy = 4,
+  kAvailableAppSetPolicyDeprecated = 4,
   kComplianceRules = 5,
   kInstallUnknownSourcesDisabled = 6,
   kMaintenanceWindow = 7,
@@ -75,8 +99,22 @@
   kPlayStoreMode = 11,
   kShortSupportMessage = 12,
   kStatusReportingSettings = 13,
-  kWorkAccountAppWhitelist = 14,
-  kMaxValue = kWorkAccountAppWhitelist,
+  kWorkAccountAppWhitelistDeprecated = 14,
+  kApkCacheEnabled = 15,
+  kDebuggingFeaturesDisabled = 16,
+  kCameraDisabled = 17,
+  kPrintingDisabled = 18,
+  kScreenCaptureDisabled = 19,
+  kShareLocationDisabled = 20,
+  kUnmuteMicrophoneDisabled = 21,
+  kSetWallpaperDisabled = 22,
+  kVpnConfigDisabled = 23,
+  kPrivateKeySelectionEnabled = 24,
+  kChoosePrivateKeyRules = 25,
+  kCredentialsConfigDisabled = 26,
+  kCaCerts = 27,
+  kRequiredKeyPairs = 28,
+  kMaxValue = kRequiredKeyPairs,
 };
 
 // Returns true if the account is managed. Otherwise false.
@@ -103,7 +141,8 @@
 InstallType GetInstallTypeEnumFromString(const std::string& install_type);
 
 // Converts a string to its corresponding ArcPolicyKey enum.
-ArcPolicyKey GetPolicyKeyFromString(const std::string& policy_key);
+absl::optional<ArcPolicyKey> GetPolicyKeyFromString(
+    const std::string& policy_key);
 
 // Parses policy JSON string to a dictionary.
 absl::optional<base::Value> ParsePolicyJson(const std::string& arc_policy);
diff --git a/chrome/browser/ash/arc/policy/arc_policy_util_unittest.cc b/chrome/browser/ash/arc/policy/arc_policy_util_unittest.cc
index 822ea7b..f25cabb 100644
--- a/chrome/browser/ash/arc/policy/arc_policy_util_unittest.cc
+++ b/chrome/browser/ash/arc/policy/arc_policy_util_unittest.cc
@@ -7,6 +7,7 @@
 #include <map>
 #include <string>
 
+#include "arc_policy_util.h"
 #include "base/json/json_writer.h"
 #include "base/test/metrics/histogram_tester.h"
 #include "base/values.h"
@@ -81,50 +82,37 @@
 TEST_F(ArcPolicyUtilTest, GetRequestedPackagesFromArcPolicy) {
   std::set<std::string> expected = {"testPackage", "testPackage6"};
   std::string policy = CreatePolicyWithAppInstalls(kTestMap);
+
   std::set<std::string> result =
       arc::policy_util::GetRequestedPackagesFromArcPolicy(policy);
 
   EXPECT_EQ(result, expected);
 }
 
+TEST_F(ArcPolicyUtilTest, RecordPolicyMetricsWithIgnoredKeys) {
+  std::set<std::string> test_keys = {
+      kArcPolicyKeyGuid,
+      kArcPolicyKeyAvailableAppSetPolicyDeprecated,
+      kArcPolicyKeyWorkAccountAppWhitelistDeprecated,
+      kArcPolicyKeyMountPhysicalMediaDisabled,
+      kArcPolicyKeyDpsInteractionsDisabled,
+  };
+  std::string policy = CreatePolicyWithKeys(test_keys);
+
+  arc::policy_util::RecordPolicyMetrics(policy);
+
+  tester_.ExpectTotalCount(kArcPolicyKeyHistogram, 0);
+}
+
 TEST_F(ArcPolicyUtilTest, RecordPolicyMetricsWithUnknownKeys) {
   std::set<std::string> test_keys = {
-      "guid",
+      "some_unknown_key",
   };
-
   std::string policy = CreatePolicyWithKeys(test_keys);
+
   arc::policy_util::RecordPolicyMetrics(policy);
 
   tester_.ExpectBucketCount(kArcPolicyKeyHistogram, ArcPolicyKey::kUnknown, 1);
-  tester_.ExpectBucketCount(kArcPolicyKeyHistogram,
-                            ArcPolicyKey::kAccountTypesWithManagementDisabled,
-                            0);
-  tester_.ExpectBucketCount(kArcPolicyKeyHistogram,
-                            ArcPolicyKey::kAlwaysOnVpnPackage, 0);
-  tester_.ExpectBucketCount(kArcPolicyKeyHistogram, ArcPolicyKey::kApplications,
-                            0);
-  tester_.ExpectBucketCount(kArcPolicyKeyHistogram,
-                            ArcPolicyKey::kAvailableAppSetPolicy, 0);
-  tester_.ExpectBucketCount(kArcPolicyKeyHistogram,
-                            ArcPolicyKey::kComplianceRules, 0);
-  tester_.ExpectBucketCount(kArcPolicyKeyHistogram,
-                            ArcPolicyKey::kInstallUnknownSourcesDisabled, 0);
-  tester_.ExpectBucketCount(kArcPolicyKeyHistogram,
-                            ArcPolicyKey::kMaintenanceWindow, 0);
-  tester_.ExpectBucketCount(kArcPolicyKeyHistogram,
-                            ArcPolicyKey::kModifyAccountsDisabled, 0);
-  tester_.ExpectBucketCount(kArcPolicyKeyHistogram,
-                            ArcPolicyKey::kPermissionGrants, 0);
-  tester_.ExpectBucketCount(kArcPolicyKeyHistogram,
-                            ArcPolicyKey::kPermittedAccessibilityServices, 0);
-  tester_.ExpectBucketCount(kArcPolicyKeyHistogram,
-                            ArcPolicyKey::kPlayStoreMode, 0);
-  tester_.ExpectBucketCount(kArcPolicyKeyHistogram,
-                            ArcPolicyKey::kShortSupportMessage, 0);
-  tester_.ExpectBucketCount(kArcPolicyKeyHistogram,
-                            ArcPolicyKey::kStatusReportingSettings, 0);
-  tester_.ExpectBucketCount(kArcPolicyKeyHistogram,
-                            ArcPolicyKey::kWorkAccountAppWhitelist, 0);
   tester_.ExpectTotalCount(kArcPolicyKeyHistogram, 1);
 }
 
@@ -133,7 +121,6 @@
       kArcPolicyKeyAccountTypesWithManagementDisabled,
       kArcPolicyKeyAlwaysOnVpnPackage,
       kArcPolicyKeyApplications,
-      kArcPolicyKeyAvailableAppSetPolicy,
       kArcPolicyKeyComplianceRules,
       kArcPolicyKeyInstallUnknownSourcesDisabled,
       kArcPolicyKeyMaintenanceWindow,
@@ -143,10 +130,23 @@
       kArcPolicyKeyPlayStoreMode,
       kArcPolicyKeyShortSupportMessage,
       kArcPolicyKeyStatusReportingSettings,
-      kArcPolicyKeyWorkAccountAppWhitelist,
+      kArcPolicyKeyApkCacheEnabled,
+      kArcPolicyKeyDebuggingFeaturesDisabled,
+      kArcPolicyKeyCameraDisabled,
+      kArcPolicyKeyPrintingDisabled,
+      kArcPolicyKeyScreenCaptureDisabled,
+      kArcPolicyKeyShareLocationDisabled,
+      kArcPolicyKeyUnmuteMicrophoneDisabled,
+      kArcPolicyKeySetWallpaperDisabled,
+      kArcPolicyKeyVpnConfigDisabled,
+      kArcPolicyKeyPrivateKeySelectionEnabled,
+      kArcPolicyKeyChoosePrivateKeyRules,
+      kArcPolicyKeyCredentialsConfigDisabled,
+      kArcPolicyKeyCaCerts,
+      kArcPolicyKeyRequiredKeyPairs,
   };
-
   std::string policy = CreatePolicyWithKeys(test_keys);
+
   arc::policy_util::RecordPolicyMetrics(policy);
 
   tester_.ExpectBucketCount(kArcPolicyKeyHistogram, ArcPolicyKey::kUnknown, 0);
@@ -158,8 +158,6 @@
   tester_.ExpectBucketCount(kArcPolicyKeyHistogram, ArcPolicyKey::kApplications,
                             1);
   tester_.ExpectBucketCount(kArcPolicyKeyHistogram,
-                            ArcPolicyKey::kAvailableAppSetPolicy, 1);
-  tester_.ExpectBucketCount(kArcPolicyKeyHistogram,
                             ArcPolicyKey::kComplianceRules, 1);
   tester_.ExpectBucketCount(kArcPolicyKeyHistogram,
                             ArcPolicyKey::kInstallUnknownSourcesDisabled, 1);
@@ -178,8 +176,33 @@
   tester_.ExpectBucketCount(kArcPolicyKeyHistogram,
                             ArcPolicyKey::kStatusReportingSettings, 1);
   tester_.ExpectBucketCount(kArcPolicyKeyHistogram,
-                            ArcPolicyKey::kWorkAccountAppWhitelist, 1);
-  tester_.ExpectTotalCount(kArcPolicyKeyHistogram, 14);
+                            ArcPolicyKey::kApkCacheEnabled, 1);
+  tester_.ExpectBucketCount(kArcPolicyKeyHistogram,
+                            ArcPolicyKey::kDebuggingFeaturesDisabled, 1);
+  tester_.ExpectBucketCount(kArcPolicyKeyHistogram,
+                            ArcPolicyKey::kCameraDisabled, 1);
+  tester_.ExpectBucketCount(kArcPolicyKeyHistogram,
+                            ArcPolicyKey::kPrintingDisabled, 1);
+  tester_.ExpectBucketCount(kArcPolicyKeyHistogram,
+                            ArcPolicyKey::kScreenCaptureDisabled, 1);
+  tester_.ExpectBucketCount(kArcPolicyKeyHistogram,
+                            ArcPolicyKey::kShareLocationDisabled, 1);
+  tester_.ExpectBucketCount(kArcPolicyKeyHistogram,
+                            ArcPolicyKey::kUnmuteMicrophoneDisabled, 1);
+  tester_.ExpectBucketCount(kArcPolicyKeyHistogram,
+                            ArcPolicyKey::kSetWallpaperDisabled, 1);
+  tester_.ExpectBucketCount(kArcPolicyKeyHistogram,
+                            ArcPolicyKey::kVpnConfigDisabled, 1);
+  tester_.ExpectBucketCount(kArcPolicyKeyHistogram,
+                            ArcPolicyKey::kPrivateKeySelectionEnabled, 1);
+  tester_.ExpectBucketCount(kArcPolicyKeyHistogram,
+                            ArcPolicyKey::kChoosePrivateKeyRules, 1);
+  tester_.ExpectBucketCount(kArcPolicyKeyHistogram,
+                            ArcPolicyKey::kCredentialsConfigDisabled, 1);
+  tester_.ExpectBucketCount(kArcPolicyKeyHistogram, ArcPolicyKey::kCaCerts, 1);
+  tester_.ExpectBucketCount(kArcPolicyKeyHistogram,
+                            ArcPolicyKey::kRequiredKeyPairs, 1);
+  tester_.ExpectTotalCount(kArcPolicyKeyHistogram, 26);
 }
 
 TEST_F(ArcPolicyUtilTest, RecordPolicyMetricsWithOneAppOfEachType) {
@@ -193,8 +216,8 @@
       {"testPackage7", "REQUIRED_FOR_SETUP"},
       {"testPackage8", "KIOSK"},
       {"testPackage9", "UNKNOWN"}};
-
   std::string policy = CreatePolicyWithAppInstalls(test_map);
+
   arc::policy_util::RecordPolicyMetrics(policy);
 
   tester_.ExpectBucketCount(kInstallTypeHistogram, kUnknownBucket, 1);
@@ -211,6 +234,7 @@
 
 TEST_F(ArcPolicyUtilTest, RecordPolicyMetricsWithComplexPolicy) {
   std::string policy = CreatePolicyWithAppInstalls(kTestMap);
+
   arc::policy_util::RecordPolicyMetrics(policy);
 
   tester_.ExpectBucketCount(kInstallTypeHistogram, kForceInstalledBucket, 1);
@@ -224,6 +248,7 @@
   std::map<std::string, std::string> test_map = {
       {"testPackage", "FORCE_INSTALLED"}};
   std::string policy = CreatePolicyWithAppInstalls(test_map);
+
   arc::policy_util::RecordPolicyMetrics(policy);
 
   tester_.ExpectBucketCount(kInstallTypeHistogram, kForceInstalledBucket, 1);
@@ -232,7 +257,9 @@
   test_map["anotherTestPackage"] = "BLOCKED";
   test_map["anotherTestPackage2"] = "KIOSK";
   policy = CreatePolicyWithAppInstalls(test_map);
+
   arc::policy_util::RecordPolicyMetrics(policy);
+
   tester_.ExpectBucketCount(kInstallTypeHistogram, kForceInstalledBucket, 2);
   tester_.ExpectBucketCount(kInstallTypeHistogram, kBlockedBucket, 1);
   tester_.ExpectBucketCount(kInstallTypeHistogram, kKioskBucket, 1);
diff --git a/chrome/browser/ash/eche_app/eche_app_manager_factory.cc b/chrome/browser/ash/eche_app/eche_app_manager_factory.cc
index bf08f5e..9f0de28 100644
--- a/chrome/browser/ash/eche_app/eche_app_manager_factory.cc
+++ b/chrome/browser/ash/eche_app/eche_app_manager_factory.cc
@@ -109,11 +109,12 @@
   }
   const auto gurl = GURL(url);
 
-  return LaunchBubble(gurl, icon, visible_name, phone_name,
-                      apps_launch_info_provider->GetConnectionStatusForUi(),
-                      apps_launch_info_provider->entry_point(),
-                      base::BindOnce(&EnsureStreamClose, profile),
-                      base::BindRepeating(&StreamGoBack, profile));
+  return LaunchBubble(
+      gurl, icon, visible_name, phone_name,
+      apps_launch_info_provider->GetConnectionStatusFromLastAttempt(),
+      apps_launch_info_provider->entry_point(),
+      base::BindOnce(&EnsureStreamClose, profile),
+      base::BindRepeating(&StreamGoBack, profile));
 }
 
 void RelaunchLast(Profile* profile) {
diff --git a/chrome/browser/ash/power/renderer_freezer_unittest.cc b/chrome/browser/ash/power/renderer_freezer_unittest.cc
index 3829f024..0c1b13b9 100644
--- a/chrome/browser/ash/power/renderer_freezer_unittest.cc
+++ b/chrome/browser/ash/power/renderer_freezer_unittest.cc
@@ -272,8 +272,8 @@
         rph_factory->CreateRenderProcessHost(profile_, site_instance.get());
 
     // Fake that the RenderProcessHost is hosting the gcm app.
-    extensions::ProcessMap::Get(profile_)
-        ->Insert(extension->id(), rph->GetID(), site_instance->GetId());
+    extensions::ProcessMap::Get(profile_)->Insert(extension->id(),
+                                                  rph->GetID());
 
     SimulateRenderProcessHostCreated(rph);
   }
diff --git a/chrome/browser/chrome_content_browser_client.cc b/chrome/browser/chrome_content_browser_client.cc
index 854b60ee..867f207 100644
--- a/chrome/browser/chrome_content_browser_client.cc
+++ b/chrome/browser/chrome_content_browser_client.cc
@@ -2394,15 +2394,6 @@
     extra_parts_[i]->SiteInstanceGotProcess(site_instance);
 }
 
-void ChromeContentBrowserClient::SiteInstanceDeleting(
-    SiteInstance* site_instance) {
-  if (!site_instance->HasProcess())
-    return;
-
-  for (size_t i = 0; i < extra_parts_.size(); ++i)
-    extra_parts_[i]->SiteInstanceDeleting(site_instance);
-}
-
 bool ChromeContentBrowserClient::ShouldSwapBrowsingInstancesForNavigation(
     SiteInstance* site_instance,
     const GURL& current_effective_url,
diff --git a/chrome/browser/chrome_content_browser_client.h b/chrome/browser/chrome_content_browser_client.h
index 3ac09b9..902dcaf 100644
--- a/chrome/browser/chrome_content_browser_client.h
+++ b/chrome/browser/chrome_content_browser_client.h
@@ -228,7 +228,6 @@
   bool ShouldEmbeddedFramesTryToReuseExistingProcess(
       content::RenderFrameHost* outermost_main_frame) override;
   void SiteInstanceGotProcess(content::SiteInstance* site_instance) override;
-  void SiteInstanceDeleting(content::SiteInstance* site_instance) override;
   bool ShouldSwapBrowsingInstancesForNavigation(
       content::SiteInstance* site_instance,
       const GURL& current_effective_url,
diff --git a/chrome/browser/chrome_content_browser_client_parts.h b/chrome/browser/chrome_content_browser_client_parts.h
index 485e534..017b9d7 100644
--- a/chrome/browser/chrome_content_browser_client_parts.h
+++ b/chrome/browser/chrome_content_browser_client_parts.h
@@ -47,7 +47,6 @@
 
   virtual void RenderProcessWillLaunch(content::RenderProcessHost* host) {}
   virtual void SiteInstanceGotProcess(content::SiteInstance* site_instance) {}
-  virtual void SiteInstanceDeleting(content::SiteInstance* site_instance) {}
   virtual void OverrideWebkitPrefs(content::WebContents* web_contents,
                                    blink::web_pref::WebPreferences* web_prefs) {
   }
diff --git a/chrome/browser/extensions/chrome_content_browser_client_extensions_part.cc b/chrome/browser/extensions/chrome_content_browser_client_extensions_part.cc
index 56b44ae..92604831 100644
--- a/chrome/browser/extensions/chrome_content_browser_client_extensions_part.cc
+++ b/chrome/browser/extensions/chrome_content_browser_client_extensions_part.cc
@@ -727,27 +727,13 @@
   if (site_instance->IsGuest())
     return;
 
+  // Note that this may be called more than once for multiple instances
+  // of the same extension, such as when the same hosted app is opened in
+  // unrelated tabs. This call will ignore duplicate insertions, which is fine,
+  // since we only need to track if the extension is in the process, rather
+  // than how many instances it has in that process.
   ProcessMap::Get(context)->Insert(extension->id(),
-                                   site_instance->GetProcess()->GetID(),
-                                   site_instance->GetId());
-}
-
-void ChromeContentBrowserClientExtensionsPart::SiteInstanceDeleting(
-    SiteInstance* site_instance) {
-  BrowserContext* context = site_instance->GetBrowserContext();
-  ExtensionRegistry* registry = ExtensionRegistry::Get(context);
-  if (!registry)
-    return;
-
-  const Extension* extension =
-      registry->enabled_extensions().GetExtensionOrAppByURL(
-          site_instance->GetSiteURL());
-  if (!extension)
-    return;
-
-  ProcessMap::Get(context)->Remove(extension->id(),
-                                   site_instance->GetProcess()->GetID(),
-                                   site_instance->GetId());
+                                   site_instance->GetProcess()->GetID());
 }
 
 bool ChromeContentBrowserClientExtensionsPart::
diff --git a/chrome/browser/extensions/chrome_content_browser_client_extensions_part.h b/chrome/browser/extensions/chrome_content_browser_client_extensions_part.h
index 83248ca..4a117a4 100644
--- a/chrome/browser/extensions/chrome_content_browser_client_extensions_part.h
+++ b/chrome/browser/extensions/chrome_content_browser_client_extensions_part.h
@@ -121,7 +121,6 @@
   // ChromeContentBrowserClientParts:
   void RenderProcessWillLaunch(content::RenderProcessHost* host) override;
   void SiteInstanceGotProcess(content::SiteInstance* site_instance) override;
-  void SiteInstanceDeleting(content::SiteInstance* site_instance) override;
   void OverrideWebkitPrefs(content::WebContents* web_contents,
                            blink::web_pref::WebPreferences* web_prefs) override;
   bool OverrideWebPreferencesAfterNavigation(
diff --git a/chrome/browser/extensions/chrome_extensions_browser_client.cc b/chrome/browser/extensions/chrome_extensions_browser_client.cc
index a077ee3..e8a8185d 100644
--- a/chrome/browser/extensions/chrome_extensions_browser_client.cc
+++ b/chrome/browser/extensions/chrome_extensions_browser_client.cc
@@ -62,6 +62,8 @@
 #include "chrome/browser/usb/usb_chooser_context.h"
 #include "chrome/browser/usb/usb_chooser_context_factory.h"
 #include "chrome/browser/web_applications/isolated_web_apps/isolated_web_app_url_info.h"
+#include "chrome/browser/web_applications/web_app_command_scheduler.h"
+#include "chrome/browser/web_applications/web_app_provider.h"
 #include "chrome/common/channel_info.h"
 #include "chrome/common/chrome_paths.h"
 #include "chrome/common/chrome_switches.h"
@@ -76,6 +78,8 @@
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/render_process_host.h"
 #include "content/public/browser/site_instance.h"
+#include "content/public/browser/storage_partition.h"
+#include "content/public/browser/storage_partition_config.h"
 #include "content/public/common/content_switches.h"
 #include "extensions/browser/api/content_settings/content_settings_service.h"
 #include "extensions/browser/api/core_extensions_browser_api_provider.h"
@@ -948,8 +952,25 @@
     base::expected<web_app::IsolatedWebAppUrlInfo, std::string> url_info =
         web_app::IsolatedWebAppUrlInfo::Create(owner_site_url);
     DCHECK(url_info.has_value()) << url_info.error();
-    return url_info->GetStoragePartitionConfigForControlledFrame(
-        browser_context, partition_name, in_memory);
+
+    content::StoragePartitionConfig storage_partition_config =
+        url_info->GetStoragePartitionConfigForControlledFrame(
+            browser_context, partition_name, in_memory);
+
+    // If persisted and not already loaded, register the StoragePartition with
+    // the web_app system.
+    content::StoragePartition* storage_partition =
+        browser_context->GetStoragePartition(storage_partition_config,
+                                             /*can_create=*/false);
+    if (!in_memory && storage_partition == nullptr) {
+      auto* profile = Profile::FromBrowserContext(browser_context);
+      auto* web_app_provider = web_app::WebAppProvider::GetForWebApps(profile);
+      CHECK(web_app_provider);
+      web_app_provider->scheduler().RegisterControlledFramePartition(
+          url_info->app_id(), partition_name, base::DoNothing());
+    }
+
+    return storage_partition_config;
   }
 
   return ExtensionsBrowserClient::GetWebViewStoragePartitionConfig(
diff --git a/chrome/browser/flag-metadata.json b/chrome/browser/flag-metadata.json
index b310f25..a20beb8 100644
--- a/chrome/browser/flag-metadata.json
+++ b/chrome/browser/flag-metadata.json
@@ -7181,6 +7181,11 @@
     "expiry_milestone": 120
   },
   {
+    "name": "tab-drag-drop",
+    "owners": [ "ranjithkagathi@google.com", "gurmeetk@google.com" ],
+    "expiry_milestone": 122
+  },
+  {
     "name": "tab-grid-new-transitions",
     "owners": [ "pakzhygitov", "bling-flags@google.com" ],
     "expiry_milestone": 120
diff --git a/chrome/browser/flag_descriptions.cc b/chrome/browser/flag_descriptions.cc
index 7122315c..6bb8165 100644
--- a/chrome/browser/flag_descriptions.cc
+++ b/chrome/browser/flag_descriptions.cc
@@ -3087,6 +3087,10 @@
     "Enables viewing the current stylus battery level in the stylus tools "
     "menu.";
 
+const char kTabDragDropName[] = "Tab Drag and Drop";
+const char kTabDragDropDescription[] =
+    "Enables Tab drag and drop UI to move tab across windows.";
+
 const char kTabEngagementReportingName[] = "Tab Engagement Metrics";
 const char kTabEngagementReportingDescription[] =
     "Tracks tab engagement and lifetime metrics.";
diff --git a/chrome/browser/flag_descriptions.h b/chrome/browser/flag_descriptions.h
index 5e42c5f..69559ce 100644
--- a/chrome/browser/flag_descriptions.h
+++ b/chrome/browser/flag_descriptions.h
@@ -1750,6 +1750,9 @@
 extern const char kSystemProxyForSystemServicesName[];
 extern const char kSystemProxyForSystemServicesDescription[];
 
+extern const char kTabDragDropName[];
+extern const char kTabDragDropDescription[];
+
 extern const char kTabEngagementReportingName[];
 extern const char kTabEngagementReportingDescription[];
 
diff --git a/chrome/browser/flags/android/chrome_feature_list.cc b/chrome/browser/flags/android/chrome_feature_list.cc
index 98a64bf..f1fab85 100644
--- a/chrome/browser/flags/android/chrome_feature_list.cc
+++ b/chrome/browser/flags/android/chrome_feature_list.cc
@@ -294,6 +294,7 @@
     &kSplitCompositorTask,
     &kStoreHoursAndroid,
     &kSwapPixelFormatToFixConvertFromTranslucent,
+    &kTabDragDropAndroid,
     &kTabEngagementReportingAndroid,
     &kTabGroupsAndroid,
     &kTabGroupsContinuationAndroid,
@@ -977,6 +978,10 @@
              "SwapPixelFormatToFixConvertFromTranslucent",
              base::FEATURE_ENABLED_BY_DEFAULT);
 
+BASE_FEATURE(kTabDragDropAndroid,
+             "TabDragDropAndroid",
+             base::FEATURE_DISABLED_BY_DEFAULT);
+
 BASE_FEATURE(kTabEngagementReportingAndroid,
              "TabEngagementReportingAndroid",
              base::FEATURE_DISABLED_BY_DEFAULT);
diff --git a/chrome/browser/flags/android/chrome_feature_list.h b/chrome/browser/flags/android/chrome_feature_list.h
index db3ad2cc..6abef2b 100644
--- a/chrome/browser/flags/android/chrome_feature_list.h
+++ b/chrome/browser/flags/android/chrome_feature_list.h
@@ -164,6 +164,7 @@
 BASE_DECLARE_FEATURE(kStoreHoursAndroid);
 BASE_DECLARE_FEATURE(kSuppressToolbarCaptures);
 BASE_DECLARE_FEATURE(kSwapPixelFormatToFixConvertFromTranslucent);
+BASE_DECLARE_FEATURE(kTabDragDropAndroid);
 BASE_DECLARE_FEATURE(kTabEngagementReportingAndroid);
 BASE_DECLARE_FEATURE(kTabGroupsAndroid);
 BASE_DECLARE_FEATURE(kTabGroupsContinuationAndroid);
diff --git a/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/ChromeFeatureList.java b/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/ChromeFeatureList.java
index 3f2b9498..7f81e8a4d 100644
--- a/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/ChromeFeatureList.java
+++ b/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/ChromeFeatureList.java
@@ -430,6 +430,7 @@
     public static final String SYNC_ANDROID_LIMIT_NTP_PROMO_IMPRESSIONS =
             "SyncAndroidLimitNTPPromoImpressions";
     public static final String SYNC_ENABLE_HISTORY_DATA_TYPE = "SyncEnableHistoryDataType";
+    public static final String TAB_DRAG_DROP_ANDROID = "TabDragDropAndroid";
     public static final String TAB_ENGAGEMENT_REPORTING_ANDROID = "TabEngagementReportingAndroid";
     public static final String TAB_GRID_LAYOUT_ANDROID = "TabGridLayoutAndroid";
     public static final String TAB_GROUPS_ANDROID = "TabGroupsAndroid";
diff --git a/chrome/browser/metrics/chrome_metrics_extensions_helper_unittest.cc b/chrome/browser/metrics/chrome_metrics_extensions_helper_unittest.cc
index 8c72ada..8bc875f 100644
--- a/chrome/browser/metrics/chrome_metrics_extensions_helper_unittest.cc
+++ b/chrome/browser/metrics/chrome_metrics_extensions_helper_unittest.cc
@@ -39,8 +39,7 @@
 
 #if BUILDFLAG(ENABLE_EXTENSIONS)
   // Tag |host| so that it's an extensions host.
-  extensions::ProcessMap::Get(profile)->Insert("1", host->GetID(),
-                                               site_instance->GetId());
+  extensions::ProcessMap::Get(profile)->Insert("1", host->GetID());
   EXPECT_TRUE(extensions_helper.IsExtensionProcess(host));
 #endif
   rph_factory.reset();
diff --git a/chrome/browser/performance_manager/public/user_tuning/user_performance_tuning_manager.h b/chrome/browser/performance_manager/public/user_tuning/user_performance_tuning_manager.h
index e4f4fda..016d245 100644
--- a/chrome/browser/performance_manager/public/user_tuning/user_performance_tuning_manager.h
+++ b/chrome/browser/performance_manager/public/user_tuning/user_performance_tuning_manager.h
@@ -120,6 +120,11 @@
       return memory_footprint_estimate_;
     }
 
+    void SetMemoryFootprintEstimateKbForTesting(
+        uint64_t memory_footprint_estimate) {
+      memory_footprint_estimate_ = memory_footprint_estimate;
+    }
+
     ::mojom::LifecycleUnitDiscardReason discard_reason() const {
       return discard_reason_;
     }
diff --git a/chrome/browser/resources/history/browser_service.ts b/chrome/browser/resources/history/browser_service.ts
index de69c208..fd67670 100644
--- a/chrome/browser/resources/history/browser_service.ts
+++ b/chrome/browser/resources/history/browser_service.ts
@@ -26,8 +26,7 @@
   removeBookmark(url: string): void;
   removeVisits(removalList: RemoveVisitsRequest): Promise<void>;
   openForeignSessionAllTabs(sessionTag: string): void;
-  openForeignSessionTab(
-      sessionTag: string, windowId: number, tabId: number, e: MouseEvent): void;
+  openForeignSessionTab(sessionTag: string, tabId: number, e: MouseEvent): void;
   deleteForeignSession(sessionTag: string): void;
   openClearBrowsingData(): void;
   recordHistogram(histogram: string, value: number, max: number): void;
@@ -59,14 +58,12 @@
   }
 
   openForeignSessionAllTabs(sessionTag: string) {
-    chrome.send('openForeignSession', [sessionTag]);
+    chrome.send('openForeignSessionAllTabs', [sessionTag]);
   }
 
-  openForeignSessionTab(
-      sessionTag: string, windowId: number, tabId: number, e: MouseEvent) {
-    chrome.send('openForeignSession', [
+  openForeignSessionTab(sessionTag: string, tabId: number, e: MouseEvent) {
+    chrome.send('openForeignSessionTab', [
       sessionTag,
-      String(windowId),
       String(tabId),
       e.button || 0,
       e.altKey,
diff --git a/chrome/browser/resources/history/synced_device_card.ts b/chrome/browser/resources/history/synced_device_card.ts
index 3e8a7a5..8fdf646 100644
--- a/chrome/browser/resources/history/synced_device_card.ts
+++ b/chrome/browser/resources/history/synced_device_card.ts
@@ -123,8 +123,7 @@
     browserService.recordHistogram(
         SYNCED_TABS_HISTOGRAM_NAME, SyncedTabsHistogram.LINK_CLICKED,
         SyncedTabsHistogram.LIMIT);
-    browserService.openForeignSessionTab(
-        this.sessionTag, tab.windowId, tab.sessionId, e);
+    browserService.openForeignSessionTab(this.sessionTag, tab.sessionId, e);
     e.preventDefault();
   }
 
diff --git a/chrome/browser/resources/settings/appearance_page/appearance_page.ts b/chrome/browser/resources/settings/appearance_page/appearance_page.ts
index ca453aaf..77d57cd 100644
--- a/chrome/browser/resources/settings/appearance_page/appearance_page.ts
+++ b/chrome/browser/resources/settings/appearance_page/appearance_page.ts
@@ -300,7 +300,8 @@
   /** @return Whether to show the "USE QT" button. */
   private showUseQt_(themeId: string): boolean {
     return (!!themeId || this.systemTheme_ !== SystemTheme.QT) &&
-        !this.appearanceBrowserProxy_.isChildAccount();
+        !this.appearanceBrowserProxy_.isChildAccount() &&
+        loadTimeData.getBoolean('allowQtTheme');
   }
 
   /**
diff --git a/chrome/browser/resources/settings/chromeos/BUILD.gn b/chrome/browser/resources/settings/chromeos/BUILD.gn
index 9916faa..9cfdf3a 100644
--- a/chrome/browser/resources/settings/chromeos/BUILD.gn
+++ b/chrome/browser/resources/settings/chromeos/BUILD.gn
@@ -176,9 +176,8 @@
   sources = [
     "$root_gen_dir/ash/webui/personalization_app/search/search.mojom-webui.ts",
   ]
-  outputs = [
-    "$tsc_input_dir/mojom-webui/search/personalization_search.mojom-webui.ts",
-  ]
+  outputs =
+      [ "$tsc_input_dir/mojom-webui/personalization_search.mojom-webui.ts" ]
 }
 
 copy("generate_search_mojom") {
@@ -190,7 +189,7 @@
     "$root_gen_dir/chrome/browser/ui/webui/settings/ash/search/search_result_icon.mojom-webui.ts",
     "$root_gen_dir/chrome/browser/ui/webui/settings/ash/search/user_action_recorder.mojom-webui.ts",
   ]
-  outputs = [ "$tsc_input_dir/mojom-webui/search/{{source_file_part}}" ]
+  outputs = [ "$tsc_input_dir/mojom-webui/{{source_file_part}}" ]
 }
 
 grit("resources") {
diff --git a/chrome/browser/resources/settings/chromeos/metrics_recorder.ts b/chrome/browser/resources/settings/chromeos/metrics_recorder.ts
index ccdc771..6419209 100644
--- a/chrome/browser/resources/settings/chromeos/metrics_recorder.ts
+++ b/chrome/browser/resources/settings/chromeos/metrics_recorder.ts
@@ -9,8 +9,8 @@
  * user action recording.
  */
 
-import {SettingChangeValue, UserActionRecorder, UserActionRecorderInterface} from './mojom-webui/search/user_action_recorder.mojom-webui.js';
 import {Setting} from './mojom-webui/setting.mojom-webui.js';
+import {SettingChangeValue, UserActionRecorder, UserActionRecorderInterface} from './mojom-webui/user_action_recorder.mojom-webui.js';
 
 let userActionRecorder: UserActionRecorderInterface|null = null;
 
diff --git a/chrome/browser/resources/settings/chromeos/metrics_utils.ts b/chrome/browser/resources/settings/chromeos/metrics_utils.ts
index 2b3c660..6b62696 100644
--- a/chrome/browser/resources/settings/chromeos/metrics_utils.ts
+++ b/chrome/browser/resources/settings/chromeos/metrics_utils.ts
@@ -8,8 +8,8 @@
 
 import {assert} from 'chrome://resources/js/assert_ts.js';
 
-import {SettingChangeValue} from './mojom-webui/search/user_action_recorder.mojom-webui.js';
 import {Setting} from './mojom-webui/setting.mojom-webui.js';
+import {SettingChangeValue} from './mojom-webui/user_action_recorder.mojom-webui.js';
 
 interface SettingMetric {
   setting: Setting;
diff --git a/chrome/browser/resources/settings/chromeos/os_settings.gni b/chrome/browser/resources/settings/chromeos/os_settings.gni
index 5c500b92..dc9a07dc 100644
--- a/chrome/browser/resources/settings/chromeos/os_settings.gni
+++ b/chrome/browser/resources/settings/chromeos/os_settings.gni
@@ -413,10 +413,10 @@
   "mojom-webui/routes.mojom-webui.ts",
   "mojom-webui/setting.mojom-webui.ts",
 
-  "mojom-webui/search/personalization_search.mojom-webui.ts",
-  "mojom-webui/search/search.mojom-webui.ts",
-  "mojom-webui/search/search_result_icon.mojom-webui.ts",
-  "mojom-webui/search/user_action_recorder.mojom-webui.ts",
+  "mojom-webui/personalization_search.mojom-webui.ts",
+  "mojom-webui/search.mojom-webui.ts",
+  "mojom-webui/search_result_icon.mojom-webui.ts",
+  "mojom-webui/user_action_recorder.mojom-webui.ts",
 ]
 
 # Files sourced from their checked-in version under src
diff --git a/chrome/browser/resources/settings/chromeos/os_settings.ts b/chrome/browser/resources/settings/chromeos/os_settings.ts
index 61aa55f..52dfb7a 100644
--- a/chrome/browser/resources/settings/chromeos/os_settings.ts
+++ b/chrome/browser/resources/settings/chromeos/os_settings.ts
@@ -133,12 +133,12 @@
 export {recordClick, recordNavigation, recordPageBlur, recordPageFocus, recordSearch, recordSettingChange, setUserActionRecorderForTesting} from './metrics_recorder.js';
 export * as appNotificationHandlerMojom from './mojom-webui/app_notification_handler.mojom-webui.js';
 export * as crosAudioConfigMojom from './mojom-webui/cros_audio_config.mojom-webui.js';
+export * as personalizationSearchMojom from './mojom-webui/personalization_search.mojom-webui.js';
 export * as routesMojom from './mojom-webui/routes.mojom-webui.js';
-export * as personalizationSearchMojom from './mojom-webui/search/personalization_search.mojom-webui.js';
-export * as searchMojom from './mojom-webui/search/search.mojom-webui.js';
-export * as searchResultIconMojom from './mojom-webui/search/search_result_icon.mojom-webui.js';
-export * as userActionRecorderMojom from './mojom-webui/search/user_action_recorder.mojom-webui.js';
+export * as searchMojom from './mojom-webui/search.mojom-webui.js';
+export * as searchResultIconMojom from './mojom-webui/search_result_icon.mojom-webui.js';
 export * as settingMojom from './mojom-webui/setting.mojom-webui.js';
+export * as userActionRecorderMojom from './mojom-webui/user_action_recorder.mojom-webui.js';
 export {AndroidSmsInfo, MultiDeviceBrowserProxy, MultiDeviceBrowserProxyImpl} from './multidevice_page/multidevice_browser_proxy.js';
 export {MultiDeviceFeature, MultiDeviceFeatureState, MultiDevicePageContentData, MultiDeviceSettingsMode, PhoneHubFeatureAccessProhibitedReason, PhoneHubFeatureAccessStatus, PhoneHubPermissionsSetupAction, PhoneHubPermissionsSetupFeatureCombination, PhoneHubPermissionsSetupFlowScreens, PhoneHubPermissionsSetupMode} from './multidevice_page/multidevice_constants.js';
 export {NotificationAccessSetupOperationStatus, SettingsMultideviceNotificationAccessSetupDialogElement} from './multidevice_page/multidevice_notification_access_setup_dialog.js';
diff --git a/chrome/browser/resources/settings/chromeos/os_settings_search_box/os_search_result_row.ts b/chrome/browser/resources/settings/chromeos/os_settings_search_box/os_search_result_row.ts
index 9a1088e..b9bdcf5 100644
--- a/chrome/browser/resources/settings/chromeos/os_settings_search_box/os_search_result_row.ts
+++ b/chrome/browser/resources/settings/chromeos/os_settings_search_box/os_search_result_row.ts
@@ -17,9 +17,9 @@
 import {OpenWindowProxyImpl} from 'chrome://resources/js/open_window_proxy.js';
 import {PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 
-import {SearchResult as PersonalizationSearchResult} from '../mojom-webui/search/personalization_search.mojom-webui.js';
-import {SearchResult as SettingsSearchResult, SearchResultIdentifier, SearchResultType} from '../mojom-webui/search/search.mojom-webui.js';
-import {SearchResultIcon} from '../mojom-webui/search/search_result_icon.mojom-webui.js';
+import {SearchResult as PersonalizationSearchResult} from '../mojom-webui/personalization_search.mojom-webui.js';
+import {SearchResult as SettingsSearchResult, SearchResultIdentifier, SearchResultType} from '../mojom-webui/search.mojom-webui.js';
+import {SearchResultIcon} from '../mojom-webui/search_result_icon.mojom-webui.js';
 import {Router} from '../router.js';
 import {SearchResult} from '../search/combined_search_handler.js';
 
diff --git a/chrome/browser/resources/settings/chromeos/os_settings_search_box/os_settings_search_box.ts b/chrome/browser/resources/settings/chromeos/os_settings_search_box/os_settings_search_box.ts
index f4d1495..baaea78 100644
--- a/chrome/browser/resources/settings/chromeos/os_settings_search_box/os_settings_search_box.ts
+++ b/chrome/browser/resources/settings/chromeos/os_settings_search_box/os_settings_search_box.ts
@@ -27,8 +27,8 @@
 
 import {castExists} from '../assert_extras.js';
 import {recordSearch} from '../metrics_recorder.js';
-import {SearchResultsObserverInterface as PersonalizationSearchResultsObserverInterface, SearchResultsObserverReceiver as PersonalizationSearchResultsObserverReceiver} from '../mojom-webui/search/personalization_search.mojom-webui.js';
-import {ParentResultBehavior, SearchResultsObserverInterface, SearchResultsObserverReceiver} from '../mojom-webui/search/search.mojom-webui.js';
+import {SearchResultsObserverInterface as PersonalizationSearchResultsObserverInterface, SearchResultsObserverReceiver as PersonalizationSearchResultsObserverReceiver} from '../mojom-webui/personalization_search.mojom-webui.js';
+import {ParentResultBehavior, SearchResultsObserverInterface, SearchResultsObserverReceiver} from '../mojom-webui/search.mojom-webui.js';
 import {routes} from '../os_settings_routes.js';
 import {Router} from '../router.js';
 import {combinedSearch, getPersonalizationSearchHandler, getSettingsSearchHandler, SearchResult} from '../search/combined_search_handler.js';
diff --git a/chrome/browser/resources/settings/chromeos/search/combined_search_handler.ts b/chrome/browser/resources/settings/chromeos/search/combined_search_handler.ts
index fc4f11b..1007bc9 100644
--- a/chrome/browser/resources/settings/chromeos/search/combined_search_handler.ts
+++ b/chrome/browser/resources/settings/chromeos/search/combined_search_handler.ts
@@ -4,8 +4,8 @@
 
 import {String16} from 'chrome://resources/mojo/mojo/public/mojom/base/string16.mojom-webui.js';
 
-import {SearchResult as PersonalizationSearchResult} from '../mojom-webui/search/personalization_search.mojom-webui.js';
-import {ParentResultBehavior, SearchResult as SettingsSearchResult} from '../mojom-webui/search/search.mojom-webui.js';
+import {SearchResult as PersonalizationSearchResult} from '../mojom-webui/personalization_search.mojom-webui.js';
+import {ParentResultBehavior, SearchResult as SettingsSearchResult} from '../mojom-webui/search.mojom-webui.js';
 
 import {getPersonalizationSearchHandler} from './personalization_search_handler.js';
 import {getSettingsSearchHandler} from './settings_search_handler.js';
diff --git a/chrome/browser/resources/settings/chromeos/search/personalization_search_handler.ts b/chrome/browser/resources/settings/chromeos/search/personalization_search_handler.ts
index f070cec..1288c769a 100644
--- a/chrome/browser/resources/settings/chromeos/search/personalization_search_handler.ts
+++ b/chrome/browser/resources/settings/chromeos/search/personalization_search_handler.ts
@@ -10,7 +10,7 @@
  * personalization search.
  */
 
-import {SearchHandler, SearchHandlerInterface} from '../mojom-webui/search/personalization_search.mojom-webui.js';
+import {SearchHandler, SearchHandlerInterface} from '../mojom-webui/personalization_search.mojom-webui.js';
 
 let personalizationSearchHandler: SearchHandlerInterface|null = null;
 
diff --git a/chrome/browser/resources/settings/chromeos/search/settings_search_handler.ts b/chrome/browser/resources/settings/chromeos/search/settings_search_handler.ts
index cbfd694d..a9857b6 100644
--- a/chrome/browser/resources/settings/chromeos/search/settings_search_handler.ts
+++ b/chrome/browser/resources/settings/chromeos/search/settings_search_handler.ts
@@ -9,7 +9,7 @@
  * OS settings search.
  */
 
-import {SearchHandler, SearchHandlerInterface} from '../mojom-webui/search/search.mojom-webui.js';
+import {SearchHandler, SearchHandlerInterface} from '../mojom-webui/search.mojom-webui.js';
 
 let settingsSearchHandler: SearchHandlerInterface|null = null;
 
diff --git a/chrome/browser/resources/side_panel/bookmarks/power_bookmark_row.html b/chrome/browser/resources/side_panel/bookmarks/power_bookmark_row.html
index 3573c177..a5745dad 100644
--- a/chrome/browser/resources/side_panel/bookmarks/power_bookmark_row.html
+++ b/chrome/browser/resources/side_panel/bookmarks/power_bookmark_row.html
@@ -34,7 +34,7 @@
   }
 
   :host-context([chrome-refresh-2023]) .sp-labelless-input:hover {
-    --cr-input-background-color: transparent;
+    --cr-input-hover-background-color: transparent;
   }
 </style>
 
diff --git a/chrome/browser/resources/side_panel/bookmarks/power_bookmark_row.ts b/chrome/browser/resources/side_panel/bookmarks/power_bookmark_row.ts
index 976aed0..c19a3dd1 100644
--- a/chrome/browser/resources/side_panel/bookmarks/power_bookmark_row.ts
+++ b/chrome/browser/resources/side_panel/bookmarks/power_bookmark_row.ts
@@ -109,16 +109,25 @@
     this.addEventListener('focus', this.onFocus_);
   }
 
+  override focus() {
+    this.$.crUrlListItem.focus();
+  }
+
   private onKeydown_(e: KeyboardEvent) {
-    if (e.shiftKey && e.key === 'Tab' &&
-        this.shadowRoot!.activeElement === this.$.crUrlListItem) {
+    if (this.shadowRoot!.activeElement !== this.$.crUrlListItem) {
+      return;
+    }
+    if (e.shiftKey && e.key === 'Tab') {
       // Hitting shift tab from CrUrlListItem to traverse focus backwards will
       // attempt to move focus to this element, which is responsible for
       // delegating focus but should itself not be focusable. So when the user
       // hits shift tab, immediately hijack focus onto itself so that the
       // browser moves focus to the focusable element before it once it
       // processes the shift tab.
-      this.focus();
+      super.focus();
+    } else if (e.key === 'Enter') {
+      // Prevent iron-list from moving focus.
+      e.stopPropagation();
     }
   }
 
diff --git a/chrome/browser/resources/side_panel/shared/sp_shared_style.css b/chrome/browser/resources/side_panel/shared/sp_shared_style.css
index f6cd51f..9259541f 100644
--- a/chrome/browser/resources/side_panel/shared/sp_shared_style.css
+++ b/chrome/browser/resources/side_panel/shared/sp_shared_style.css
@@ -87,11 +87,6 @@
   --cr-input-padding-top: 8px;
 }
 
-:host-context([chrome-refresh-2023]) .sp-labelless-input:hover {
-  --cr-input-background-color:
-      var(--color-side-panel-textfield-background-hover);
-}
-
 .sp-icon-buttons-row {
   align-items: center;
   display: grid;
diff --git a/chrome/browser/resources/signin/profile_picker/profile_card.html b/chrome/browser/resources/signin/profile_picker/profile_card.html
index 81e78f3..71cfcdc8 100644
--- a/chrome/browser/resources/signin/profile_picker/profile_card.html
+++ b/chrome/browser/resources/signin/profile_picker/profile_card.html
@@ -80,6 +80,11 @@
     top: 0;
   }
 
+  :host-context([chrome-refresh-2023]) cr-input {
+    --cr-input-hover-background-color: transparent;
+    --cr-input-border-bottom: none;
+  }
+
   #hoverUnderline {
     border-bottom: 2px solid var(--google-grey-300);
     border-radius: 0;
diff --git a/chrome/browser/resources/webui_gallery/demos/cr_input/index.html b/chrome/browser/resources/webui_gallery/demos/cr_input/index.html
index 831f5a2..6a4ef4a 100644
--- a/chrome/browser/resources/webui_gallery/demos/cr_input/index.html
+++ b/chrome/browser/resources/webui_gallery/demos/cr_input/index.html
@@ -1,5 +1,5 @@
 <!doctype html>
-<html>
+<html $i18n{chromeRefresh2023Attribute}>
   <head>
     <meta charset="utf-8">
     <title>cr-input demo</title>
diff --git a/chrome/browser/touch_to_fill/android/javatests/src/org/chromium/chrome/browser/touch_to_fill/TouchToFillViewTest.java b/chrome/browser/touch_to_fill/android/javatests/src/org/chromium/chrome/browser/touch_to_fill/TouchToFillViewTest.java
index 4aeed49..6b16a22 100644
--- a/chrome/browser/touch_to_fill/android/javatests/src/org/chromium/chrome/browser/touch_to_fill/TouchToFillViewTest.java
+++ b/chrome/browser/touch_to_fill/android/javatests/src/org/chromium/chrome/browser/touch_to_fill/TouchToFillViewTest.java
@@ -51,6 +51,7 @@
 import org.chromium.base.Callback;
 import org.chromium.base.test.util.CommandLineFlags;
 import org.chromium.base.test.util.CriteriaHelper;
+import org.chromium.base.test.util.DisabledTest;
 import org.chromium.base.test.util.DoNotBatch;
 import org.chromium.base.test.util.ScalableTimeout;
 import org.chromium.chrome.browser.app.ChromeActivity;
@@ -474,6 +475,7 @@
                         CAM.getUsername()));
     }
 
+    @DisabledTest(message = "https:://crbug.com/1447085")
     @Test
     @MediumTest
     public void testSheetStartsInFullHeightForAccessibility() {
diff --git a/chrome/browser/ui/BUILD.gn b/chrome/browser/ui/BUILD.gn
index 89e744c..274aca58 100644
--- a/chrome/browser/ui/BUILD.gn
+++ b/chrome/browser/ui/BUILD.gn
@@ -305,8 +305,6 @@
     "translate/translate_bubble_model.h",
     "translate/translate_bubble_model_impl.cc",
     "translate/translate_bubble_model_impl.h",
-    "translate/translate_bubble_ui_action_logger.cc",
-    "translate/translate_bubble_ui_action_logger.h",
     "translate/translate_language_list_model.h",
     "ui_features.cc",
     "ui_features.h",
diff --git a/chrome/browser/ui/browser_commands.cc b/chrome/browser/ui/browser_commands.cc
index e0ba15a..1a974c3 100644
--- a/chrome/browser/ui/browser_commands.cc
+++ b/chrome/browser/ui/browser_commands.cc
@@ -92,7 +92,6 @@
 #include "chrome/browser/ui/tabs/tab_group.h"
 #include "chrome/browser/ui/tabs/tab_group_model.h"
 #include "chrome/browser/ui/tabs/tab_strip_user_gesture_details.h"
-#include "chrome/browser/ui/translate/translate_bubble_ui_action_logger.h"
 #include "chrome/browser/ui/ui_features.h"
 #include "chrome/browser/ui/user_education/reopen_tab_in_product_help.h"
 #include "chrome/browser/ui/user_education/reopen_tab_in_product_help_factory.h"
@@ -208,23 +207,6 @@
 const char kBackForwardNavigationIsTriggered[] =
     "back_forward_navigation_is_triggered";
 
-translate::TranslateBubbleUiEvent TranslateBubbleResultToUiEvent(
-    ShowTranslateBubbleResult result) {
-  switch (result) {
-    default:
-      NOTREACHED();
-      [[fallthrough]];
-    case ShowTranslateBubbleResult::SUCCESS:
-      return translate::TranslateBubbleUiEvent::BUBBLE_SHOWN;
-    case ShowTranslateBubbleResult::BROWSER_WINDOW_MINIMIZED:
-      return translate::TranslateBubbleUiEvent::
-          BUBBLE_NOT_SHOWN_WINDOW_MINIMIZED;
-    case ShowTranslateBubbleResult::EDITABLE_FIELD_IS_ACTIVE:
-      return translate::TranslateBubbleUiEvent::
-          BUBBLE_NOT_SHOWN_EDITABLE_FIELD_IS_ACTIVE;
-  }
-}
-
 // Creates a new tabbed browser window, with the same size, type and profile as
 // |original_browser|'s window, inserts |contents| into it, and shows it.
 void CreateAndShowNewWindowWithContents(
@@ -1454,12 +1436,9 @@
     else if (chrome_translate_client->GetLanguageState().IsPageTranslated())
       step = translate::TRANSLATE_STEP_AFTER_TRANSLATE;
   }
-  ShowTranslateBubbleResult result = browser->window()->ShowTranslateBubble(
+  browser->window()->ShowTranslateBubble(
       web_contents, step, source_language, target_language,
       translate::TranslateErrors::NONE, true);
-  if (result != ShowTranslateBubbleResult::SUCCESS)
-    translate::ReportTranslateBubbleUiAction(
-        TranslateBubbleResultToUiEvent(result));
 }
 
 void ManagePasswordsForPage(Browser* browser) {
diff --git a/chrome/browser/ui/color/chrome_color_id.h b/chrome/browser/ui/color/chrome_color_id.h
index 700596b..33b8ad3 100644
--- a/chrome/browser/ui/color/chrome_color_id.h
+++ b/chrome/browser/ui/color/chrome_color_id.h
@@ -33,6 +33,7 @@
   E_CPONLY(kColorBookmarkBarBackground) \
   E_CPONLY(kColorBookmarkBarForeground) \
   E_CPONLY(kColorBookmarkBarSeparator) \
+  E_CPONLY(kColorBookmarkBarSeparatorChromeRefresh) \
   E_CPONLY(kColorBookmarkButtonIcon) \
   E_CPONLY(kColorBookmarkDragImageBackground) \
   E_CPONLY(kColorBookmarkDragImageCountBackground) \
@@ -365,7 +366,6 @@
   E_CPONLY(kColorSidePanelFilterChipBackgroundHover) \
   E_CPONLY(kColorSidePanelFilterChipBackgroundSelected) \
   E_CPONLY(kColorSidePanelScrollbarThumb) \
-  E_CPONLY(kColorSidePanelTextfieldBackgroundHover) \
   E_CPONLY(kColorSidePanelTextfieldBorder) \
   /* Status bubble colors. */ \
   E_CPONLY(kColorStatusBubbleBackgroundFrameActive) \
diff --git a/chrome/browser/ui/color/chrome_color_mixer.cc b/chrome/browser/ui/color/chrome_color_mixer.cc
index 0d54adf..d8564a0c 100644
--- a/chrome/browser/ui/color/chrome_color_mixer.cc
+++ b/chrome/browser/ui/color/chrome_color_mixer.cc
@@ -186,6 +186,8 @@
       gfx::kGoogleGrey500, kColorBookmarkBarBackground, 6.0f);
   mixer[kColorBookmarkFolderIcon] = {ui::kColorIcon};
   mixer[kColorBookmarkBarSeparator] = {kColorToolbarSeparator};
+  mixer[kColorBookmarkBarSeparatorChromeRefresh] = {
+      kColorTabBackgroundInactiveFrameActive};
   mixer[kColorBookmarkDragImageBackground] = {ui::kColorAccent};
   mixer[kColorBookmarkDragImageCountBackground] = {ui::kColorAlertHighSeverity};
   mixer[kColorBookmarkDragImageCountForeground] =
diff --git a/chrome/browser/ui/color/material_chrome_color_mixer.cc b/chrome/browser/ui/color/material_chrome_color_mixer.cc
index e0b1f2b..ebe406ed 100644
--- a/chrome/browser/ui/color/material_chrome_color_mixer.cc
+++ b/chrome/browser/ui/color/material_chrome_color_mixer.cc
@@ -58,6 +58,7 @@
       AdjustHighlightColorForContrast(ui::kColorSysPrimary, kColorToolbar);
   mixer[kColorBookmarkBarBackground] = {ui::kColorSysBase};
   mixer[kColorBookmarkBarForeground] = {ui::kColorSysOnSurfaceSubtle};
+  mixer[kColorBookmarkBarSeparatorChromeRefresh] = {ui::kColorSysOnBaseDivider};
   mixer[kColorBookmarkButtonIcon] = {kColorBookmarkBarForeground};
   mixer[kColorBookmarkFolderIcon] = {kColorBookmarkBarForeground};
   mixer[kColorBookmarkDragImageBackground] = {ui::kColorSysPrimary};
diff --git a/chrome/browser/ui/color/material_side_panel_color_mixer.cc b/chrome/browser/ui/color/material_side_panel_color_mixer.cc
index fb74500..c199667 100644
--- a/chrome/browser/ui/color/material_side_panel_color_mixer.cc
+++ b/chrome/browser/ui/color/material_side_panel_color_mixer.cc
@@ -42,8 +42,6 @@
   mixer[kColorSidePanelFilterChipBackgroundSelected] = {
       ui::kColorSysTonalContainer};
 
-  mixer[kColorSidePanelTextfieldBackgroundHover] = {
-      ui::kColorSysStateHoverOnSubtle};
   mixer[kColorSidePanelTextfieldBorder] = {ui::kColorSysNeutralOutline};
 
   /* Customize Chrome */
diff --git a/chrome/browser/ui/translate/translate_bubble_model_impl.cc b/chrome/browser/ui/translate/translate_bubble_model_impl.cc
index cee9706..4464602 100644
--- a/chrome/browser/ui/translate/translate_bubble_model_impl.cc
+++ b/chrome/browser/ui/translate/translate_bubble_model_impl.cc
@@ -7,7 +7,6 @@
 #include <utility>
 
 #include "chrome/browser/translate/chrome_translate_client.h"
-#include "chrome/browser/ui/translate/translate_bubble_ui_action_logger.h"
 #include "components/translate/core/browser/language_state.h"
 #include "components/translate/core/browser/translate_ui_delegate.h"
 #include "components/translate/core/browser/translate_ui_languages_manager.h"
diff --git a/chrome/browser/ui/translate/translate_bubble_ui_action_logger.cc b/chrome/browser/ui/translate/translate_bubble_ui_action_logger.cc
deleted file mode 100644
index 40c3ee6..0000000
--- a/chrome/browser/ui/translate/translate_bubble_ui_action_logger.cc
+++ /dev/null
@@ -1,18 +0,0 @@
-// Copyright 2022 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chrome/browser/ui/translate/translate_bubble_ui_action_logger.h"
-
-#include "base/check_op.h"
-#include "base/metrics/histogram_macros.h"
-
-namespace translate {
-
-void ReportTranslateBubbleUiAction(translate::TranslateBubbleUiEvent action) {
-  UMA_HISTOGRAM_ENUMERATION(
-      kTranslateBubbleUiEventHistogramName, action,
-      translate::TranslateBubbleUiEvent::TRANSLATE_BUBBLE_UI_EVENT_MAX);
-}
-
-}  // namespace translate
diff --git a/chrome/browser/ui/translate/translate_bubble_ui_action_logger.h b/chrome/browser/ui/translate/translate_bubble_ui_action_logger.h
deleted file mode 100644
index 86e6780..0000000
--- a/chrome/browser/ui/translate/translate_bubble_ui_action_logger.h
+++ /dev/null
@@ -1,101 +0,0 @@
-// Copyright 2022 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CHROME_BROWSER_UI_TRANSLATE_TRANSLATE_BUBBLE_UI_ACTION_LOGGER_H_
-#define CHROME_BROWSER_UI_TRANSLATE_TRANSLATE_BUBBLE_UI_ACTION_LOGGER_H_
-
-namespace translate {
-
-// Histogram for recording the UI events related to the Full Page Translate
-// bubble.
-constexpr char kTranslateBubbleUiEventHistogramName[] =
-    "Translate.BubbleUiEvent";
-
-enum class TranslateBubbleUiEvent {
-  // Update TranslateBubbleUiEvent in enums.xml when making changes.
-  // Start with 1 to match existing UMA values: see http://crbug.com/612558
-  // The user clicked the advanced option.
-  // [DEPRECATED] SET_STATE_OPTIONS = 1,
-
-  // The user clicked "Done" and went back from the advanced option.
-  // [DEPRECATED] LEAVE_STATE_OPTIONS = 2,
-
-  // The user clicked the advanced link.
-  // [DEPRECATED] ADVANCED_LINK_CLICKED = 3,
-
-  // The user checked the "always translate" checkbox.
-  ALWAYS_TRANSLATE_CHECKED = 4,
-
-  // The user unchecked the "always translate" checkbox.
-  ALWAYS_TRANSLATE_UNCHECKED = 5,
-
-  // The user selected "Nope" in the "Options" menu.
-  // [DEPRECATED] NOPE_MENU_CLICKED = 6,
-
-  // The user selected "Never translate language" in the "Options" menu.
-  NEVER_TRANSLATE_LANGUAGE_MENU_CLICKED = 7,
-
-  // The user selected "Never translate this site" in the "Options" menu.
-  NEVER_TRANSLATE_SITE_MENU_CLICKED = 8,
-
-  // The user clicked the target language tab to start a translation.
-  TARGET_LANGUAGE_TAB_SELECTED = 9,
-
-  // The user clicked the "Done" button.
-  DONE_BUTTON_CLICKED = 10,
-
-  // The user clicked the "Cancel" button.
-  // [DEPRECATED] CANCEL_BUTTON_CLICKED = 11,
-
-  // The user clicked the "Closed" [X] button.
-  CLOSE_BUTTON_CLICKED = 12,
-
-  // The user clicked the "Try Again" button.
-  TRY_AGAIN_BUTTON_CLICKED = 13,
-
-  // The user clicked the source language tab to revert the translation.
-  SOURCE_LANGUAGE_TAB_SELECTED = 14,
-
-  // The user clicked the "Settings" link.
-  // [DEPRECATED] SETTINGS_LINK_CLICKED = 15,
-
-  // The user made a selection in the source language combobox.
-  SOURCE_LANGUAGE_MENU_ITEM_CLICKED = 16,
-
-  // The user made a selection in the target language combobox.
-  TARGET_LANGUAGE_MENU_ITEM_CLICKED = 17,
-
-  // The user activated the translate page action icon.
-  // [DEPRECATED] PAGE_ACTION_ICON_ACTIVATED = 18,
-
-  // The user deactivated the translate page action icon.
-  // [DEPRECATED] PAGE_ACTION_ICON_DEACTIVATED = 19,
-
-  // The bubble was shown to the user.
-  BUBBLE_SHOWN = 20,
-
-  // The bubble could not be shown to the user, for various reasons.
-  // [DEPRECATED] BUBBLE_NOT_SHOWN_WINDOW_NOT_VALID = 21,
-  BUBBLE_NOT_SHOWN_WINDOW_MINIMIZED = 22,
-  // [DEPRECATED] BUBBLE_NOT_SHOWN_WINDOW_NOT_ACTIVE = 23,
-  // [DEPRECATED] BUBBLE_NOT_SHOWN_WEB_CONTENTS_NOT_ACTIVE = 24,
-  BUBBLE_NOT_SHOWN_EDITABLE_FIELD_IS_ACTIVE = 25,
-
-  // The user clicked the “Page is Not in {Source Language}” item, or the
-  // “Choose Another Language” item, in the options
-  // menu.
-  CHANGE_SOURCE_OR_TARGET_LANGUAGE_OPTIONS_CLICKED = 26,
-
-  // The user clicked the advanced button.
-  // [DEPRECATED] ADVANCED_BUTTON_CLICKED = 27,
-
-  TRANSLATE_BUBBLE_UI_EVENT_MAX
-};
-
-// Logs metrics for the user's TranslateBubbleUiEvent |action|.
-void ReportTranslateBubbleUiAction(translate::TranslateBubbleUiEvent action);
-
-}  // namespace translate
-
-#endif  // CHROME_BROWSER_UI_TRANSLATE_TRANSLATE_BUBBLE_UI_ACTION_LOGGER_H_
diff --git a/chrome/browser/ui/views/bookmarks/bookmark_bar_view.cc b/chrome/browser/ui/views/bookmarks/bookmark_bar_view.cc
index 0712452..ae89a6c8b4 100644
--- a/chrome/browser/ui/views/bookmarks/bookmark_bar_view.cc
+++ b/chrome/browser/ui/views/bookmarks/bookmark_bar_view.cc
@@ -103,6 +103,7 @@
 #include "ui/base/pointer/touch_ui_controller.h"
 #include "ui/base/resource/resource_bundle.h"
 #include "ui/base/theme_provider.h"
+#include "ui/base/ui_base_features.h"
 #include "ui/base/window_open_disposition.h"
 #include "ui/base/window_open_disposition_utils.h"
 #include "ui/color/color_id.h"
@@ -620,15 +621,26 @@
  public:
   METADATA_HEADER(ButtonSeparatorView);
   ButtonSeparatorView() {
-    // Total width of the separator and surrounding padding.
-    constexpr int kSeparatorWidth = 9;
-    constexpr int kPaddingWidth = kSeparatorWidth - kThickness;
-    constexpr int kLeadingPadding = (kPaddingWidth + 1) / 2;
+    const int leading_padding = features::IsChromeRefresh2023() ? 16 : 4;
+    const int trailing_padding = features::IsChromeRefresh2023() ? 8 : 3;
+    const int separator_thickness =
+        features::IsChromeRefresh2023() ? 2 : kThickness;
+    const gfx::Insets border_insets =
+        gfx::Insets::TLBR(0, leading_padding, 0, trailing_padding);
+    const ui::ColorId color_id = features::IsChromeRefresh2023()
+                                     ? kColorBookmarkBarSeparatorChromeRefresh
+                                     : kColorBookmarkBarSeparator;
 
-    SetColorId(kColorBookmarkBarSeparator);
-    SetBorder(views::CreateEmptyBorder(gfx::Insets::TLBR(
-        0, kLeadingPadding, 0, kPaddingWidth - kLeadingPadding)));
-    SetPreferredLength(gfx::kFaviconSize);
+    SetColorId(color_id);
+    SetPreferredSize(
+        gfx::Size(leading_padding + separator_thickness + trailing_padding,
+                  gfx::kFaviconSize));
+    if (features::IsChromeRefresh2023()) {
+      SetBorder(views::CreateThemedRoundedRectBorder(1, 100, border_insets,
+                                                     color_id));
+    } else {
+      SetBorder(views::CreateEmptyBorder(border_insets));
+    }
   }
   ButtonSeparatorView(const ButtonSeparatorView&) = delete;
   ButtonSeparatorView& operator=(const ButtonSeparatorView&) = delete;
diff --git a/chrome/browser/ui/views/location_bar/location_bar_view.cc b/chrome/browser/ui/views/location_bar/location_bar_view.cc
index 4b93b2b..7679dc35 100644
--- a/chrome/browser/ui/views/location_bar/location_bar_view.cc
+++ b/chrome/browser/ui/views/location_bar/location_bar_view.cc
@@ -610,18 +610,18 @@
   int text_overriding_permission_chip_indent = 0;
   if (OmniboxFieldTrial::IsChromeRefreshIconsEnabled() &&
       OmniboxFieldTrial::IsCr23LayoutEnabled()) {
-    icon_left = 4;
+    icon_left = 5;
     text_left = 8;
-    icon_indent = 8;
+    icon_indent = 7;
     text_indent = 5;
-    icon_keyword_indent = 4;
+    icon_keyword_indent = 3;
     text_keyword_indent = -9;
   } else if (OmniboxFieldTrial::IsChromeRefreshIconsEnabled()) {
-    icon_left = 4;
+    icon_left = 5;
     text_left = 5;
-    icon_indent = 2;
+    icon_indent = 1;
     text_indent = 12;
-    icon_keyword_indent = -2;
+    icon_keyword_indent = -3;
     text_keyword_indent = -6;
     text_overriding_permission_chip_indent = 3;
   } else if (OmniboxFieldTrial::IsCr23LayoutEnabled()) {
diff --git a/chrome/browser/ui/views/performance_controls/high_efficiency_bubble_view.cc b/chrome/browser/ui/views/performance_controls/high_efficiency_bubble_view.cc
index 4792980..28f500f0 100644
--- a/chrome/browser/ui/views/performance_controls/high_efficiency_bubble_view.cc
+++ b/chrome/browser/ui/views/performance_controls/high_efficiency_bubble_view.cc
@@ -34,6 +34,9 @@
 
 DEFINE_CLASS_ELEMENT_IDENTIFIER_VALUE(HighEfficiencyBubbleView,
                                       kHighEfficiencyDialogBodyElementId);
+DEFINE_CLASS_ELEMENT_IDENTIFIER_VALUE(
+    HighEfficiencyBubbleView,
+    kHighEfficiencyDialogResourceViewElementId);
 DEFINE_CLASS_ELEMENT_IDENTIFIER_VALUE(HighEfficiencyBubbleView,
                                       kHighEfficiencyDialogOkButton);
 DEFINE_CLASS_ELEMENT_IDENTIFIER_VALUE(HighEfficiencyBubbleView,
@@ -147,7 +150,8 @@
     dialog_model_builder.AddCustomField(
         std::make_unique<views::BubbleDialogModelHost::CustomView>(
             std::make_unique<HighEfficiencyResourceView>(memory_savings),
-            views::BubbleDialogModelHost::FieldType::kText));
+            views::BubbleDialogModelHost::FieldType::kText),
+        kHighEfficiencyDialogResourceViewElementId);
   }
 
   if (base::FeatureList::IsEnabled(
diff --git a/chrome/browser/ui/views/performance_controls/high_efficiency_bubble_view.h b/chrome/browser/ui/views/performance_controls/high_efficiency_bubble_view.h
index 239a0f89..ed5cc93 100644
--- a/chrome/browser/ui/views/performance_controls/high_efficiency_bubble_view.h
+++ b/chrome/browser/ui/views/performance_controls/high_efficiency_bubble_view.h
@@ -16,6 +16,8 @@
   HighEfficiencyBubbleView& operator=(const HighEfficiencyBubbleView&) = delete;
 
   DECLARE_CLASS_ELEMENT_IDENTIFIER_VALUE(kHighEfficiencyDialogBodyElementId);
+  DECLARE_CLASS_ELEMENT_IDENTIFIER_VALUE(
+      kHighEfficiencyDialogResourceViewElementId);
   DECLARE_CLASS_ELEMENT_IDENTIFIER_VALUE(kHighEfficiencyDialogOkButton);
   DECLARE_CLASS_ELEMENT_IDENTIFIER_VALUE(kHighEfficiencyDialogCancelButton);
 
diff --git a/chrome/browser/ui/views/performance_controls/high_efficiency_bubble_view_unittest.cc b/chrome/browser/ui/views/performance_controls/high_efficiency_bubble_view_unittest.cc
index c9dc281e..2e5f047a 100644
--- a/chrome/browser/ui/views/performance_controls/high_efficiency_bubble_view_unittest.cc
+++ b/chrome/browser/ui/views/performance_controls/high_efficiency_bubble_view_unittest.cc
@@ -288,9 +288,9 @@
   base::test::ScopedFeatureList feature_list_;
 };
 
-// The memory savings should be rendered within the dialog.
+// The memory savings should be rendered within the resource view.
 TEST_F(HighEfficiencyBubbleViewMemorySavingsImprovementsTest,
-       ShouldRenderMemorySavingsInDialog) {
+       ShouldRenderMemorySavingsInResourceView) {
   SetTabDiscardState(0, true);
 
   ClickPageActionChip();
@@ -301,3 +301,22 @@
   EXPECT_TRUE(label->GetText().find(ui::FormatBytes(
                   kMemorySavingsKilobytes * 1024)) != std::string::npos);
 }
+
+// The memory savings should not be rendered within the text above the resource
+// view.
+TEST_F(HighEfficiencyBubbleViewMemorySavingsImprovementsTest,
+       ShouldNotRenderMemorySavingsInDialogBodyText) {
+  SetTabDiscardState(0, true);
+
+  ClickPageActionChip();
+
+  views::StyledLabel* label = GetDialogLabel<views::StyledLabel>(
+      HighEfficiencyBubbleView::kHighEfficiencyDialogBodyElementId);
+  EXPECT_EQ(
+      label->GetText().find(ui::FormatBytes(kMemorySavingsKilobytes * 1024)),
+      std::string::npos);
+
+  EXPECT_NE(
+      label->GetText().find(u"Memory Saver freed up memory for other tasks"),
+      std::string::npos);
+}
diff --git a/chrome/browser/ui/views/performance_controls/high_efficiency_interactive_ui_test.cc b/chrome/browser/ui/views/performance_controls/high_efficiency_interactive_ui_test.cc
index 5ed22b5c..22516dd 100644
--- a/chrome/browser/ui/views/performance_controls/high_efficiency_interactive_ui_test.cc
+++ b/chrome/browser/ui/views/performance_controls/high_efficiency_interactive_ui_test.cc
@@ -755,11 +755,11 @@
 
 // Tests the new memory savings reporting improvements on the high efficiency
 // dialog.
-class HighEfficiencyMemorySavingsReportingImprovmentsTest
+class HighEfficiencyMemorySavingsReportingImprovementsTest
     : public HighEfficiencyInteractiveTest {
  public:
-  HighEfficiencyMemorySavingsReportingImprovmentsTest() = default;
-  ~HighEfficiencyMemorySavingsReportingImprovmentsTest() override = default;
+  HighEfficiencyMemorySavingsReportingImprovementsTest() = default;
+  ~HighEfficiencyMemorySavingsReportingImprovementsTest() override = default;
 
   void SetUp() override {
     scoped_feature_list_.InitAndEnableFeature(
@@ -779,7 +779,7 @@
 
 // The high efficiency chip dialog renders a gauge style visualization that
 // must be rendered correctly.
-IN_PROC_BROWSER_TEST_F(HighEfficiencyMemorySavingsReportingImprovmentsTest,
+IN_PROC_BROWSER_TEST_F(HighEfficiencyMemorySavingsReportingImprovementsTest,
                        RenderVisualizationInDialog) {
   RunTestSequence(
       SetOnIncompatibleAction(OnIncompatibleAction::kSkipTest,
@@ -788,8 +788,19 @@
       NavigateWebContents(kFirstTabContents, GetURL("/title1.html")),
       AddInstrumentedTab(kSecondTabContents, GURL(chrome::kChromeUINewTabURL)),
       ForceRefreshMemoryMetrics(), DiscardAndSelectTab(0, kFirstTabContents),
+      Do(base::BindLambdaForTesting([&]() {
+        content::WebContents* web_contents =
+            browser()->tab_strip_model()->GetWebContentsAt(0);
+        auto* pre_discard_resource_usage =
+            performance_manager::user_tuning::UserPerformanceTuningManager::
+                PreDiscardResourceUsage::FromWebContents(web_contents);
+        pre_discard_resource_usage->SetMemoryFootprintEstimateKbForTesting(
+            135 * 1024);
+      })),
       PressButton(kHighEfficiencyChipElementId),
-      WaitForShow(HighEfficiencyBubbleView::kHighEfficiencyDialogBodyElementId),
-      Screenshot(HighEfficiencyBubbleView::kHighEfficiencyDialogBodyElementId,
-                 "HighEfficiencyResourceView", "4497874"));
+      WaitForShow(
+          HighEfficiencyBubbleView::kHighEfficiencyDialogResourceViewElementId),
+      Screenshot(
+          HighEfficiencyBubbleView::kHighEfficiencyDialogResourceViewElementId,
+          "HighEfficiencyResourceView", "4546555"));
 }
diff --git a/chrome/browser/ui/views/translate/translate_bubble_controller.cc b/chrome/browser/ui/views/translate/translate_bubble_controller.cc
index acaf344..915a443d 100644
--- a/chrome/browser/ui/views/translate/translate_bubble_controller.cc
+++ b/chrome/browser/ui/views/translate/translate_bubble_controller.cc
@@ -14,7 +14,6 @@
 #include "chrome/browser/ui/translate/partial_translate_bubble_model_impl.h"
 #include "chrome/browser/ui/translate/partial_translate_bubble_ui_action_logger.h"
 #include "chrome/browser/ui/translate/translate_bubble_model_impl.h"
-#include "chrome/browser/ui/translate/translate_bubble_ui_action_logger.h"
 #include "chrome/browser/ui/views/translate/partial_translate_bubble_view.h"
 #include "components/contextual_search/core/browser/contextual_search_delegate_impl.h"
 #include "components/translate/content/browser/partial_translate_manager.h"
@@ -106,8 +105,6 @@
   translate_bubble_view_->SetViewState(step, error_type);
 
   translate_bubble_view_->ShowForReason(reason);
-  translate::ReportTranslateBubbleUiAction(
-      translate::TranslateBubbleUiEvent::BUBBLE_SHOWN);
 
   translate_bubble_view_->model()->ReportUIChange(true);
 
diff --git a/chrome/browser/ui/views/translate/translate_bubble_view.cc b/chrome/browser/ui/views/translate/translate_bubble_view.cc
index d872ca2..1760ff26 100644
--- a/chrome/browser/ui/views/translate/translate_bubble_view.cc
+++ b/chrome/browser/ui/views/translate/translate_bubble_view.cc
@@ -26,7 +26,6 @@
 #include "chrome/browser/translate/translate_service.h"
 #include "chrome/browser/ui/chrome_pages.h"
 #include "chrome/browser/ui/translate/translate_bubble_model_impl.h"
-#include "chrome/browser/ui/translate/translate_bubble_ui_action_logger.h"
 #include "chrome/browser/ui/views/chrome_layout_provider.h"
 #include "chrome/browser/ui/views/controls/md_text_button_with_down_arrow.h"
 #include "chrome/browser/ui/views/translate/translate_icon_view.h"
@@ -374,9 +373,6 @@
       if (should_never_translate_language_) {
         should_always_translate_ = false;
         model_->SetAlwaysTranslate(should_always_translate_);
-        translate::ReportTranslateBubbleUiAction(
-            translate::TranslateBubbleUiEvent::
-                NEVER_TRANSLATE_LANGUAGE_MENU_CLICKED);
         model_->SetNeverTranslateLanguage(true);
         RevertOrDeclineTranslation();
       } else {
@@ -389,9 +385,6 @@
           translate::UIInteraction::kNeverTranslateSite);
       should_never_translate_site_ = !should_never_translate_site_;
       if (should_never_translate_site_) {
-        translate::ReportTranslateBubbleUiAction(
-            translate::TranslateBubbleUiEvent::
-                NEVER_TRANSLATE_SITE_MENU_CLICKED);
         model_->SetNeverTranslateSite(true);
         RevertOrDeclineTranslation();
       } else {
@@ -400,16 +393,10 @@
       break;
 
     case OptionsMenuItem::CHANGE_TARGET_LANGUAGE:
-      translate::ReportTranslateBubbleUiAction(
-          translate::TranslateBubbleUiEvent::
-              CHANGE_SOURCE_OR_TARGET_LANGUAGE_OPTIONS_CLICKED);
       SwitchView(TranslateBubbleModel::VIEW_STATE_TARGET_LANGUAGE);
       break;
 
     case OptionsMenuItem::CHANGE_SOURCE_LANGUAGE:
-      translate::ReportTranslateBubbleUiAction(
-          translate::TranslateBubbleUiEvent::
-              CHANGE_SOURCE_OR_TARGET_LANGUAGE_OPTIONS_CLICKED);
       SwitchView(TranslateBubbleModel::VIEW_STATE_SOURCE_LANGUAGE);
       break;
 
@@ -487,16 +474,12 @@
   model_->ReportUIInteraction(translate::UIInteraction::kTranslate);
   model_->Translate();
   SwitchView(TranslateBubbleModel::VIEW_STATE_TRANSLATING);
-  translate::ReportTranslateBubbleUiAction(
-      translate::TranslateBubbleUiEvent::TARGET_LANGUAGE_TAB_SELECTED);
 }
 
 void TranslateBubbleView::ShowOriginal() {
   model_->ReportUIInteraction(translate::UIInteraction::kRevert);
   model_->RevertTranslation();
   SwitchView(TranslateBubbleModel::VIEW_STATE_BEFORE_TRANSLATE);
-  translate::ReportTranslateBubbleUiAction(
-      translate::TranslateBubbleUiEvent::SOURCE_LANGUAGE_TAB_SELECTED);
 }
 
 void TranslateBubbleView::ConfirmAdvancedOptions() {
@@ -515,9 +498,6 @@
     tabbed_pane_->SelectTabAt(1);
     SwitchView(TranslateBubbleModel::VIEW_STATE_AFTER_TRANSLATE);
   }
-
-  translate::ReportTranslateBubbleUiAction(
-      translate::TranslateBubbleUiEvent::DONE_BUTTON_CLICKED);
 }
 
 void TranslateBubbleView::SourceLanguageChanged() {
@@ -525,8 +505,6 @@
   model_->UpdateSourceLanguageIndex(
       source_language_combobox_->GetSelectedIndex().value());
   UpdateAdvancedView();
-  translate::ReportTranslateBubbleUiAction(
-      translate::TranslateBubbleUiEvent::SOURCE_LANGUAGE_MENU_ITEM_CLICKED);
 }
 
 void TranslateBubbleView::TargetLanguageChanged() {
@@ -534,18 +512,12 @@
   model_->UpdateTargetLanguageIndex(
       target_language_combobox_->GetSelectedIndex().value());
   UpdateAdvancedView();
-  translate::ReportTranslateBubbleUiAction(
-      translate::TranslateBubbleUiEvent::TARGET_LANGUAGE_MENU_ITEM_CLICKED);
 }
 
 void TranslateBubbleView::AlwaysTranslatePressed() {
   model_->ReportUIInteraction(
       translate::UIInteraction::kAlwaysTranslateLanguage);
   should_always_translate_ = GetAlwaysTranslateCheckbox()->GetChecked();
-  translate::ReportTranslateBubbleUiAction(
-      should_always_translate_
-          ? translate::TranslateBubbleUiEvent::ALWAYS_TRANSLATE_CHECKED
-          : translate::TranslateBubbleUiEvent::ALWAYS_TRANSLATE_UNCHECKED);
   // In the tab UI the always translate button should apply immediately
   // except for in an advanced view.
   if (GetViewState() != TranslateBubbleModel::VIEW_STATE_SOURCE_LANGUAGE) {
@@ -735,11 +707,7 @@
       provider->GetDistanceMetric(views::DISTANCE_RELATED_BUTTON_HORIZONTAL));
   auto try_again_button = std::make_unique<views::MdTextButton>(
       base::BindRepeating(
-          [](TranslateBubbleModel* model) {
-            translate::ReportTranslateBubbleUiAction(
-                translate::TranslateBubbleUiEvent::TRY_AGAIN_BUTTON_CLICKED);
-            model->Translate();
-          },
+          [](TranslateBubbleModel* model) { model->Translate(); },
           base::Unretained(model_.get())),
       l10n_util::GetStringUTF16(IDS_TRANSLATE_BUBBLE_TRY_AGAIN));
   try_again_button->SetID(BUTTON_ID_TRY_AGAIN);
@@ -976,8 +944,6 @@
   auto close_button =
       views::BubbleFrameView::CreateCloseButton(base::BindRepeating(
           [](View* view) {
-            translate::ReportTranslateBubbleUiAction(
-                translate::TranslateBubbleUiEvent::CLOSE_BUTTON_CLICKED);
             view->GetWidget()->CloseWithReason(
                 views::Widget::ClosedReason::kCloseButtonClicked);
           },
diff --git a/chrome/browser/ui/views/translate/translate_bubble_view_interactive_uitest.cc b/chrome/browser/ui/views/translate/translate_bubble_view_interactive_uitest.cc
index 7ebeb3e..f8f1a802 100644
--- a/chrome/browser/ui/views/translate/translate_bubble_view_interactive_uitest.cc
+++ b/chrome/browser/ui/views/translate/translate_bubble_view_interactive_uitest.cc
@@ -19,7 +19,6 @@
 #include "chrome/browser/translate/translate_test_utils.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/browser_commands.h"
-#include "chrome/browser/ui/translate/translate_bubble_ui_action_logger.h"
 #include "chrome/browser/ui/views/translate/translate_bubble_controller.h"
 #include "chrome/common/chrome_switches.h"
 #include "chrome/grit/generated_resources.h"
@@ -88,8 +87,6 @@
  public:
   TranslateBubbleViewUITest() = default;
   ~TranslateBubbleViewUITest() override = default;
-  explicit TranslateBubbleViewUITest(const TranslateBubbleUiEvent&) = delete;
-  TranslateBubbleUiEvent& operator=(const TranslateBubbleUiEvent&) = delete;
 
   void SetUp() override {
     set_open_about_blank_on_browser_launch(true);
diff --git a/chrome/browser/ui/views/translate/translate_bubble_view_unittest.cc b/chrome/browser/ui/views/translate/translate_bubble_view_unittest.cc
index 0aac5b4..435db99 100644
--- a/chrome/browser/ui/views/translate/translate_bubble_view_unittest.cc
+++ b/chrome/browser/ui/views/translate/translate_bubble_view_unittest.cc
@@ -9,11 +9,9 @@
 
 #include "base/memory/raw_ptr.h"
 #include "base/strings/utf_string_conversions.h"
-#include "base/test/metrics/histogram_tester.h"
 #include "base/test/scoped_feature_list.h"
 #include "build/build_config.h"
 #include "chrome/browser/ui/translate/translate_bubble_model.h"
-#include "chrome/browser/ui/translate/translate_bubble_ui_action_logger.h"
 #include "chrome/grit/generated_resources.h"
 #include "chrome/test/views/chrome_views_test_base.h"
 #include "components/translate/core/browser/translate_prefs.h"
@@ -238,20 +236,15 @@
 };
 
 TEST_F(TranslateBubbleViewTest, TargetLanguageTabTriggersTranslate) {
-  base::HistogramTester histogram_tester;
   CreateAndShowBubble();
   EXPECT_FALSE(mock_model_->translate_called_);
 
   // Press the target language tab to start translation.
   bubble_->TabSelectedAt(1);
   EXPECT_TRUE(mock_model_->translate_called_);
-  histogram_tester.ExpectBucketCount(
-      translate::kTranslateBubbleUiEventHistogramName,
-      translate::TranslateBubbleUiEvent::TARGET_LANGUAGE_TAB_SELECTED, 1);
 }
 
 TEST_F(TranslateBubbleViewTest, OptionsMenuNeverTranslateLanguage) {
-  base::HistogramTester histogram_tester;
   CreateAndShowBubble();
 
   EXPECT_FALSE(bubble_->GetWidget()->IsClosed());
@@ -268,14 +261,9 @@
   EXPECT_TRUE(denial_button_clicked());
   EXPECT_TRUE(mock_model_->never_translate_language_);
   EXPECT_TRUE(bubble_->GetWidget()->IsClosed());
-  histogram_tester.ExpectBucketCount(
-      translate::kTranslateBubbleUiEventHistogramName,
-      translate::TranslateBubbleUiEvent::NEVER_TRANSLATE_LANGUAGE_MENU_CLICKED,
-      1);
 }
 
 TEST_F(TranslateBubbleViewTest, OptionsMenuNeverTranslateSite) {
-  base::HistogramTester histogram_tester;
   // NEVER_TRANSLATE_SITE should only show up for sites that can be blocklisted.
   mock_model_->SetCanAddSiteToNeverPromptList(true);
   CreateAndShowBubble();
@@ -294,13 +282,9 @@
   EXPECT_TRUE(denial_button_clicked());
   EXPECT_TRUE(mock_model_->never_translate_site_);
   EXPECT_TRUE(bubble_->GetWidget()->IsClosed());
-  histogram_tester.ExpectBucketCount(
-      translate::kTranslateBubbleUiEventHistogramName,
-      translate::TranslateBubbleUiEvent::NEVER_TRANSLATE_SITE_MENU_CLICKED, 1);
 }
 
 TEST_F(TranslateBubbleViewTest, AlwaysTranslateCheckboxShortcut) {
-  base::HistogramTester histogram_tester;
   mock_model_->SetShouldShowAlwaysTranslateShortcut(true);
   CreateAndShowBubble();
 
@@ -321,13 +305,9 @@
   EXPECT_EQ(TranslateBubbleModel::VIEW_STATE_TRANSLATING,
             bubble_->GetViewState());
   EXPECT_EQ(bubble_->tabbed_pane_->GetSelectedTabIndex(), size_t{1});
-  histogram_tester.ExpectBucketCount(
-      translate::kTranslateBubbleUiEventHistogramName,
-      translate::TranslateBubbleUiEvent::ALWAYS_TRANSLATE_CHECKED, 1);
 }
 
 TEST_F(TranslateBubbleViewTest, AlwaysTranslateCheckboxAndCloseButton) {
-  base::HistogramTester histogram_tester;
   CreateAndShowBubble();
   bubble_->SwitchView(TranslateBubbleModel::VIEW_STATE_SOURCE_LANGUAGE);
 
@@ -344,21 +324,14 @@
   PressButton(TranslateBubbleView::BUTTON_ID_ALWAYS_TRANSLATE);
   EXPECT_FALSE(mock_model_->should_always_translate_);
   EXPECT_EQ(0, mock_model_->set_always_translate_called_count_);
-  histogram_tester.ExpectBucketCount(
-      translate::kTranslateBubbleUiEventHistogramName,
-      translate::TranslateBubbleUiEvent::ALWAYS_TRANSLATE_UNCHECKED, 1);
 
   // Click the cancel button. The state is not saved.
   PressButton(TranslateBubbleView::BUTTON_ID_CLOSE);
   EXPECT_FALSE(mock_model_->should_always_translate_);
   EXPECT_EQ(0, mock_model_->set_always_translate_called_count_);
-  histogram_tester.ExpectBucketCount(
-      translate::kTranslateBubbleUiEventHistogramName,
-      translate::TranslateBubbleUiEvent::CLOSE_BUTTON_CLICKED, 1);
 }
 
 TEST_F(TranslateBubbleViewTest, AlwaysTranslateCheckboxAndDoneButton) {
-  base::HistogramTester histogram_tester;
   CreateAndShowBubble();
   bubble_->SwitchView(TranslateBubbleModel::VIEW_STATE_SOURCE_LANGUAGE);
 
@@ -379,13 +352,9 @@
   PressButton(TranslateBubbleView::BUTTON_ID_DONE);
   EXPECT_TRUE(mock_model_->should_always_translate_);
   EXPECT_EQ(1, mock_model_->set_always_translate_called_count_);
-  histogram_tester.ExpectBucketCount(
-      translate::kTranslateBubbleUiEventHistogramName,
-      translate::TranslateBubbleUiEvent::DONE_BUTTON_CLICKED, 1);
 }
 
 TEST_F(TranslateBubbleViewTest, SourceResetButton) {
-  base::HistogramTester histogram_tester;
   CreateAndShowBubble();
   bubble_->SwitchView(TranslateBubbleModel::VIEW_STATE_SOURCE_LANGUAGE);
 
@@ -398,9 +367,6 @@
   bubble_->SourceLanguageChanged();
   EXPECT_EQ(10u, bubble_->source_language_combobox_->GetSelectedIndex());
   EXPECT_TRUE(bubble_->advanced_reset_button_source_->GetEnabled());
-  histogram_tester.ExpectBucketCount(
-      translate::kTranslateBubbleUiEventHistogramName,
-      translate::TranslateBubbleUiEvent::SOURCE_LANGUAGE_MENU_ITEM_CLICKED, 1);
 
   // Press the reset button. Language should change back to initial selection.
   PressButton(TranslateBubbleView::BUTTON_ID_RESET);
@@ -409,7 +375,6 @@
 }
 
 TEST_F(TranslateBubbleViewTest, TargetResetButton) {
-  base::HistogramTester histogram_tester;
   CreateAndShowBubble();
   bubble_->SwitchView(TranslateBubbleModel::VIEW_STATE_TARGET_LANGUAGE);
 
@@ -422,9 +387,6 @@
   bubble_->TargetLanguageChanged();
   EXPECT_EQ(10u, bubble_->target_language_combobox_->GetSelectedIndex());
   EXPECT_TRUE(bubble_->advanced_reset_button_target_->GetEnabled());
-  histogram_tester.ExpectBucketCount(
-      translate::kTranslateBubbleUiEventHistogramName,
-      translate::TranslateBubbleUiEvent::TARGET_LANGUAGE_MENU_ITEM_CLICKED, 1);
 
   // Press the reset button. Language should change back to initial selection.
   PressButton(TranslateBubbleView::BUTTON_ID_RESET);
@@ -432,7 +394,6 @@
 }
 
 TEST_F(TranslateBubbleViewTest, SourceDoneButton) {
-  base::HistogramTester histogram_tester;
   CreateAndShowBubble();
   bubble_->SwitchView(TranslateBubbleModel::VIEW_STATE_SOURCE_LANGUAGE);
 
@@ -447,16 +408,12 @@
   EXPECT_TRUE(mock_model_->translate_called_);
   EXPECT_EQ(10, mock_model_->source_language_index_);
   EXPECT_EQ(20, mock_model_->target_language_index_);
-  histogram_tester.ExpectBucketCount(
-      translate::kTranslateBubbleUiEventHistogramName,
-      translate::TranslateBubbleUiEvent::DONE_BUTTON_CLICKED, 1);
 
   EXPECT_EQ(TranslateBubbleModel::VIEW_STATE_AFTER_TRANSLATE,
             bubble_->GetViewState());
 }
 
 TEST_F(TranslateBubbleViewTest, TargetDoneButton) {
-  base::HistogramTester histogram_tester;
   CreateAndShowBubble();
   bubble_->SwitchView(TranslateBubbleModel::VIEW_STATE_TARGET_LANGUAGE);
 
@@ -471,9 +428,6 @@
   EXPECT_TRUE(mock_model_->translate_called_);
   EXPECT_EQ(10, mock_model_->source_language_index_);
   EXPECT_EQ(20, mock_model_->target_language_index_);
-  histogram_tester.ExpectBucketCount(
-      translate::kTranslateBubbleUiEventHistogramName,
-      translate::TranslateBubbleUiEvent::DONE_BUTTON_CLICKED, 1);
 
   EXPECT_EQ(TranslateBubbleModel::VIEW_STATE_AFTER_TRANSLATE,
             bubble_->GetViewState());
diff --git a/chrome/browser/ui/views/translate/translate_icon_view.cc b/chrome/browser/ui/views/translate/translate_icon_view.cc
index f4931d4..a2061fa0 100644
--- a/chrome/browser/ui/views/translate/translate_icon_view.cc
+++ b/chrome/browser/ui/views/translate/translate_icon_view.cc
@@ -10,7 +10,6 @@
 #include "chrome/app/vector_icons/vector_icons.h"
 #include "chrome/browser/ui/browser_command_controller.h"
 #include "chrome/browser/ui/browser_commands.h"
-#include "chrome/browser/ui/translate/translate_bubble_ui_action_logger.h"
 #include "chrome/browser/ui/view_ids.h"
 #include "chrome/browser/ui/views/translate/translate_bubble_controller.h"
 #include "chrome/browser/ui/views/translate/translate_bubble_view.h"
diff --git a/chrome/browser/ui/webui/history/foreign_session_handler.cc b/chrome/browser/ui/webui/history/foreign_session_handler.cc
index 84798fe6..44f8e885 100644
--- a/chrome/browser/ui/webui/history/foreign_session_handler.cc
+++ b/chrome/browser/ui/webui/history/foreign_session_handler.cc
@@ -151,8 +151,7 @@
 // static
 void ForeignSessionHandler::OpenForeignSessionWindows(
     content::WebUI* web_ui,
-    const std::string& session_string_value,
-    size_t window_num) {
+    const std::string& session_string_value) {
   sync_sessions::OpenTabsUIDelegate* open_tabs = GetOpenTabsUIDelegate(web_ui);
   if (!open_tabs)
     return;
@@ -165,14 +164,8 @@
     return;
   }
 
-  // Bounds check `window_num` before using it anywhere. crbug.com/1408120
-  CHECK_LT(window_num, windows.size());
-  auto iter_begin = windows.begin() + window_num;
-  DCHECK(iter_begin != windows.end())
-      << "Because we CHECKed that windows_num is less than the size.";
-  auto iter_end = iter_begin + 1;
   SessionRestore::RestoreForeignSessionWindows(Profile::FromWebUI(web_ui),
-                                               iter_begin, iter_end);
+                                               windows.begin(), windows.end());
 }
 
 // static
@@ -194,8 +187,13 @@
       base::BindRepeating(&ForeignSessionHandler::HandleGetForeignSessions,
                           base::Unretained(this)));
   web_ui()->RegisterMessageCallback(
-      "openForeignSession",
-      base::BindRepeating(&ForeignSessionHandler::HandleOpenForeignSession,
+      "openForeignSessionAllTabs",
+      base::BindRepeating(
+          &ForeignSessionHandler::HandleOpenForeignSessionAllTabs,
+          base::Unretained(this)));
+  web_ui()->RegisterMessageCallback(
+      "openForeignSessionTab",
+      base::BindRepeating(&ForeignSessionHandler::HandleOpenForeignSessionTab,
                           base::Unretained(this)));
   web_ui()->RegisterMessageCallback(
       "setForeignSessionCollapsed",
@@ -317,19 +315,22 @@
   return session_list;
 }
 
-void ForeignSessionHandler::HandleOpenForeignSession(
+void ForeignSessionHandler::HandleOpenForeignSessionAllTabs(
     const base::Value::List& args) {
-  size_t num_args = args.size();
-  // Expect either 1 or 8 args. For restoring an entire session, only
-  // one argument is required -- the session tag. To restore a tab,
-  // the additional args required are the window id, the tab id,
-  // and 4 properties of the event object (button, altKey, ctrlKey,
-  // metaKey, shiftKey) for determining how to open the tab.
-  if (num_args != 8U && num_args != 1U) {
-    LOG(ERROR) << "openForeignSession called with " << args.size()
-               << " arguments.";
+  CHECK_EQ(args.size(), 1U);
+
+  // Extract the session tag (always provided).
+  if (!args[0].is_string()) {
+    LOG(ERROR) << "Failed to extract session tag.";
     return;
   }
+  const std::string& session_string_value = args[0].GetString();
+  OpenForeignSessionWindows(web_ui(), session_string_value);
+}
+
+void ForeignSessionHandler::HandleOpenForeignSessionTab(
+    const base::Value::List& args) {
+  CHECK_EQ(args.size(), 7U);
 
   // Extract the session tag (always provided).
   if (!args[0].is_string()) {
@@ -338,31 +339,22 @@
   }
   const std::string& session_string_value = args[0].GetString();
 
-  // Extract window number.
-  size_t window_num = 0;
-  if (num_args >= 2 &&
-      (!args[1].is_string() ||
-       !base::StringToSizeT(args[1].GetString(), &window_num))) {
-    LOG(ERROR) << "Failed to extract window number.";
-    return;
-  }
-
   // Extract tab id.
   SessionID::id_type tab_id_value = 0;
-  if (num_args >= 3 &&
-      (!args[2].is_string() ||
-       !base::StringToInt(args[2].GetString(), &tab_id_value))) {
+  if (!args[1].is_string() ||
+      !base::StringToInt(args[1].GetString(), &tab_id_value)) {
     LOG(ERROR) << "Failed to extract tab SessionID.";
     return;
   }
 
   SessionID tab_id = SessionID::FromSerializedValue(tab_id_value);
-  if (tab_id.is_valid()) {
-    WindowOpenDisposition disposition = webui::GetDispositionFromClick(args, 3);
-    OpenForeignSessionTab(web_ui(), session_string_value, tab_id, disposition);
-  } else {
-    OpenForeignSessionWindows(web_ui(), session_string_value, window_num);
+  if (!tab_id.is_valid()) {
+    LOG(ERROR) << "Failed to deserialize tab ID.";
+    return;
   }
+
+  WindowOpenDisposition disposition = webui::GetDispositionFromClick(args, 2);
+  OpenForeignSessionTab(web_ui(), session_string_value, tab_id, disposition);
 }
 
 void ForeignSessionHandler::HandleDeleteForeignSession(
diff --git a/chrome/browser/ui/webui/history/foreign_session_handler.h b/chrome/browser/ui/webui/history/foreign_session_handler.h
index 4f36651c..61763675 100644
--- a/chrome/browser/ui/webui/history/foreign_session_handler.h
+++ b/chrome/browser/ui/webui/history/foreign_session_handler.h
@@ -68,9 +68,9 @@
                                     SessionID tab_id,
                                     const WindowOpenDisposition& disposition);
 
-  static void OpenForeignSessionWindows(content::WebUI* web_ui,
-                                        const std::string& session_string_value,
-                                        size_t window_num);
+  static void OpenForeignSessionWindows(
+      content::WebUI* web_ui,
+      const std::string& session_string_value);
 
   // Returns a pointer to the current session model associator or nullptr.
   static sync_sessions::OpenTabsUIDelegate* GetOpenTabsUIDelegate(
@@ -79,6 +79,11 @@
   void SetWebUIForTesting(content::WebUI* web_ui) { set_web_ui(web_ui); }
 
  private:
+  FRIEND_TEST_ALL_PREFIXES(ForeignSessionHandlerTest,
+                           HandleOpenForeignSessionAllTabs);
+  FRIEND_TEST_ALL_PREFIXES(ForeignSessionHandlerTest,
+                           HandleOpenForeignSessionTab);
+
   void OnForeignSessionUpdated();
 
   base::Value::List GetForeignSessions();
@@ -86,10 +91,12 @@
   // Returns a string used to show the user when a session was last modified.
   std::u16string FormatSessionTime(const base::Time& time);
 
-  // Determines which session is to be opened, and then calls
-  // OpenForeignSession, to begin the process of opening a new browser window.
-  // This is a javascript callback handler.
-  void HandleOpenForeignSession(const base::Value::List& args);
+  // Opens all the tabs of a foreign session, potentially spanning multiple
+  // windows. This as a javascript callback handler.
+  void HandleOpenForeignSessionAllTabs(const base::Value::List& args);
+
+  // Opens a single foreign session tab. This is a javascript callback handler.
+  void HandleOpenForeignSessionTab(const base::Value::List& args);
 
   // Determines whether foreign sessions should be obtained from the sync model.
   // This is a javascript callback handler, and it is also called when the sync
diff --git a/chrome/browser/ui/webui/history/foreign_session_handler_unittest.cc b/chrome/browser/ui/webui/history/foreign_session_handler_unittest.cc
index 5c5a3288..9f2dbe0 100644
--- a/chrome/browser/ui/webui/history/foreign_session_handler_unittest.cc
+++ b/chrome/browser/ui/webui/history/foreign_session_handler_unittest.cc
@@ -7,13 +7,40 @@
 #include "base/callback_list.h"
 #include "chrome/browser/sync/session_sync_service_factory.h"
 #include "chrome/test/base/chrome_render_view_host_test_harness.h"
+#include "components/sessions/core/session_id.h"
 #include "components/sync_sessions/session_sync_service.h"
 #include "content/public/test/test_web_ui.h"
+#include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace browser_sync {
 
-namespace {
+class MockOpenTabsUIDelegate : public sync_sessions::OpenTabsUIDelegate {
+ public:
+  MockOpenTabsUIDelegate() = default;
+
+  MOCK_METHOD1(
+      GetAllForeignSessions,
+      bool(std::vector<const sync_sessions::SyncedSession*>* sessions));
+
+  MOCK_METHOD3(GetForeignTab,
+               bool(const std::string& tag,
+                    const SessionID tab_id,
+                    const sessions::SessionTab** tab));
+
+  MOCK_METHOD1(DeleteForeignSession, void(const std::string& tag));
+
+  MOCK_METHOD2(GetForeignSession,
+               bool(const std::string& tag,
+                    std::vector<const sessions::SessionWindow*>* windows));
+
+  MOCK_METHOD2(GetForeignSessionTabs,
+               bool(const std::string& tag,
+                    std::vector<const sessions::SessionTab*>* tabs));
+
+  MOCK_METHOD1(GetLocalSession,
+               bool(const sync_sessions::SyncedSession** local_session));
+};
 
 // Partial SessionSyncService that can fake behavior for
 // SubscribeToForeignSessionsChanged() including the notification to
@@ -28,8 +55,8 @@
   // SessionSyncService overrides.
   syncer::GlobalIdMapper* GetGlobalIdMapper() const override { return nullptr; }
 
-  sync_sessions::OpenTabsUIDelegate* GetOpenTabsUIDelegate() override {
-    return nullptr;
+  MockOpenTabsUIDelegate* GetOpenTabsUIDelegate() override {
+    return &mock_open_tabs_ui_delegate_;
   }
 
   base::CallbackListSubscription SubscribeToForeignSessionsChanged(
@@ -47,6 +74,7 @@
 
  private:
   base::RepeatingClosureList subscriber_list_;
+  MockOpenTabsUIDelegate mock_open_tabs_ui_delegate_;
 };
 
 class ForeignSessionHandlerTest : public ChromeRenderViewHostTestHarness {
@@ -129,6 +157,31 @@
   EXPECT_EQ(0U, web_ui()->call_data().size());
 }
 
-}  // namespace
+TEST_F(ForeignSessionHandlerTest, HandleOpenForeignSessionAllTabs) {
+  EXPECT_CALL(*session_sync_service()->GetOpenTabsUIDelegate(),
+              GetForeignSession("my_session_tag", testing::_))
+      .Times(testing::AtLeast(1));
+
+  base::Value::List list_args;
+  list_args.Append("my_session_tag");
+  handler()->HandleOpenForeignSessionAllTabs(list_args);
+}
+
+TEST_F(ForeignSessionHandlerTest, HandleOpenForeignSessionTab) {
+  EXPECT_CALL(*session_sync_service()->GetOpenTabsUIDelegate(),
+              GetForeignTab("my_session_tag",
+                            SessionID::FromSerializedValue(456), testing::_))
+      .Times(testing::AtLeast(1));
+
+  base::Value::List list_args;
+  list_args.Append("my_session_tag");
+  list_args.Append("456");
+  list_args.Append(1.0);
+  list_args.Append(false);
+  list_args.Append(false);
+  list_args.Append(false);
+  list_args.Append(false);
+  handler()->HandleOpenForeignSessionTab(list_args);
+}
 
 }  // namespace browser_sync
diff --git a/chrome/browser/ui/webui/print_preview/local_printer_handler_chromeos.cc b/chrome/browser/ui/webui/print_preview/local_printer_handler_chromeos.cc
index 0e4e7a49..43b25a1 100644
--- a/chrome/browser/ui/webui/print_preview/local_printer_handler_chromeos.cc
+++ b/chrome/browser/ui/webui/print_preview/local_printer_handler_chromeos.cc
@@ -229,11 +229,12 @@
   size_t size_in_kb = print_data->size() / 1024;
   base::UmaHistogramMemoryKB("Printing.CUPS.PrintDocumentSize", size_in_kb);
 
+  std::string printer_id = *settings.FindString(kSettingDeviceName);
   auto call_start_local_print_callback =
       base::BindOnce(&LocalPrinterHandlerChromeos::CallStartLocalPrint,
                      weak_ptr_factory_.GetWeakPtr(), std::move(print_data),
                      std::move(callback));
-  GetAshJobSettings(*settings.FindString(kSettingDeviceName),
+  GetAshJobSettings(std::move(printer_id),
                     std::move(call_start_local_print_callback),
                     std::move(settings));
 }
diff --git a/chrome/browser/ui/webui/settings/ash/search/BUILD.gn b/chrome/browser/ui/webui/settings/ash/search/BUILD.gn
index 0beca303..6f2027b1 100644
--- a/chrome/browser/ui/webui/settings/ash/search/BUILD.gn
+++ b/chrome/browser/ui/webui/settings/ash/search/BUILD.gn
@@ -19,6 +19,6 @@
     "//mojo/public/mojom/base",
   ]
 
-  webui_module_path = "/search"
+  webui_module_path = "/"
   use_typescript_sources = true
 }
diff --git a/chrome/browser/ui/webui/settings/settings_localized_strings_provider.cc b/chrome/browser/ui/webui/settings/settings_localized_strings_provider.cc
index 2b41d7dd..2c4b03cb 100644
--- a/chrome/browser/ui/webui/settings/settings_localized_strings_provider.cc
+++ b/chrome/browser/ui/webui/settings/settings_localized_strings_provider.cc
@@ -243,6 +243,12 @@
   html_source->AddBoolean(
       "allowDeletingBrowserHistory",
       profile->GetPrefs()->GetBoolean(prefs::kAllowDeletingBrowserHistory));
+#if BUILDFLAG(IS_LINUX)
+  bool allow_qt_theme = base::FeatureList::IsEnabled(ui::kAllowQt);
+#else
+  bool allow_qt_theme = false;
+#endif
+  html_source->AddBoolean("allowQtTheme", allow_qt_theme);
 }
 
 void AddA11yStrings(content::WebUIDataSource* html_source) {
@@ -2412,7 +2418,6 @@
 
 void AddSearchEnginesStrings(content::WebUIDataSource* html_source) {
   static constexpr webui::LocalizedString kLocalizedStrings[] = {
-      {"searchEnginesPageTitle", IDS_SETTINGS_SEARCH_ENGINES},
       {"searchEnginesPageExplanation",
        IDS_SETTINGS_SEARCH_ENGINES_PAGE_EXPLANATION},
       {"searchEnginesAddSearchEngine",
@@ -2424,7 +2429,6 @@
       {"searchEnginesDeleteConfirmationDescription",
        IDS_SETTINGS_SEARCH_ENGINES_DELETE_CONFIRMATION_DESCRIPTION},
       {"searchEngines", IDS_SETTINGS_SEARCH_ENGINES},
-      {"searchEnginesDefault", IDS_SETTINGS_SEARCH_ENGINES_DEFAULT_ENGINES},
       {"searchEnginesSearchEngines",
        IDS_SETTINGS_SEARCH_ENGINES_SEARCH_ENGINES},
       {"searchEnginesSearchEnginesExplanation",
@@ -2435,8 +2439,6 @@
       {"searchEnginesNoSitesAdded", IDS_SETTINGS_SEARCH_ENGINES_NO_SITES_ADDED},
       {"searchEnginesInactiveShortcuts",
        IDS_SETTINGS_SEARCH_ENGINES_INACTIVE_SHORTCUTS},
-      {"searchEnginesNoInactiveShortcuts",
-       IDS_SETTINGS_SEARCH_ENGINES_NO_INACTIVE_SHORTCUTS},
       {"searchEnginesNoOtherEngines",
        IDS_SETTINGS_SEARCH_ENGINES_NO_OTHER_ENGINES},
       {"searchEnginesExtension", IDS_SETTINGS_SEARCH_ENGINES_EXTENSION_ENGINES},
@@ -2445,7 +2447,6 @@
       {"searchEnginesSearch", IDS_SETTINGS_SEARCH_ENGINES_SEARCH},
       {"searchEnginesSearchEngine", IDS_SETTINGS_SEARCH_ENGINES_SEARCH_ENGINE},
       {"searchEnginesSiteOrPage", IDS_SETTINGS_SEARCH_ENGINES_SITE_OR_PAGE},
-      {"searchEnginesInactiveSite", IDS_SETTINGS_SEARCH_ENGINES_INACTIVE_SITE},
       {"searchEnginesShortcut", IDS_SETTINGS_SEARCH_ENGINES_SHORTCUT},
       {"searchEnginesQueryURL", IDS_SETTINGS_SEARCH_ENGINES_QUERY_URL},
       {"searchEnginesQueryURLExplanation",
@@ -2453,8 +2454,6 @@
       {"searchEnginesMakeDefault", IDS_SETTINGS_SEARCH_ENGINES_MAKE_DEFAULT},
       {"searchEnginesActivate", IDS_SETTINGS_SEARCH_ENGINES_ACTIVATE},
       {"searchEnginesDeactivate", IDS_SETTINGS_SEARCH_ENGINES_DEACTIVATE},
-      {"searchEnginesRemoveFromList",
-       IDS_SETTINGS_SEARCH_ENGINES_REMOVE_FROM_LIST},
       {"searchEnginesManageExtension",
        IDS_SETTINGS_SEARCH_ENGINES_MANAGE_EXTENSION},
       {"searchEnginesKeyboardShortcutsTitle",
@@ -2469,8 +2468,6 @@
        IDS_SETTINGS_SEARCH_ENGINES_ADDITIONAL_SITES},
       {"searchEnginesAdditionalInactiveSites",
        IDS_SETTINGS_SEARCH_ENGINES_ADDITIONAL_INACTIVE_SITES},
-      {"searchEnginesAdditionalExtensions",
-       IDS_SETTINGS_SEARCH_ENGINES_ADDITIONAL_EXTENSIONS},
   };
   html_source->AddLocalizedStrings(kLocalizedStrings);
 }
diff --git a/chrome/browser/vr/chrome_xr_integration_client.cc b/chrome/browser/vr/chrome_xr_integration_client.cc
index b965b86..e636d8ff 100644
--- a/chrome/browser/vr/chrome_xr_integration_client.cc
+++ b/chrome/browser/vr/chrome_xr_integration_client.cc
@@ -41,6 +41,9 @@
 #include "components/webxr/android/cardboard_device_provider.h"
 #include "components/webxr/android/vr_compositor_delegate_provider.h"
 #endif
+#if BUILDFLAG(ENABLE_OPENXR)
+#include "components/webxr/android/openxr_device_provider.h"
+#endif
 #endif  // BUILDFLAG(IS_WIN)
 
 namespace {
@@ -119,11 +122,18 @@
   content::XRProviderList providers;
 
 #if BUILDFLAG(IS_ANDROID)
-  bool add_gvr_device_provider = true;
+  // May be unused if all runtimes are disabled.
+  [[maybe_unused]] bool preferred_vr_runtime_added = false;
+#if BUILDFLAG(ENABLE_OPENXR)
+  if (!preferred_vr_runtime_added &&
+      base::FeatureList::IsEnabled(device::features::kOpenXR)) {
+    providers.emplace_back(std::make_unique<webxr::OpenXrDeviceProvider>());
+    preferred_vr_runtime_added = true;
+  }
+#endif  // BUILDFLAG(ENABLE_OPENXR)
 #if BUILDFLAG(ENABLE_CARDBOARD)
-  // If the cardboard runtime is enabled we want to use it rather than the GVR
-  // runtime.
-  if (base::FeatureList::IsEnabled(device::features::kEnableCardboard)) {
+  if (!preferred_vr_runtime_added &&
+      base::FeatureList::IsEnabled(device::features::kEnableCardboard)) {
     base::android::ScopedJavaLocalRef<jobject>
         j_vr_compositor_delegate_provider =
             vr::Java_VrCompositorDelegateProviderImpl_Constructor(
@@ -131,14 +141,15 @@
     providers.emplace_back(std::make_unique<webxr::CardboardDeviceProvider>(
         std::make_unique<webxr::VrCompositorDelegateProvider>(
             std::move(j_vr_compositor_delegate_provider))));
-    add_gvr_device_provider = false;
+    preferred_vr_runtime_added = true;
   }
-#endif  // ENABLE_CARDBOARD
-  if (add_gvr_device_provider) {
+#endif  // BUILDFLAG(ENABLE_CARDBOARD)
 #if BUILDFLAG(ENABLE_GVR_SERVICES)
+  if (!preferred_vr_runtime_added) {
     providers.push_back(std::make_unique<device::GvrDeviceProvider>());
-#endif
+    preferred_vr_runtime_added = true;
   }
+#endif  // BUILDFLAG(ENABLE_GVR_SERVICES)
 #if BUILDFLAG(ENABLE_ARCORE)
   base::android::ScopedJavaLocalRef<jobject> j_ar_compositor_delegate_provider =
       vr::Java_ArCompositorDelegateProviderImpl_Constructor(
diff --git a/chrome/browser/web_applications/BUILD.gn b/chrome/browser/web_applications/BUILD.gn
index bc43b85c..8d7314e 100644
--- a/chrome/browser/web_applications/BUILD.gn
+++ b/chrome/browser/web_applications/BUILD.gn
@@ -103,6 +103,8 @@
     "isolated_web_apps/isolated_web_app_validator.h",
     "isolated_web_apps/pending_install_info.cc",
     "isolated_web_apps/pending_install_info.h",
+    "isolated_web_apps/register_controlled_frame_partition_command.cc",
+    "isolated_web_apps/register_controlled_frame_partition_command.h",
     "isolated_web_apps/signed_web_bundle_reader.cc",
     "isolated_web_apps/signed_web_bundle_reader.h",
     "locks/all_apps_lock.cc",
@@ -635,6 +637,7 @@
     "isolated_web_apps/isolated_web_app_url_loader_factory_unittest.cc",
     "isolated_web_apps/isolated_web_app_validator_unittest.cc",
     "isolated_web_apps/pending_install_info_unittest.cc",
+    "isolated_web_apps/register_controlled_frame_partition_command_unittest.cc",
     "isolated_web_apps/signed_web_bundle_reader_unittest.cc",
     "locks/web_app_lock_manager_unittest.cc",
     "os_integration/file_handling_sub_manager_unittest.cc",
@@ -806,6 +809,7 @@
     "isolated_web_apps/get_isolated_web_app_browsing_data_command_browsertest.cc",
     "isolated_web_apps/install_isolated_web_app_from_command_line_browsertest.cc",
     "isolated_web_apps/isolated_web_app_browsertest.cc",
+    "isolated_web_apps/isolated_web_app_browsing_data_browsertest.cc",
     "isolated_web_apps/isolated_web_app_error_page_browsertest.cc",
     "isolated_web_apps/isolated_web_app_url_loader_factory_browsertest.cc",
     "manifest_update_manager_browsertest.cc",
diff --git a/chrome/browser/web_applications/isolated_web_apps/isolated_web_app_browsing_data_browsertest.cc b/chrome/browser/web_applications/isolated_web_apps/isolated_web_app_browsing_data_browsertest.cc
new file mode 100644
index 0000000..c6520991
--- /dev/null
+++ b/chrome/browser/web_applications/isolated_web_apps/isolated_web_app_browsing_data_browsertest.cc
@@ -0,0 +1,137 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <memory>
+
+#include "base/check_deref.h"
+#include "base/test/scoped_feature_list.h"
+#include "base/test/test_future.h"
+#include "chrome/browser/ui/browser.h"
+#include "chrome/browser/ui/web_applications/test/isolated_web_app_test_utils.h"
+#include "chrome/browser/web_applications/isolated_web_apps/isolated_web_app_url_info.h"
+#include "chrome/browser/web_applications/web_app_provider.h"
+#include "components/services/storage/public/mojom/local_storage_control.mojom.h"
+#include "content/public/browser/storage_partition.h"
+#include "content/public/browser/web_contents.h"
+#include "content/public/common/content_features.h"
+#include "content/public/test/browser_test.h"
+#include "content/public/test/browser_test_utils.h"
+#include "net/test/embedded_test_server/embedded_test_server.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "url/origin.h"
+
+using ::testing::Eq;
+
+namespace web_app {
+
+// Evaluates to true if the test value is within 5% of the given value.
+MATCHER_P(IsApproximately, approximate_value, "") {
+  return arg > (approximate_value * 0.95) && arg < (approximate_value * 1.05);
+}
+
+class IsolatedWebAppBrowsingDataTest : public IsolatedWebAppBrowserTestHarness {
+ protected:
+  IsolatedWebAppBrowsingDataTest() {
+    scoped_feature_list_.InitAndEnableFeature(features::kIwaControlledFrame);
+  }
+
+  IsolatedWebAppUrlInfo InstallIsolatedWebApp() {
+    server_ =
+        CreateAndStartServer(FILE_PATH_LITERAL("web_apps/simple_isolated_app"));
+    web_app::IsolatedWebAppUrlInfo url_info =
+        InstallDevModeProxyIsolatedWebApp(server_->GetOrigin());
+    return url_info;
+  }
+
+  net::EmbeddedTestServer* dev_server() { return server_.get(); }
+
+  WebAppProvider& web_app_provider() {
+    return CHECK_DEREF(WebAppProvider::GetForTest(profile()));
+  }
+
+  int64_t GetIwaUsage(const IsolatedWebAppUrlInfo& url_info) {
+    base::test::TestFuture<base::flat_map<url::Origin, int64_t>> future;
+    web_app_provider().scheduler().GetIsolatedWebAppBrowsingData(
+        future.GetCallback());
+    base::flat_map<url::Origin, int64_t> result = future.Get();
+    return result.contains(url_info.origin()) ? result.at(url_info.origin())
+                                              : 0;
+  }
+
+  void AddUsageIfMissing(const content::ToRenderFrameHost& target) {
+    EXPECT_TRUE(
+        ExecJs(target, "localStorage.setItem('test', '!'.repeat(1000))"));
+
+    base::test::TestFuture<void> test_future;
+    target.render_frame_host()
+        ->GetStoragePartition()
+        ->GetLocalStorageControl()
+        ->Flush(test_future.GetCallback());
+    EXPECT_TRUE(test_future.Wait());
+  }
+
+  [[nodiscard]] bool CreateControlledFrame(content::WebContents* web_contents,
+                                           const GURL& src,
+                                           const std::string& partition) {
+    static std::string kCreateControlledFrame = R"(
+      (async function() {
+        const controlledframe = document.createElement('controlledframe');
+        controlledframe.setAttribute('src', $1);
+        controlledframe.setAttribute('partition', $2);
+        await new Promise((resolve) => {
+          controlledframe.addEventListener('loadcommit', resolve);
+          document.body.appendChild(controlledframe);
+        });
+      })();
+    )";
+    return ExecJs(web_contents,
+                  content::JsReplace(kCreateControlledFrame, src, partition));
+  }
+
+ private:
+  base::test::ScopedFeatureList scoped_feature_list_;
+  std::unique_ptr<net::EmbeddedTestServer> server_;
+};
+
+IN_PROC_BROWSER_TEST_F(IsolatedWebAppBrowsingDataTest,
+                       ControlledFrameUsageIsCounted) {
+  IsolatedWebAppUrlInfo url_info = InstallIsolatedWebApp();
+  Browser* browser = LaunchWebAppBrowserAndWait(url_info.app_id());
+  content::WebContents* web_contents =
+      browser->tab_strip_model()->GetActiveWebContents();
+
+  EXPECT_THAT(GetIwaUsage(url_info), Eq(0));
+
+  // Add some usage to the IWA and make sure it's counted.
+  AddUsageIfMissing(web_contents);
+  EXPECT_THAT(GetIwaUsage(url_info), IsApproximately(1000));
+
+  // Create a persisted <controlledframe>, add some usage to it.
+  EXPECT_TRUE(CreateControlledFrame(web_contents,
+                                    dev_server()->GetURL("/empty_title.html"),
+                                    "persist:partition_name"));
+  ASSERT_EQ(1UL, web_contents->GetInnerWebContents().size());
+  AddUsageIfMissing(web_contents->GetInnerWebContents()[0]);
+  EXPECT_THAT(GetIwaUsage(url_info), IsApproximately(2000));
+
+  // Create another persisted <controlledframe> with a different partition name.
+  EXPECT_TRUE(CreateControlledFrame(web_contents,
+                                    dev_server()->GetURL("/empty_title.html"),
+                                    "persist:partition_name_2"));
+  ASSERT_EQ(2UL, web_contents->GetInnerWebContents().size());
+  AddUsageIfMissing(web_contents->GetInnerWebContents()[0]);
+  AddUsageIfMissing(web_contents->GetInnerWebContents()[1]);
+  EXPECT_THAT(GetIwaUsage(url_info), IsApproximately(3000));
+
+  // Create an in-memory <controlledframe> that won't count towards IWA usage.
+  EXPECT_TRUE(CreateControlledFrame(
+      web_contents, dev_server()->GetURL("/empty_title.html"), "unpersisted"));
+  ASSERT_EQ(3UL, web_contents->GetInnerWebContents().size());
+  AddUsageIfMissing(web_contents->GetInnerWebContents()[0]);
+  AddUsageIfMissing(web_contents->GetInnerWebContents()[1]);
+  AddUsageIfMissing(web_contents->GetInnerWebContents()[2]);
+  EXPECT_THAT(GetIwaUsage(url_info), IsApproximately(3000));
+}
+
+}  // namespace web_app
diff --git a/chrome/browser/web_applications/isolated_web_apps/register_controlled_frame_partition_command.cc b/chrome/browser/web_applications/isolated_web_apps/register_controlled_frame_partition_command.cc
new file mode 100644
index 0000000..373eeecd
--- /dev/null
+++ b/chrome/browser/web_applications/isolated_web_apps/register_controlled_frame_partition_command.cc
@@ -0,0 +1,45 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/web_applications/isolated_web_apps/register_controlled_frame_partition_command.h"
+
+#include <string>
+
+#include "base/functional/callback.h"
+#include "base/task/sequenced_task_runner.h"
+#include "base/values.h"
+#include "chrome/browser/web_applications/locks/app_lock.h"
+#include "chrome/browser/web_applications/web_app.h"
+#include "chrome/browser/web_applications/web_app_id.h"
+#include "chrome/browser/web_applications/web_app_registry_update.h"
+
+namespace web_app {
+
+base::Value RegisterControlledFramePartitionWithLock(
+    const AppId& app_id,
+    const std::string& partition_name,
+    base::OnceClosure callback,
+    AppLock& lock) {
+  {
+    ScopedRegistryUpdate update(&lock.sync_bridge());
+    WebApp* iwa = update->UpdateApp(app_id);
+    CHECK(iwa && iwa->isolation_data().has_value());
+
+    // TODO(crbug.com/1445795): If the StoragePartition is flagged for deletion,
+    // clear the flag.
+    WebApp::IsolationData isolation_data = *iwa->isolation_data();
+    isolation_data.controlled_frame_partitions.insert(partition_name);
+    iwa->SetIsolationData(isolation_data);
+  }
+
+  base::Value::Dict debug_info;
+  debug_info.Set("app_id", app_id);
+  debug_info.Set("partition_name", partition_name);
+
+  base::SequencedTaskRunner::GetCurrentDefault()->PostTask(FROM_HERE,
+                                                           std::move(callback));
+  return base::Value(debug_info.Clone());
+}
+
+}  // namespace web_app
diff --git a/chrome/browser/web_applications/isolated_web_apps/register_controlled_frame_partition_command.h b/chrome/browser/web_applications/isolated_web_apps/register_controlled_frame_partition_command.h
new file mode 100644
index 0000000..d604618
--- /dev/null
+++ b/chrome/browser/web_applications/isolated_web_apps/register_controlled_frame_partition_command.h
@@ -0,0 +1,29 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_WEB_APPLICATIONS_ISOLATED_WEB_APPS_REGISTER_CONTROLLED_FRAME_PARTITION_COMMAND_H_
+#define CHROME_BROWSER_WEB_APPLICATIONS_ISOLATED_WEB_APPS_REGISTER_CONTROLLED_FRAME_PARTITION_COMMAND_H_
+
+#include <string>
+
+#include "base/functional/callback_forward.h"
+#include "base/values.h"
+#include "chrome/browser/web_applications/web_app_id.h"
+
+namespace web_app {
+
+class AppLock;
+
+// Registers a <controlledframe>'s persisted StoragePartition with the web_app
+// system so that its usage can be attributed to its owning IWA and cleaned up
+// when the app is uninstalled.
+base::Value RegisterControlledFramePartitionWithLock(
+    const AppId& app_id,
+    const std::string& partition_name,
+    base::OnceClosure callback,
+    AppLock& lock);
+
+}  // namespace web_app
+
+#endif  // CHROME_BROWSER_WEB_APPLICATIONS_ISOLATED_WEB_APPS_REGISTER_CONTROLLED_FRAME_PARTITION_COMMAND_H_
diff --git a/chrome/browser/web_applications/isolated_web_apps/register_controlled_frame_partition_command_unittest.cc b/chrome/browser/web_applications/isolated_web_apps/register_controlled_frame_partition_command_unittest.cc
new file mode 100644
index 0000000..2e2802f628
--- /dev/null
+++ b/chrome/browser/web_applications/isolated_web_apps/register_controlled_frame_partition_command_unittest.cc
@@ -0,0 +1,124 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/web_applications/isolated_web_apps/register_controlled_frame_partition_command.h"
+
+#include <string>
+#include <vector>
+
+#include "base/run_loop.h"
+#include "base/test/scoped_feature_list.h"
+#include "base/types/expected.h"
+#include "chrome/browser/ui/web_applications/test/isolated_web_app_test_utils.h"
+#include "chrome/browser/web_applications/isolated_web_apps/isolated_web_app_url_info.h"
+#include "chrome/browser/web_applications/test/web_app_install_test_utils.h"
+#include "chrome/browser/web_applications/test/web_app_test.h"
+#include "chrome/browser/web_applications/web_app_provider.h"
+#include "chrome/browser/web_applications/web_app_registrar.h"
+#include "content/public/browser/storage_partition_config.h"
+#include "content/public/common/content_features.h"
+#include "url/gurl.h"
+
+using ::testing::UnorderedElementsAre;
+
+namespace web_app {
+
+class RegisterControlledFramePartitionCommandTest : public WebAppTest {
+ public:
+  RegisterControlledFramePartitionCommandTest() {
+    scoped_feature_list_.InitAndEnableFeature(features::kIsolatedWebApps);
+  }
+
+  void SetUp() override {
+    WebAppTest::SetUp();
+
+    test::AwaitStartWebAppProviderAndSubsystems(profile());
+  }
+
+  void RunCommand(const IsolatedWebAppUrlInfo& url_info,
+                  const std::string& partition_name) {
+    base::RunLoop loop;
+    provider().scheduler().ScheduleCallbackWithLock(
+        "RegisterControlledFramePartition",
+        std::make_unique<AppLockDescription>(url_info.app_id()),
+        base::BindOnce(&RegisterControlledFramePartitionWithLock,
+                       url_info.app_id(), partition_name, loop.QuitClosure()),
+        FROM_HERE);
+    loop.Run();
+  }
+
+  IsolatedWebAppUrlInfo InstallIsolatedWebApp(const GURL& url) {
+    AddDummyIsolatedAppToRegistry(profile(), url, "IWA Name");
+    base::expected<IsolatedWebAppUrlInfo, std::string> url_info =
+        IsolatedWebAppUrlInfo::Create(url);
+    CHECK(url_info.has_value());
+    return *url_info;
+  }
+
+  WebAppProvider& provider() { return *WebAppProvider::GetForTest(profile()); }
+
+  WebAppRegistrar& registrar() { return provider().registrar_unsafe(); }
+
+ private:
+  base::test::ScopedFeatureList scoped_feature_list_;
+};
+
+TEST_F(RegisterControlledFramePartitionCommandTest, CanRegisterPartition) {
+  const GURL app_url(
+      "isolated-app://"
+      "berugqztij5biqquuk3mfwpsaibuegaqcitgfchwuosuofdjabzqaaic");
+  IsolatedWebAppUrlInfo url_info = InstallIsolatedWebApp(app_url);
+
+  RunCommand(url_info, "partition name");
+
+  std::vector<content::StoragePartitionConfig> storage_partitions =
+      registrar().GetIsolatedWebAppStoragePartitionConfigs(url_info.app_id());
+  ASSERT_THAT(storage_partitions,
+              UnorderedElementsAre(
+                  url_info.storage_partition_config(profile()),
+                  url_info.GetStoragePartitionConfigForControlledFrame(
+                      profile(), "partition name", /*in_memory=*/false)));
+}
+
+TEST_F(RegisterControlledFramePartitionCommandTest,
+       CanRegisterMultiplePartitions) {
+  const GURL app_url(
+      "isolated-app://"
+      "berugqztij5biqquuk3mfwpsaibuegaqcitgfchwuosuofdjabzqaaic");
+  IsolatedWebAppUrlInfo url_info = InstallIsolatedWebApp(app_url);
+
+  RunCommand(url_info, "partition name 1");
+  RunCommand(url_info, "partition name 2");
+
+  std::vector<content::StoragePartitionConfig> storage_partitions =
+      registrar().GetIsolatedWebAppStoragePartitionConfigs(url_info.app_id());
+  ASSERT_THAT(storage_partitions,
+              UnorderedElementsAre(
+                  url_info.storage_partition_config(profile()),
+                  url_info.GetStoragePartitionConfigForControlledFrame(
+                      profile(), "partition name 1", /*in_memory=*/false),
+                  url_info.GetStoragePartitionConfigForControlledFrame(
+                      profile(), "partition name 2", /*in_memory=*/false)));
+}
+
+TEST_F(RegisterControlledFramePartitionCommandTest,
+       DuplicatePartitionsIgnored) {
+  const GURL app_url(
+      "isolated-app://"
+      "berugqztij5biqquuk3mfwpsaibuegaqcitgfchwuosuofdjabzqaaic");
+  IsolatedWebAppUrlInfo url_info = InstallIsolatedWebApp(app_url);
+
+  RunCommand(url_info, "partition name");
+  RunCommand(url_info, "partition name");
+
+  std::vector<content::StoragePartitionConfig> storage_partitions =
+      registrar().GetIsolatedWebAppStoragePartitionConfigs(url_info.app_id());
+  ASSERT_THAT(storage_partitions,
+              UnorderedElementsAre(
+                  url_info.storage_partition_config(profile()),
+                  url_info.GetStoragePartitionConfigForControlledFrame(
+                      profile(), "partition name", /*in_memory=*/false)));
+}
+
+}  // namespace web_app
diff --git a/chrome/browser/web_applications/proto/web_app.proto b/chrome/browser/web_applications/proto/web_app.proto
index 4046c0c..33ca7b74 100644
--- a/chrome/browser/web_applications/proto/web_app.proto
+++ b/chrome/browser/web_applications/proto/web_app.proto
@@ -175,6 +175,10 @@
     DevModeBundle dev_mode_bundle = 2;
     DevModeProxy dev_mode_proxy = 3;
   }
+
+  // The partition_name of every <controlledframe> StoragePartition managed by
+  // this Isolated Web App.
+  repeated string controlled_frame_partitions = 4;
 }
 
 // Full WebApp object data. See detailed comments in
diff --git a/chrome/browser/web_applications/test/web_app_test_utils.cc b/chrome/browser/web_applications/test/web_app_test_utils.cc
index d431a62..17f7a76 100644
--- a/chrome/browser/web_applications/test/web_app_test_utils.cc
+++ b/chrome/browser/web_applications/test/web_app_test_utils.cc
@@ -918,9 +918,12 @@
     };
     static_assert(std::size(location_types) == kNumLocationTypes);
 
-    IsolatedWebAppLocation location(
+    WebApp::IsolationData isolation_data(
         location_types[random.next_uint(kNumLocationTypes)]);
-    app->SetIsolationData(WebApp::IsolationData(location));
+    if (random.next_bool()) {
+      isolation_data.controlled_frame_partitions.insert("partition_name");
+    }
+    app->SetIsolationData(isolation_data);
   }
 
   return app;
diff --git a/chrome/browser/web_applications/web_app.cc b/chrome/browser/web_applications/web_app.cc
index 8a6b1020..661cc5a 100644
--- a/chrome/browser/web_applications/web_app.cc
+++ b/chrome/browser/web_applications/web_app.cc
@@ -742,6 +742,11 @@
 
 WebApp::IsolationData::IsolationData(IsolatedWebAppLocation location)
     : location(location) {}
+WebApp::IsolationData::IsolationData(
+    IsolatedWebAppLocation location,
+    const std::set<std::string>& controlled_frame_partitions)
+    : location(location),
+      controlled_frame_partitions(controlled_frame_partitions) {}
 WebApp::IsolationData::~IsolationData() = default;
 WebApp::IsolationData::IsolationData(const WebApp::IsolationData&) = default;
 WebApp::IsolationData& WebApp::IsolationData::operator=(
@@ -752,7 +757,8 @@
 
 bool WebApp::IsolationData::operator==(
     const WebApp::IsolationData& other) const {
-  return location == other.location;
+  return location == other.location &&
+         controlled_frame_partitions == other.controlled_frame_partitions;
 }
 bool WebApp::IsolationData::operator!=(
     const WebApp::IsolationData& other) const {
@@ -763,6 +769,11 @@
   base::Value::Dict value;
   value.Set("isolated_web_app_location",
             IsolatedWebAppLocationAsDebugValue(location));
+  base::Value::List* partitions =
+      value.EnsureList("controlled_frame_partitions");
+  for (const std::string& partition : controlled_frame_partitions) {
+    partitions->Append(partition);
+  }
   return base::Value(std::move(value));
 }
 
diff --git a/chrome/browser/web_applications/web_app.h b/chrome/browser/web_applications/web_app.h
index d7a0a510..c443f9c9 100644
--- a/chrome/browser/web_applications/web_app.h
+++ b/chrome/browser/web_applications/web_app.h
@@ -6,6 +6,7 @@
 #define CHROME_BROWSER_WEB_APPLICATIONS_WEB_APP_H_
 
 #include <iosfwd>
+#include <set>
 #include <string>
 #include <vector>
 
@@ -332,6 +333,8 @@
   // IWA-specific information like bundle location.
   struct IsolationData {
     explicit IsolationData(IsolatedWebAppLocation location);
+    IsolationData(IsolatedWebAppLocation location,
+                  const std::set<std::string>& controlled_frame_partitions);
     ~IsolationData();
     IsolationData(const IsolationData&);
     IsolationData& operator=(const IsolationData&);
@@ -343,6 +346,7 @@
     base::Value AsDebugValue() const;
 
     IsolatedWebAppLocation location;
+    std::set<std::string> controlled_frame_partitions;
   };
   const absl::optional<IsolationData>& isolation_data() const {
     return isolation_data_;
diff --git a/chrome/browser/web_applications/web_app_command_scheduler.cc b/chrome/browser/web_applications/web_app_command_scheduler.cc
index 20d0272..75c2a31 100644
--- a/chrome/browser/web_applications/web_app_command_scheduler.cc
+++ b/chrome/browser/web_applications/web_app_command_scheduler.cc
@@ -35,6 +35,7 @@
 #include "chrome/browser/web_applications/isolated_web_apps/get_isolated_web_app_browsing_data_command.h"
 #include "chrome/browser/web_applications/isolated_web_apps/install_isolated_web_app_command.h"
 #include "chrome/browser/web_applications/isolated_web_apps/isolated_web_app_url_info.h"
+#include "chrome/browser/web_applications/isolated_web_apps/register_controlled_frame_partition_command.h"
 #include "chrome/browser/web_applications/locks/all_apps_lock.h"
 #include "chrome/browser/web_applications/locks/app_lock.h"
 #include "chrome/browser/web_applications/locks/noop_lock.h"
@@ -382,6 +383,25 @@
       call_location);
 }
 
+void WebAppCommandScheduler::RegisterControlledFramePartition(
+    const AppId& app_id,
+    const std::string& partition_name,
+    base::OnceClosure callback,
+    const base::Location& location) {
+  if (IsShuttingDown()) {
+    base::SequencedTaskRunner::GetCurrentDefault()->PostTask(
+        FROM_HERE, std::move(callback));
+    return;
+  }
+
+  provider_->scheduler().ScheduleCallbackWithLock<AppLock>(
+      "RegisterControlledFramePartition",
+      std::make_unique<AppLockDescription>(app_id),
+      base::BindOnce(&RegisterControlledFramePartitionWithLock, app_id,
+                     partition_name, std::move(callback)),
+      location);
+}
+
 void WebAppCommandScheduler::InstallFromSync(const WebApp& web_app,
                                              OnceInstallCallback callback,
                                              const base::Location& location) {
diff --git a/chrome/browser/web_applications/web_app_command_scheduler.h b/chrome/browser/web_applications/web_app_command_scheduler.h
index 58a8ef0c..32fabd0b 100644
--- a/chrome/browser/web_applications/web_app_command_scheduler.h
+++ b/chrome/browser/web_applications/web_app_command_scheduler.h
@@ -183,6 +183,14 @@
       base::OnceCallback<void(base::flat_map<url::Origin, int64_t>)> callback,
       const base::Location& call_location = FROM_HERE);
 
+  // Registers a <controlledframe>'s StoragePartition with the given Isolated
+  // Web App.
+  void RegisterControlledFramePartition(
+      const AppId& app_id,
+      const std::string& partition_name,
+      base::OnceClosure callback,
+      const base::Location& location = FROM_HERE);
+
   // Scheduler a command that installs a web app from sync.
   void InstallFromSync(const WebApp& web_app,
                        OnceInstallCallback callback,
diff --git a/chrome/browser/web_applications/web_app_database.cc b/chrome/browser/web_applications/web_app_database.cc
index 41a79c4..e6e0edc 100644
--- a/chrome/browser/web_applications/web_app_database.cc
+++ b/chrome/browser/web_applications/web_app_database.cc
@@ -4,6 +4,7 @@
 
 #include "chrome/browser/web_applications/web_app_database.h"
 
+#include <set>
 #include <string>
 #include <utility>
 #include <vector>
@@ -812,6 +813,11 @@
 
   if (web_app.isolation_data().has_value()) {
     auto* mutable_data = local_data->mutable_isolation_data();
+    for (const std::string& partition :
+         web_app.isolation_data()->controlled_frame_partitions) {
+      mutable_data->add_controlled_frame_partitions(partition);
+    }
+
     absl::visit(
         base::Overloaded{
             [&mutable_data](const InstalledBundle& bundle) {
@@ -1502,6 +1508,11 @@
   }
 
   if (local_data.has_isolation_data()) {
+    const google::protobuf::RepeatedPtrField<std::string>& partitions =
+        local_data.isolation_data().controlled_frame_partitions();
+    std::set<std::string> controlled_frame_partitions(partitions.begin(),
+                                                      partitions.end());
+
     switch (local_data.isolation_data().location_case()) {
       case IsolationDataProto::LocationCase::kInstalledBundle: {
         absl::optional<base::FilePath> path = ProtoToFilePath(
@@ -1511,8 +1522,8 @@
                          "parse error: cannot deserialize file path";
           return nullptr;
         }
-        web_app->SetIsolationData(
-            WebApp::IsolationData(InstalledBundle{.path = *path}));
+        web_app->SetIsolationData(WebApp::IsolationData(
+            InstalledBundle{.path = *path}, controlled_frame_partitions));
         break;
       }
 
@@ -1524,8 +1535,8 @@
                          "parse error: cannot deserialize file path";
           return nullptr;
         }
-        web_app->SetIsolationData(
-            WebApp::IsolationData(DevModeBundle{.path = *path}));
+        web_app->SetIsolationData(WebApp::IsolationData(
+            DevModeBundle{.path = *path}, controlled_frame_partitions));
         break;
       }
 
@@ -1540,8 +1551,8 @@
                      local_data.isolation_data().dev_mode_proxy().proxy_url();
           return nullptr;
         }
-        web_app->SetIsolationData(
-            WebApp::IsolationData(DevModeProxy{.proxy_url = proxy_url}));
+        web_app->SetIsolationData(WebApp::IsolationData(
+            DevModeProxy{.proxy_url = proxy_url}, controlled_frame_partitions));
         break;
       }
 
diff --git a/chrome/browser/web_applications/web_app_registrar.cc b/chrome/browser/web_applications/web_app_registrar.cc
index ce048141..8303e528 100644
--- a/chrome/browser/web_applications/web_app_registrar.cc
+++ b/chrome/browser/web_applications/web_app_registrar.cc
@@ -884,8 +884,14 @@
     return {};
   }
 
-  // TODO(crbug.com/1311065): Include Controlled Frame StoragePartitions.
-  return {url_info->storage_partition_config(profile_)};
+  std::vector<content::StoragePartitionConfig> partitions = {
+      url_info->storage_partition_config(profile_)};
+  for (const std::string& partition :
+       isolated_web_app->isolation_data()->controlled_frame_partitions) {
+    partitions.push_back(url_info->GetStoragePartitionConfigForControlledFrame(
+        profile_, partition, /*in_memory=*/false));
+  }
+  return partitions;
 }
 
 std::string WebAppRegistrar::GetAppShortName(const AppId& app_id) const {
diff --git a/chrome/browser/web_applications/web_app_unittest.cc b/chrome/browser/web_applications/web_app_unittest.cc
index 33aa7044..b940b37 100644
--- a/chrome/browser/web_applications/web_app_unittest.cc
+++ b/chrome/browser/web_applications/web_app_unittest.cc
@@ -343,7 +343,8 @@
           "installed_bundle": {
             "path": "random_path"
           }
-        }
+        },
+        "controlled_frame_partitions": []
       })")
                                             .value();
 
diff --git a/chrome/browser/webid/federated_identity_auto_reauthn_permission_context.cc b/chrome/browser/webid/federated_identity_auto_reauthn_permission_context.cc
index ac54b22..baa467c 100644
--- a/chrome/browser/webid/federated_identity_auto_reauthn_permission_context.cc
+++ b/chrome/browser/webid/federated_identity_auto_reauthn_permission_context.cc
@@ -54,7 +54,7 @@
       ContentSettingsType::FEDERATED_IDENTITY_AUTO_REAUTHN_PERMISSION);
 }
 
-void FederatedIdentityAutoReauthnPermissionContext::RecordDisplayAndEmbargo(
+void FederatedIdentityAutoReauthnPermissionContext::RecordEmbargoForAutoReauthn(
     const url::Origin& relying_party_embedder) {
   const GURL rp_embedder_url = relying_party_embedder.GetURL();
   permission_autoblocker_->RecordDisplayAndEmbargo(
@@ -62,6 +62,14 @@
       ContentSettingsType::FEDERATED_IDENTITY_AUTO_REAUTHN_PERMISSION);
 }
 
+void FederatedIdentityAutoReauthnPermissionContext::RemoveEmbargoForAutoReauthn(
+    const url::Origin& relying_party_embedder) {
+  const GURL rp_embedder_url = relying_party_embedder.GetURL();
+  permission_autoblocker_->RemoveEmbargoAndResetCounts(
+      rp_embedder_url,
+      ContentSettingsType::FEDERATED_IDENTITY_AUTO_REAUTHN_PERMISSION);
+}
+
 void FederatedIdentityAutoReauthnPermissionContext::SetRequiresUserMediation(
     const GURL& rp_url,
     bool requires_user_mediation) {
diff --git a/chrome/browser/webid/federated_identity_auto_reauthn_permission_context.h b/chrome/browser/webid/federated_identity_auto_reauthn_permission_context.h
index 8b17e40..371f7cb 100644
--- a/chrome/browser/webid/federated_identity_auto_reauthn_permission_context.h
+++ b/chrome/browser/webid/federated_identity_auto_reauthn_permission_context.h
@@ -40,7 +40,9 @@
       const url::Origin& relying_party_embedder) override;
   base::Time GetAutoReauthnEmbargoStartTime(
       const url::Origin& relying_party_embedder) override;
-  void RecordDisplayAndEmbargo(
+  void RecordEmbargoForAutoReauthn(
+      const url::Origin& relying_party_embedder) override;
+  void RemoveEmbargoForAutoReauthn(
       const url::Origin& relying_party_embedder) override;
   void SetRequiresUserMediation(const GURL& rp_url,
                                 bool requires_user_mediation) override;
diff --git a/chrome/browser/webid/federated_identity_auto_reauthn_permission_context_unittest.cc b/chrome/browser/webid/federated_identity_auto_reauthn_permission_context_unittest.cc
index a6e4e53ca..83526cb 100644
--- a/chrome/browser/webid/federated_identity_auto_reauthn_permission_context_unittest.cc
+++ b/chrome/browser/webid/federated_identity_auto_reauthn_permission_context_unittest.cc
@@ -67,14 +67,14 @@
 }
 
 // Test that
-// FederatedIdentityAutoReauthnPermissionContext::RecordDisplayAndEmbargo()
+// FederatedIdentityAutoReauthnPermissionContext::RecordEmbargoForAutoReauthn()
 // blocks the permission if it is enabled.
 TEST_F(FederatedIdentityAutoReauthnPermissionContextTest, EnabledEmbargo) {
   GURL rp_url("https://rp.com");
   EXPECT_FALSE(context_->IsAutoReauthnEmbargoed(url::Origin::Create(rp_url)));
 
   // Embargoing `rp_url` should block the content setting for `rp_url`.
-  context_->RecordDisplayAndEmbargo(url::Origin::Create(rp_url));
+  context_->RecordEmbargoForAutoReauthn(url::Origin::Create(rp_url));
   EXPECT_TRUE(context_->IsAutoReauthnEmbargoed(url::Origin::Create(rp_url)));
 }
 
@@ -86,7 +86,7 @@
   EXPECT_FALSE(context_->IsAutoReauthnEmbargoed(url::Origin::Create(rp_url)));
 
   // Auto re-authn flow triggers embargo.
-  context_->RecordDisplayAndEmbargo(url::Origin::Create(rp_url));
+  context_->RecordEmbargoForAutoReauthn(url::Origin::Create(rp_url));
   EXPECT_TRUE(context_->IsAutoReauthnEmbargoed(url::Origin::Create(rp_url)));
 
   // Auto re-authn is still in embargo state after 9 mins.
@@ -109,7 +109,7 @@
   EXPECT_FALSE(context_->IsAutoReauthnEmbargoed(url::Origin::Create(rp_url)));
 
   // Auto re-authn flow triggers embargo.
-  context_->RecordDisplayAndEmbargo(url::Origin::Create(rp_url));
+  context_->RecordEmbargoForAutoReauthn(url::Origin::Create(rp_url));
   EXPECT_TRUE(context_->IsAutoReauthnEmbargoed(url::Origin::Create(rp_url)));
 
   // User signing out sets the `RequiresUserMediation` bit.
@@ -127,28 +127,14 @@
   EXPECT_EQ(CONTENT_SETTING_BLOCK, GetContentSetting(rp_url));
 }
 
-// Test that successful re-authn resets `RequiresUserMediation` bit but not
-// embargo bit.
-TEST_F(FederatedIdentityAutoReauthnPermissionContextTest,
-       SuccessfulReAuthnDoesNotResetEmbargo) {
+// Test that auto re-authn embargo can be reset.
+TEST_F(FederatedIdentityAutoReauthnPermissionContextTest, ResetEmbargo) {
   GURL rp_url("https://rp.com");
-  host_content_settings_map_->SetDefaultContentSetting(
-      ContentSettingsType::FEDERATED_IDENTITY_AUTO_REAUTHN_PERMISSION,
-      CONTENT_SETTING_ALLOW);
-  EXPECT_EQ(CONTENT_SETTING_ALLOW, GetContentSetting(rp_url));
 
   // Auto re-authn flow triggers embargo.
-  context_->RecordDisplayAndEmbargo(url::Origin::Create(rp_url));
+  context_->RecordEmbargoForAutoReauthn(url::Origin::Create(rp_url));
   EXPECT_TRUE(context_->IsAutoReauthnEmbargoed(url::Origin::Create(rp_url)));
 
-  // User signing out sets the `RequiresUserMediation` bit.
-  context_->SetRequiresUserMediation(rp_url, true);
-  EXPECT_EQ(CONTENT_SETTING_BLOCK, GetContentSetting(rp_url));
-
-  // User signing back in resets the `RequiresUserMediation` bit.
-  context_->SetRequiresUserMediation(rp_url, false);
-  EXPECT_EQ(CONTENT_SETTING_ALLOW, GetContentSetting(rp_url));
-
-  // Auto re-authn is still in embargo state.
-  EXPECT_TRUE(context_->IsAutoReauthnEmbargoed(url::Origin::Create(rp_url)));
+  context_->RemoveEmbargoForAutoReauthn(url::Origin::Create(rp_url));
+  EXPECT_FALSE(context_->IsAutoReauthnEmbargoed(url::Origin::Create(rp_url)));
 }
diff --git a/chrome/build/lacros64.pgo.txt b/chrome/build/lacros64.pgo.txt
index 6a52286a..a34225f 100644
--- a/chrome/build/lacros64.pgo.txt
+++ b/chrome/build/lacros64.pgo.txt
@@ -1 +1 @@
-chrome-chromeos-amd64-generic-main-1684511851-ee9700ef5c462699a0096e252df784745f1f2aeb.profdata
+chrome-chromeos-amd64-generic-main-1684540798-b9aa8703d2e51d0776f8163cd4208225ad0641e8.profdata
diff --git a/chrome/build/linux.pgo.txt b/chrome/build/linux.pgo.txt
index 14a49b1a..60ae4dc 100644
--- a/chrome/build/linux.pgo.txt
+++ b/chrome/build/linux.pgo.txt
@@ -1 +1 @@
-chrome-linux-main-1684519166-8dea3a29c1b315123d5d0396bc650cd0a5c6e91b.profdata
+chrome-linux-main-1684540798-ec543f9b6478012404bd54048a7f2cfc6c06da40.profdata
diff --git a/chrome/build/mac-arm.pgo.txt b/chrome/build/mac-arm.pgo.txt
index 750afac..d782e63 100644
--- a/chrome/build/mac-arm.pgo.txt
+++ b/chrome/build/mac-arm.pgo.txt
@@ -1 +1 @@
-chrome-mac-arm-main-1684519166-993e978a5bd776668ae7f2e61ce5661995f9a428.profdata
+chrome-mac-arm-main-1684547483-a37a60dc42641dabb87da39ee4aff0f039e5607b.profdata
diff --git a/chrome/build/mac.pgo.txt b/chrome/build/mac.pgo.txt
index ea91384..4944fcf 100644
--- a/chrome/build/mac.pgo.txt
+++ b/chrome/build/mac.pgo.txt
@@ -1 +1 @@
-chrome-mac-main-1684497579-6f9cf570334cc2f9f520c7ae674eabe5b64b329c.profdata
+chrome-mac-main-1684540798-853b78d2023bfe2c8a206cb0282a997a38a444a4.profdata
diff --git a/chrome/build/win32.pgo.txt b/chrome/build/win32.pgo.txt
index ffb387e..7614b00 100644
--- a/chrome/build/win32.pgo.txt
+++ b/chrome/build/win32.pgo.txt
@@ -1 +1 @@
-chrome-win32-main-1684519166-56536d9beb12720cf1161b573aaf47368379f831.profdata
+chrome-win32-main-1684529573-1995607560dca6a515bef834117b173272024d8b.profdata
diff --git a/chrome/build/win64.pgo.txt b/chrome/build/win64.pgo.txt
index d99cf1b65..48407758 100644
--- a/chrome/build/win64.pgo.txt
+++ b/chrome/build/win64.pgo.txt
@@ -1 +1 @@
-chrome-win64-main-1684519166-49b604f1c7bac7716c8390ce6b6297e53aa3755d.profdata
+chrome-win64-main-1684540738-bfb97d1a416c843861feb90169313d0c6e30659a.profdata
diff --git a/chrome/test/data/web_apps/sample_web_app.json b/chrome/test/data/web_apps/sample_web_app.json
index a0418d42..daadfeda 100644
--- a/chrome/test/data/web_apps/sample_web_app.json
+++ b/chrome/test/data/web_apps/sample_web_app.json
@@ -189,6 +189,7 @@
    "is_locally_installed": true,
    "is_uninstalling": false,
    "isolation_data": {
+      "controlled_frame_partitions": [ "partition_name" ],
       "isolated_web_app_location": {
          "dev_mode_bundle": {
             "path": "1234"
diff --git a/chrome/test/data/webui/chromeos/print_management/print_management_test.ts b/chrome/test/data/webui/chromeos/print_management/print_management_test.ts
index 9a503cf..94b4c193 100644
--- a/chrome/test/data/webui/chromeos/print_management/print_management_test.ts
+++ b/chrome/test/data/webui/chromeos/print_management/print_management_test.ts
@@ -1141,6 +1141,30 @@
     return flushTasks();
   }
 
+  /**
+   * Gets the trimmed text content for the requested element in the
+   * PrinterSetupInfoElement shadowDOM. Both `printerSetupInfoElement` and the
+   * element being looked up cannot be null.
+   */
+  function getElementTextContent(selector: string): string {
+    assertTrue(!!printerSetupInfoElement);
+    const element =
+        querySelector<HTMLElement>(printerSetupInfoElement!, selector);
+    assertTrue(!!element);
+
+    return element!.textContent?.trim() ?? '';
+  }
+
+  /**
+   * Gets the localized string matching the provided localization key using the
+   * `i18n` function on `PrinterSetupInfoElement`.
+   */
+  function getLocalizedString(localizationKey: string): string {
+    assertTrue(!!printerSetupInfoElement);
+
+    return printerSetupInfoElement!.i18n(localizationKey);
+  }
+
   // Verify core elements of element rendered.
   test('ensureBasicLayoutRenders', async () => {
     await initPrinterSetupInfoElement();
@@ -1154,4 +1178,22 @@
     assertTrue(isVisible(
         querySelector<CrButtonElement>(printerSetupInfoElement!, 'cr-button')));
   });
+
+  // Verify expected localized strings are used in UI.
+  test('ensureLocalizedStringsMatch', async () => {
+    await initPrinterSetupInfoElement();
+
+    const expectedNoJobsMessage = getLocalizedString('emptyStateNoJobsMessage');
+    const expectedOpenPrinterSettingsMessage =
+        getLocalizedString('emptyStatePrinterSettingsMessage');
+    const expectedButtonLabel = getLocalizedString('managePrintersButtonLabel');
+
+    // Assert text content matches localized strings.
+    assertEquals(
+        expectedNoJobsMessage, getElementTextContent('.message-heading'));
+    assertEquals(
+        expectedOpenPrinterSettingsMessage,
+        getElementTextContent('.message-detail'));
+    assertEquals(expectedButtonLabel, getElementTextContent('cr-button'));
+  });
 });
diff --git a/chrome/test/data/webui/history/history_synced_tabs_test.ts b/chrome/test/data/webui/history/history_synced_tabs_test.ts
index 303f21c..acef6a7 100644
--- a/chrome/test/data/webui/history/history_synced_tabs_test.ts
+++ b/chrome/test/data/webui/history/history_synced_tabs_test.ts
@@ -264,7 +264,6 @@
         })
         .then(args => {
           assertEquals('Chromebook', args.sessionTag, 'sessionTag is correct');
-          assertEquals(123, args.windowId, 'windowId is correct');
           assertEquals(456, args.tabId, 'tabId is correct');
           assertFalse(args.e.altKey, 'altKey is defined');
           assertFalse(args.e.ctrlKey, 'ctrlKey is defined');
diff --git a/chrome/test/data/webui/history/test_browser_service.ts b/chrome/test/data/webui/history/test_browser_service.ts
index 331d555..a5fb30fc 100644
--- a/chrome/test/data/webui/history/test_browser_service.ts
+++ b/chrome/test/data/webui/history/test_browser_service.ts
@@ -101,11 +101,9 @@
 
   openForeignSessionAllTabs() {}
 
-  openForeignSessionTab(
-      sessionTag: string, windowId: number, tabId: number, e: MouseEvent) {
+  openForeignSessionTab(sessionTag: string, tabId: number, e: MouseEvent) {
     this.methodCalled('openForeignSessionTab', {
       sessionTag: sessionTag,
-      windowId: windowId,
       tabId: tabId,
       e: e,
     });
diff --git a/chrome/test/data/webui/settings/chromeos/BUILD.gn b/chrome/test/data/webui/settings/chromeos/BUILD.gn
index 925fa7c..420b2d9d 100644
--- a/chrome/test/data/webui/settings/chromeos/BUILD.gn
+++ b/chrome/test/data/webui/settings/chromeos/BUILD.gn
@@ -77,7 +77,6 @@
     "os_sync_controls_subpage_test.js",
     "people_page_account_manager_subpage_test.js",
     "personalization_page_with_personalization_hub_test.js",
-    "printer_status_tests.js",
     "search_engine_test.js",
     "select_to_speak_subpage_tests.js",
     "settings_traffic_counters_test.js",
@@ -231,6 +230,7 @@
 
     "os_printing_page/cups_printers_entry_test.ts",
     "os_printing_page/os_printing_page_test.ts",
+    "os_printing_page/printer_status_test.ts",
 
     "os_privacy_page/os_privacy_page_test.js",
     "os_privacy_page/privacy_hub_subpage_test.ts",
diff --git a/chrome/test/data/webui/settings/chromeos/printer_status_tests.js b/chrome/test/data/webui/settings/chromeos/os_printing_page/printer_status_test.ts
similarity index 95%
rename from chrome/test/data/webui/settings/chromeos/printer_status_tests.js
rename to chrome/test/data/webui/settings/chromeos/os_printing_page/printer_status_test.ts
index 6d5e37f..6ed28be 100644
--- a/chrome/test/data/webui/settings/chromeos/printer_status_tests.js
+++ b/chrome/test/data/webui/settings/chromeos/os_printing_page/printer_status_test.ts
@@ -2,10 +2,12 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import {computePrinterState, getStatusReasonFromPrinterStatus, PrinterState, PrinterStatusReason, PrinterStatusSeverity} from 'chrome://os-settings/lazy_load.js';
-import {assertEquals} from 'chrome://webui-test/chromeos/chai_assert.js';
+import 'chrome://os-settings/lazy_load.js';
 
-suite('PrinterStatus', function() {
+import {computePrinterState, getStatusReasonFromPrinterStatus, PrinterState, PrinterStatusReason, PrinterStatusSeverity} from 'chrome://os-settings/lazy_load.js';
+import {assertEquals} from 'chrome://webui-test/chai_assert.js';
+
+suite('PrinterStatus', () => {
   // Verify that a printer status missing a printer id returns UNKNOWN_REASON.
   test('getStatusReasonMissingPrinterId', () => {
     const printerStatus = {
diff --git a/chrome/test/data/webui/settings/chromeos/os_settings_browsertest.js b/chrome/test/data/webui/settings/chromeos/os_settings_browsertest.js
index 697c684..c1491f9 100644
--- a/chrome/test/data/webui/settings/chromeos/os_settings_browsertest.js
+++ b/chrome/test/data/webui/settings/chromeos/os_settings_browsertest.js
@@ -560,6 +560,10 @@
      ]
    }
  ],
+ [
+   'OsPrintingPagePrinterStatus',
+   'os_printing_page/printer_status_test.js',
+ ],
  ['OsPrivacyPage', 'os_privacy_page/os_privacy_page_test.js'],
  [
    'OsPrivacyPagePrivacyHubSubpage',
@@ -612,10 +616,6 @@
    'PersonalizationPageWithPersonalizationHub',
    'personalization_page_with_personalization_hub_test.js',
  ],
- [
-   'PrinterStatus',
-   'printer_status_tests.js',
- ],
  ['ResetPage', 'os_reset_page_test.js'],
  [
    'SettingsSchedulerSlider',
diff --git a/chromeos/chromeos_strings.grd b/chromeos/chromeos_strings.grd
index 92f5b23..d8c92995 100644
--- a/chromeos/chromeos_strings.grd
+++ b/chromeos/chromeos_strings.grd
@@ -696,6 +696,15 @@
         <message name="IDS_PRINT_MANAGEMENT_COLLAPSE_PRINTING_STATUS" desc="A text that indicates to the user that the print job is currently printing. This text only appears if the width of the app's window is small and cannot display the entire ongoing printing status.">
           Printing
         </message>
+        <message translateable="false" name="IDS_PRINT_MANAGEMENT_EMPTY_STATE_NO_JOBS_MESSAGE" desc="The message shown to users if they do not have any print jobs in progress i.e. being printed.">
+          No printer jobs
+        </message>
+        <message translateable="false" name="IDS_PRINT_MANAGEMENT_EMPTY_STATE_PRINTER_SETTINGS_MESSAGE" desc="The message shown to users to suggest navigating to Printer settings when they do not have any print jobs.">
+          Go to Printer settings to manage your printers
+        </message>
+        <message translateable="false" name="IDS_PRINT_MANAGEMENT_EMPTY_STATE_MANAGE_PRINTERS_LABEL" desc="The label for the button that opens Printer settings when they do not have any print jobs.">
+          Manage printers
+        </message>
 
         <!-- Scanning App -->
         <message name="IDS_SCANNING_APP_TITLE" desc="The title of the scanning app.">
diff --git a/chromeos/profiles/orderfile.newest.txt b/chromeos/profiles/orderfile.newest.txt
index 9fe2e38..58e930c 100644
--- a/chromeos/profiles/orderfile.newest.txt
+++ b/chromeos/profiles/orderfile.newest.txt
@@ -1 +1 @@
-chromeos-chrome-orderfile-field-115-5735.18-1684144513-benchmark-115.0.5773.0-r1.orderfile.xz
+chromeos-chrome-orderfile-field-115-5735.18-1684144513-benchmark-115.0.5776.0-r1.orderfile.xz
diff --git a/chromeos/ui/frame/BUILD.gn b/chromeos/ui/frame/BUILD.gn
index 6a5e25b..a47b953 100644
--- a/chromeos/ui/frame/BUILD.gn
+++ b/chromeos/ui/frame/BUILD.gn
@@ -79,6 +79,7 @@
     "//skia",
     "//ui/aura",
     "//ui/base",
+    "//ui/chromeos/styles:cros_tokens_color_mappings_generator",
     "//ui/color",
     "//ui/platform_window",
     "//ui/strings:ui_strings_grit",
diff --git a/chromeos/ui/frame/multitask_menu/multitask_button.cc b/chromeos/ui/frame/multitask_menu/multitask_button.cc
index ba08c76..6a6c1b78 100644
--- a/chromeos/ui/frame/multitask_menu/multitask_button.cc
+++ b/chromeos/ui/frame/multitask_menu/multitask_button.cc
@@ -9,6 +9,7 @@
 #include "ui/gfx/canvas.h"
 #include "ui/views/animation/ink_drop.h"
 #include "ui/views/controls/highlight_path_generator.h"
+#include "ui/chromeos/styles/cros_tokens_color_mappings.h"
 
 namespace chromeos {
 
@@ -34,7 +35,7 @@
   SetPreferredSize(is_portrait_mode_ ? kMultitaskButtonPortraitSize
                                      : kMultitaskButtonLandscapeSize);
   views::InkDrop::Get(this)->SetMode(views::InkDropHost::InkDropMode::ON);
-  views::InkDrop::Get(this)->SetBaseColor(kMultitaskButtonDefaultColor);
+  views::InkDrop::Get(this)->SetBaseColor(cros_tokens::kCrosSysOnSurface);
   views::InstallRoundRectHighlightPathGenerator(
       this, gfx::Insets(), kMultitaskBaseButtonBorderRadius);
   SetAccessibleName(name);
@@ -53,19 +54,28 @@
   pattern_flags.setAntiAlias(true);
   pattern_flags.setStyle(cc::PaintFlags::kFill_Style);
 
+  const auto* color_provider = GetColorProvider();
+
   if (paint_as_active_ || GetState() == Button::STATE_HOVERED ||
       GetState() == Button::STATE_PRESSED) {
-    fill_flags.setColor(kMultitaskButtonViewHoverColor);
-    border_flags.setColor(kMultitaskButtonPrimaryHoverColor);
-    pattern_flags.setColor(gfx::kGoogleBlue600);
+    fill_flags.setColor(SkColorSetA(color_provider->GetColor(cros_tokens::kCrosSysPrimary),
+                  kMultitaskHoverBackgroundOpacity));
+    const auto hovered_color =
+      color_provider->GetColor(cros_tokens::kCrosSysPrimary);
+    border_flags.setColor(hovered_color);
+    pattern_flags.setColor(hovered_color);
   } else if (GetState() == Button::STATE_DISABLED) {
-    fill_flags.setColor(kMultitaskButtonViewHoverColor);
-    border_flags.setColor(kMultitaskButtonDisabledColor);
-    pattern_flags.setColor(kMultitaskButtonDisabledColor);
+    // TODO(b/281107973): Get colors from UX for when button is disabled
+    fill_flags.setColor(cros_tokens::kButtonBackgroundColorPrimaryDisabled);
+    border_flags.setColor(cros_tokens::kIconColorDisabled);
+    pattern_flags.setColor(cros_tokens::kIconColorDisabled);
   } else {
     fill_flags.setColor(SK_ColorTRANSPARENT);
-    border_flags.setColor(kMultitaskButtonDefaultColor);
-    pattern_flags.setColor(kMultitaskButtonDefaultColor);
+    const auto default_color =
+        SkColorSetA(color_provider->GetColor(cros_tokens::kCrosSysOnSurface),
+                    kMultitaskDefaultButtonOpacity);
+    border_flags.setColor(default_color);
+    pattern_flags.setColor(default_color);
   }
 
   canvas->DrawRoundRect(gfx::RectF(GetLocalBounds()),
diff --git a/chromeos/ui/frame/multitask_menu/multitask_menu_constants.h b/chromeos/ui/frame/multitask_menu/multitask_menu_constants.h
index 5bc8d9f..4d35f92 100644
--- a/chromeos/ui/frame/multitask_menu/multitask_menu_constants.h
+++ b/chromeos/ui/frame/multitask_menu/multitask_menu_constants.h
@@ -17,23 +17,9 @@
 constexpr int kMultitaskBaseButtonBorderRadius = 7;
 constexpr int kButtonBorderSize = 1;
 constexpr int kButtonCornerRadius = 4;
-
-// Default color for button pattern and border in default state.
-// TODO(shidi): Will replace these once color provider is integrated.
-constexpr SkColor kMultitaskButtonDefaultColor =
-    SkColorSetA(gfx::kGoogleGrey600, SK_AlphaOPAQUE * 0.58);
-
-// When a button is hovered, the color changed to
-// `kMultitaskButtonPrimaryHoverColor`, and the other button on the same
-// MultitaskButtonView changed to `kMultitaskButtonViewHoverColor`.
-constexpr SkColor kMultitaskButtonPrimaryHoverColor =
-    SkColorSetA(gfx::kGoogleBlue600, SK_AlphaOPAQUE);
-constexpr SkColor kMultitaskButtonViewHoverColor =
-    SkColorSetA(gfx::kGoogleBlue600, SK_AlphaOPAQUE * 0.12);
-
-// The pattern color of both primary and secondary buttons when it's disabled.
-constexpr SkColor kMultitaskButtonDisabledColor =
-    SkColorSetA(gfx::kGoogleGrey600, SK_AlphaOPAQUE * 0.28);
+constexpr SkAlpha kMultitaskDefaultButtonOpacity = SK_AlphaOPAQUE * 0.21;
+constexpr SkAlpha kMultitaskHoverButtonOpacity = SK_AlphaOPAQUE * 0.40;
+constexpr SkAlpha kMultitaskHoverBackgroundOpacity = SK_AlphaOPAQUE * 0.12;
 
 }  // namespace chromeos
 
diff --git a/chromeos/ui/frame/multitask_menu/multitask_menu_view.cc b/chromeos/ui/frame/multitask_menu/multitask_menu_view.cc
index d2e25e3..96d32ad9 100644
--- a/chromeos/ui/frame/multitask_menu/multitask_menu_view.cc
+++ b/chromeos/ui/frame/multitask_menu/multitask_menu_view.cc
@@ -33,6 +33,7 @@
 #include "ui/views/background.h"
 #include "ui/views/controls/label.h"
 #include "ui/views/widget/widget.h"
+#include "ui/chromeos/styles/cros_tokens_color_mappings.h"
 
 namespace chromeos {
 
@@ -181,6 +182,8 @@
       close_callback_(std::move(close_callback)) {
   DCHECK(window);
   DCHECK(close_callback_);
+  SetBackground(views::CreateThemedSolidBackground(
+      cros_tokens::kCrosSysSystemBaseElevated));
   SetUseDefaultFillLayout(true);
 
   window_observation_.Observe(window);
diff --git a/chromeos/ui/frame/multitask_menu/split_button_view.cc b/chromeos/ui/frame/multitask_menu/split_button_view.cc
index 23c5520f..e3c19ab6 100644
--- a/chromeos/ui/frame/multitask_menu/split_button_view.cc
+++ b/chromeos/ui/frame/multitask_menu/split_button_view.cc
@@ -14,6 +14,7 @@
 #include "ui/gfx/color_palette.h"
 #include "ui/gfx/geometry/point_f.h"
 #include "ui/views/controls/highlight_path_generator.h"
+#include "ui/chromeos/styles/cros_tokens_color_mappings.h"
 
 namespace chromeos {
 
@@ -34,11 +35,6 @@
 constexpr gfx::Insets kRightButtonInsets = gfx::Insets::TLBR(4, 2, 4, 4);
 constexpr gfx::Insets kBottomButtonInsets = gfx::Insets::TLBR(2, 4, 4, 4);
 
-// Change to secondary hover color when the other button on the same
-// `SplitButtonView` is hovered.
-constexpr SkColor kSplitButtonSecondaryHoverColor =
-    SkColorSetA(gfx::kGoogleBlue600, SK_AlphaOPAQUE * 0.4);
-
 bool IsHoveredOrPressedState(views::Button::ButtonState button_state) {
   return button_state == views::Button::STATE_PRESSED ||
          button_state == views::Button::STATE_HOVERED;
@@ -90,7 +86,7 @@
               const std::u16string& a11y_name,
               const gfx::Insets& insets)
       : views::Button(std::move(pressed_callback)),
-        button_color_(kMultitaskButtonDefaultColor),
+        button_color_(cros_tokens::kCrosSysOnSurface),
         insets_(insets),
         hovered_pressed_callback_(std::move(hovered_pressed_callback)) {
     // Subtract by the preferred insets so that the focus ring is drawn around
@@ -111,8 +107,9 @@
   void OnPaintBackground(gfx::Canvas* canvas) override {
     cc::PaintFlags pattern_flags;
     pattern_flags.setAntiAlias(true);
+    // TODO(b/281107973): Get colors from UX for when button is disabled
     pattern_flags.setColor(GetEnabled() ? button_color_
-                                        : kMultitaskButtonDisabledColor);
+                                        : cros_tokens::kIconColorDisabled);
     pattern_flags.setStyle(cc::PaintFlags::kFill_Style);
     gfx::Rect pattern_bounds = GetLocalBounds();
     pattern_bounds.Inset(insets_);
@@ -196,20 +193,27 @@
 }
 
 void SplitButtonView::OnButtonHoveredOrPressed() {
-  border_color_ = kMultitaskButtonPrimaryHoverColor;
-  fill_color_ = kMultitaskButtonViewHoverColor;
+  const auto* color_provider = GetColorProvider();
+  const auto primary_hover_color =
+      color_provider->GetColor(cros_tokens::kCrosSysPrimary);
+  const auto secondary_hover_color =
+      SkColorSetA(color_provider->GetColor(cros_tokens::kCrosSysPrimary),
+                  kMultitaskHoverButtonOpacity);
+
   if (IsHoveredOrPressedState(right_bottom_button_->GetState())) {
-    right_bottom_button_->set_button_color(kMultitaskButtonPrimaryHoverColor);
-    left_top_button_->set_button_color(kSplitButtonSecondaryHoverColor);
+    right_bottom_button_->set_button_color(primary_hover_color);
+    left_top_button_->set_button_color(secondary_hover_color);
   } else if (IsHoveredOrPressedState(left_top_button_->GetState())) {
-    left_top_button_->set_button_color(kMultitaskButtonPrimaryHoverColor);
-    right_bottom_button_->set_button_color(kSplitButtonSecondaryHoverColor);
+    left_top_button_->set_button_color(primary_hover_color);
+    right_bottom_button_->set_button_color(secondary_hover_color);
   } else {
     // Reset color.
-    border_color_ = kMultitaskButtonDefaultColor;
     fill_color_ = SK_ColorTRANSPARENT;
-    right_bottom_button_->set_button_color(kMultitaskButtonDefaultColor);
-    left_top_button_->set_button_color(kMultitaskButtonDefaultColor);
+    const auto default_color =
+        SkColorSetA(color_provider->GetColor(cros_tokens::kCrosSysOnSurface),
+                    kMultitaskDefaultButtonOpacity);
+    right_bottom_button_->set_button_color(default_color);
+    left_top_button_->set_button_color(default_color);
   }
   SchedulePaint();
 }
@@ -235,8 +239,12 @@
 }
 
 void SplitButtonView::OnThemeChanged() {
-  // TODO(shidi): Implement the theme change after dark/light mode integration.
   views::View::OnThemeChanged();
+  border_color_ =
+      SkColorSetA(GetColorProvider()->GetColor(cros_tokens::kCrosSysOnSurface),
+                  kMultitaskDefaultButtonOpacity);
+  right_bottom_button_->set_button_color(border_color_);
+  left_top_button_->set_button_color(border_color_);
 }
 
 BEGIN_METADATA(SplitButtonView, View)
diff --git a/chromeos/ui/frame/multitask_menu/split_button_view.h b/chromeos/ui/frame/multitask_menu/split_button_view.h
index f3b6f2db..884037e 100644
--- a/chromeos/ui/frame/multitask_menu/split_button_view.h
+++ b/chromeos/ui/frame/multitask_menu/split_button_view.h
@@ -59,7 +59,7 @@
 
   const SplitButtonType type_;
 
-  SkColor border_color_ = kMultitaskButtonDefaultColor;
+  SkColor border_color_ = SK_ColorTRANSPARENT;
   SkColor fill_color_ = SK_ColorTRANSPARENT;
 };
 
diff --git a/components/omnibox/browser/autocomplete_controller.cc b/components/omnibox/browser/autocomplete_controller.cc
index 58dce28..b7577af 100644
--- a/components/omnibox/browser/autocomplete_controller.cc
+++ b/components/omnibox/browser/autocomplete_controller.cc
@@ -1045,11 +1045,19 @@
 #if BUILDFLAG(BUILD_WITH_TFLITE_LIB)
     // Use a WeakPtr since the model is not owned and `this` may no longer be
     // alive. `SortCullAndAnnotateResult()` is called when the model is done.
-    RunUrlScoringModel(base::BindOnce(
-        &AutocompleteController::SortCullAndAnnotateResult,
-        weak_ptr_factory_.GetWeakPtr(), last_default_match,
-        last_default_associated_keyword, force_notify_default_match_changed,
-        default_match_to_preserve));
+    if (OmniboxFieldTrial::IsMlBatchUrlScoringEnabled()) {
+      RunBatchUrlScoringModel(base::BindOnce(
+          &AutocompleteController::SortCullAndAnnotateResult,
+          weak_ptr_factory_.GetWeakPtr(), last_default_match,
+          last_default_associated_keyword, force_notify_default_match_changed,
+          default_match_to_preserve));
+    } else {
+      RunUrlScoringModel(base::BindOnce(
+          &AutocompleteController::SortCullAndAnnotateResult,
+          weak_ptr_factory_.GetWeakPtr(), last_default_match,
+          last_default_associated_keyword, force_notify_default_match_changed,
+          default_match_to_preserve));
+    }
     return;
 #endif  // BUILDFLAG(BUILD_WITH_TFLITE_LIB)
   }
@@ -1575,16 +1583,38 @@
             match.stripped_destination_url.spec(), barrier_callback);
   }
 }
-#endif  // BUILDFLAG(BUILD_WITH_TFLITE_LIB)
 
-void AutocompleteController::CancelUrlScoringModel() {
-  // Try to cancel any pending requests to the scoring model and invalidate the
-  // WeakPtr to prevent its callbacks from being called.
-  scoring_model_task_tracker_.TryCancelAll();
-  weak_ptr_factory_.InvalidateWeakPtrs();
+void AutocompleteController::RunBatchUrlScoringModel(
+    base::OnceClosure completion_callback) {
+  TRACE_EVENT0("omnibox", "AutocompleteController::RunBatchUrlScoringModel");
+
+  std::vector<const metrics::OmniboxEventProto::Suggestion::ScoringSignals*>
+      batch_scoring_signals;
+  std::vector<std::string> stripped_destination_urls;
+  // Run the model for the eligible matches.
+  for (auto& match : result_) {
+    if (!match.scoring_signals.has_value()) {
+      continue;
+    }
+    batch_scoring_signals.push_back(&match.scoring_signals.value());
+    stripped_destination_urls.push_back(match.stripped_destination_url.spec());
+  }
+
+  // If no eligible matches to score, call `completion_callback` immediately.
+  if (batch_scoring_signals.empty()) {
+    std::move(completion_callback).Run();
+    return;
+  }
+
+  provider_client_->GetAutocompleteScoringModelService()
+      ->BatchScoreAutocompleteUrlMatches(
+          &scoring_model_task_tracker_, batch_scoring_signals,
+          stripped_destination_urls,
+          base::BindOnce(&AutocompleteController::OnUrlScoringModelDone,
+                         weak_ptr_factory_.GetWeakPtr(), base::ElapsedTimer(),
+                         std::move(completion_callback)));
 }
 
-#if BUILDFLAG(BUILD_WITH_TFLITE_LIB)
 void AutocompleteController::OnUrlScoringModelDone(
     const base::ElapsedTimer elapsed_timer,
     base::OnceClosure completion_callback,
@@ -1600,14 +1630,12 @@
        ++match_itr) {
     // `stripped_destination_url` is computed for all the matches before
     // executing the model and no new matches are added since the model is
-    // executed only when all the providers are done.
-    if (match_itr->stripped_destination_url.is_empty()) {
-      NOTREACHED()
-          << "ACMatch::stripped_destination_url expected but not computed for "
-          << AutocompleteMatchType::ToString(match_itr->type);
-      continue;
+    // executed only when all the providers are done. However not all matches
+    // have a stripped destination url. One known type is `SEARCH_OTHER_ENGINE`.
+    // TODO(crbug.com/1446688): Find a more unique way to identify the matches.
+    if (!match_itr->stripped_destination_url.is_empty()) {
+      url_to_match_map[match_itr->stripped_destination_url.spec()] = match_itr;
     }
-    url_to_match_map[match_itr->stripped_destination_url.spec()] = match_itr;
   }
 
   // The goal is to redistribute the existing relevance scores among the
@@ -1669,3 +1697,10 @@
   std::move(completion_callback).Run();
 }
 #endif  // BUILDFLAG(BUILD_WITH_TFLITE_LIB)
+
+void AutocompleteController::CancelUrlScoringModel() {
+  // Try to cancel any pending requests to the scoring model and invalidate the
+  // WeakPtr to prevent its callbacks from being called.
+  scoring_model_task_tracker_.TryCancelAll();
+  weak_ptr_factory_.InvalidateWeakPtrs();
+}
diff --git a/components/omnibox/browser/autocomplete_controller.h b/components/omnibox/browser/autocomplete_controller.h
index 1488870..873cba5d 100644
--- a/components/omnibox/browser/autocomplete_controller.h
+++ b/components/omnibox/browser/autocomplete_controller.h
@@ -374,13 +374,12 @@
   // `OnUrlScoringModelDone()` callback which is called once the model is done
   // for all the eligible matches, whether successfully or not.
   void RunUrlScoringModel(base::OnceClosure completion_callback);
-#endif  // BUILDFLAG(BUILD_WITH_TFLITE_LIB)
 
-  // Tries to cancel any pending requests to the scoring model and prevents
-  // `OnUrlScoringModelDone()` and its completion callback from being called.
-  void CancelUrlScoringModel();
+  // Runs the async batch scoring for all the eligible matches in
+  // `results_.matches_`. Passes `completion_callback` to
+  // `OnBatchUrlScoringModelDone()` callback upon finishing scoring.
+  void RunBatchUrlScoringModel(base::OnceClosure completion_callback);
 
-#if BUILDFLAG(BUILD_WITH_TFLITE_LIB)
   // Called when the async scoring model is done running for all the eligible
   // matches in `results_.matches_`. Redistributes the existing relevance scores
   // to the matches based on the model output (i.e. highest relevance now
@@ -392,6 +391,10 @@
       std::vector<AutocompleteScoringModelService::Result> results);
 #endif  // BUILDFLAG(BUILD_WITH_TFLITE_LIB)
 
+  // Tries to cancel any pending requests to the scoring model and prevents
+  // `OnUrlScoringModelDone()` and its completion callback from being called.
+  void CancelUrlScoringModel();
+
   base::ObserverList<Observer> observers_;
 
   // The client passed to the providers.
diff --git a/components/omnibox/browser/autocomplete_scoring_model_service.cc b/components/omnibox/browser/autocomplete_scoring_model_service.cc
index 8679dec..24f63148 100644
--- a/components/omnibox/browser/autocomplete_scoring_model_service.cc
+++ b/components/omnibox/browser/autocomplete_scoring_model_service.cc
@@ -72,24 +72,30 @@
 void AutocompleteScoringModelService::BatchScoreAutocompleteUrlMatches(
     base::CancelableTaskTracker* tracker,
     const std::vector<const ScoringSignals*>& batch_scoring_signals,
-    const std::vector<size_t>& match_indexes,
-    const std::vector<GURL>& match_destination_urls,
+    const std::vector<std::string>& stripped_destination_urls,
     BatchResultCallback batch_result_callback) {
   TRACE_EVENT0(
       "omnibox",
       "AutocompleteScoringModelService::BatchScoreAutocompleteUrlMatches");
 
+  // Function for creating a result vector with null scores.
+  auto create_null_results = [&]() {
+    std::vector<Result> results;
+    for (size_t i = 0; i < batch_scoring_signals.size(); i++) {
+      results.emplace_back(absl::nullopt, stripped_destination_urls.at(i));
+    }
+    return results;
+  };
+
   if (!UrlScoringModelAvailable()) {
-    std::move(batch_result_callback)
-        .Run(absl::nullopt, match_indexes, match_destination_urls);
+    std::move(batch_result_callback).Run(create_null_results());
     return;
   }
 
   absl::optional<std::vector<std::vector<float>>> batch_input =
       url_scoring_model_handler_->GetBatchModelInput(batch_scoring_signals);
   if (!batch_input) {
-    std::move(batch_result_callback)
-        .Run(absl::nullopt, match_indexes, match_destination_urls);
+    std::move(batch_result_callback).Run(create_null_results());
     return;
   }
 
@@ -97,8 +103,8 @@
       tracker,
       base::BindOnce(&AutocompleteScoringModelService::ProcessBatchModelOutput,
                      weak_ptr_factory_.GetWeakPtr(),
-                     std::move(batch_result_callback), match_indexes,
-                     match_destination_urls),
+                     std::move(batch_result_callback),
+                     stripped_destination_urls),
       *batch_input);
 }
 
@@ -124,23 +130,18 @@
 
 void AutocompleteScoringModelService::ProcessBatchModelOutput(
     BatchResultCallback batch_result_callback,
-    const std::vector<size_t>& match_indexes,
-    const std::vector<GURL>& match_destination_urls,
-    const std::vector<
-        absl::optional<AutocompleteScoringModelExecutor::ModelOutput>>&
-        batch_model_output) {
+    const std::vector<std::string>& stripped_destination_urls,
+    const std::vector<absl::optional<ModelOutput>>& batch_model_output) {
   TRACE_EVENT0("omnibox",
                "AutocompleteScoringModelService::ProcessBatchModelOutput");
 
-  std::vector<absl::optional<float>> batch_output_scores;
-  for (const auto& output : batch_model_output) {
-    if (output) {
-      batch_output_scores.push_back(output.value()[0]);
-    } else {
-      batch_output_scores.push_back(absl::nullopt);
-    }
+  std::vector<Result> batch_results;
+  for (size_t i = 0; i < stripped_destination_urls.size(); i++) {
+    const auto& output = batch_model_output.at(i);
+    batch_results.emplace_back(
+        output ? absl::optional<float>(output->at(0)) : absl::nullopt,
+        stripped_destination_urls.at(i));
   }
 
-  std::move(batch_result_callback)
-      .Run(batch_output_scores, match_indexes, match_destination_urls);
+  std::move(batch_result_callback).Run(std::move(batch_results));
 }
diff --git a/components/omnibox/browser/autocomplete_scoring_model_service.h b/components/omnibox/browser/autocomplete_scoring_model_service.h
index 459806c..f0650975 100644
--- a/components/omnibox/browser/autocomplete_scoring_model_service.h
+++ b/components/omnibox/browser/autocomplete_scoring_model_service.h
@@ -24,10 +24,9 @@
  public:
   using Result = std::tuple<absl::optional<float>, std::string>;
   using ResultCallback = base::OnceCallback<void(Result)>;
-  using BatchResultCallback = base::OnceCallback<void(
-      absl::optional<std::vector<absl::optional<float>>>,
-      std::vector<size_t>,
-      std::vector<GURL>)>;
+  using BatchResult = std::vector<Result>;
+  using BatchResultCallback = base::OnceCallback<void(BatchResult)>;
+  using ModelOutput = AutocompleteScoringModelExecutor::ModelOutput;
   using ScoringSignals =
       ::metrics::OmniboxEventProto::Suggestion::ScoringSignals;
 
@@ -42,7 +41,9 @@
       const AutocompleteScoringModelService&) = delete;
 
   // Invokes the model to score the given `scoring_signals` and calls
-  // `result_callback` with an optional relevance score generated by the model.
+  // `result_callback` with a relevance score generated by the model.
+  //  When the model is not available or the model input is null, calls
+  //  `result_callback` with a null result.
   void ScoreAutocompleteUrlMatch(base::CancelableTaskTracker* tracker,
                                  const ScoringSignals& scoring_signals,
                                  const std::string& stripped_destination_url,
@@ -50,12 +51,12 @@
 
   // Invokes the model to scores a batch of URL candidates with their signals.
   // Calls `batch_result_callback` with a batch of optional prediction scores
-  // from the model.
+  // from the model. When the model is not available or any model input is null,
+  // calls `result_callback` with a vector of null results.
   void BatchScoreAutocompleteUrlMatches(
       base::CancelableTaskTracker* tracker,
       const std::vector<const ScoringSignals*>& batch_scoring_signals,
-      const std::vector<size_t>& match_indexes,
-      const std::vector<GURL>& match_destination_urls,
+      const std::vector<std::string>& stripped_destination_urls,
       BatchResultCallback batch_result_callback);
 
   // Returns whether the scoring model is loaded and the pointer to the
@@ -66,19 +67,14 @@
   // Processes the model output and invokes the callback with the relevance
   // score from the model output. Invokes the callback with nullopt if the model
   // output is nullopt or an empty vector (which is unexpected).
-  void ProcessModelOutput(
-      ResultCallback result_callback,
-      const std::string& stripped_destination_url,
-      const absl::optional<AutocompleteScoringModelExecutor::ModelOutput>&
-          model_output);
+  void ProcessModelOutput(ResultCallback result_callback,
+                          const std::string& stripped_destination_url,
+                          const absl::optional<ModelOutput>& model_output);
 
   void ProcessBatchModelOutput(
       BatchResultCallback batch_result_callback,
-      const std::vector<size_t>& match_indexes,
-      const std::vector<GURL>& match_destination_urls,
-      const std::vector<
-          absl::optional<AutocompleteScoringModelExecutor::ModelOutput>>&
-          batch_model_output);
+      const std::vector<std::string>& stripped_destination_urls,
+      const std::vector<absl::optional<ModelOutput>>& batch_model_output);
 
   scoped_refptr<base::SequencedTaskRunner> model_executor_task_runner_;
 
diff --git a/components/omnibox/browser/omnibox_field_trial.cc b/components/omnibox/browser/omnibox_field_trial.cc
index af44e1c..58d4a57 100644
--- a/components/omnibox/browser/omnibox_field_trial.cc
+++ b/components/omnibox/browser/omnibox_field_trial.cc
@@ -1053,11 +1053,17 @@
     "MlUrlScoringPreserveDefault",
     false);
 
+// If true, the ML model scores a batch of urls.
+const base::FeatureParam<bool> kMlBatchUrlScoring(&omnibox::kMlUrlScoring,
+                                                  "MlBatchUrlScoring",
+                                                  false);
+
 MLConfig::MLConfig() {
   log_url_scoring_signals =
       base::FeatureList::IsEnabled(omnibox::kLogUrlScoringSignals);
   enable_scoring_signals_annotators = kEnableScoringSignalsAnnotators.Get();
   ml_url_scoring = base::FeatureList::IsEnabled(omnibox::kMlUrlScoring);
+  ml_batch_url_scoring = kMlBatchUrlScoring.Get();
   ml_url_scoring_counterfactual = kMlUrlScoringCounterfactual.Get();
   ml_url_scoring_increase_num_candidates =
       kMlUrlScoringIncreaseNumCandidates.Get();
@@ -1095,6 +1101,9 @@
   return false;
 #endif  // BUILDFLAG(BUILD_WITH_TFLITE_LIB)
 }
+bool IsMlBatchUrlScoringEnabled() {
+  return IsMlUrlScoringEnabled() && GetMLConfig().ml_batch_url_scoring;
+}
 bool IsMlUrlScoringCounterfactual() {
   return IsMlUrlScoringEnabled() && GetMLConfig().ml_url_scoring_counterfactual;
 }
diff --git a/components/omnibox/browser/omnibox_field_trial.h b/components/omnibox/browser/omnibox_field_trial.h
index 41dc875..955c3da 100644
--- a/components/omnibox/browser/omnibox_field_trial.h
+++ b/components/omnibox/browser/omnibox_field_trial.h
@@ -612,6 +612,9 @@
   // Equivalent to omnibox::kMlUrlScoring.
   bool ml_url_scoring{false};
 
+  // If true, runs batch ML scoring of URL candidates.
+  bool ml_batch_url_scoring{false};
+
   // If true, runs the ML scoring model but does not assign new relevance scores
   // to the URL suggestions and does not rerank them.
   // Equivalent to OmniboxFieldTrial::kMlUrlScoringCounterfactual.
@@ -671,6 +674,9 @@
 // URL suggestions and reranks them.
 bool IsMlUrlScoringEnabled();
 
+// Whether batch ML url scoring is enabled.
+bool IsMlBatchUrlScoringEnabled();
+
 // If true, runs the ML scoring model but does not assign new relevance scores
 // to URL suggestions.
 bool IsMlUrlScoringCounterfactual();
diff --git a/components/safe_browsing/android/BUILD.gn b/components/safe_browsing/android/BUILD.gn
index 4b8db50..bdde59a 100644
--- a/components/safe_browsing/android/BUILD.gn
+++ b/components/safe_browsing/android/BUILD.gn
@@ -17,7 +17,6 @@
   annotation_processor_deps = [ "//base/android/jni_generator:jni_processor" ]
   sources = [
     "java/src/org/chromium/components/safe_browsing/SafeBrowsingApiBridge.java",
-    "java/src/org/chromium/components/safe_browsing/SafeBrowsingApiHandler.java",
     "java/src/org/chromium/components/safe_browsing/SafetyNetApiHandler.java",
   ]
 }
diff --git a/components/safe_browsing/android/java/src/org/chromium/components/safe_browsing/SafeBrowsingApiBridge.java b/components/safe_browsing/android/java/src/org/chromium/components/safe_browsing/SafeBrowsingApiBridge.java
index 527472b..c4497f7e 100644
--- a/components/safe_browsing/android/java/src/org/chromium/components/safe_browsing/SafeBrowsingApiBridge.java
+++ b/components/safe_browsing/android/java/src/org/chromium/components/safe_browsing/SafeBrowsingApiBridge.java
@@ -15,7 +15,7 @@
 /**
  * Helper for calling GMSCore Safe Browsing API from native code.
  *
- * The {@link #setHandler(SafeBrowsingApiHandler)} must be invoked first. After that
+ * The {@link #setHandler(SafetyNetApiHandler)} must be invoked first. After that
  * {@link #startUriLookup(long, String, int[])} and {@link #startAllowlistLookup(String, int)} can
  * be used to check the URLs. The handler would be initialized lazily on the first URL check.
  *
@@ -34,7 +34,7 @@
     private static boolean sHandlerInitCalled;
 
     @GuardedBy("sLock")
-    private static SafeBrowsingApiHandler sHandler;
+    private static SafetyNetApiHandler sHandler;
 
     @GuardedBy("sLock")
     private static UrlCheckTimeObserver sUrlCheckTimeObserver;
@@ -44,11 +44,11 @@
     }
 
     /**
-     * Sets the {@link SafeBrowsingApiHandler} object once and for the lifetime of this process.
+     * Sets the {@link SafetyNetApiHandler} object once and for the lifetime of this process.
      *
      * @param handler An instance that has not been initialized.
      */
-    public static void setHandler(SafeBrowsingApiHandler handler) {
+    public static void setHandler(SafetyNetApiHandler handler) {
         synchronized (sLock) {
             assert sHandler == null;
             sHandler = handler;
@@ -68,10 +68,10 @@
     }
 
     /**
-     * Initializes the singleton SafeBrowsingApiHandler instance on the first call. On subsequent
+     * Initializes the singleton SafetyNetApiHandler instance on the first call. On subsequent
      * calls it does nothing, returns the same value as returned on the first call.
      *
-     * The caller must {@link #setHandler(SafeBrowsingApiHandler)} first.
+     * The caller must {@link #setHandler(SafetyNetApiHandler)} first.
      *
      * @return true iff the initialization succeeded.
      */
@@ -87,7 +87,7 @@
      */
     public interface UrlCheckTimeObserver {
         /**
-         * @param urlCheckTimeDeltaMicros Time it took for {@link SafeBrowsingApiHandler} to check
+         * @param urlCheckTimeDeltaMicros Time it took for {@link SafetyNetApiHandler} to check
          * the URL.
          */
         void onUrlCheckTime(long urlCheckTimeDeltaMicros);
@@ -106,7 +106,7 @@
     }
 
     @GuardedBy("sLock")
-    private static SafeBrowsingApiHandler getHandler() {
+    private static SafetyNetApiHandler getHandler() {
         if (!sHandlerInitCalled) {
             sHandler = initHandler();
             sHandlerInitCalled = true;
@@ -115,14 +115,14 @@
     }
 
     /**
-     * Initializes the SafeBrowsingApiHandler, if supported.
+     * Initializes the SafetyNetApiHandler, if supported.
      *
-     * The caller must {@link #setHandler(SafeBrowsingApiHandler)} first.
+     * The caller must {@link #setHandler(SafetyNetApiHandler)} first.
      *
      * @return the handler if it is usable, or null if the API is not supported.
      */
     @GuardedBy("sLock")
-    private static SafeBrowsingApiHandler initHandler() {
+    private static SafetyNetApiHandler initHandler() {
         try (TraceEvent t = TraceEvent.scoped("SafeBrowsingApiBridge.initHandler")) {
             if (DEBUG) {
                 Log.i(TAG, "initHandler");
@@ -132,7 +132,7 @@
         }
     }
 
-    private static class LookupDoneObserver implements SafeBrowsingApiHandler.Observer {
+    private static class LookupDoneObserver implements SafetyNetApiHandler.Observer {
         @Override
         public void onUrlCheckDone(
                 long callbackId, int resultStatus, String metadata, long checkDelta) {
@@ -181,8 +181,6 @@
      * Must only be called if {@link #ensureInitialized()} returns true.
      *
      * @return true iff the uri is in the allowlist.
-     *
-     * TODO(crbug.com/995926): Make this call async.
      */
     @CalledByNative
     private static boolean startAllowlistLookup(String uri, int threatType) {
diff --git a/components/safe_browsing/android/java/src/org/chromium/components/safe_browsing/SafeBrowsingApiHandler.java b/components/safe_browsing/android/java/src/org/chromium/components/safe_browsing/SafeBrowsingApiHandler.java
deleted file mode 100644
index a2d318e4..0000000
--- a/components/safe_browsing/android/java/src/org/chromium/components/safe_browsing/SafeBrowsingApiHandler.java
+++ /dev/null
@@ -1,65 +0,0 @@
-// Copyright 2016 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-package org.chromium.components.safe_browsing;
-
-import androidx.annotation.IntDef;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-
-/**
- * Java interface that a SafeBrowsingApiHandler must implement when used with
- * {@code SafeBrowsingApiBridge}.
- * TODO(crbug.com/1444515): Remove this interface once it is fully renamed to SafetyNetApiHandler.
- */
-public interface SafeBrowsingApiHandler {
-    /**
-     * Observer to be notified when the SafeBrowsingApiHandler determines the verdict for a url.
-     */
-    interface Observer {
-        // Note: |checkDelta| is the time the remote call took in microseconds.
-        void onUrlCheckDone(long callbackId, @SafeBrowsingResult int resultStatus, String metadata,
-                long checkDelta);
-    }
-
-    // Possible values for resultStatus. Native side has the same definitions.
-    @IntDef({SafeBrowsingResult.INTERNAL_ERROR, SafeBrowsingResult.SUCCESS,
-            SafeBrowsingResult.TIMEOUT})
-    @Retention(RetentionPolicy.SOURCE)
-    @interface SafeBrowsingResult {
-        int INTERNAL_ERROR = -1;
-        int SUCCESS = 0;
-        int TIMEOUT = 1;
-    }
-
-    /**
-     * Verifies that SafeBrowsingApiHandler can operate and initializes if feasible.
-     * Should be called on the same sequence as |startUriLookup|.
-     *
-     * @param result The object on which to call the callback functions when URL checking
-     * is complete.
-     *
-     * @return whether Safe Browsing is supported for this installation.
-     */
-    boolean init(Observer result);
-
-    /**
-     * Start a URI-lookup to determine if it matches one of the specified threats.
-     * This is called on every URL resource Chrome loads, on the same sequence as |init|.
-     */
-    void startUriLookup(long callbackId, String uri, int[] threatsOfInterest);
-
-    /**
-     * Start a check to determine if a uri is in an allowlist. If true, password protection
-     * service will consider the uri to be safe.
-     *
-     * @param uri The uri from a password protection event(user focuses on password form
-     *      * or user reuses their password)
-     * @param threatType determines the type of the allowlist that the uri will be matched to.
-     *
-     * @return true if the uri is found in the corresponding allowlist. Otherwise, false.
-     */
-    boolean startAllowlistLookup(String uri, int threatType);
-}
diff --git a/components/safe_browsing/android/java/src/org/chromium/components/safe_browsing/SafetyNetApiHandler.java b/components/safe_browsing/android/java/src/org/chromium/components/safe_browsing/SafetyNetApiHandler.java
index 6844575..c628721 100644
--- a/components/safe_browsing/android/java/src/org/chromium/components/safe_browsing/SafetyNetApiHandler.java
+++ b/components/safe_browsing/android/java/src/org/chromium/components/safe_browsing/SafetyNetApiHandler.java
@@ -4,9 +4,61 @@
 
 package org.chromium.components.safe_browsing;
 
+import androidx.annotation.IntDef;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
 /**
  * Java interface that a SafetyNetApiHandler must implement when used with
  * {@code SafeBrowsingApiBridge}.
- * TODO(crbug.com/1444515): Port SafeBrowsingApiHandler to this interface.
  */
-public interface SafetyNetApiHandler extends SafeBrowsingApiHandler {}
+public interface SafetyNetApiHandler {
+    /**
+     * Observer to be notified when the SafetyNetApiHandler determines the verdict for a url.
+     */
+    interface Observer {
+        // Note: |checkDelta| is the time the remote call took in microseconds.
+        void onUrlCheckDone(long callbackId, @SafeBrowsingResult int resultStatus, String metadata,
+                long checkDelta);
+    }
+
+    // Possible values for resultStatus. Native side has the same definitions.
+    @IntDef({SafeBrowsingResult.INTERNAL_ERROR, SafeBrowsingResult.SUCCESS,
+            SafeBrowsingResult.TIMEOUT})
+    @Retention(RetentionPolicy.SOURCE)
+    @interface SafeBrowsingResult {
+        int INTERNAL_ERROR = -1;
+        int SUCCESS = 0;
+        int TIMEOUT = 1;
+    }
+
+    /**
+     * Verifies that SafetyNetApiHandler can operate and initializes if feasible.
+     * Should be called on the same sequence as |startUriLookup|.
+     *
+     * @param result The object on which to call the callback functions when URL checking
+     * is complete.
+     *
+     * @return whether Safe Browsing is supported for this installation.
+     */
+    boolean init(Observer result);
+
+    /**
+     * Start a URI-lookup to determine if it matches one of the specified threats.
+     * This is called on every URL resource Chrome loads, on the same sequence as |init|.
+     */
+    void startUriLookup(long callbackId, String uri, int[] threatsOfInterest);
+
+    /**
+     * Start a check to determine if a uri is in an allowlist. If true, password protection
+     * service will consider the uri to be safe.
+     *
+     * @param uri The uri from a password protection event(user focuses on password form
+     *      * or user reuses their password)
+     * @param threatType determines the type of the allowlist that the uri will be matched to.
+     *
+     * @return true if the uri is found in the corresponding allowlist. Otherwise, false.
+     */
+    boolean startAllowlistLookup(String uri, int threatType);
+}
diff --git a/components/safe_browsing/android/native_java_unittests/src/org/chromium/components/safe_browsing/SafeBrowsingApiHandlerBridgeNativeUnitTestHelper.java b/components/safe_browsing/android/native_java_unittests/src/org/chromium/components/safe_browsing/SafeBrowsingApiHandlerBridgeNativeUnitTestHelper.java
index b1cfa43..d14cfc2 100644
--- a/components/safe_browsing/android/native_java_unittests/src/org/chromium/components/safe_browsing/SafeBrowsingApiHandlerBridgeNativeUnitTestHelper.java
+++ b/components/safe_browsing/android/native_java_unittests/src/org/chromium/components/safe_browsing/SafeBrowsingApiHandlerBridgeNativeUnitTestHelper.java
@@ -18,10 +18,10 @@
  */
 public class SafeBrowsingApiHandlerBridgeNativeUnitTestHelper {
     /**
-     * A fake SafeBrowsingApiHandler which verifies the parameters of the overridden functions and
+     * A fake SafetyNetApiHandler which verifies the parameters of the overridden functions and
      * returns lookup result based on preset values.
      */
-    public static class MockSafeBrowsingApiHandler implements SafeBrowsingApiHandler {
+    public static class MockSafetyNetApiHandler implements SafetyNetApiHandler {
         private Observer mObserver;
         // The result that will be returned in {@link #startUriLookup(long, String, int[])}.
         private static int sResult = SafeBrowsingResult.SUCCESS;
@@ -94,7 +94,7 @@
 
     @CalledByNative
     static void setUp() {
-        SafeBrowsingApiBridge.setHandler(new MockSafeBrowsingApiHandler());
+        SafeBrowsingApiBridge.setHandler(new MockSafetyNetApiHandler());
     }
 
     @CalledByNative
@@ -104,26 +104,26 @@
 
     @CalledByNative
     static void setExpectedThreatsOfInterest(String uri, int[] threatsOfInterest) {
-        MockSafeBrowsingApiHandler.setExpectedThreatsOfInterest(uri, threatsOfInterest);
+        MockSafetyNetApiHandler.setExpectedThreatsOfInterest(uri, threatsOfInterest);
     }
 
     @CalledByNative
     static void setMetadata(String uri, String metadata) {
-        MockSafeBrowsingApiHandler.setMetadata(uri, metadata);
+        MockSafetyNetApiHandler.setMetadata(uri, metadata);
     }
 
     @CalledByNative
     static void setCsdAllowlistMatch(String uri, boolean match) {
-        MockSafeBrowsingApiHandler.setCsdAllowlistMatch(uri, match);
+        MockSafetyNetApiHandler.setCsdAllowlistMatch(uri, match);
     }
 
     @CalledByNative
     static void setHighConfidenceAllowlistMatch(String uri, boolean match) {
-        MockSafeBrowsingApiHandler.setHighConfidenceAllowlistMatch(uri, match);
+        MockSafetyNetApiHandler.setHighConfidenceAllowlistMatch(uri, match);
     }
 
     @CalledByNative
     static void setResult(int result) {
-        MockSafeBrowsingApiHandler.setResult(result);
+        MockSafetyNetApiHandler.setResult(result);
     }
 }
diff --git a/components/segmentation_platform/internal/database/cached_result_writer.cc b/components/segmentation_platform/internal/database/cached_result_writer.cc
index 4ce1cd4..125cf0f8 100644
--- a/components/segmentation_platform/internal/database/cached_result_writer.cc
+++ b/components/segmentation_platform/internal/database/cached_result_writer.cc
@@ -22,7 +22,7 @@
 CachedResultWriter::~CachedResultWriter() = default;
 
 void CachedResultWriter::UpdatePrefsIfExpired(
-    Config* config,
+    const Config* config,
     proto::ClientResult client_result,
     const PlatformOptions& platform_options) {
   if (!IsPrefUpdateRequiredForClient(config, platform_options) ||
@@ -37,7 +37,7 @@
 }
 
 bool CachedResultWriter::IsPrefUpdateRequiredForClient(
-    Config* config,
+    const Config* config,
     const PlatformOptions& platform_options) {
   absl::optional<proto::ClientResult> client_result =
       result_prefs_->ReadClientResultFromPrefs(config->segmentation_key);
@@ -69,7 +69,7 @@
 }
 
 void CachedResultWriter::UpdateNewClientResultToPrefs(
-    Config* config,
+    const Config* config,
     const proto::ClientResult& client_result) {
   auto prev_client_result =
       result_prefs_->ReadClientResultFromPrefs(config->segmentation_key);
diff --git a/components/segmentation_platform/internal/database/cached_result_writer.h b/components/segmentation_platform/internal/database/cached_result_writer.h
index 371acfd..226456c0 100644
--- a/components/segmentation_platform/internal/database/cached_result_writer.h
+++ b/components/segmentation_platform/internal/database/cached_result_writer.h
@@ -34,7 +34,7 @@
   // Updates the prefs only if the previous result in the pref is expired or
   // unavailable or `force_refresh_results` is set as true. In both the above
   // cases, fetch result for model either from database or running the model.
-  void UpdatePrefsIfExpired(Config* config,
+  void UpdatePrefsIfExpired(const Config* config,
                             proto::ClientResult client_result,
                             const PlatformOptions& platform_options);
 
@@ -42,11 +42,11 @@
   // Checks the following to determine whether to update pref with new result.
   // 1. Previous model results for client are either expired or unavailable.
   // 2. `force_refresh_results` option is set to true.
-  bool IsPrefUpdateRequiredForClient(Config* config,
+  bool IsPrefUpdateRequiredForClient(const Config* config,
                                      const PlatformOptions& platform_options);
 
   // Updates the supplied `client_result` as new result for the client in prefs.
-  void UpdateNewClientResultToPrefs(Config* config,
+  void UpdateNewClientResultToPrefs(const Config* config,
                                     const proto::ClientResult& client_result);
 
   // Helper class to read/write results to the prefs.
diff --git a/components/segmentation_platform/internal/database/config_holder.h b/components/segmentation_platform/internal/database/config_holder.h
index 1868bf9..678ea02c 100644
--- a/components/segmentation_platform/internal/database/config_holder.h
+++ b/components/segmentation_platform/internal/database/config_holder.h
@@ -39,6 +39,7 @@
   absl::optional<std::string> GetKeyForSegmentId(
       proto::SegmentId segment_id) const;
 
+  // Returns config for the given `segment id`.
   const Config* GetConfigForSegmentId(proto::SegmentId segment_id) const;
 
   // Returns true if the Config is legacy, does not support output config and
diff --git a/components/segmentation_platform/internal/database/signal_storage_config.cc b/components/segmentation_platform/internal/database/signal_storage_config.cc
index e9db9ce..178dd150 100644
--- a/components/segmentation_platform/internal/database/signal_storage_config.cc
+++ b/components/segmentation_platform/internal/database/signal_storage_config.cc
@@ -111,6 +111,9 @@
     uint64_t signal_hash,
     uint64_t event_hash,
     proto::SignalType signal_type) {
+  if (min_signal_collection_length.is_zero()) {
+    return true;
+  }
   const proto::SignalStorageConfig* config =
       FindSignal(signal_hash, event_hash, signal_type);
   if (!config || config->collection_start_time_s() == 0)
diff --git a/components/segmentation_platform/internal/metadata/metadata_utils.cc b/components/segmentation_platform/internal/metadata/metadata_utils.cc
index c28febd6..d4833a0 100644
--- a/components/segmentation_platform/internal/metadata/metadata_utils.cc
+++ b/components/segmentation_platform/internal/metadata/metadata_utils.cc
@@ -448,10 +448,15 @@
 }
 
 bool ConfigUsesLegacyOutput(const Config* config) {
-  // List of config segments ids that doesn't support multi output and uses
+  return (config->segments.size() >= 1 &&
+          SegmentUsesLegacyOutput(config->segments.begin()->first));
+}
+
+bool SegmentUsesLegacyOutput(proto::SegmentId segment_id) {
+  // List of segments ids that doesn't support multi output and uses
   // legacy output. Please delete `SegmentId` from this list if segment is
   // migrated to support multi output.
-  base::flat_set<SegmentId> config_ids_use_legacy{
+  base::flat_set<SegmentId> segment_ids_use_legacy{
       SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB,
       SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_SHARE,
       SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_VOICE,
@@ -468,8 +473,7 @@
       SegmentId::INTENTIONAL_USER_SEGMENT,
       SegmentId::RESUME_HEAVY_USER_SEGMENT};
 
-  return (config->segments.size() >= 1 &&
-          config_ids_use_legacy.contains(config->segments.begin()->first));
+  return segment_ids_use_legacy.contains(segment_id);
 }
 
 }  // namespace metadata_utils
diff --git a/components/segmentation_platform/internal/metadata/metadata_utils.h b/components/segmentation_platform/internal/metadata/metadata_utils.h
index 1ddbaa42..6aead9cb 100644
--- a/components/segmentation_platform/internal/metadata/metadata_utils.h
+++ b/components/segmentation_platform/internal/metadata/metadata_utils.h
@@ -136,6 +136,10 @@
 // output.
 bool ConfigUsesLegacyOutput(const Config* config);
 
+// Returns true if segment has not migrated to multi output and uses legacy
+// output.
+bool SegmentUsesLegacyOutput(proto::SegmentId segment_id);
+
 }  // namespace metadata_utils
 }  // namespace segmentation_platform
 
diff --git a/components/segmentation_platform/internal/scheduler/execution_service.cc b/components/segmentation_platform/internal/scheduler/execution_service.cc
index 02e5135..02c6deb9 100644
--- a/components/segmentation_platform/internal/scheduler/execution_service.cc
+++ b/components/segmentation_platform/internal/scheduler/execution_service.cc
@@ -76,7 +76,7 @@
       model_executor_.get(), all_segment_ids, clock, platform_options);
 }
 
-void ExecutionService::OnNewModelInfoReady(
+void ExecutionService::OnNewModelInfoReadyLegacy(
     const proto::SegmentInfo& segment_info) {
   // TODO(crbug.com/1420015): Change path flow as
   // SPSI->RRM->EE::RequestModelExecution and migrate
diff --git a/components/segmentation_platform/internal/scheduler/execution_service.h b/components/segmentation_platform/internal/scheduler/execution_service.h
index ee4b1247..4722ae41 100644
--- a/components/segmentation_platform/internal/scheduler/execution_service.h
+++ b/components/segmentation_platform/internal/scheduler/execution_service.h
@@ -69,9 +69,10 @@
     return training_data_collector_.get();
   }
 
+  // DEPRECATED: New multi output supporting models doesn't use it.
   // Called whenever a new or updated model is available. Must be a valid
   // SegmentInfo with valid metadata and features.
-  void OnNewModelInfoReady(const proto::SegmentInfo& segment_info);
+  void OnNewModelInfoReadyLegacy(const proto::SegmentInfo& segment_info);
 
   // Gets the model provider for execution.
   ModelProvider* GetModelProvider(SegmentId segment_id);
diff --git a/components/segmentation_platform/internal/scheduler/model_execution_scheduler_impl.h b/components/segmentation_platform/internal/scheduler/model_execution_scheduler_impl.h
index 3ff2899a..aed23f33 100644
--- a/components/segmentation_platform/internal/scheduler/model_execution_scheduler_impl.h
+++ b/components/segmentation_platform/internal/scheduler/model_execution_scheduler_impl.h
@@ -89,6 +89,7 @@
 
   // In-flight model execution requests. Will be killed if we get a model
   // update.
+  // TODO(ritikagup) : Remove outstanding request handling if not required.
   std::map<SegmentId,
            base::CancelableOnceCallback<
                ModelExecutor::ModelExecutionCallback::RunType>>
diff --git a/components/segmentation_platform/internal/segmentation_platform_service_impl.cc b/components/segmentation_platform/internal/segmentation_platform_service_impl.cc
index 6e25938..4c6e0bf1 100644
--- a/components/segmentation_platform/internal/segmentation_platform_service_impl.cc
+++ b/components/segmentation_platform/internal/segmentation_platform_service_impl.cc
@@ -135,8 +135,8 @@
           init_params->device_info_tracker));
 
   result_refresh_manager_ = std::make_unique<ResultRefreshManager>(
-      config_holder->configs(),
-      std::move(storage_service_->cached_result_writer()), platform_options_);
+      config_holder, std::move(storage_service_->cached_result_writer()),
+      platform_options_);
 }
 
 SegmentationPlatformServiceImpl::~SegmentationPlatformServiceImpl() {
@@ -305,8 +305,12 @@
 
   signal_handler_.OnSignalListUpdated();
 
-  execution_service_.OnNewModelInfoReady(segment_info);
-  request_dispatcher_->OnModelUpdated(segment_info.segment_id());
+  if (!metadata_utils::SegmentUsesLegacyOutput(segment_info.segment_id())) {
+    result_refresh_manager_->OnModelUpdated(&segment_info, &execution_service_);
+    request_dispatcher_->OnModelUpdated(segment_info.segment_id());
+  } else {
+    execution_service_.OnNewModelInfoReadyLegacy(segment_info);
+  }
 
   // Update the service status for proxy.
   base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
@@ -340,15 +344,20 @@
   std::map<std::string, std::unique_ptr<SegmentResultProvider>>
       result_providers;
   for (const auto& config : storage_service_->config_holder()->configs()) {
-    result_providers[config->segmentation_key] = SegmentResultProvider::Create(
-        storage_service_->segment_info_database(),
-        storage_service_->signal_storage_config(),
-        storage_service_->default_model_manager(), &execution_service_, clock_,
-        platform_options_.force_refresh_results);
+    result_providers[config->segmentation_key] = CreateSegmentResultProvider();
   }
   return result_providers;
 }
 
+std::unique_ptr<SegmentResultProvider>
+SegmentationPlatformServiceImpl::CreateSegmentResultProvider() {
+  return SegmentResultProvider::Create(
+      storage_service_->segment_info_database(),
+      storage_service_->signal_storage_config(),
+      storage_service_->default_model_manager(), &execution_service_, clock_,
+      platform_options_.force_refresh_results);
+}
+
 // static
 void SegmentationPlatformService::RegisterProfilePrefs(
     PrefRegistrySimple* registry) {
diff --git a/components/segmentation_platform/internal/segmentation_platform_service_impl.h b/components/segmentation_platform/internal/segmentation_platform_service_impl.h
index aa33a37f..c0220563 100644
--- a/components/segmentation_platform/internal/segmentation_platform_service_impl.h
+++ b/components/segmentation_platform/internal/segmentation_platform_service_impl.h
@@ -151,6 +151,8 @@
   std::map<std::string, std::unique_ptr<SegmentResultProvider>>
   CreateSegmentResultProviders();
 
+  // Creates SegmentResultProvider.
+  std::unique_ptr<SegmentResultProvider> CreateSegmentResultProvider();
   std::unique_ptr<ModelProviderFactory> model_provider_factory_;
 
   scoped_refptr<base::SequencedTaskRunner> task_runner_;
diff --git a/components/segmentation_platform/internal/segmentation_platform_service_impl_unittest.cc b/components/segmentation_platform/internal/segmentation_platform_service_impl_unittest.cc
index 4bde224b..4fa8b803 100644
--- a/components/segmentation_platform/internal/segmentation_platform_service_impl_unittest.cc
+++ b/components/segmentation_platform/internal/segmentation_platform_service_impl_unittest.cc
@@ -177,7 +177,7 @@
  protected:
   void TestInitializationFlow() {
     // ServiceProxy will load new segment infos from the DB.
-    EXPECT_CALL(observer_, OnClientInfoAvailable(_)).Times(3);
+    EXPECT_CALL(observer_, OnClientInfoAvailable(_)).Times(5);
 
     // Let the DB loading complete successfully.
     EXPECT_CALL(observer_, OnServiceStatusChanged(true, 7));
diff --git a/components/segmentation_platform/internal/selection/result_refresh_manager.cc b/components/segmentation_platform/internal/selection/result_refresh_manager.cc
index 84bd80d..ef0b4b5 100644
--- a/components/segmentation_platform/internal/selection/result_refresh_manager.cc
+++ b/components/segmentation_platform/internal/selection/result_refresh_manager.cc
@@ -18,7 +18,8 @@
 }
 
 // Collects training data after model execution.
-void CollectTrainingData(Config* config, ExecutionService* execution_service) {
+void CollectTrainingData(const Config* config,
+                         ExecutionService* execution_service) {
   // The execution service and training data collector might be null in testing.
   if (execution_service && execution_service->training_data_collector()) {
     for (const auto& segment : config->segments) {
@@ -32,10 +33,10 @@
 }  // namespace
 
 ResultRefreshManager::ResultRefreshManager(
-    const std::vector<std::unique_ptr<Config>>& configs,
+    const ConfigHolder* config_holder,
     CachedResultWriter* cached_result_writer,
     const PlatformOptions& platform_options)
-    : configs_(configs),
+    : config_holder_(config_holder),
       cached_result_writer_(cached_result_writer),
       platform_options_(platform_options) {}
 
@@ -47,7 +48,7 @@
     ExecutionService* execution_service) {
   result_providers_ = std::move(result_providers);
 
-  for (const auto& config : *configs_) {
+  for (const auto& config : config_holder_->configs()) {
     if (config->on_demand_execution ||
         metadata_utils::ConfigUsesLegacyOutput(config.get())) {
       continue;
@@ -61,7 +62,7 @@
 
 void ResultRefreshManager::GetCachedResultOrRunModel(
     SegmentResultProvider* segment_result_provider,
-    Config* config,
+    const Config* config,
     ExecutionService* execution_service) {
   auto result_options =
       std::make_unique<SegmentResultProvider::GetResultOptions>();
@@ -82,9 +83,20 @@
   segment_result_provider->GetSegmentResult(std::move(result_options));
 }
 
+void ResultRefreshManager::OnModelUpdated(proto::SegmentInfo* segment_info,
+                                          ExecutionService* execution_service) {
+  const Config* config =
+      config_holder_->GetConfigForSegmentId(segment_info->segment_id());
+  if (config->segmentation_key.empty()) {
+    return;
+  }
+  GetCachedResultOrRunModel(result_providers_[config->segmentation_key].get(),
+                            config, execution_service);
+}
+
 void ResultRefreshManager::OnGetCachedResultOrRunModel(
     SegmentResultProvider* segment_result_provider,
-    Config* config,
+    const Config* config,
     ExecutionService* execution_service,
     std::unique_ptr<SegmentResultProvider::SegmentResult> result) {
   SegmentResultProvider::ResultState result_state = result->state;
diff --git a/components/segmentation_platform/internal/selection/result_refresh_manager.h b/components/segmentation_platform/internal/selection/result_refresh_manager.h
index cee567d..8dd30396 100644
--- a/components/segmentation_platform/internal/selection/result_refresh_manager.h
+++ b/components/segmentation_platform/internal/selection/result_refresh_manager.h
@@ -26,7 +26,7 @@
 // invalid.
 class ResultRefreshManager {
  public:
-  ResultRefreshManager(const std::vector<std::unique_ptr<Config>>& configs,
+  ResultRefreshManager(const ConfigHolder* config_holder,
                        CachedResultWriter* cached_result_writer,
                        const PlatformOptions& platform_options);
 
@@ -49,24 +49,28 @@
           result_providers,
       ExecutionService* execution_service);
 
+  // This is triggered when model info is updated. This ensures model execution,
+  // updating prefs and database if required on model update.
+  void OnModelUpdated(proto::SegmentInfo* segment_info,
+                      ExecutionService* execution_service);
+
  private:
   // Gives result for the model based on `run_model`. If `run_model` is false,
   // tries to get the result from database, else tries to get the result by
   // executing model. It also saves to the result to database after model
   // execution.
   void GetCachedResultOrRunModel(SegmentResultProvider* segment_result_provider,
-                                 Config* config,
+                                 const Config* config,
                                  ExecutionService* execution_service);
 
   void OnGetCachedResultOrRunModel(
       SegmentResultProvider* segment_result_provider,
-      Config* config,
+      const Config* config,
       ExecutionService* execution_service,
       std::unique_ptr<SegmentResultProvider::SegmentResult> result);
 
   // Configs for all registered clients.
-  const raw_ref<const std::vector<std::unique_ptr<Config>>, ExperimentalAsh>
-      configs_;
+  const raw_ptr<const ConfigHolder> config_holder_;
 
   // Stores `SegmentResultProvider` for all clients.
   std::map<std::string, std::unique_ptr<SegmentResultProvider>>
diff --git a/components/segmentation_platform/internal/selection/result_refresh_manager_unittest.cc b/components/segmentation_platform/internal/selection/result_refresh_manager_unittest.cc
index 1ab1d6b2..8dbffe3 100644
--- a/components/segmentation_platform/internal/selection/result_refresh_manager_unittest.cc
+++ b/components/segmentation_platform/internal/selection/result_refresh_manager_unittest.cc
@@ -34,8 +34,10 @@
 const char kTestClient2[] = "client_2";
 
 // Test Ids.
-const proto::SegmentId kSegmentId =
+const proto::SegmentId kSegmentId1 =
     proto::SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_SEARCH_USER;
+const proto::SegmentId kSegmentId2 =
+    proto::SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_TABLET_PRODUCTIVITY_USER;
 
 class MockResultProvider : public SegmentResultProvider {
  public:
@@ -52,17 +54,21 @@
     base::SetRecordActionTaskRunner(
         task_environment_.GetMainThreadTaskRunner());
 
-    configs_.emplace_back(
-        test_utils::CreateTestConfig(kTestClient1, kSegmentId));
-    configs_.emplace_back(
-        test_utils::CreateTestConfig(kTestClient2, kSegmentId));
+    std::vector<std::unique_ptr<Config>> configs;
+    configs.emplace_back(
+        test_utils::CreateTestConfig(kTestClient1, kSegmentId1));
+    configs.emplace_back(
+        test_utils::CreateTestConfig(kTestClient2, kSegmentId2));
+    config_holder_ = std::make_unique<ConfigHolder>(std::move(configs));
+
     cached_result_writer_ = SetupCachedResultWriter();
 
     client1_result_provider_ = std::make_unique<MockResultProvider>();
     client2_result_provider_ = std::make_unique<MockResultProvider>();
 
     result_refresh_manager_ = std::make_unique<ResultRefreshManager>(
-        configs_, cached_result_writer_.get(), PlatformOptions(false));
+        config_holder_.get(), cached_result_writer_.get(),
+        PlatformOptions(false));
   }
 
   std::unique_ptr<CachedResultWriter> SetupCachedResultWriter() {
@@ -78,17 +84,18 @@
   }
 
   void ExpectSegmentResult(
+      const proto::SegmentId segment_id,
       MockResultProvider* segment_result_provider,
       const proto::PredictionResult& result,
       const SegmentResultProvider::ResultState& result_state,
       bool ignore_db_scores) {
     EXPECT_CALL(*segment_result_provider, GetSegmentResult(_))
         .WillOnce(
-            Invoke([result, result_state, ignore_db_scores](
+            Invoke([segment_id, result, result_state, ignore_db_scores](
                        std::unique_ptr<SegmentResultProvider::GetResultOptions>
                            options) {
               EXPECT_EQ(options->ignore_db_scores, ignore_db_scores);
-              EXPECT_EQ(options->segment_id, kSegmentId);
+              EXPECT_EQ(options->segment_id, segment_id);
               auto segment_result =
                   std::make_unique<SegmentResultProvider::SegmentResult>(
                       result_state, result, /*rank=*/1);
@@ -113,7 +120,7 @@
 
   base::test::TaskEnvironment task_environment_{
       base::test::TaskEnvironment::TimeSource::MOCK_TIME};
-  std::vector<std::unique_ptr<Config>> configs_;
+  std::unique_ptr<ConfigHolder> config_holder_;
   std::unique_ptr<MockResultProvider> client1_result_provider_;
   std::unique_ptr<MockResultProvider> client2_result_provider_;
   std::unique_ptr<ResultRefreshManager> result_refresh_manager_;
@@ -131,7 +138,7 @@
           test_utils::GetTestOutputConfigForBinaryClassifier(),
           /*timestamp=*/base::Time::Now());
 
-  ExpectSegmentResult(client1_result_provider_.get(),
+  ExpectSegmentResult(kSegmentId1, client1_result_provider_.get(),
                       result_from_db_for_client1,
                       SegmentResultProvider::ResultState::kSuccessFromDatabase,
                       /*ignore_db_scores=*/false);
@@ -143,7 +150,7 @@
           test_utils::GetTestOutputConfigForBinaryClassifier(),
           /*timestamp=*/base::Time::Now());
 
-  ExpectSegmentResult(client2_result_provider_.get(),
+  ExpectSegmentResult(kSegmentId2, client2_result_provider_.get(),
                       result_from_model_for_client2,
                       SegmentResultProvider::ResultState::kTfliteModelScoreUsed,
                       /*ignore_db_scores=*/false);
@@ -167,14 +174,15 @@
           /*model_scores=*/{},
           test_utils::GetTestOutputConfigForBinaryClassifier(),
           /*timestamp=*/base::Time::Now());
-  ExpectSegmentResult(client1_result_provider_.get(), result_for_client,
+  ExpectSegmentResult(kSegmentId1, client1_result_provider_.get(),
+                      result_for_client,
                       SegmentResultProvider::ResultState::kSignalsNotCollected,
                       /*ignore_db_scores=*/false);
 
   // Client 2 tries gets model result by running the model and model execution
   // fails.
   ExpectSegmentResult(
-      client2_result_provider_.get(), result_for_client,
+      kSegmentId2, client2_result_provider_.get(), result_for_client,
       SegmentResultProvider::ResultState::kDefaultModelExecutionFailed,
       /*ignore_db_scores=*/false);
 
diff --git a/components/services/storage/public/mojom/cache_storage_control.mojom b/components/services/storage/public/mojom/cache_storage_control.mojom
index 46a7e921..160a8ec 100644
--- a/components/services/storage/public/mojom/cache_storage_control.mojom
+++ b/components/services/storage/public/mojom/cache_storage_control.mojom
@@ -19,16 +19,14 @@
   kBackgroundFetch,
 };
 
-// Observer interface for receiving callbacks after cache storage changes.
+// Observer interface for receiving callbacks after cache storage changes. This
+// is used by Devtools.
 interface CacheStorageObserver {
   // Called when caches are created or deleted.
-  // TODO(crbug.com/1218097): Pass bucket information also.
-  OnCacheListChanged(blink.mojom.StorageKey storage_key);
+  OnCacheListChanged(BucketLocator bucket_locator);
 
   // Called when the content of a cache has been modified.
-  // TODO(crbug.com/1218097): Pass bucket information also.
-  OnCacheContentChanged(blink.mojom.StorageKey storage_key,
-                        string cache_name);
+  OnCacheContentChanged(BucketLocator bucket_locator, string cache_name);
 };
 
 // Controls the state of CacheStorage within a partition. This is a privileged
diff --git a/components/translate/core/browser/translate_browser_metrics.cc b/components/translate/core/browser/translate_browser_metrics.cc
index 142fe44..f8b61a8b 100644
--- a/components/translate/core/browser/translate_browser_metrics.cc
+++ b/components/translate/core/browser/translate_browser_metrics.cc
@@ -20,8 +20,6 @@
     "Translate.LanguageDetection.ContentLength";
 const char kTranslateUnsupportedLanguageAtInitiation[] =
     "Translate.UnsupportedLanguageAtInitiation";
-const char kTranslateSourceLanguage[] = "Translate.SourceLanguage";
-const char kTranslateTargetLanguage[] = "Translate.TargetLanguage";
 const char kTranslateHrefHintStatus[] = "Translate.HrefHint.Status";
 const char kTranslateHrefHintPrefsFilterStatus[] =
     "Translate.HrefHint.PrefsFilterStatus";
@@ -55,16 +53,6 @@
                            language_code);
 }
 
-void ReportTranslateSourceLanguage(base::StringPiece language) {
-  base::UmaHistogramSparse(kTranslateSourceLanguage,
-                           base::HashMetricName(language));
-}
-
-void ReportTranslateTargetLanguage(base::StringPiece language) {
-  base::UmaHistogramSparse(kTranslateTargetLanguage,
-                           base::HashMetricName(language));
-}
-
 void ReportTranslateHrefHintStatus(HrefTranslateStatus status) {
   base::UmaHistogramEnumeration(kTranslateHrefHintStatus, status);
 }
diff --git a/components/translate/core/browser/translate_browser_metrics_unittest.cc b/components/translate/core/browser/translate_browser_metrics_unittest.cc
index 94419e4c..81e0afd 100644
--- a/components/translate/core/browser/translate_browser_metrics_unittest.cc
+++ b/components/translate/core/browser/translate_browser_metrics_unittest.cc
@@ -326,36 +326,6 @@
   EXPECT_EQ(1, recorder.GetCount(ENGLISH));
 }
 
-TEST(TranslateBrowserMetricsTest, ReportedTranslateSourceLanguage) {
-  const int ENGLISH = -74147910;
-  const int FRENCH = 1704315002;
-
-  MetricsRecorder recorder("Translate.SourceLanguage");
-  EXPECT_EQ(0, recorder.GetTotalCount());
-
-  TranslateBrowserMetrics::ReportTranslateSourceLanguage("en");
-  TranslateBrowserMetrics::ReportTranslateSourceLanguage("fr");
-  TranslateBrowserMetrics::ReportTranslateSourceLanguage("en");
-
-  EXPECT_EQ(2, recorder.GetCount(ENGLISH));
-  EXPECT_EQ(1, recorder.GetCount(FRENCH));
-}
-
-TEST(TranslateBrowserMetricsTest, ReportedTranslateTargetLanguage) {
-  const int ENGLISH = -74147910;
-  const int FRENCH = 1704315002;
-
-  MetricsRecorder recorder("Translate.TargetLanguage");
-  EXPECT_EQ(0, recorder.GetTotalCount());
-
-  TranslateBrowserMetrics::ReportTranslateTargetLanguage("en");
-  TranslateBrowserMetrics::ReportTranslateTargetLanguage("fr");
-  TranslateBrowserMetrics::ReportTranslateTargetLanguage("en");
-
-  EXPECT_EQ(2, recorder.GetCount(ENGLISH));
-  EXPECT_EQ(1, recorder.GetCount(FRENCH));
-}
-
 TEST(TranslateBrowserMetricsTest, ReportTranslateHrefHintStatus) {
   MetricsRecorder recorder("Translate.HrefHint.Status");
   recorder.CheckTranslateHrefHintStatus(0, 0, 0, 0);
diff --git a/components/translate/core/browser/translate_manager.cc b/components/translate/core/browser/translate_manager.cc
index 8424c20..385cfed 100644
--- a/components/translate/core/browser/translate_manager.cc
+++ b/components/translate/core/browser/translate_manager.cc
@@ -332,10 +332,6 @@
   translate_driver_->PrepareToTranslatePage(page_seq_no_, original_source_lang,
                                             target_lang, triggered_from_menu);
 
-  // Log the source and target languages of the translate request.
-  TranslateBrowserMetrics::ReportTranslateSourceLanguage(original_source_lang);
-  TranslateBrowserMetrics::ReportTranslateTargetLanguage(target_lang);
-
   // If the source language matches the UI language, it means the translation
   // prompt is being forced by an experiment. Report this so the count of how
   // often it happens can be decremented (meaning the user didn't decline or
diff --git a/components/translate/core/browser/translate_manager_unittest.cc b/components/translate/core/browser/translate_manager_unittest.cc
index b589c2a5..219199ca 100644
--- a/components/translate/core/browser/translate_manager_unittest.cc
+++ b/components/translate/core/browser/translate_manager_unittest.cc
@@ -33,6 +33,7 @@
 #include "components/translate/core/browser/translate_metrics_logger_impl.h"
 #include "components/translate/core/browser/translate_pref_names.h"
 #include "components/translate/core/browser/translate_prefs.h"
+#include "components/translate/core/browser/translate_step.h"
 #include "components/translate/core/common/translate_constants.h"
 #include "components/translate/core/common/translate_util.h"
 #include "components/variations/scoped_variations_ids_provider.h"
@@ -54,8 +55,6 @@
 const char kInitiationStatusName[] = "Translate.InitiationStatus.v2";
 const char kMenuTranslationIsAvailableName[] =
     "Translate.MenuTranslation.IsAvailable";
-const char kTranslateSourceLanguageName[] = "Translate.SourceLanguage";
-const char kTranslateTargetLanguageName[] = "Translate.TargetLanguage";
 
 // Overrides NetworkChangeNotifier, simulating connection type changes
 // for tests.
@@ -1240,8 +1239,14 @@
                                                     accept_languages_prefs);
   ON_CALL(mock_translate_client_, GetAcceptLanguagesService())
       .WillByDefault(Return(&accept_languages));
+
+  // TranslateManager::ShowTranslateUI should only call
+  // TranslateClient::ShowTranslateUI (not do translation). If it also calls
+  // TranslateManager::TranslatePage, ShowTranslateUI is called with a different
+  // TranslateStep.
   EXPECT_CALL(mock_translate_client_,
-              ShowTranslateUI(_, _, _, _, false /* triggered_from_menu */))
+              ShowTranslateUI(TRANSLATE_STEP_BEFORE_TRANSLATE, _, _, _,
+                              false /* triggered_from_menu */))
       .WillOnce(Return(true));
 
   translate_manager_ = std::make_unique<TranslateManager>(
@@ -1254,8 +1259,6 @@
 
   translate_manager_->ShowTranslateUI();
 
-  // TranslateManager::ShowTranslateUI should only call
-  // TranslateClient::ShowTranslateUI (not do translation).
   histogram_tester.ExpectTotalCount(kInitiationStatusName, 0);
 }
 
@@ -1270,6 +1273,9 @@
       .WillByDefault(Return(true));
   ON_CALL(mock_translate_client_, GetAcceptLanguagesService())
       .WillByDefault(Return(&accept_languages));
+  // TranslateManager::ShowTranslateUI should result in a translation, reflected
+  // by a call to TranslateClient::ShowTranslateUI using the
+  // TRANSLATE_STEP_TRANSLATING step.
   EXPECT_CALL(
       mock_translate_client_,
       ShowTranslateUI(translate::TRANSLATE_STEP_TRANSLATING, "en", "de",
@@ -1278,20 +1284,11 @@
   translate_manager_ = std::make_unique<TranslateManager>(
       &mock_translate_client_, &mock_translate_ranker_, &mock_language_model_);
 
-  base::HistogramTester histogram_tester;
   prefs_.SetBoolean(prefs::kOfferTranslateEnabled, true);
   translate_manager_->GetLanguageState()->LanguageDetermined("en", true);
   network_notifier_.SimulateOnline();
 
   translate_manager_->ShowTranslateUI(/* auto_translate= */ true);
-
-  // TranslateManager::ShowTranslateUI should call translation.
-  histogram_tester.ExpectTotalCount(kTranslateSourceLanguageName, 1);
-  histogram_tester.ExpectBucketCount(kTranslateSourceLanguageName,
-                                     base::HashMetricName("en"), 1);
-  histogram_tester.ExpectTotalCount(kTranslateTargetLanguageName, 1);
-  histogram_tester.ExpectBucketCount(kTranslateTargetLanguageName,
-                                     base::HashMetricName("de"), 1);
 }
 
 TEST_F(TranslateManagerTest, ShowTranslateUI_PageAlreadyTranslated) {
@@ -1305,13 +1302,17 @@
       .WillByDefault(Return(true));
   ON_CALL(mock_translate_client_, GetAcceptLanguagesService())
       .WillByDefault(Return(&accept_languages));
+  // TranslateManager::ShowTranslateUI should only call
+  // TranslateClient::ShowTranslateUI (not do translation). When translation is
+  // triggered ShowTranslateUI is called using the TRANSLATE_STEP_TRANSLATING
+  // step.
   EXPECT_CALL(mock_translate_client_,
-              ShowTranslateUI(_, _, _, _, false /* triggered_from_menu */))
+              ShowTranslateUI(TRANSLATE_STEP_AFTER_TRANSLATE, _, _, _,
+                              false /* triggered_from_menu */))
       .WillOnce(Return(true));
   translate_manager_ = std::make_unique<TranslateManager>(
       &mock_translate_client_, &mock_translate_ranker_, &mock_language_model_);
 
-  base::HistogramTester histogram_tester;
   prefs_.SetBoolean(prefs::kOfferTranslateEnabled, true);
   translate_manager_->GetLanguageState()->LanguageDetermined("en", true);
   translate_manager_->GetLanguageState()->SetCurrentLanguage("de");
@@ -1319,14 +1320,6 @@
   network_notifier_.SimulateOnline();
 
   translate_manager_->ShowTranslateUI(/* auto_translate= */ true);
-
-  // TranslateManager::ShowTranslateUI should not call translation.
-  histogram_tester.ExpectTotalCount(kTranslateSourceLanguageName, 0);
-  histogram_tester.ExpectBucketCount(kTranslateSourceLanguageName,
-                                     base::HashMetricName("en"), 0);
-  histogram_tester.ExpectTotalCount(kTranslateTargetLanguageName, 0);
-  histogram_tester.ExpectBucketCount(kTranslateTargetLanguageName,
-                                     base::HashMetricName("de"), 0);
 }
 
 TEST_F(TranslateManagerTest,
@@ -1341,6 +1334,9 @@
                                                     accept_languages_prefs);
   ON_CALL(mock_translate_client_, GetAcceptLanguagesService())
       .WillByDefault(Return(&accept_languages));
+  // TranslateManager::ShowTranslateUI should result in a translation, reflected
+  // by a call to TranslateClient::ShowTranslateUI using the
+  // TRANSLATE_STEP_TRANSLATING step and the specified languages.
   EXPECT_CALL(
       mock_translate_client_,
       ShowTranslateUI(translate::TRANSLATE_STEP_TRANSLATING, "en", "pl",
@@ -1357,15 +1353,6 @@
 
   translate_manager_->ShowTranslateUI("pl", /* auto_translate */ true,
                                       /* triggered_from_menu= */ false);
-
-  // TranslateManager::ShowTranslateUI should call translation for the
-  // explicit target language.
-  histogram_tester.ExpectTotalCount(kTranslateSourceLanguageName, 1);
-  histogram_tester.ExpectBucketCount(kTranslateSourceLanguageName,
-                                     base::HashMetricName("en"), 1);
-  histogram_tester.ExpectTotalCount(kTranslateTargetLanguageName, 1);
-  histogram_tester.ExpectBucketCount(kTranslateTargetLanguageName,
-                                     base::HashMetricName("pl"), 1);
 }
 
 TEST_F(TranslateManagerTest, ShowTranslateUI_ExplicitTargetSameAsTarget) {
@@ -1379,28 +1366,24 @@
                                                     accept_languages_prefs);
   ON_CALL(mock_translate_client_, GetAcceptLanguagesService())
       .WillByDefault(Return(&accept_languages));
-  EXPECT_CALL(mock_translate_client_,
-              ShowTranslateUI(_, _, _, _, false /* triggered_from_menu */))
+  // TranslateManager::ShowTranslateUI should result in a translation, reflected
+  // by a call to TranslateClient::ShowTranslateUI using the
+  // TRANSLATE_STEP_TRANSLATING step and the specified languages.
+  EXPECT_CALL(
+      mock_translate_client_,
+      ShowTranslateUI(TRANSLATE_STEP_TRANSLATING, "de", "pl",
+                      TranslateErrors::NONE, false /* triggered_from_menu */))
       .WillOnce(Return(true));
 
   translate_manager_ = std::make_unique<TranslateManager>(
       &mock_translate_client_, &mock_translate_ranker_, &mock_language_model_);
 
-  base::HistogramTester histogram_tester;
   prefs_.SetBoolean(prefs::kOfferTranslateEnabled, true);
   translate_manager_->GetLanguageState()->LanguageDetermined("de", true);
   network_notifier_.SimulateOnline();
 
   translate_manager_->ShowTranslateUI("pl", /* auto_translate */ true,
                                       /* triggered_from_menu= */ false);
-
-  // TranslateManager::ShowTranslateUI should call translation.
-  histogram_tester.ExpectTotalCount(kTranslateSourceLanguageName, 1);
-  histogram_tester.ExpectBucketCount(kTranslateSourceLanguageName,
-                                     base::HashMetricName("de"), 1);
-  histogram_tester.ExpectTotalCount(kTranslateTargetLanguageName, 1);
-  histogram_tester.ExpectBucketCount(kTranslateTargetLanguageName,
-                                     base::HashMetricName("pl"), 1);
 }
 
 TEST_F(TranslateManagerTest, GetActiveTranslateMetricsLogger) {
diff --git a/components/translate/core/browser/translate_ui_delegate.cc b/components/translate/core/browser/translate_ui_delegate.cc
index e312436..317bc7b 100644
--- a/components/translate/core/browser/translate_ui_delegate.cc
+++ b/components/translate/core/browser/translate_ui_delegate.cc
@@ -21,14 +21,12 @@
 namespace {
 
 const char kDeclineTranslate[] = "Translate.DeclineTranslate";
-const char kRevertTranslation[] = "Translate.RevertTranslation";
 const char kPerformTranslate[] = "Translate.Translate";
 const char kPerformTranslateAmpCacheUrl[] = "Translate.Translate.AMPCacheURL";
 const char kNeverTranslateLang[] = "Translate.NeverTranslateLang";
 const char kNeverTranslateSite[] = "Translate.NeverTranslateSite";
 const char kAlwaysTranslateLang[] = "Translate.AlwaysTranslateLang";
 const char kModifySourceLang[] = "Translate.ModifyOriginalLang";
-const char kModifyTargetLang[] = "Translate.ModifyTargetLang";
 const char kShowErrorUI[] = "Translate.ShowErrorUI";
 
 // Returns whether |url| fits pattern of an AMP cache url.
@@ -126,8 +124,6 @@
     return;
   }
 
-  UMA_HISTOGRAM_BOOLEAN(kModifyTargetLang, true);
-
   if (translate_manager_) {
     translate_manager_->GetActiveTranslateMetricsLogger()->LogTargetLanguage(
         translate_ui_languages_manager_->GetLanguageCodeAt(language_index),
@@ -208,7 +204,6 @@
   if (translate_manager_ &&
       translate_manager_->GetLanguageState()->IsPageTranslated()) {
     translate_manager_->RevertTranslation();
-    UMA_HISTOGRAM_BOOLEAN(kRevertTranslation, true);
   }
 }
 
diff --git a/components/webxr/DEPS b/components/webxr/DEPS
index 91b715a..6208ce49 100644
--- a/components/webxr/DEPS
+++ b/components/webxr/DEPS
@@ -11,6 +11,7 @@
   "+services/viz/public/cpp/gpu/context_provider_command_buffer.h",
   "+ui/gfx",
   "+ui/gl/android",
+  "+third_party/openxr/src/include/openxr",
   # The third_party version of openxr_platform depends on certain platform
   # headers being included before it, so we disallow direct dependency on it.
   # Places needing to take a dependency on code in openxr_platform.h should
diff --git a/components/webxr/android/BUILD.gn b/components/webxr/android/BUILD.gn
index f750815..78e5c8a 100644
--- a/components/webxr/android/BUILD.gn
+++ b/components/webxr/android/BUILD.gn
@@ -48,6 +48,8 @@
 
     if (enable_openxr) {
       sources += [
+        "openxr_device_provider.cc",
+        "openxr_device_provider.h",
         "openxr_platform_helper_android.cc",
         "openxr_platform_helper_android.h",
       ]
@@ -80,6 +82,8 @@
         "//device/vr",
         "//third_party/openxr:openxr_headers",
       ]
+
+      public_configs = [ "//third_party/openxr:config" ]
     }
 
     libs = [ "android" ]
diff --git a/components/webxr/android/java/src/org/chromium/components/webxr/XrSessionCoordinator.java b/components/webxr/android/java/src/org/chromium/components/webxr/XrSessionCoordinator.java
index abf4fb3f..fc204dd 100644
--- a/components/webxr/android/java/src/org/chromium/components/webxr/XrSessionCoordinator.java
+++ b/components/webxr/android/java/src/org/chromium/components/webxr/XrSessionCoordinator.java
@@ -70,6 +70,7 @@
     // Helper, obtains android Activity out of passed in WebContents instance.
     // Equivalent to ChromeActivity.fromWebContents(), but does not require that
     // the resulting instance is a ChromeActivity.
+    @CalledByNative
     public static Activity getActivity(final WebContents webContents) {
         if (webContents == null) return null;
         WindowAndroid window = webContents.getTopLevelNativeWindow();
diff --git a/components/webxr/android/openxr_device_provider.cc b/components/webxr/android/openxr_device_provider.cc
new file mode 100644
index 0000000..252c005
--- /dev/null
+++ b/components/webxr/android/openxr_device_provider.cc
@@ -0,0 +1,110 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/webxr/android/openxr_device_provider.h"
+
+#include <utility>
+
+#include "base/android/jni_android.h"
+#include "base/functional/bind.h"
+#include "base/logging.h"
+#include "base/system/sys_info.h"
+#include "base/task/single_thread_task_runner.h"
+#include "components/viz/common/gpu/context_provider.h"
+#include "components/webxr/android/openxr_platform_helper_android.h"
+#include "components/webxr/android/xr_session_coordinator.h"
+#include "components/webxr/mailbox_to_surface_bridge_impl.h"
+#include "content/public/browser/android/compositor.h"
+#include "content/public/browser/browser_task_traits.h"
+#include "content/public/browser/browser_thread.h"
+#include "device/vr/openxr/context_provider_callbacks.h"
+#include "device/vr/openxr/openxr_device.h"
+#include "device/vr/openxr/openxr_platform.h"
+#include "gpu/command_buffer/client/shared_memory_limits.h"
+#include "gpu/command_buffer/common/context_creation_attribs.h"
+#include "third_party/openxr/src/include/openxr/openxr.h"
+
+namespace webxr {
+
+OpenXrDeviceProvider::OpenXrDeviceProvider() = default;
+
+OpenXrDeviceProvider::~OpenXrDeviceProvider() {
+  // Must make sure that the OpenXrPlatformHelper outlives the OpenXrDevice.
+  openxr_device_.reset();
+  openxr_platform_helper_.reset();
+}
+
+void OpenXrDeviceProvider::Initialize(device::VRDeviceProviderClient* client) {
+  CHECK(!initialized_);
+
+  openxr_platform_helper_ = std::make_unique<OpenXrPlatformHelperAndroid>();
+
+  if (openxr_platform_helper_->EnsureInitialized()) {
+    DVLOG(2) << __func__ << ": OpenXr is supported, creating device";
+    // Unretained is safe since we own the device this callback is being passed
+    // to and we ensure that it does not outlive us. The device is expected to
+    // wind down any threads that it spins up as well (for e.g. rendering), so
+    // this destruction is also safe.
+    // The OpenXrDevice passes this off to different render loops as it creates
+    // them, so we can't just use a WeakPtr of ourselves here, since it would
+    // technically end up dereferenced on different threads (albeit all
+    // children).
+    openxr_device_ = std::make_unique<device::OpenXrDevice>(
+        base::BindRepeating(&OpenXrDeviceProvider::CreateContextProviderAsync,
+                            base::Unretained(this)),
+        openxr_platform_helper_.get());
+
+    client->AddRuntime(openxr_device_->GetId(), openxr_device_->GetDeviceData(),
+                       openxr_device_->BindXRRuntime());
+  } else {
+    DVLOG(2) << __func__ << ": No OpenXR Hardware found.";
+  }
+
+  initialized_ = true;
+  client->OnProviderInitialized();
+}
+
+bool OpenXrDeviceProvider::Initialized() {
+  return initialized_;
+}
+
+void OpenXrDeviceProvider::CreateContextProviderAsync(
+    VizContextProviderCallback viz_context_provider_callback) {
+  content::GetUIThreadTaskRunner({})->PostTask(
+      FROM_HERE,
+      base::BindOnce(
+          [](int surface_handle,
+             content::Compositor::ContextProviderCallback callback) {
+            // Our attributes must be compatible with the shared
+            // offscreen surface used by virtualized contexts,
+            // otherwise mailbox synchronization doesn't work
+            // properly - it assumes a shared underlying GL context.
+            // See GetCompositorContextAttributes in
+            // content/browser/renderer_host/compositor_impl_android.cc
+            // and https://crbug.com/699330.
+            gpu::ContextCreationAttribs attributes;
+            attributes.alpha_size = -1;
+            attributes.red_size = 8;
+            attributes.green_size = 8;
+            attributes.blue_size = 8;
+            attributes.stencil_size = 0;
+            attributes.depth_size = 0;
+            attributes.samples = 0;
+            attributes.sample_buffers = 0;
+            attributes.bind_generates_resource = false;
+            if (base::SysInfo::IsLowEndDevice()) {
+              attributes.alpha_size = 0;
+              attributes.red_size = 5;
+              attributes.green_size = 6;
+              attributes.blue_size = 5;
+            }
+            content::Compositor::CreateContextProvider(
+                surface_handle, attributes,
+                gpu::SharedMemoryLimits::ForMailboxContext(),
+                std::move(callback));
+          },
+          gpu::kNullSurfaceHandle, std::move(viz_context_provider_callback)));
+}
+
+}  // namespace webxr
diff --git a/components/webxr/android/openxr_device_provider.h b/components/webxr/android/openxr_device_provider.h
new file mode 100644
index 0000000..8cf7a67
--- /dev/null
+++ b/components/webxr/android/openxr_device_provider.h
@@ -0,0 +1,43 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_WEBXR_ANDROID_OPENXR_DEVICE_PROVIDER_H_
+#define COMPONENTS_WEBXR_ANDROID_OPENXR_DEVICE_PROVIDER_H_
+
+#include <memory>
+
+#include "components/webxr/android/openxr_platform_helper_android.h"
+#include "device/vr/openxr/context_provider_callbacks.h"
+#include "device/vr/openxr/openxr_device.h"
+#include "device/vr/public/cpp/vr_device_provider.h"
+
+namespace device {
+class OpenXrDevice;
+}
+namespace webxr {
+
+class OpenXrDeviceProvider : public device::VRDeviceProvider {
+ public:
+  OpenXrDeviceProvider();
+  ~OpenXrDeviceProvider() override;
+
+  OpenXrDeviceProvider(const OpenXrDeviceProvider&) = delete;
+  OpenXrDeviceProvider& operator=(const OpenXrDeviceProvider&) = delete;
+
+  void Initialize(device::VRDeviceProviderClient* client) override;
+  bool Initialized() override;
+
+ private:
+  void CreateContextProviderAsync(
+      VizContextProviderCallback viz_context_provider_callback);
+
+  // Must outlive `openxr_device_`
+  std::unique_ptr<OpenXrPlatformHelperAndroid> openxr_platform_helper_;
+  std::unique_ptr<device::OpenXrDevice> openxr_device_;
+  bool initialized_ = false;
+};
+
+}  // namespace webxr
+
+#endif  // COMPONENTS_WEBXR_ANDROID_OPENXR_DEVICE_PROVIDER_H_
diff --git a/components/webxr/android/openxr_platform_helper_android.cc b/components/webxr/android/openxr_platform_helper_android.cc
index f73dbc3..7c7be5b 100644
--- a/components/webxr/android/openxr_platform_helper_android.cc
+++ b/components/webxr/android/openxr_platform_helper_android.cc
@@ -6,6 +6,7 @@
 #include <vector>
 
 #include "base/android/jni_android.h"
+#include "components/webxr/android/webxr_utils.h"
 #include "components/webxr/android/xr_session_coordinator.h"
 #include "device/vr/openxr/android/openxr_graphics_binding_open_gles.h"
 #include "device/vr/openxr/openxr_platform.h"
@@ -27,8 +28,15 @@
 
 const void* OpenXrPlatformHelperAndroid::GetPlatformCreateInfo(
     const device::OpenXrCreateInfo& create_info) {
-  // TODO(alcooper): Implement this.
-  return nullptr;
+  // Re-compute the create_info_ that we need every time in case the activity
+  // has changed.
+  activity_ = XrSessionCoordinator::GetActivity(GetJavaWebContents(
+      create_info.render_process_id, create_info.render_frame_id));
+
+  create_info_.next = nullptr;
+  create_info_.applicationVM = base::android::GetVM();
+  create_info_.applicationActivity = activity_.obj();
+  return &create_info_;
 }
 
 bool OpenXrPlatformHelperAndroid::Initialize() {
@@ -47,7 +55,6 @@
 
   app_context_ = XrSessionCoordinator::GetApplicationContext();
   XrLoaderInitInfoAndroidKHR loaderInitInfoAndroid;
-  memset(&loaderInitInfoAndroid, 0, sizeof(loaderInitInfoAndroid));
   loaderInitInfoAndroid.type = XR_TYPE_LOADER_INIT_INFO_ANDROID_KHR;
   loaderInitInfoAndroid.next = nullptr;
   loaderInitInfoAndroid.applicationVM = base::android::GetVM();
diff --git a/components/webxr/android/openxr_platform_helper_android.h b/components/webxr/android/openxr_platform_helper_android.h
index 549bbc82..1c2c2314 100644
--- a/components/webxr/android/openxr_platform_helper_android.h
+++ b/components/webxr/android/openxr_platform_helper_android.h
@@ -8,6 +8,7 @@
 #include "device/vr/openxr/openxr_platform_helper.h"
 
 #include "base/android/scoped_java_ref.h"
+#include "device/vr/openxr/openxr_platform.h"
 
 namespace webxr {
 
@@ -25,6 +26,9 @@
   bool Initialize() override;
 
  private:
+  XrInstanceCreateInfoAndroidKHR create_info_{
+      XR_TYPE_INSTANCE_CREATE_INFO_ANDROID_KHR};
+  base::android::ScopedJavaGlobalRef<jobject> activity_;
   base::android::ScopedJavaGlobalRef<jobject> app_context_;
 };
 
diff --git a/components/webxr/android/xr_session_coordinator.cc b/components/webxr/android/xr_session_coordinator.cc
index 3a0f21a..11180ca 100644
--- a/components/webxr/android/xr_session_coordinator.cc
+++ b/components/webxr/android/xr_session_coordinator.cc
@@ -181,4 +181,11 @@
   return Java_XrSessionCoordinator_getApplicationContext(env);
 }
 
+// static
+ScopedJavaLocalRef<jobject> XrSessionCoordinator::GetActivity(
+    ScopedJavaLocalRef<jobject> web_contents) {
+  JNIEnv* env = AttachCurrentThread();
+  return Java_XrSessionCoordinator_getActivity(env, web_contents);
+}
+
 }  // namespace webxr
diff --git a/components/webxr/android/xr_session_coordinator.h b/components/webxr/android/xr_session_coordinator.h
index 15e111bb..68a3521 100644
--- a/components/webxr/android/xr_session_coordinator.h
+++ b/components/webxr/android/xr_session_coordinator.h
@@ -19,6 +19,10 @@
   // activity.
   static base::android::ScopedJavaLocalRef<jobject> GetApplicationContext();
 
+  // Used to query the current Activity from the specified WebContents.
+  static base::android::ScopedJavaLocalRef<jobject> GetActivity(
+      base::android::ScopedJavaLocalRef<jobject> web_contents);
+
   explicit XrSessionCoordinator();
   ~XrSessionCoordinator() override;
 
diff --git a/content/browser/BUILD.gn b/content/browser/BUILD.gn
index 8622055..7f8aea52 100644
--- a/content/browser/BUILD.gn
+++ b/content/browser/BUILD.gn
@@ -2381,7 +2381,6 @@
       "font_access/font_enumeration_data_source_linux.h",
       "font_service.cc",
       "font_service.h",
-      "media/stable_video_decoder_factory.cc",
       "media/video_encode_accelerator_provider_launcher.cc",
       "media/video_encode_accelerator_provider_launcher.h",
       "memory/swap_metrics_driver_impl_linux.cc",
@@ -2400,11 +2399,15 @@
       "//components/crash/content/browser/error_reporting",
       "//components/services/font:lib",
       "//content/common:sandbox_support_linux",
-      "//media/mojo/mojom/stable:stable_video_decoder",
       "//third_party/blink/public/mojom:memory_usage_monitor_linux_mojo_bindings",
     ]
   }
 
+  if (allow_oop_video_decoder) {
+    sources += [ "media/stable_video_decoder_factory.cc" ]
+    deps += [ "//media/mojo/mojom/stable:stable_video_decoder" ]
+  }
+
   if (is_chromeos) {
     sources += [
       "handwriting/handwriting_recognition_service_impl_cros.cc",
diff --git a/content/browser/buckets/bucket_host.cc b/content/browser/buckets/bucket_host.cc
index bdded35f..0c83bd5 100644
--- a/content/browser/buckets/bucket_host.cc
+++ b/content/browser/buckets/bucket_host.cc
@@ -47,6 +47,13 @@
   return remote;
 }
 
+void BucketHost::PassStorageBucketBinding(
+    base::WeakPtr<BucketContext> bucket_context,
+    mojo::PendingReceiver<blink::mojom::BucketHost> receiver) {
+  DCHECK(bucket_context);
+  receivers_.Add(this, std::move(receiver), bucket_context);
+}
+
 void BucketHost::Persist(PersistCallback callback) {
   if (bucket_info_.is_null() || !receivers_.current_context()) {
     std::move(callback).Run(false, /*success=*/false);
@@ -150,8 +157,9 @@
 void BucketHost::DidValidateForExpires(ExpiresCallback callback,
                                        bool bucket_exists) {
   absl::optional<base::Time> expires;
-  if (bucket_exists && !bucket_info_.expiration.is_null())
+  if (bucket_exists && !bucket_info_.expiration.is_null()) {
     expires = bucket_info_.expiration;
+  }
 
   std::move(callback).Run(expires, bucket_exists);
 }
@@ -159,8 +167,9 @@
 void BucketHost::GetIdbFactory(
     mojo::PendingReceiver<blink::mojom::IDBFactory> receiver) {
   auto bucket_context = receivers_.current_context();
-  if (!bucket_context)
+  if (!bucket_context) {
     return;
+  }
 
   GlobalRenderFrameHostId rfh_id =
       bucket_context->GetAssociatedRenderFrameHostId();
@@ -177,16 +186,18 @@
 void BucketHost::GetCaches(
     mojo::PendingReceiver<blink::mojom::CacheStorage> caches) {
   auto bucket_context = receivers_.current_context();
-  if (!bucket_context)
+  if (!bucket_context) {
     return;
+  }
 
   bucket_context->BindCacheStorageForBucket(bucket_info_, std::move(caches));
 }
 
 void BucketHost::GetDirectory(GetDirectoryCallback callback) {
   auto bucket_context = receivers_.current_context();
-  if (!bucket_context)
+  if (!bucket_context) {
     return;
+  }
 
   bucket_context->GetSandboxedFileSystemForBucket(bucket_info_,
                                                   std::move(callback));
@@ -199,8 +210,9 @@
 }
 
 void BucketHost::OnReceiverDisconnected() {
-  if (!receivers_.empty())
+  if (!receivers_.empty()) {
     return;
+  }
   // Destroys `this`.
   bucket_manager_host_->RemoveBucketHost(bucket_id_);
 }
diff --git a/content/browser/buckets/bucket_host.h b/content/browser/buckets/bucket_host.h
index c274da3..1b934923 100644
--- a/content/browser/buckets/bucket_host.h
+++ b/content/browser/buckets/bucket_host.h
@@ -43,6 +43,10 @@
   // for the StorageBucket object.
   mojo::PendingRemote<blink::mojom::BucketHost> CreateStorageBucketBinding(
       base::WeakPtr<BucketContext> context);
+  // Pass a mojo data pipe sent from the renderer for the StorageBucket object.
+  void PassStorageBucketBinding(
+      base::WeakPtr<BucketContext> bucket_context,
+      mojo::PendingReceiver<blink::mojom::BucketHost> receiver);
 
   // blink::mojom::BucketHost
   void Persist(PersistCallback callback) override;
diff --git a/content/browser/buckets/bucket_manager_host.cc b/content/browser/buckets/bucket_manager_host.cc
index 00cfea1..4fe495d 100644
--- a/content/browser/buckets/bucket_manager_host.cc
+++ b/content/browser/buckets/bucket_manager_host.cc
@@ -11,6 +11,7 @@
 #include "base/task/sequenced_task_runner.h"
 #include "base/types/pass_key.h"
 #include "components/services/storage/public/cpp/buckets/bucket_info.h"
+#include "content/browser/buckets/bucket_host.h"
 #include "content/browser/buckets/bucket_manager.h"
 #include "content/browser/buckets/bucket_utils.h"
 #include "content/browser/storage_partition_impl.h"
@@ -131,13 +132,13 @@
 
 void BucketManagerHost::GetBucketForDevtools(
     const std::string& name,
-    GetBucketForDevtoolsCallback callback) {
+    mojo::PendingReceiver<blink::mojom::BucketHost> receiver) {
   GetQuotaManagerProxy()->GetBucketByNameUnsafe(
       storage_key_, name, blink::mojom::StorageType::kTemporary,
       base::SequencedTaskRunner::GetCurrentDefault(),
-      base::BindOnce(&BucketManagerHost::DidGetBucket,
+      base::BindOnce(&BucketManagerHost::DidGetBucketForDevtools,
                      weak_factory_.GetWeakPtr(), receivers_.current_context(),
-                     std::move(callback)));
+                     std::move(receiver)));
 }
 
 void BucketManagerHost::Keys(KeysCallback callback) {
@@ -226,6 +227,27 @@
                           blink::mojom::BucketError::kUnknown);
 }
 
+void BucketManagerHost::DidGetBucketForDevtools(
+    base::WeakPtr<BucketContext> bucket_context,
+    mojo::PendingReceiver<blink::mojom::BucketHost> receiver,
+    storage::QuotaErrorOr<storage::BucketInfo> result) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+  if (!bucket_context || !result.has_value()) {
+    return;
+  }
+
+  const auto& bucket = result.value();
+  auto it = bucket_map_.find(bucket.id);
+  if (it == bucket_map_.end()) {
+    it = bucket_map_
+             .emplace(bucket.id, std::make_unique<BucketHost>(this, bucket))
+             .first;
+  }
+
+  it->second->PassStorageBucketBinding(bucket_context, std::move(receiver));
+}
+
 void BucketManagerHost::DidGetBuckets(
     KeysCallback callback,
     storage::QuotaErrorOr<std::set<storage::BucketInfo>> buckets) {
diff --git a/content/browser/buckets/bucket_manager_host.h b/content/browser/buckets/bucket_manager_host.h
index 858132a8..4ab0180 100644
--- a/content/browser/buckets/bucket_manager_host.h
+++ b/content/browser/buckets/bucket_manager_host.h
@@ -66,8 +66,9 @@
                   OpenBucketCallback callback) override;
   // Gets the bucket with the given name. Doesn't create the bucket if it
   // doesn't exist.
-  void GetBucketForDevtools(const std::string& name,
-                            GetBucketForDevtoolsCallback callback) override;
+  void GetBucketForDevtools(
+      const std::string& name,
+      mojo::PendingReceiver<blink::mojom::BucketHost> receiver) override;
   void Keys(KeysCallback callback) override;
   void DeleteBucket(const std::string& name,
                     DeleteBucketCallback callback) override;
@@ -95,7 +96,7 @@
 
   void DidGetBucketForDevtools(
       base::WeakPtr<BucketContext> bucket_context,
-      GetBucketForDevtoolsCallback callback,
+      mojo::PendingReceiver<blink::mojom::BucketHost> receiver,
       storage::QuotaErrorOr<storage::BucketInfo> result);
 
   SEQUENCE_CHECKER(sequence_checker_);
diff --git a/content/browser/cache_storage/cache_storage_manager.cc b/content/browser/cache_storage/cache_storage_manager.cc
index 09f723cb..3316b6b 100644
--- a/content/browser/cache_storage/cache_storage_manager.cc
+++ b/content/browser/cache_storage/cache_storage_manager.cc
@@ -134,12 +134,14 @@
 };
 
 IndexResult ValidateIndex(proto::CacheStorageIndex index) {
-  if (!index.has_origin())
+  if (!index.has_origin()) {
     return IndexResult::kMissingOrigin;
+  }
 
   GURL url(index.origin());
-  if (url.is_empty())
+  if (url.is_empty()) {
     return IndexResult::kEmptyOriginUrl;
+  }
 
   // TODO(https://crbug.com/1199077): Consider adding a
   // 'index.has_storage_key()' check here once we've ensured that a
@@ -183,8 +185,9 @@
       index_file_directory_path.AppendASCII(CacheStorage::kIndexFileName);
   base::File::Info file_info;
   base::Time index_last_modified;
-  if (GetFileInfo(index_path, &file_info))
+  if (GetFileInfo(index_path, &file_info)) {
     index_last_modified = file_info.last_modified;
+  }
   std::string protobuf;
   base::ReadFileToString(index_path, &protobuf);
 
@@ -265,8 +268,9 @@
               : storage::mojom::CacheStorageOwner::kCacheAPI;
       auto other_owner_path = CacheStorageManager::ConstructBucketPath(
           profile_path, bucket_locator, other_owner);
-      if (index_file_directory_path == other_owner_path)
+      if (index_file_directory_path == other_owner_path) {
         return;
+      }
     }
     RecordIndexValidationResult(IndexResult::kPathMismatch);
     return;
@@ -617,16 +621,18 @@
 void CacheStorageManager::NotifyCacheListChanged(
     const storage::BucketLocator& bucket_locator) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  for (const auto& observer : observers_)
-    observer->OnCacheListChanged(bucket_locator.storage_key);
+  for (const auto& observer : observers_) {
+    observer->OnCacheListChanged(bucket_locator);
+  }
 }
 
 void CacheStorageManager::NotifyCacheContentChanged(
     const storage::BucketLocator& bucket_locator,
     const std::string& name) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  for (const auto& observer : observers_)
-    observer->OnCacheContentChanged(bucket_locator.storage_key, name);
+  for (const auto& observer : observers_) {
+    observer->OnCacheContentChanged(bucket_locator, name);
+  }
 }
 
 void CacheStorageManager::CacheStorageUnreferenced(
@@ -657,8 +663,9 @@
 
   if (IsMemoryBacked()) {
     for (const auto& bucket_details : cache_storage_map_) {
-      if (bucket_details.first.second != owner)
+      if (bucket_details.first.second != owner) {
         continue;
+      }
       const storage::BucketLocator& bucket_locator = bucket_details.first.first;
       usages.emplace_back(bucket_locator, storage::mojom::StorageUsageInfo::New(
                                               bucket_locator.storage_key,
@@ -790,12 +797,14 @@
   if (IsMemoryBacked()) {
     std::vector<blink::StorageKey> storage_keys;
     for (const auto& key_value : cache_storage_map_) {
-      if (key_value.first.second != owner)
+      if (key_value.first.second != owner) {
         continue;
+      }
 
       const storage::BucketLocator& bucket_locator = key_value.first.first;
-      if (!bucket_locator.is_default)
+      if (!bucket_locator.is_default) {
         continue;
+      }
 
       storage_keys.push_back(bucket_locator.storage_key);
     }
@@ -926,8 +935,9 @@
     // search for the corresponding `storage::BucketLocator` keys, given a
     // `blink::StorageKey`.
     for (const auto& key_value : cache_storage_map_) {
-      if (key_value.first.second != owner)
+      if (key_value.first.second != owner) {
         continue;
+      }
       const storage::BucketLocator& bucket_locator = key_value.first.first;
       if (!BucketMatchesOriginsForDeletion(bucket_locator, origins)) {
         continue;
@@ -1055,8 +1065,9 @@
       -bucket_size, base::Time::Now(),
       base::SequencedTaskRunner::GetCurrentDefault(), base::DoNothing());
 
-  if (owner == storage::mojom::CacheStorageOwner::kCacheAPI)
+  if (owner == storage::mojom::CacheStorageOwner::kCacheAPI) {
     NotifyCacheListChanged(bucket_locator);
+  }
 
   if (IsMemoryBacked()) {
     scheduler_task_runner_->PostTask(
@@ -1132,8 +1143,9 @@
 void CacheStorageManager::OnMemoryPressure(
     base::MemoryPressureListener::MemoryPressureLevel level) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  if (level != base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL)
+  if (level != base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL) {
     return;
+  }
 
   for (auto& entry : cache_storage_map_) {
     entry.second->ReleaseUnreferencedCaches();
diff --git a/content/browser/cache_storage/cache_storage_manager_unittest.cc b/content/browser/cache_storage/cache_storage_manager_unittest.cc
index aebdbff..859c8e2 100644
--- a/content/browser/cache_storage/cache_storage_manager_unittest.cc
+++ b/content/browser/cache_storage/cache_storage_manager_unittest.cc
@@ -133,8 +133,9 @@
 
  private:
   void MaybeComplete() {
-    if (paused_ || !client_)
+    if (paused_ || !client_) {
       return;
+    }
     client_->OnComplete(net::OK, data_.length());
     client_.reset();
     producer_handle_.reset();
@@ -168,8 +169,9 @@
  private:
   void ExecuteTask(base::OnceClosure task) {
     std::move(task).Run();
-    if (callback_)
+    if (callback_) {
       std::move(callback_).Run();
+    }
   }
 
   base::OnceClosure callback_;
@@ -199,18 +201,21 @@
   base::File::Info info;
   const base::FilePath index_path =
       cache_dir.AppendASCII(CacheStorage::kIndexFileName);
-  if (!GetFileInfo(index_path, &info))
+  if (!GetFileInfo(index_path, &info)) {
     return false;
+  }
   base::Time index_last_modified = info.last_modified;
 
   base::FileEnumerator enumerator(cache_dir, false,
                                   base::FileEnumerator::DIRECTORIES);
   for (base::FilePath file_path = enumerator.Next(); !file_path.empty();
        file_path = enumerator.Next()) {
-    if (!GetFileInfo(file_path, &info))
+    if (!GetFileInfo(file_path, &info)) {
       return false;
-    if (index_last_modified <= info.last_modified)
+    }
+    if (index_last_modified <= info.last_modified) {
       return false;
+    }
   }
 
   return true;
@@ -223,12 +228,13 @@
       : receiver_(this, std::move(observer)),
         loop_(std::make_unique<base::RunLoop>()) {}
 
-  void OnCacheListChanged(const blink::StorageKey& storage_key) override {
+  void OnCacheListChanged(
+      const storage::BucketLocator& bucket_locator) override {
     ++notify_list_changed_count;
     loop_->Quit();
   }
 
-  void OnCacheContentChanged(const blink::StorageKey& storage_key,
+  void OnCacheContentChanged(const storage::BucketLocator& bucket_locator,
                              const std::string& cache_name) override {
     ++notify_content_changed_count;
     loop_->Quit();
@@ -262,8 +268,9 @@
 
   void SetUp() override {
     base::FilePath temp_dir_path;
-    if (!MemoryOnly())
+    if (!MemoryOnly()) {
       ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
+    }
 
     CreateStorageManager();
   }
@@ -362,8 +369,9 @@
         base::MakeRefCounted<BlobStorageContextWrapper>(std::move(remote));
 
     base::FilePath temp_dir_path;
-    if (!MemoryOnly())
+    if (!MemoryOnly()) {
       temp_dir_path = temp_dir_.GetPath();
+    }
 
     quota_policy_ = base::MakeRefCounted<storage::MockSpecialStoragePolicy>();
     mock_quota_manager_ = base::MakeRefCounted<storage::MockQuotaManager>(
@@ -549,8 +557,9 @@
         base::BindOnce(&CacheStorageManagerTest::CacheMatchCallback,
                        base::Unretained(this), base::Unretained(&loop)));
     loop.Run();
-    if (callback_error_ == CacheStorageError::kSuccess)
+    if (callback_error_ == CacheStorageError::kSuccess) {
       CheckOpHistograms(histogram_tester, "Match");
+    }
     return callback_error_ == CacheStorageError::kSuccess;
   }
 
@@ -580,8 +589,9 @@
         base::BindOnce(&CacheStorageManagerTest::CacheMatchCallback,
                        base::Unretained(this), base::Unretained(&loop)));
     loop.Run();
-    if (callback_error_ == CacheStorageError::kSuccess)
+    if (callback_error_ == CacheStorageError::kSuccess) {
       CheckOpHistograms(histogram_tester, "MatchAll");
+    }
     return callback_error_ == CacheStorageError::kSuccess;
   }
 
@@ -844,8 +854,9 @@
                               blink::mojom::QuotaStatusCode status_code,
                               int64_t usage,
                               int64_t quota) {
-    if (status_code == blink::mojom::QuotaStatusCode::kOk)
+    if (status_code == blink::mojom::QuotaStatusCode::kOk) {
       *out_usage = usage;
+    }
     run_loop->Quit();
   }
 
@@ -1575,8 +1586,9 @@
 }
 
 TEST_F(CacheStorageManagerTest, TestErrorInitializingCache) {
-  if (MemoryOnly())
+  if (MemoryOnly()) {
     return;
+  }
   const GURL kFooURL("http://example.com/foo");
   const std::string kCacheName = "foo";
 
diff --git a/content/browser/devtools/protocol/storage_handler.cc b/content/browser/devtools/protocol/storage_handler.cc
index a20b251..dc5f16b 100644
--- a/content/browser/devtools/protocol/storage_handler.cc
+++ b/content/browser/devtools/protocol/storage_handler.cc
@@ -190,22 +190,23 @@
     storage_keys_.erase(storage_key);
   }
 
-  void OnCacheListChanged(const blink::StorageKey& storage_key) override {
+  void OnCacheListChanged(
+      const storage::BucketLocator& bucket_locator) override {
     DCHECK_CURRENTLY_ON(BrowserThread::UI);
-    auto found = storage_keys_.find(storage_key);
+    auto found = storage_keys_.find(bucket_locator.storage_key);
     if (found == storage_keys_.end()) {
       return;
     }
-    owner_->NotifyCacheStorageListChanged(storage_key);
+    owner_->NotifyCacheStorageListChanged(bucket_locator);
   }
 
-  void OnCacheContentChanged(const blink::StorageKey& storage_key,
+  void OnCacheContentChanged(const storage::BucketLocator& bucket_locator,
                              const std::string& cache_name) override {
     DCHECK_CURRENTLY_ON(BrowserThread::UI);
-    if (storage_keys_.find(storage_key) == storage_keys_.end()) {
+    if (storage_keys_.find(bucket_locator.storage_key) == storage_keys_.end()) {
       return;
     }
-    owner_->NotifyCacheStorageContentChanged(storage_key, cache_name);
+    owner_->NotifyCacheStorageContentChanged(bucket_locator, cache_name);
   }
 
  private:
@@ -862,18 +863,22 @@
 }
 
 void StorageHandler::NotifyCacheStorageListChanged(
-    const blink::StorageKey& storage_key) {
+    const storage::BucketLocator& bucket_locator) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
-  frontend_->CacheStorageListUpdated(storage_key.origin().Serialize(),
-                                     storage_key.Serialize());
+  frontend_->CacheStorageListUpdated(
+      bucket_locator.storage_key.origin().Serialize(),
+      bucket_locator.storage_key.Serialize(),
+      base::NumberToString(bucket_locator.id.value()));
 }
 
 void StorageHandler::NotifyCacheStorageContentChanged(
-    const blink::StorageKey& storage_key,
+    const storage::BucketLocator& bucket_locator,
     const std::string& name) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
-  frontend_->CacheStorageContentUpdated(storage_key.origin().Serialize(),
-                                        storage_key.Serialize(), name);
+  frontend_->CacheStorageContentUpdated(
+      bucket_locator.storage_key.origin().Serialize(),
+      bucket_locator.storage_key.Serialize(),
+      base::NumberToString(bucket_locator.id.value()), name);
 }
 
 void StorageHandler::NotifyIndexedDBListChanged(
diff --git a/content/browser/devtools/protocol/storage_handler.h b/content/browser/devtools/protocol/storage_handler.h
index 739a5e7..803c971 100644
--- a/content/browser/devtools/protocol/storage_handler.h
+++ b/content/browser/devtools/protocol/storage_handler.h
@@ -169,9 +169,11 @@
       const std::string& owner_origin,
       const SharedStorageEventParams& params);
 
-  void NotifyCacheStorageListChanged(const blink::StorageKey& storage_key);
-  void NotifyCacheStorageContentChanged(const blink::StorageKey& storage_key,
-                                        const std::string& name);
+  void NotifyCacheStorageListChanged(
+      const storage::BucketLocator& bucket_locator);
+  void NotifyCacheStorageContentChanged(
+      const storage::BucketLocator& bucket_locator,
+      const std::string& name);
   void NotifyIndexedDBListChanged(storage::BucketLocator bucket_locator);
   void NotifyIndexedDBContentChanged(storage::BucketLocator bucket_locator,
                                      const std::u16string& database_name,
diff --git a/content/browser/renderer_host/render_process_host_impl.cc b/content/browser/renderer_host/render_process_host_impl.cc
index 1fd116305..da1be345 100644
--- a/content/browser/renderer_host/render_process_host_impl.cc
+++ b/content/browser/renderer_host/render_process_host_impl.cc
@@ -226,10 +226,13 @@
 #include "third_party/blink/public/mojom/memory_usage_monitor_linux.mojom.h"  // nogncheck
 
 #include "content/browser/media/video_encode_accelerator_provider_launcher.h"
-#include "content/public/browser/stable_video_decoder_factory.h"
 #include "media/mojo/mojom/video_encode_accelerator.mojom.h"
 #endif
 
+#if BUILDFLAG(ALLOW_OOP_VIDEO_DECODER)
+#include "content/public/browser/stable_video_decoder_factory.h"
+#endif
+
 #if BUILDFLAG(IS_APPLE)
 #include "content/browser/child_process_task_port_provider_mac.h"
 #endif
diff --git a/content/browser/webid/federated_auth_request_impl.cc b/content/browser/webid/federated_auth_request_impl.cc
index 00cee196..db1c9c3c 100644
--- a/content/browser/webid/federated_auth_request_impl.cc
+++ b/content/browser/webid/federated_auth_request_impl.cc
@@ -1508,7 +1508,12 @@
     // Embargo auto re-authn to mitigate a deadloop where an auto
     // re-authenticated user gets auto re-authenticated again soon after logging
     // out of the active session.
-    auto_reauthn_permission_delegate_->RecordDisplayAndEmbargo(
+    auto_reauthn_permission_delegate_->RecordEmbargoForAutoReauthn(
+        GetEmbeddingOrigin());
+  } else {
+    // Once a user has explicitly selected an account, there is no need to block
+    // auto re-authn with embargo.
+    auto_reauthn_permission_delegate_->RemoveEmbargoForAutoReauthn(
         GetEmbeddingOrigin());
   }
 
diff --git a/content/browser/webid/federated_auth_request_impl_unittest.cc b/content/browser/webid/federated_auth_request_impl_unittest.cc
index f73a586..5abdee2 100644
--- a/content/browser/webid/federated_auth_request_impl_unittest.cc
+++ b/content/browser/webid/federated_auth_request_impl_unittest.cc
@@ -705,7 +705,7 @@
  public:
   std::set<url::Origin> embargoed_origins_;
 
-  void RecordDisplayAndEmbargo(const url::Origin& origin) override {
+  void RecordEmbargoForAutoReauthn(const url::Origin& origin) override {
     embargoed_origins_.insert(origin);
   }
 };
diff --git a/content/browser/webid/test/mock_auto_reauthn_permission_delegate.h b/content/browser/webid/test/mock_auto_reauthn_permission_delegate.h
index 19f1582..258ea20 100644
--- a/content/browser/webid/test/mock_auto_reauthn_permission_delegate.h
+++ b/content/browser/webid/test/mock_auto_reauthn_permission_delegate.h
@@ -25,7 +25,8 @@
   MOCK_METHOD0(IsAutoReauthnSettingEnabled, bool());
   MOCK_METHOD1(IsAutoReauthnEmbargoed, bool(const url::Origin&));
   MOCK_METHOD1(GetAutoReauthnEmbargoStartTime, base::Time(const url::Origin&));
-  MOCK_METHOD1(RecordDisplayAndEmbargo, void(const url::Origin&));
+  MOCK_METHOD1(RecordEmbargoForAutoReauthn, void(const url::Origin&));
+  MOCK_METHOD1(RemoveEmbargoForAutoReauthn, void(const url::Origin&));
   MOCK_METHOD2(SetRequiresUserMediation, void(const GURL&, bool));
   MOCK_METHOD1(RequiresUserMediation, bool(const GURL&));
 };
diff --git a/content/browser/xr/service/vr_service_impl.cc b/content/browser/xr/service/vr_service_impl.cc
index 393ce94..92973c54 100644
--- a/content/browser/xr/service/vr_service_impl.cc
+++ b/content/browser/xr/service/vr_service_impl.cc
@@ -715,6 +715,11 @@
         send_renderer_information ||
         request.runtime_id == device::mojom::XRDeviceId::CARDBOARD_DEVICE_ID;
 #endif
+#if BUILDFLAG(ENABLE_OPENXR) && BUILDFLAG(IS_ANDROID)
+    send_renderer_information =
+        send_renderer_information ||
+        request.runtime_id == device::mojom::XRDeviceId::OPENXR_DEVICE_ID;
+#endif
     if (send_renderer_information) {
       runtime_options->render_process_id =
           render_frame_host_->GetProcess()->GetID();
diff --git a/content/browser/xr/service/xr_runtime_manager_impl.cc b/content/browser/xr/service/xr_runtime_manager_impl.cc
index 827ef4d1..001b2ad 100644
--- a/content/browser/xr/service/xr_runtime_manager_impl.cc
+++ b/content/browser/xr/service/xr_runtime_manager_impl.cc
@@ -226,6 +226,14 @@
 }
 
 BrowserXRRuntimeImpl* XRRuntimeManagerImpl::GetImmersiveVrRuntime() {
+// OpenXR is the highest priority if it's available.
+#if BUILDFLAG(ENABLE_OPENXR)
+  auto* openxr = GetRuntime(device::mojom::XRDeviceId::OPENXR_DEVICE_ID);
+  if (openxr) {
+    return openxr;
+  }
+#endif
+
 #if BUILDFLAG(IS_ANDROID)
 #if BUILDFLAG(ENABLE_CARDBOARD)
   auto* cardboard = GetRuntime(device::mojom::XRDeviceId::CARDBOARD_DEVICE_ID);
@@ -241,13 +249,6 @@
 #endif
 #endif
 
-#if BUILDFLAG(ENABLE_OPENXR)
-  auto* openxr = GetRuntime(device::mojom::XRDeviceId::OPENXR_DEVICE_ID);
-  if (openxr) {
-    return openxr;
-  }
-#endif
-
   return nullptr;
 }
 
diff --git a/content/public/browser/BUILD.gn b/content/public/browser/BUILD.gn
index 2d60a858..d95a0af 100644
--- a/content/public/browser/BUILD.gn
+++ b/content/public/browser/BUILD.gn
@@ -5,6 +5,7 @@
 import("//build/config/chromeos/ui_mode.gni")
 import("//build/config/ui.gni")
 import("//device/vr/buildflags/buildflags.gni")
+import("//media/media_options.gni")
 import("//ppapi/buildflags/buildflags.gni")
 import("//third_party/protobuf/proto_library.gni")
 import("//third_party/webrtc/webrtc.gni")
@@ -631,10 +632,11 @@
   }
 
   if (is_linux || is_chromeos) {
-    sources += [
-      "stable_video_decoder_factory.h",
-      "zygote_host/zygote_host_linux.h",
-    ]
+    sources += [ "zygote_host/zygote_host_linux.h" ]
+  }
+
+  if (allow_oop_video_decoder) {
+    sources += [ "stable_video_decoder_factory.h" ]
     public_deps += [ "//media/mojo/mojom/stable:stable_video_decoder" ]
   }
 
diff --git a/content/public/browser/federated_identity_auto_reauthn_permission_context_delegate.h b/content/public/browser/federated_identity_auto_reauthn_permission_context_delegate.h
index a445d1f..2364c5ad 100644
--- a/content/public/browser/federated_identity_auto_reauthn_permission_context_delegate.h
+++ b/content/public/browser/federated_identity_auto_reauthn_permission_context_delegate.h
@@ -43,7 +43,12 @@
 
   // Records that an auto re-authn prompt was displayed to the user and places
   // the permission under embargo for the passed-in |relying_party_embedder|.
-  virtual void RecordDisplayAndEmbargo(
+  virtual void RecordEmbargoForAutoReauthn(
+      const url::Origin& relying_party_embedder) = 0;
+
+  // Remove embargo for auto re-authn for the passed-in
+  // |relying_party_embedder|.
+  virtual void RemoveEmbargoForAutoReauthn(
       const url::Origin& relying_party_embedder) = 0;
 
   // Updates the "RequiresUserMediation" bit for the site. It's set to true when
diff --git a/content/shell/browser/shell_federated_permission_context.cc b/content/shell/browser/shell_federated_permission_context.cc
index e2f5e8e..e8a0683 100644
--- a/content/shell/browser/shell_federated_permission_context.cc
+++ b/content/shell/browser/shell_federated_permission_context.cc
@@ -75,7 +75,10 @@
   return base::Time();
 }
 
-void ShellFederatedPermissionContext::RecordDisplayAndEmbargo(
+void ShellFederatedPermissionContext::RecordEmbargoForAutoReauthn(
+    const url::Origin& relying_party_embedder) {}
+
+void ShellFederatedPermissionContext::RemoveEmbargoForAutoReauthn(
     const url::Origin& relying_party_embedder) {}
 
 void ShellFederatedPermissionContext::AddIdpSigninStatusObserver(
diff --git a/content/shell/browser/shell_federated_permission_context.h b/content/shell/browser/shell_federated_permission_context.h
index 3962245..e82dae1b 100644
--- a/content/shell/browser/shell_federated_permission_context.h
+++ b/content/shell/browser/shell_federated_permission_context.h
@@ -46,7 +46,9 @@
       const url::Origin& relying_party_embedder) override;
   base::Time GetAutoReauthnEmbargoStartTime(
       const url::Origin& relying_party_embedder) override;
-  void RecordDisplayAndEmbargo(
+  void RecordEmbargoForAutoReauthn(
+      const url::Origin& relying_party_embedder) override;
+  void RemoveEmbargoForAutoReauthn(
       const url::Origin& relying_party_embedder) override;
   void SetRequiresUserMediation(const GURL& rp_url,
                                 bool requires_user_mediation) override;
@@ -107,7 +109,8 @@
   // A vector of registered IdPs.
   std::vector<GURL> idp_registry_;
 
-  // A set of embargoed origins.
+  // A set of embargoed origins which have a FedCM embargo. An origin is added
+  // to the set when the user dismisses the FedCM UI.
   std::set<url::Origin> embargoed_origins_;
 
   // A set of urls that require user mediation.
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 8a24258..a5fe994 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
@@ -222,6 +222,77 @@
 crbug.com/1345782 [ mac amd-0x679e passthrough angle-opengl ] deqp/functional/gles3/shadertexturefunction/texturesize.html [ Skip ]
 crbug.com/1345782 [ mac amd-0x67ef passthrough angle-opengl ] deqp/functional/gles3/shadertexturefunction/texturesize.html [ Skip ]
 
+# Skipped due to low capacity of Lacros devices
+crbug.com/1446435 [ chromeos lacros-chrome passthrough ] conformance/ogles/* [ Skip ]
+crbug.com/1446435 [ chromeos lacros-chrome passthrough ] conformance/textures/canvas/* [ Skip ]
+crbug.com/1446435 [ chromeos lacros-chrome passthrough ] conformance/textures/canvas_sub_rectangle/* [ Skip ]
+crbug.com/1446435 [ chromeos lacros-chrome passthrough ] conformance/textures/image/* [ Skip ]
+crbug.com/1446435 [ chromeos lacros-chrome passthrough ] conformance/textures/image_data/* [ Skip ]
+crbug.com/1446435 [ chromeos lacros-chrome passthrough ] conformance/textures/svg_image/* [ Skip ]
+crbug.com/1446435 [ chromeos lacros-chrome passthrough ] conformance/textures/video/* [ Skip ]
+crbug.com/1446435 [ chromeos lacros-chrome passthrough ] conformance/textures/webgl_canvas/* [ Skip ]
+crbug.com/1446435 [ chromeos lacros-chrome passthrough ] conformance/textures/image_bitmap_from_image_data/* [ Skip ]
+crbug.com/1446435 [ chromeos lacros-chrome passthrough ] conformance/textures/image_bitmap_from_image/* [ Skip ]
+crbug.com/1446435 [ chromeos lacros-chrome passthrough ] conformance/textures/image_bitmap_from_video/* [ Skip ]
+crbug.com/1446435 [ chromeos lacros-chrome passthrough ] conformance/textures/image_bitmap_from_canvas/* [ Skip ]
+crbug.com/1446435 [ chromeos lacros-chrome passthrough ] conformance/textures/image_bitmap_from_blob/* [ Skip ]
+crbug.com/1446435 [ chromeos lacros-chrome passthrough ] conformance/textures/image_bitmap_from_image_bitmap/* [ Skip ]
+crbug.com/1446435 [ chromeos lacros-chrome passthrough ] conformance/uniforms/* [ Skip ]
+crbug.com/1446435 [ chromeos lacros-chrome passthrough ] conformance/more/* [ Skip ]
+crbug.com/1446435 [ chromeos lacros-chrome passthrough ] deqp/* [ Skip ]
+crbug.com/1446435 [ chromeos lacros-chrome passthrough ] conformance2/textures/canvas/* [ Skip ]
+crbug.com/1446435 [ chromeos lacros-chrome passthrough ] conformance2/textures/canvas_sub_rectangle/* [ Skip ]
+crbug.com/1446435 [ chromeos lacros-chrome passthrough ] conformance2/textures/image/* [ Skip ]
+crbug.com/1446435 [ chromeos lacros-chrome passthrough ] conformance2/textures/image_data/* [ Skip ]
+crbug.com/1446435 [ chromeos lacros-chrome passthrough ] conformance2/textures/svg_image/* [ Skip ]
+crbug.com/1446435 [ chromeos lacros-chrome passthrough ] conformance2/textures/video/* [ Skip ]
+crbug.com/1446435 [ chromeos lacros-chrome passthrough ] conformance2/textures/webgl_canvas/* [ Skip ]
+crbug.com/1446435 [ chromeos lacros-chrome passthrough ] conformance2/textures/image_bitmap_from_image_data/* [ Skip ]
+crbug.com/1446435 [ chromeos lacros-chrome passthrough ] conformance2/textures/image_bitmap_from_image/* [ Skip ]
+crbug.com/1446435 [ chromeos lacros-chrome passthrough ] conformance2/textures/image_bitmap_from_video/* [ Skip ]
+crbug.com/1446435 [ chromeos lacros-chrome passthrough ] conformance2/textures/image_bitmap_from_canvas/* [ Skip ]
+crbug.com/1446435 [ chromeos lacros-chrome passthrough ] conformance2/textures/image_bitmap_from_blob/* [ Skip ]
+crbug.com/1446435 [ chromeos lacros-chrome passthrough ] conformance2/textures/image_bitmap_from_image_bitmap/* [ Skip ]
+
+# Failures on Lacros, skip as failures causes re-login which takes ~1 min
+crbug.com/1446435 [ chromeos lacros-chrome chromeos-board-octopus passthrough ] WebglExtension_OVR_multiview2 [ Skip ]
+crbug.com/1446435 [ chromeos lacros-chrome chromeos-board-octopus passthrough ] WebglExtension_WEBGL_compressed_texture_pvrtc [ Skip ]
+crbug.com/1446435 [ chromeos lacros-chrome chromeos-board-octopus passthrough ] WebglExtension_WEBGL_provoking_vertex [ Skip ]
+crbug.com/1446435 [ chromeos lacros-chrome chromeos-board-octopus passthrough ] WebglExtension_WEBGL_webcodecs_video_frame [ Skip ]
+
+crbug.com/1446435 [ chromeos lacros-chrome chromeos-board-octopus passthrough ] conformance/extensions/webgl-compressed-texture-astc.html [ Skip ]
+
+crbug.com/1446435 [ chromeos lacros-chrome chromeos-board-jacuzzi passthrough ] WebglExtension_EXT_texture_compression_bptc [ Skip ]
+crbug.com/1446435 [ chromeos lacros-chrome chromeos-board-jacuzzi passthrough ] WebglExtension_EXT_texture_compression_rgtc [ Skip ]
+crbug.com/1446435 [ chromeos lacros-chrome chromeos-board-jacuzzi passthrough ] WebglExtension_EXT_texture_norm16 [ Skip ]
+crbug.com/1446435 [ chromeos lacros-chrome chromeos-board-jacuzzi passthrough ] WebglExtension_OES_texture_float_linear [ Skip ]
+crbug.com/1446435 [ chromeos lacros-chrome chromeos-board-jacuzzi passthrough ] WebglExtension_OVR_multiview2 [ Skip ]
+crbug.com/1446435 [ chromeos lacros-chrome chromeos-board-jacuzzi passthrough ] WebglExtension_WEBGL_clip_cull_distance [ Skip ]
+crbug.com/1446435 [ chromeos lacros-chrome chromeos-board-jacuzzi passthrough ] WebglExtension_WEBGL_compressed_texture_pvrtc [ Skip ]
+crbug.com/1446435 [ chromeos lacros-chrome chromeos-board-jacuzzi passthrough ] WebglExtension_WEBGL_compressed_texture_s3tc [ Skip ]
+crbug.com/1446435 [ chromeos lacros-chrome chromeos-board-jacuzzi passthrough ] WebglExtension_WEBGL_compressed_texture_s3tc_srgb [ Skip ]
+crbug.com/1446435 [ chromeos lacros-chrome chromeos-board-jacuzzi passthrough ] WebglExtension_WEBGL_provoking_vertex [ Skip ]
+crbug.com/1446435 [ chromeos lacros-chrome chromeos-board-jacuzzi passthrough ] WebglExtension_WEBGL_webcodecs_video_frame [ Skip ]
+
+crbug.com/1446435 [ chromeos lacros-chrome chromeos-board-jacuzzi passthrough ] conformance/glsl/bugs/constant-precision-qualifier.html [ Skip ]
+crbug.com/1446435 [ chromeos lacros-chrome chromeos-board-jacuzzi passthrough ] conformance/limits/gl-max-texture-dimensions.html [ Skip ]
+crbug.com/1446435 [ chromeos lacros-chrome chromeos-board-jacuzzi passthrough ] conformance/misc/shader-precision-format.html [ Skip ]
+crbug.com/1446435 [ chromeos lacros-chrome chromeos-board-jacuzzi passthrough ] conformance/renderbuffers/stencil-renderbuffer-initialization.html [ Skip ]
+crbug.com/1446435 [ chromeos lacros-chrome chromeos-board-jacuzzi passthrough ] conformance2/buffers/uniform-buffers.html [ Skip ]
+crbug.com/1446435 [ chromeos lacros-chrome chromeos-board-jacuzzi passthrough ] conformance2/glsl3/array-as-return-value.html [ Skip ]
+crbug.com/1446435 [ chromeos lacros-chrome chromeos-board-jacuzzi passthrough ] conformance2/glsl3/matrix-row-major.html [ Skip ]
+crbug.com/1446435 [ chromeos lacros-chrome chromeos-board-jacuzzi passthrough ] conformance2/glsl3/tricky-loop-conditions.html [ Skip ]
+crbug.com/1446435 [ chromeos lacros-chrome chromeos-board-jacuzzi passthrough ] conformance2/glsl3/uninitialized-local-global-variables.html [ Skip ]
+crbug.com/1446435 [ chromeos lacros-chrome chromeos-board-jacuzzi passthrough ] conformance2/misc/expando-loss-2.html [ Skip ]
+crbug.com/1446435 [ chromeos lacros-chrome chromeos-board-jacuzzi passthrough ] conformance2/misc/uninitialized-test-2.html [ Skip ]
+crbug.com/1446435 [ chromeos lacros-chrome chromeos-board-jacuzzi passthrough ] conformance2/programs/active-built-in-attribs.html [ Skip ]
+crbug.com/1446435 [ chromeos lacros-chrome chromeos-board-jacuzzi passthrough ] conformance2/query/query.html [ Skip ]
+crbug.com/1446435 [ chromeos lacros-chrome chromeos-board-jacuzzi passthrough ] conformance2/reading/read-pixels-pack-parameters.html [ Skip ]
+crbug.com/1446435 [ chromeos lacros-chrome chromeos-board-jacuzzi passthrough ] conformance2/rendering/blitframebuffer-multisampled-readbuffer.html [ Skip ]
+crbug.com/1446435 [ chromeos lacros-chrome chromeos-board-jacuzzi passthrough ] conformance2/rendering/blitframebuffer-r11f-g11f-b10f.html [ Skip ]
+crbug.com/1446435 [ chromeos lacros-chrome chromeos-board-jacuzzi passthrough ] conformance2/textures/misc/immutable-tex-render-feedback.html [ Skip ]
+crbug.com/1446435 [ chromeos lacros-chrome chromeos-board-jacuzzi passthrough ] conformance2/transform_feedback/transform_feedback.html [ Skip ]
+
 ###############################
 # Permanent Slow Expectations #
 ###############################
@@ -769,13 +840,6 @@
 # Failures on validating command decoder only; won't fix.
 crbug.com/angleproject/5038 [ chromeos chromeos-board-amd64-generic mesa_ge_21.0 no-passthrough target-cpu-64 ] conformance/extensions/ext-color-buffer-half-float.html [ Failure ]
 
-#####################
-# Lacros failures #
-#####################
-
-crbug.com/1392806 [ chromeos lacros-chrome chromeos-board-jacuzzi passthrough ] conformance2/rendering/blitframebuffer-multisampled-readbuffer.html [ Failure ]
-crbug.com/1392806 [ chromeos lacros-chrome chromeos-board-jacuzzi passthrough ] conformance2/rendering/blitframebuffer-r11f-g11f-b10f.html [ Failure ]
-
 ##############################
 # Lacros-like Linux Failures #
 ##############################
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 3d79141..abb1263 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
@@ -326,6 +326,29 @@
 crbug.com/1417485 [ android android-nexus-5x passthrough ] conformance/glsl/samplers/glsl-function-texture2dprojlod.html [ Failure ]
 crbug.com/1417485 [ android android-nexus-5x passthrough ] conformance/reading/read-pixels-test.html [ Failure ]
 
+# Skipped due to low capacity of Lacros devices
+crbug.com/1446435 [ chromeos lacros-chrome passthrough ] conformance/ogles/* [ Skip ]
+crbug.com/1446435 [ chromeos lacros-chrome passthrough ] conformance/textures/canvas/* [ Skip ]
+crbug.com/1446435 [ chromeos lacros-chrome passthrough ] conformance/textures/canvas_sub_rectangle/* [ Skip ]
+crbug.com/1446435 [ chromeos lacros-chrome passthrough ] conformance/textures/image/* [ Skip ]
+crbug.com/1446435 [ chromeos lacros-chrome passthrough ] conformance/textures/image_data/* [ Skip ]
+crbug.com/1446435 [ chromeos lacros-chrome passthrough ] conformance/textures/svg_image/* [ Skip ]
+crbug.com/1446435 [ chromeos lacros-chrome passthrough ] conformance/textures/video/* [ Skip ]
+crbug.com/1446435 [ chromeos lacros-chrome passthrough ] conformance/textures/webgl_canvas/* [ Skip ]
+crbug.com/1446435 [ chromeos lacros-chrome passthrough ] conformance/textures/image_bitmap_from_image_data/* [ Skip ]
+crbug.com/1446435 [ chromeos lacros-chrome passthrough ] conformance/textures/image_bitmap_from_image/* [ Skip ]
+crbug.com/1446435 [ chromeos lacros-chrome passthrough ] conformance/textures/image_bitmap_from_video/* [ Skip ]
+crbug.com/1446435 [ chromeos lacros-chrome passthrough ] conformance/textures/image_bitmap_from_canvas/* [ Skip ]
+crbug.com/1446435 [ chromeos lacros-chrome passthrough ] conformance/textures/image_bitmap_from_blob/* [ Skip ]
+crbug.com/1446435 [ chromeos lacros-chrome passthrough ] conformance/textures/image_bitmap_from_image_bitmap/* [ Skip ]
+crbug.com/1446435 [ chromeos lacros-chrome passthrough ] conformance/uniforms/* [ Skip ]
+crbug.com/1446435 [ chromeos lacros-chrome passthrough ] conformance/more/* [ Skip ]
+crbug.com/1446435 [ chromeos lacros-chrome passthrough ] deqp/* [ Skip ]
+
+# Failures on Lacros, skip as failures causes re-login which takes ~1 min
+crbug.com/1446435 [ chromeos lacros-chrome chromeos-board-octopus passthrough ] WebglExtension_WEBGL_webcodecs_video_frame [ Skip ]
+crbug.com/1446435 [ chromeos lacros-chrome chromeos-board-jacuzzi passthrough ] WebglExtension_WEBGL_webcodecs_video_frame [ Skip ]
+
 ###############################
 # Permanent Slow Expectations #
 ###############################
diff --git a/device/vr/BUILD.gn b/device/vr/BUILD.gn
index 63d596e..4fca20e 100644
--- a/device/vr/BUILD.gn
+++ b/device/vr/BUILD.gn
@@ -257,7 +257,9 @@
         ":openxr_data",
         "//components/version_info",
         "//device/base",
+        "//gpu/command_buffer/service:gles2",
         "//third_party/openxr",
+        "//ui/gl/init",
       ]
     }
   }
diff --git a/device/vr/openxr/android/openxr_graphics_binding_open_gles.cc b/device/vr/openxr/android/openxr_graphics_binding_open_gles.cc
index 8f2a16d..08f614d 100644
--- a/device/vr/openxr/android/openxr_graphics_binding_open_gles.cc
+++ b/device/vr/openxr/android/openxr_graphics_binding_open_gles.cc
@@ -11,6 +11,11 @@
 #include "gpu/GLES2/gl2extchromium.h"
 #include "third_party/openxr/src/include/openxr/openxr.h"
 #include "ui/gfx/buffer_types.h"
+#include "ui/gl/gl_context.h"
+#include "ui/gl/gl_context_egl.h"
+#include "ui/gl/gl_surface.h"
+#include "ui/gl/gl_utils.h"
+#include "ui/gl/init/gl_factory.h"
 
 namespace device {
 
@@ -23,22 +28,116 @@
 OpenXrGraphicsBindingOpenGLES::OpenXrGraphicsBindingOpenGLES() = default;
 OpenXrGraphicsBindingOpenGLES::~OpenXrGraphicsBindingOpenGLES() = default;
 
-bool OpenXrGraphicsBindingOpenGLES::Initialize() {
+bool OpenXrGraphicsBindingOpenGLES::Initialize(XrInstance instance,
+                                               XrSystemId system) {
+  if (initialized_) {
+    return true;
+  }
+
+  PFN_xrGetOpenGLESGraphicsRequirementsKHR get_graphics_requirements_fn{
+      nullptr};
+  if (XR_FAILED(xrGetInstanceProcAddr(
+          instance, "xrGetOpenGLESGraphicsRequirementsKHR",
+          (PFN_xrVoidFunction*)(&get_graphics_requirements_fn)))) {
+    return false;
+  }
+
+  // TODO(alcooper): Validate/set version based on the output here.
+  XrGraphicsRequirementsOpenGLESKHR graphics_requirements = {
+      XR_TYPE_GRAPHICS_REQUIREMENTS_OPENGL_ES_KHR};
+  if (XR_FAILED(get_graphics_requirements_fn(instance, system,
+                                             &graphics_requirements))) {
+    return false;
+  }
+
+  // None of the other runtimes support ANGLE, so we disable it too for now.
+  // TODO(alcooper): Investigate if we can support ANGLE or if we'll run into
+  // similar problems as cardboard.
+  gl::init::DisableANGLE();
+
+  // Everything below is a hacky first pass at making a session and likely needs
+  // to be re-written with proper context/surfaces/types.
+  gl::GLDisplay* display = nullptr;
+  if (gl::GetGLImplementation() == gl::kGLImplementationNone) {
+    display = gl::init::InitializeGLOneOff(
+        /*gpu_preference=*/gl::GpuPreference::kDefault);
+    if (!display) {
+      DLOG(ERROR) << "gl::init::InitializeGLOneOff failed";
+      return false;
+    }
+  }
+  display = gl::GetDefaultDisplayEGL();
+
+  DCHECK(gl::GetGLImplementation() != gl::kGLImplementationEGLANGLE);
+
+  scoped_refptr<gl::GLSurface> surface;
+  surface = gl::init::CreateOffscreenGLSurfaceWithFormat(display, {0, 0},
+                                                         gl::GLSurfaceFormat());
+
+  DVLOG(3) << "surface=" << surface.get();
+  if (!surface.get()) {
+    DLOG(ERROR) << "gl::init::CreateViewGLSurface failed";
+    return false;
+  }
+
+  gl::GLContextAttribs context_attribs;
+  // OpenXr's shared EGL context needs to be compatible with ours.
+  // Any mismatches result in a EGL_BAD_MATCH error, including different reset
+  // notification behavior according to
+  // https://www.khronos.org/registry/EGL/specs/eglspec.1.5.pdf page 56.
+  // Chromium defaults to lose context on reset when the robustness extension is
+  // present, even if robustness features are not requested specifically.
+  context_attribs.lose_context_on_reset = false;
+
+  scoped_refptr<gl::GLContextEGL> egl_context = new gl::GLContextEGL(nullptr);
+  scoped_refptr<gl::GLContext> context = gl::InitializeGLContext(
+      egl_context.get(), surface.get(), context_attribs);
+  if (!context.get()) {
+    DLOG(ERROR) << "gl::init::CreateGLContext failed";
+    return false;
+  }
+  if (!context->MakeCurrent(surface.get())) {
+    DLOG(ERROR) << "gl::GLContext::MakeCurrent() failed";
+    return false;
+  }
+
+  // Assign the surface and context members now that initialization has
+  // succeeded.
+  surface_ = std::move(surface);
+  context_ = std::move(context);
+  egl_context_ = std::move(egl_context);
+
+  binding_.display = display->GetDisplay();
+  binding_.config = (EGLConfig)0;
+  binding_.context = egl_context_->GetHandle();
+
+  initialized_ = true;
   return true;
 }
 
 const void* OpenXrGraphicsBindingOpenGLES::GetSessionCreateInfo() const {
+  CHECK(initialized_);
   return &binding_;
 }
 
-int64_t OpenXrGraphicsBindingOpenGLES::GetSwapchainFormat() const {
-  return GL_RGBA;
+int64_t OpenXrGraphicsBindingOpenGLES::GetSwapchainFormat(
+    XrSession session) const {
+  uint32_t format_length = 0;
+  RETURN_IF_XR_FAILED(
+      xrEnumerateSwapchainFormats(session, 0, &format_length, nullptr));
+  std::vector<int64_t> swapchain_formats(format_length);
+  RETURN_IF_XR_FAILED(
+      xrEnumerateSwapchainFormats(session, (uint32_t)swapchain_formats.size(),
+                                  &format_length, swapchain_formats.data()));
+  DCHECK(!swapchain_formats.empty());
+  // TODO(alcooper): Care about the swapchain format that we pick.
+  return swapchain_formats[0];
 }
 
 XrResult OpenXrGraphicsBindingOpenGLES::EnumerateSwapchainImages(
     const XrSwapchain& color_swapchain,
     std::vector<SwapChainInfo>& color_swapchain_images) const {
-  CHECK(color_swapchain == XR_NULL_HANDLE);
+  CHECK(color_swapchain != XR_NULL_HANDLE);
   CHECK(color_swapchain_images.empty());
 
   uint32_t chain_length;
diff --git a/device/vr/openxr/android/openxr_graphics_binding_open_gles.h b/device/vr/openxr/android/openxr_graphics_binding_open_gles.h
index 267f081..b089749 100644
--- a/device/vr/openxr/android/openxr_graphics_binding_open_gles.h
+++ b/device/vr/openxr/android/openxr_graphics_binding_open_gles.h
@@ -7,11 +7,18 @@
 
 #include <vector>
 
+#include "base/memory/scoped_refptr.h"
 #include "device/vr/openxr/openxr_graphics_binding.h"
 #include "device/vr/openxr/openxr_platform.h"
 #include "device/vr/vr_export.h"
 #include "third_party/openxr/src/include/openxr/openxr.h"
 
+namespace gl {
+class GLContext;
+class GLSurface;
+class GLContextEGL;
+}  // namespace gl
+
 namespace device {
 
 // OpenGLES version of the OpenXrGraphicsBinding. Used to manage rendering when
@@ -23,16 +30,21 @@
   ~OpenXrGraphicsBindingOpenGLES() override;
 
   // OpenXrGraphicsBinding
-  bool Initialize() override;
+  bool Initialize(XrInstance instance, XrSystemId system) override;
   const void* GetSessionCreateInfo() const override;
-  int64_t GetSwapchainFormat() const override;
+  int64_t GetSwapchainFormat(XrSession session) const override;
   XrResult EnumerateSwapchainImages(
       const XrSwapchain& color_swapchain,
       std::vector<SwapChainInfo>& color_swapchain_images) const override;
 
  private:
+  bool initialized_ = false;
   XrGraphicsBindingOpenGLESAndroidKHR binding_{
       XR_TYPE_GRAPHICS_BINDING_OPENGL_ES_ANDROID_KHR, nullptr};
+
+  scoped_refptr<gl::GLSurface> surface_;
+  scoped_refptr<gl::GLContext> context_;
+  scoped_refptr<gl::GLContextEGL> egl_context_;
 };
 
 }  // namespace device
diff --git a/device/vr/openxr/openxr_api_wrapper.cc b/device/vr/openxr/openxr_api_wrapper.cc
index 3b8b76d..b020e54 100644
--- a/device/vr/openxr/openxr_api_wrapper.cc
+++ b/device/vr/openxr/openxr_api_wrapper.cc
@@ -42,7 +42,6 @@
 
 namespace {
 
-static constexpr XrSystemId kInvalidSystem = -1;
 // The primary view configuration is always enabled and active in OpenXR. We
 // currently only support the stereo view configuration.
 static constexpr XrViewConfigurationType kPrimaryViewConfiguration =
@@ -204,7 +203,7 @@
   session_ = XR_NULL_HANDLE;
   blend_mode_ = XR_ENVIRONMENT_BLEND_MODE_MAX_ENUM;
   stage_bounds_ = {};
-  system_ = kInvalidSystem;
+  system_ = XR_NULL_SYSTEM_ID;
   instance_ = XR_NULL_HANDLE;
   stage_parameters_enabled_ = false;
   enabled_features_.clear();
@@ -289,7 +288,7 @@
 }
 
 bool OpenXrApiWrapper::HasSystem() const {
-  return system_ != kInvalidSystem && primary_view_config_.Initialized();
+  return system_ != XR_NULL_SYSTEM_ID && primary_view_config_.Initialized();
 }
 
 bool OpenXrApiWrapper::HasBlendMode() const {
@@ -486,6 +485,11 @@
   enabled_features_ = enabled_features;
   graphics_binding_ = graphics_binding;
 
+  if (!graphics_binding_->Initialize(instance_, system_)) {
+    DLOG(ERROR) << __func__ << " Failed to initialize graphics binding";
+    return XR_ERROR_INITIALIZATION_FAILED;
+  }
+
   // These are the only features that use stage parameters. If none of them were
   // requested for the session, we can avoid querying this every frame.
   stage_parameters_enabled_ = base::ranges::any_of(
@@ -551,7 +555,8 @@
 
   XrSwapchainCreateInfo swapchain_create_info = {XR_TYPE_SWAPCHAIN_CREATE_INFO};
   swapchain_create_info.arraySize = 1;
-  swapchain_create_info.format = graphics_binding_->GetSwapchainFormat();
+  swapchain_create_info.format =
+      graphics_binding_->GetSwapchainFormat(session_);
 
   swapchain_create_info.width = swapchain_size_.width();
   swapchain_create_info.height = swapchain_size_.height();
diff --git a/device/vr/openxr/openxr_device.cc b/device/vr/openxr/openxr_device.cc
index d217ce7..275c7b4 100644
--- a/device/vr/openxr/openxr_device.cc
+++ b/device/vr/openxr/openxr_device.cc
@@ -139,8 +139,10 @@
     mojom::XRRuntime::RequestSessionCallback callback) {
   DCHECK(!request_session_callback_);
 
-  // TODO(alcooper): Pass the appropriate info from options here.
-  if (XR_FAILED(platform_helper_->CreateInstance(&instance_))) {
+  OpenXrCreateInfo create_info;
+  create_info.render_process_id = options->render_process_id;
+  create_info.render_frame_id = options->render_frame_id;
+  if (XR_FAILED(platform_helper_->CreateInstance(&instance_, create_info))) {
     DVLOG(1) << __func__ << " Failed to create an XrInstance";
     std::move(callback).Run(nullptr);
     return;
diff --git a/device/vr/openxr/openxr_graphics_binding.h b/device/vr/openxr/openxr_graphics_binding.h
index 2711cd3..6f267242 100644
--- a/device/vr/openxr/openxr_graphics_binding.h
+++ b/device/vr/openxr/openxr_graphics_binding.h
@@ -26,14 +26,14 @@
   virtual ~OpenXrGraphicsBinding() = default;
 
   // Ensures that the GraphicsBinding is ready for use.
-  virtual bool Initialize() = 0;
+  virtual bool Initialize(XrInstance instance, XrSystemId system) = 0;
 
   // Gets a pointer to a platform-specific XrGraphicsBindingFoo. The pointer is
   // guaranteed to live as long as this class does.
   virtual const void* GetSessionCreateInfo() const = 0;
 
   // Gets the format that we expect from the platform swapchain.
-  virtual int64_t GetSwapchainFormat() const = 0;
+  virtual int64_t GetSwapchainFormat(XrSession session) const = 0;
 
   // Calls xrEnumerateSwapChain and stores all relevant data in the passed in
   // array of `SwapChainInfo`s.
diff --git a/device/vr/openxr/openxr_platform_helper.cc b/device/vr/openxr/openxr_platform_helper.cc
index d4698ce..3724a8b 100644
--- a/device/vr/openxr/openxr_platform_helper.cc
+++ b/device/vr/openxr/openxr_platform_helper.cc
@@ -138,6 +138,8 @@
 
   if (create_info.has_value()) {
     instance_create_info.next = GetPlatformCreateInfo(create_info.value());
+  } else if (BUILDFLAG(IS_ANDROID)) {
+    LOG(ERROR) << "Android was missing CreateInfo";
   }
 
   XrResult result = xrCreateInstance(&instance_create_info, instance);
diff --git a/device/vr/openxr/openxr_render_loop.cc b/device/vr/openxr/openxr_render_loop.cc
index 0f6c1b2d..f7c0d8a9 100644
--- a/device/vr/openxr/openxr_render_loop.cc
+++ b/device/vr/openxr/openxr_render_loop.cc
@@ -55,6 +55,7 @@
 }
 
 mojom::XRFrameDataPtr OpenXrRenderLoop::GetNextFrameData() {
+  DVLOG(3) << __func__;
   mojom::XRFrameDataPtr frame_data = mojom::XRFrameData::New();
   frame_data->frame_id = next_frame_id_;
 
@@ -139,8 +140,8 @@
   graphics_binding_ = platform_helper_->GetGraphicsBinding();
 #endif
 
-  if (!graphics_binding_ || !graphics_binding_->Initialize()) {
-    DVLOG(1) << "Could not create or initialize graphics binding";
+  if (!graphics_binding_) {
+    DVLOG(1) << "Could not create graphics binding";
     // We aren't actually presenting yet; so ExitPresent won't clean us up.
     // Still call it to log the failure reason; but also explicitly call
     // StopRuntime, which should be resilient to duplicate calls.
@@ -314,8 +315,10 @@
 void OpenXrRenderLoop::SubmitFrame(int16_t frame_index,
                                    const gpu::MailboxHolder& mailbox,
                                    base::TimeDelta time_waited) {
+  DVLOG(3) << __func__ << " frame_index=" << frame_index;
   CHECK(!IsUsingSharedImages());
-  // TODO(alcooper): Finalize actual fallback path
+  DCHECK(BUILDFLAG(IS_ANDROID));
+  // TODO(alcooper): Finalize actual rendering path
   SubmitFrameMissing(frame_index, mailbox.sync_token);
 }
 
@@ -323,6 +326,7 @@
     int16_t frame_index,
     const gpu::SyncToken& sync_token,
     base::TimeDelta time_waited) {
+  DVLOG(3) << __func__ << " frame_index=" << frame_index;
   gpu::gles2::GLES2Interface* gl = context_provider_->ContextGL();
   gl->WaitSyncTokenCHROMIUM(sync_token.GetConstData());
   const GLuint id = gl->CreateGpuFenceCHROMIUM();
diff --git a/device/vr/openxr/test/fake_openxr_impl_api.cc b/device/vr/openxr/test/fake_openxr_impl_api.cc
index c080f46..e623f7d 100644
--- a/device/vr/openxr/test/fake_openxr_impl_api.cc
+++ b/device/vr/openxr/test/fake_openxr_impl_api.cc
@@ -554,6 +554,34 @@
   return XR_SUCCESS;
 }
 
+XrResult xrEnumerateSwapchainFormats(XrSession session,
+                                     uint32_t format_capacity_input,
+                                     uint32_t* format_count_output,
+                                     int64_t* formats) {
+  DVLOG(2) << __FUNCTION__;
+  RETURN_IF_XR_FAILED(g_test_helper.ValidateSession(session));
+  RETURN_IF(format_capacity_input != 1 && format_capacity_input != 0,
+            XR_ERROR_SIZE_INSUFFICIENT,
+            "xrEnumerateSwapchainFormats does not equal length returned by "
+            "previous call");
+  RETURN_IF(format_count_output == nullptr, XR_ERROR_VALIDATION_FAILURE,
+            "format_count_output is nullptr");
+  *format_count_output = 1;
+  if (format_capacity_input == 0) {
+    return XR_SUCCESS;
+  }
+
+  RETURN_IF(format_capacity_input < *format_count_output,
+            XR_ERROR_SIZE_INSUFFICIENT,
+            "format_capacity_input is less than required size");
+  RETURN_IF(formats == nullptr, XR_ERROR_VALIDATION_FAILURE,
+            "Formats Array is nullptr");
+  // This is what is hardcoded in `OpenXrGraphicsBindingD3D11`.
+  formats[0] = DXGI_FORMAT_R8G8B8A8_UNORM_SRGB;
+
+  return XR_SUCCESS;
+}
+
 XrResult xrEnumerateSwapchainImages(XrSwapchain swapchain,
                                     uint32_t image_capacity_input,
                                     uint32_t* image_count_output,
@@ -1131,6 +1159,9 @@
   } else if (strcmp(name, "xrEnumerateInstanceExtensionProperties") == 0) {
     *function = reinterpret_cast<PFN_xrVoidFunction>(
         xrEnumerateInstanceExtensionProperties);
+  } else if (strcmp(name, "xrEnumerateSwapchainFormats") == 0) {
+    *function =
+        reinterpret_cast<PFN_xrVoidFunction>(xrEnumerateSwapchainFormats);
   } else if (strcmp(name, "xrEnumerateSwapchainImages") == 0) {
     *function =
         reinterpret_cast<PFN_xrVoidFunction>(xrEnumerateSwapchainImages);
diff --git a/device/vr/openxr/windows/openxr_graphics_binding_d3d11.cc b/device/vr/openxr/windows/openxr_graphics_binding_d3d11.cc
index d18e75e..4945d56 100644
--- a/device/vr/openxr/windows/openxr_graphics_binding_d3d11.cc
+++ b/device/vr/openxr/windows/openxr_graphics_binding_d3d11.cc
@@ -8,6 +8,7 @@
 #include <wrl.h>
 
 #include "base/check.h"
+#include "base/containers/contains.h"
 #include "base/logging.h"
 #include "device/vr/openxr/openxr_api_wrapper.h"
 #include "device/vr/openxr/openxr_platform.h"
@@ -32,7 +33,8 @@
 
 OpenXrGraphicsBindingD3D11::~OpenXrGraphicsBindingD3D11() = default;
 
-bool OpenXrGraphicsBindingD3D11::Initialize() {
+bool OpenXrGraphicsBindingD3D11::Initialize(XrInstance instance,
+                                            XrSystemId system) {
   if (initialized_) {
     return true;
   }
@@ -48,7 +50,7 @@
   }
 
   LUID luid;
-  if (!weak_platform_helper_->TryGetLuid(&luid)) {
+  if (!weak_platform_helper_->TryGetLuid(&luid, system)) {
     DVLOG(1) << __func__ << " Did not get a luid";
     return false;
   }
@@ -70,7 +72,8 @@
   return &binding_;
 }
 
-int64_t OpenXrGraphicsBindingD3D11::GetSwapchainFormat() const {
+int64_t OpenXrGraphicsBindingD3D11::GetSwapchainFormat(
+    XrSession session) const {
   // OpenXR's swapchain format expects to describe the texture content.
   // The result of a swapchain image created from OpenXR API always contains a
   // typeless texture. On the other hand, WebGL API uses CSS color convention
diff --git a/device/vr/openxr/windows/openxr_graphics_binding_d3d11.h b/device/vr/openxr/windows/openxr_graphics_binding_d3d11.h
index b62c48f..091a5dc0 100644
--- a/device/vr/openxr/windows/openxr_graphics_binding_d3d11.h
+++ b/device/vr/openxr/windows/openxr_graphics_binding_d3d11.h
@@ -28,9 +28,9 @@
   ~OpenXrGraphicsBindingD3D11() override;
 
   // OpenXrGraphicsBinding
-  bool Initialize() override;
+  bool Initialize(XrInstance instance, XrSystemId system) override;
   const void* GetSessionCreateInfo() const override;
-  int64_t GetSwapchainFormat() const override;
+  int64_t GetSwapchainFormat(XrSession session) const override;
   XrResult EnumerateSwapchainImages(
       const XrSwapchain& color_swapchain,
       std::vector<SwapChainInfo>& color_swapchain_images) const override;
diff --git a/device/vr/openxr/windows/openxr_platform_helper_windows.cc b/device/vr/openxr/windows/openxr_platform_helper_windows.cc
index 7d8e516..f7093bdd 100644
--- a/device/vr/openxr/windows/openxr_platform_helper_windows.cc
+++ b/device/vr/openxr/windows/openxr_platform_helper_windows.cc
@@ -110,15 +110,15 @@
 // Returns the LUID of the adapter the OpenXR runtime is on. Returns false and
 // sets luid to {0, 0} if the LUID could not be determined. Also returns false
 // if the value of the retrieved LUID is {0, 0}.
-bool OpenXrPlatformHelperWindows::TryGetLuid(LUID* luid) {
+bool OpenXrPlatformHelperWindows::TryGetLuid(LUID* luid, XrSystemId system) {
   CHECK(luid);
   XrInstance instance = GetOrCreateXrInstance();
   if (instance == XR_NULL_HANDLE) {
     return false;
   }
 
-  XrSystemId system;
-  if (XR_FAILED(OpenXrApiWrapper::GetSystem(instance, &system))) {
+  if (system == XR_NULL_SYSTEM_ID &&
+      XR_FAILED(OpenXrApiWrapper::GetSystem(instance, &system))) {
     return false;
   }
 
diff --git a/device/vr/openxr/windows/openxr_platform_helper_windows.h b/device/vr/openxr/windows/openxr_platform_helper_windows.h
index 91b3945..fe56656 100644
--- a/device/vr/openxr/windows/openxr_platform_helper_windows.h
+++ b/device/vr/openxr/windows/openxr_platform_helper_windows.h
@@ -46,7 +46,7 @@
 
   // Called by the D3D11 GraphicsBinding to set up the texture helper and also
   // used when creating the XRDeviceData.
-  bool TryGetLuid(LUID* luid);
+  bool TryGetLuid(LUID* luid, XrSystemId system = XR_NULL_SYSTEM_ID);
 
  private:
   XrInstance GetOrCreateXrInstance();
diff --git a/device/vr/public/cpp/features.cc b/device/vr/public/cpp/features.cc
index ff78af4..d1230c3 100644
--- a/device/vr/public/cpp/features.cc
+++ b/device/vr/public/cpp/features.cc
@@ -52,7 +52,10 @@
 
 #if BUILDFLAG(ENABLE_OPENXR)
 // Controls WebXR support for the OpenXR Runtime.
-BASE_FEATURE(kOpenXR, "OpenXR", base::FEATURE_ENABLED_BY_DEFAULT);
+BASE_FEATURE(kOpenXR,
+             "OpenXR",
+             BUILDFLAG(IS_WIN) ? base::FEATURE_ENABLED_BY_DEFAULT
+                               : base::FEATURE_DISABLED_BY_DEFAULT);
 
 // Some WebXR features may have been enabled for ARCore, but are not yet ready
 // to be plumbed up from the OpenXR backend. This feature provides a mechanism
diff --git a/device/vr/windows/compositor_base.cc b/device/vr/windows/compositor_base.cc
index f59cb91..90e0d54e 100644
--- a/device/vr/windows/compositor_base.cc
+++ b/device/vr/windows/compositor_base.cc
@@ -101,18 +101,27 @@
 
 void XRCompositorCommon::SubmitFrameMissing(int16_t frame_index,
                                             const gpu::SyncToken& sync_token) {
+  DVLOG(3) << __func__ << " frame_index=" << frame_index;
   TRACE_EVENT_INSTANT0("xr", "SubmitFrameMissing", TRACE_EVENT_SCOPE_THREAD);
   if (pending_frame_) {
     // WebXR for this frame is hidden.
     pending_frame_->waiting_for_webxr_ = false;
   }
   webxr_has_pose_ = false;
+#if (BUILDFLAG(IS_ANDROID))
+  // We haven't finished the rendering path on Android yet so are just calling
+  // this to ensure that the page stays un-stuck while we aren't actually
+  // rendering anything.
+  // TODO(alcooper): Clean this up.
+  pending_frame_->webxr_submitted_ = true;
+#endif
   MaybeCompositeAndSubmit();
 }
 
 void XRCompositorCommon::SubmitFrame(int16_t frame_index,
                                      const gpu::MailboxHolder& mailbox,
                                      base::TimeDelta time_waited) {
+  DVLOG(3) << __func__ << " frame_index=" << frame_index;
   NOTREACHED();
 }
 
@@ -120,6 +129,7 @@
     int16_t frame_index,
     const gpu::SyncToken& sync_token,
     base::TimeDelta time_waited) {
+  DVLOG(3) << __func__ << " frame_index=" << frame_index;
   NOTREACHED();
 }
 
@@ -128,6 +138,7 @@
     int16_t frame_index,
     mojo::PlatformHandle texture_handle,
     const gpu::SyncToken& sync_token) {
+  DVLOG(3) << __func__ << " frame_index=" << frame_index;
   TRACE_EVENT1("xr", "SubmitFrameWithTextureHandle", "frameIndex", frame_index);
   webxr_has_pose_ = false;
   // Tell the browser that WebXR has submitted a frame.
@@ -162,6 +173,7 @@
 #endif
 
 void XRCompositorCommon::CleanUp() {
+  DVLOG(1) << __func__;
   submit_client_.reset();
   webxr_has_pose_ = false;
   presentation_receiver_.reset();
@@ -310,6 +322,7 @@
 }
 
 void XRCompositorCommon::ExitPresent(ExitXrPresentReason reason) {
+  DVLOG(1) << __func__ << " reason=" << base::to_underlying(reason);
   TRACE_EVENT_INSTANT1("xr", "ExitPresent", TRACE_EVENT_SCOPE_THREAD, "reason",
                        base::to_underlying(reason));
   if (!is_presenting_)
@@ -377,6 +390,7 @@
 void XRCompositorCommon::Init() {}
 
 void XRCompositorCommon::StartPendingFrame() {
+  DVLOG(3) << __func__ << " pending_frame_=" << pending_frame_.has_value();
   if (!pending_frame_) {
     pending_frame_.emplace();
     pending_frame_->waiting_for_webxr_ = webxr_visible_;
@@ -397,6 +411,15 @@
     return;
   }
 
+  // HasSessionEnded() may do some work that alters the state of
+  // `is_presenting_`, in which case we'll get the ExitPresent log to know that
+  // we've ignored this request; but otherwise, we should log the rest of the
+  // state after that.
+  DVLOG(3) << __func__ << " is_presenting_=" << is_presenting_
+           << " webxr_visible=" << webxr_visible_
+           << " on_webxr_submitted_=" << !!on_webxr_submitted_
+           << " webxr_has_pose_=" << webxr_has_pose_;
+
   if (!is_presenting_) {
     return;
   }
@@ -467,6 +490,7 @@
 void XRCompositorCommon::SendFrameData(
     XRFrameDataProvider::GetFrameDataCallback callback,
     mojom::XRFrameDataPtr frame_data) {
+  DVLOG(3) << __func__;
   TRACE_EVENT0("xr", "SendFrameData");
 
   // This method represents a call from the renderer process. If our visibility
@@ -526,6 +550,7 @@
 
 void XRCompositorCommon::RequestNextOverlayPose(
     RequestNextOverlayPoseCallback callback) {
+  DVLOG(3) << __func__;
   // We will only request poses while the overlay is visible.
   DCHECK(overlay_visible_);
   TRACE_EVENT_INSTANT0("xr", "RequestOverlayPose", TRACE_EVENT_SCOPE_THREAD);
@@ -538,6 +563,8 @@
 
 void XRCompositorCommon::SetOverlayAndWebXRVisibility(bool overlay_visible,
                                                       bool webxr_visible) {
+  DVLOG(1) << __func__ << " overlay_visible=" << overlay_visible
+           << " webxr_visible=" << webxr_visible;
   TRACE_EVENT_INSTANT2("xr", "SetOverlayAndWebXRVisibility",
                        TRACE_EVENT_SCOPE_THREAD, "overlay", overlay_visible,
                        "webxr", webxr_visible);
diff --git a/docs/updater/dev_manual.md b/docs/updater/dev_manual.md
index 1cb8356..cf28f067 100644
--- a/docs/updater/dev_manual.md
+++ b/docs/updater/dev_manual.md
@@ -94,6 +94,10 @@
   ```
     tools/mb/mb run -v --swarmed --internal --no-default-dimensions -d pool chrome.tests -d os Windows-10 out/WinDefault updater_tests
   ```
+* If `mb` command failed with error `isolate: original error: interactive login is required`, you need to login:
+  ```
+   tools/luci-go/isolate login
+  ```
 * If your test introduces dependency on a new app on macOS, you need to let
  `mb` tool know so it can correctly figure out the dependency. Example:
   https://crrev.com/c/3470143.
diff --git a/docs/webui_build_configuration.md b/docs/webui_build_configuration.md
index dcb823a..8d0d2fcf 100644
--- a/docs/webui_build_configuration.md
+++ b/docs/webui_build_configuration.md
@@ -518,6 +518,14 @@
 mojo_files_deps: List of Mojo targets that generate |mojo_files|. Must be
                  defined if |mojo_files| is defined.
 
+mojo_base_path: Specifies the directory under which Mojo files will be served at
+                runtime. Optional parameter. Defaults to the top level folder
+                '.', which results in Mojo files being served from
+                'chrome://<webui_name>/foo.mojom-webui.js'.
+                Example: Passing 'mojom-webui' would result in Mojo files being
+                served from
+                'chrome://<webui_name>/mojom-webui/foo.mojom-webui.js'.
+
 TypeScript (ts_library()) related params:
 ts_composite: See |composite| in ts_library(). Defaults to false, optional.
 ts_out_dir: See |out_dir| in ts_library(). Optional parameter, defaults
diff --git a/extensions/browser/api/web_request/web_request_permissions_unittest.cc b/extensions/browser/api/web_request/web_request_permissions_unittest.cc
index bf9846a..49fa9e2 100644
--- a/extensions/browser/api/web_request/web_request_permissions_unittest.cc
+++ b/extensions/browser/api/web_request/web_request_permissions_unittest.cc
@@ -205,10 +205,8 @@
   // If the origin is labeled by the WebStoreAppId, it becomes protected.
   {
     const int kWebstoreProcessId = 42;
-    const content::SiteInstanceId kSiteInstanceId(23);
     ProcessMap::Get(browser_context())
-        ->Insert(extensions::kWebStoreAppId, kWebstoreProcessId,
-                 kSiteInstanceId);
+        ->Insert(extensions::kWebStoreAppId, kWebstoreProcessId);
     WebRequestInfo sensitive_request_info(create_request_params(
         non_sensitive_url, WebRequestResourceType::SCRIPT, kWebstoreProcessId));
     EXPECT_TRUE(WebRequestPermissions::HideRequest(permission_helper,
diff --git a/extensions/browser/process_map.cc b/extensions/browser/process_map.cc
index c9cae10..b5dc01f3 100644
--- a/extensions/browser/process_map.cc
+++ b/extensions/browser/process_map.cc
@@ -48,12 +48,8 @@
 
 // Item
 struct ProcessMap::Item {
-  Item(const std::string& extension_id,
-       int process_id,
-       content::SiteInstanceId site_instance_id)
-      : extension_id(extension_id),
-        process_id(process_id),
-        site_instance_id(site_instance_id) {}
+  Item(const std::string& extension_id, int process_id)
+      : extension_id(extension_id), process_id(process_id) {}
 
   Item(const Item&) = delete;
   Item& operator=(const Item&) = delete;
@@ -64,14 +60,12 @@
   Item& operator=(ProcessMap::Item&&) = default;
 
   bool operator<(const ProcessMap::Item& other) const {
-    return std::tie(extension_id, process_id, site_instance_id) <
-           std::tie(other.extension_id, other.process_id,
-                    other.site_instance_id);
+    return std::tie(extension_id, process_id) <
+           std::tie(other.extension_id, other.process_id);
   }
 
   std::string extension_id;
   int process_id = 0;
-  content::SiteInstanceId site_instance_id;
 };
 
 
@@ -85,16 +79,8 @@
   return ProcessMapFactory::GetForBrowserContext(browser_context);
 }
 
-bool ProcessMap::Insert(const std::string& extension_id,
-                        int process_id,
-                        content::SiteInstanceId site_instance_id) {
-  return items_.insert(Item(extension_id, process_id, site_instance_id)).second;
-}
-
-bool ProcessMap::Remove(const std::string& extension_id,
-                        int process_id,
-                        content::SiteInstanceId site_instance_id) {
-  return items_.erase(Item(extension_id, process_id, site_instance_id)) > 0;
+bool ProcessMap::Insert(const std::string& extension_id, int process_id) {
+  return items_.insert(Item(extension_id, process_id)).second;
 }
 
 int ProcessMap::RemoveAllFromProcess(int process_id) {
diff --git a/extensions/browser/process_map.h b/extensions/browser/process_map.h
index a554353d..c3511382 100644
--- a/extensions/browser/process_map.h
+++ b/extensions/browser/process_map.h
@@ -90,13 +90,8 @@
 
   size_t size() const { return items_.size(); }
 
-  bool Insert(const std::string& extension_id,
-              int process_id,
-              content::SiteInstanceId site_instance_id);
+  bool Insert(const std::string& extension_id, int process_id);
 
-  bool Remove(const std::string& extension_id,
-              int process_id,
-              content::SiteInstanceId site_instance_id);
   int RemoveAllFromProcess(int process_id);
 
   bool Contains(const std::string& extension_id, int process_id) const;
diff --git a/extensions/browser/process_map_unittest.cc b/extensions/browser/process_map_unittest.cc
index b2a6870..bdeeaa63 100644
--- a/extensions/browser/process_map_unittest.cc
+++ b/extensions/browser/process_map_unittest.cc
@@ -62,55 +62,56 @@
 
   // Test behavior when empty.
   EXPECT_FALSE(map.Contains("a", 1));
-  EXPECT_FALSE(map.Remove("a", 1, content::SiteInstanceId(1)));
+  EXPECT_FALSE(map.RemoveAllFromProcess(1));
   EXPECT_EQ(0u, map.size());
 
   // Test insertion and behavior with one item.
-  EXPECT_TRUE(map.Insert("a", 1, content::SiteInstanceId(1)));
+  EXPECT_TRUE(map.Insert("a", 1));
   EXPECT_TRUE(map.Contains("a", 1));
   EXPECT_FALSE(map.Contains("a", 2));
   EXPECT_FALSE(map.Contains("b", 1));
   EXPECT_EQ(1u, map.size());
 
   // Test inserting a duplicate item.
-  EXPECT_FALSE(map.Insert("a", 1, content::SiteInstanceId(1)));
+  EXPECT_FALSE(map.Insert("a", 1));
   EXPECT_TRUE(map.Contains("a", 1));
   EXPECT_EQ(1u, map.size());
 
   // Insert some more items.
-  EXPECT_TRUE(map.Insert("a", 2, content::SiteInstanceId(2)));
-  EXPECT_TRUE(map.Insert("b", 1, content::SiteInstanceId(3)));
-  EXPECT_TRUE(map.Insert("b", 2, content::SiteInstanceId(4)));
+  EXPECT_TRUE(map.Insert("a", 2));
+  EXPECT_TRUE(map.Insert("b", 3));
+  EXPECT_TRUE(map.Insert("b", 4));
   EXPECT_EQ(4u, map.size());
 
   EXPECT_TRUE(map.Contains("a", 1));
   EXPECT_TRUE(map.Contains("a", 2));
-  EXPECT_TRUE(map.Contains("b", 1));
-  EXPECT_TRUE(map.Contains("b", 2));
+  EXPECT_TRUE(map.Contains("b", 3));
+  EXPECT_TRUE(map.Contains("b", 4));
+
   EXPECT_FALSE(map.Contains("a", 3));
+  EXPECT_FALSE(map.Contains("b", 2));
+  EXPECT_FALSE(map.Contains("a", 5));
+  EXPECT_FALSE(map.Contains("c", 3));
 
-  // Note that this only differs from an existing item because of the site
-  // instance id.
-  EXPECT_TRUE(map.Insert("a", 1, content::SiteInstanceId(5)));
-  EXPECT_TRUE(map.Contains("a", 1));
-
-  // Test removal.
-  EXPECT_TRUE(map.Remove("a", 1, content::SiteInstanceId(1)));
-  EXPECT_FALSE(map.Remove("a", 1, content::SiteInstanceId(1)));
-  EXPECT_EQ(4u, map.size());
-
-  // Should still return true because there were two site instances for this
-  // extension/process pair.
-  EXPECT_TRUE(map.Contains("a", 1));
-
-  EXPECT_TRUE(map.Remove("a", 1, content::SiteInstanceId(5)));
+  // At this point we have {a,1}, {a,2}, {b,3}, and {b,4} in the map. Test
+  // removal of these processes.
+  EXPECT_EQ(1, map.RemoveAllFromProcess(1));
   EXPECT_EQ(3u, map.size());
   EXPECT_FALSE(map.Contains("a", 1));
+  EXPECT_TRUE(map.Contains("a", 2));
 
-  EXPECT_EQ(2, map.RemoveAllFromProcess(2));
-  EXPECT_EQ(1u, map.size());
+  EXPECT_EQ(1, map.RemoveAllFromProcess(2));
+  EXPECT_EQ(2u, map.size());
   EXPECT_EQ(0, map.RemoveAllFromProcess(2));
+  EXPECT_EQ(2u, map.size());
+  EXPECT_EQ(1, map.RemoveAllFromProcess(3));
   EXPECT_EQ(1u, map.size());
+  EXPECT_EQ(0, map.RemoveAllFromProcess(3));
+  EXPECT_EQ(1u, map.size());
+  EXPECT_EQ(1, map.RemoveAllFromProcess(4));
+  EXPECT_EQ(0u, map.size());
+  EXPECT_EQ(0, map.RemoveAllFromProcess(4));
+  EXPECT_EQ(0u, map.size());
 }
 
 TEST(ExtensionProcessMapTest, GetMostLikelyContextType) {
@@ -135,13 +136,13 @@
       extensions::Feature::CONTENT_SCRIPT_CONTEXT,
       map.GetMostLikelyContextType(extension.get(), 2, &untrusted_webui_url));
 
-  map.Insert("b", 3, content::SiteInstanceId(1));
+  map.Insert("b", 3);
   extension =
       CreateExtensionWithFlags(extensions::TypeToCreate::kExtension, "b");
   EXPECT_EQ(extensions::Feature::BLESSED_EXTENSION_CONTEXT,
             map.GetMostLikelyContextType(extension.get(), 3, &extension_url));
 
-  map.Insert("c", 4, content::SiteInstanceId(2));
+  map.Insert("c", 4);
   extension =
       CreateExtensionWithFlags(extensions::TypeToCreate::kPlatformApp, "c");
   EXPECT_EQ(extensions::Feature::BLESSED_EXTENSION_CONTEXT,
@@ -149,30 +150,30 @@
 
   map.set_is_lock_screen_context(true);
 
-  map.Insert("d", 5, content::SiteInstanceId(3));
+  map.Insert("d", 5);
   extension =
       CreateExtensionWithFlags(extensions::TypeToCreate::kPlatformApp, "d");
   EXPECT_EQ(extensions::Feature::LOCK_SCREEN_EXTENSION_CONTEXT,
             map.GetMostLikelyContextType(extension.get(), 5, &extension_url));
 
-  map.Insert("e", 6, content::SiteInstanceId(4));
+  map.Insert("e", 6);
   extension =
       CreateExtensionWithFlags(extensions::TypeToCreate::kExtension, "e");
   EXPECT_EQ(extensions::Feature::LOCK_SCREEN_EXTENSION_CONTEXT,
             map.GetMostLikelyContextType(extension.get(), 6, &extension_url));
 
-  map.Insert("f", 7, content::SiteInstanceId(5));
+  map.Insert("f", 7);
   extension =
       CreateExtensionWithFlags(extensions::TypeToCreate::kHostedApp, "f");
   EXPECT_EQ(extensions::Feature::BLESSED_WEB_PAGE_CONTEXT,
             map.GetMostLikelyContextType(extension.get(), 7, &web_url));
 
-  map.Insert("g", 8, content::SiteInstanceId(6));
+  map.Insert("g", 8);
   EXPECT_EQ(extensions::Feature::WEBUI_UNTRUSTED_CONTEXT,
             map.GetMostLikelyContextType(/*extension=*/nullptr, 8,
                                          &untrusted_webui_url));
 
-  map.Insert("h", 9, content::SiteInstanceId(7));
+  map.Insert("h", 9);
   EXPECT_EQ(extensions::Feature::WEB_PAGE_CONTEXT,
             map.GetMostLikelyContextType(/*extension=*/nullptr, 9, &web_url));
 }
diff --git a/extensions/browser/script_result_queue.cc b/extensions/browser/script_result_queue.cc
index 6270ffc..4bed5b70 100644
--- a/extensions/browser/script_result_queue.cc
+++ b/extensions/browser/script_result_queue.cc
@@ -16,7 +16,7 @@
 ScriptResultQueue::~ScriptResultQueue() = default;
 
 void ScriptResultQueue::OnScriptResult(const base::Value& script_result) {
-  results_.push_back(script_result.Clone());
+  results_.Append(script_result.Clone());
   if (quit_closure_)
     std::move(quit_closure_).Run();
 }
diff --git a/extensions/browser/script_result_queue.h b/extensions/browser/script_result_queue.h
index 70e2f8e..7c97577a 100644
--- a/extensions/browser/script_result_queue.h
+++ b/extensions/browser/script_result_queue.h
@@ -5,8 +5,6 @@
 #ifndef EXTENSIONS_BROWSER_SCRIPT_RESULT_QUEUE_H_
 #define EXTENSIONS_BROWSER_SCRIPT_RESULT_QUEUE_H_
 
-#include <vector>
-
 #include "base/functional/callback.h"
 #include "base/scoped_observation.h"
 #include "base/values.h"
@@ -29,14 +27,12 @@
   // Returns the next result, optionally waiting for it to come in.
   base::Value GetNextResult();
 
-  const std::vector<base::Value>& results() const { return results_; }
-
  private:
   // The index of the next result to return.
   size_t next_result_index_ = 0u;
 
   // The collection of all script results this queue has seen.
-  std::vector<base::Value> results_;
+  base::Value::List results_;
 
   // Quit closure to call when waiting for a result.
   base::OnceClosure quit_closure_;
diff --git a/extensions/shell/browser/shell_content_browser_client.cc b/extensions/shell/browser/shell_content_browser_client.cc
index 3e71111..25e946d 100644
--- a/extensions/shell/browser/shell_content_browser_client.cc
+++ b/extensions/shell/browser/shell_content_browser_client.cc
@@ -163,26 +163,7 @@
     return;
 
   ProcessMap::Get(browser_main_parts_->browser_context())
-      ->Insert(extension->id(),
-               site_instance->GetProcess()->GetID(),
-               site_instance->GetId());
-}
-
-void ShellContentBrowserClient::SiteInstanceDeleting(
-    content::SiteInstance* site_instance) {
-  // Don't do anything if we're shutting down.
-  if (content::BrowserMainRunner::ExitedMainMessageLoop())
-    return;
-
-  // If this isn't an extension renderer there's nothing to do.
-  const Extension* extension = GetExtension(site_instance);
-  if (!extension)
-    return;
-
-  ProcessMap::Get(browser_main_parts_->browser_context())
-      ->Remove(extension->id(),
-               site_instance->GetProcess()->GetID(),
-               site_instance->GetId());
+      ->Insert(extension->id(), site_instance->GetProcess()->GetID());
 }
 
 void ShellContentBrowserClient::AppendExtraCommandLineSwitches(
diff --git a/extensions/shell/browser/shell_content_browser_client.h b/extensions/shell/browser/shell_content_browser_client.h
index 79830e4..2777d0f6 100644
--- a/extensions/shell/browser/shell_content_browser_client.h
+++ b/extensions/shell/browser/shell_content_browser_client.h
@@ -66,7 +66,6 @@
                                const GURL& site_url) override;
   bool IsHandledURL(const GURL& url) override;
   void SiteInstanceGotProcess(content::SiteInstance* site_instance) override;
-  void SiteInstanceDeleting(content::SiteInstance* site_instance) override;
   void AppendExtraCommandLineSwitches(base::CommandLine* command_line,
                                       int child_process_id) override;
   content::SpeechRecognitionManagerDelegate*
diff --git a/ios/chrome/browser/translate/translate_constants.h b/ios/chrome/browser/translate/translate_constants.h
index 626b2afc..60e0172 100644
--- a/ios/chrome/browser/translate/translate_constants.h
+++ b/ios/chrome/browser/translate/translate_constants.h
@@ -22,6 +22,5 @@
 // UMA histogram names.
 // Note: These string constants are repeated in TranslateCompactInfoBar.java.
 extern const char kEventHistogram[];
-extern const char kTranslationCountHistogram[];
 
 #endif  // IOS_CHROME_BROWSER_TRANSLATE_TRANSLATE_CONSTANTS_H_
diff --git a/ios/chrome/browser/translate/translate_constants.mm b/ios/chrome/browser/translate/translate_constants.mm
index cb437df..da1789e9 100644
--- a/ios/chrome/browser/translate/translate_constants.mm
+++ b/ios/chrome/browser/translate/translate_constants.mm
@@ -9,5 +9,3 @@
 #endif
 
 const char kEventHistogram[] = "Translate.CompactInfobar.Event";
-const char kTranslationCountHistogram[] =
-    "Translate.CompactInfobar.TranslationsPerPage";
diff --git a/ios/chrome/browser/ui/content_suggestions/BUILD.gn b/ios/chrome/browser/ui/content_suggestions/BUILD.gn
index f9a2616..75e8c8a9 100644
--- a/ios/chrome/browser/ui/content_suggestions/BUILD.gn
+++ b/ios/chrome/browser/ui/content_suggestions/BUILD.gn
@@ -198,6 +198,7 @@
     "//ios/chrome/browser/ui/content_suggestions/cells",
     "//ios/chrome/browser/ui/content_suggestions/cells:constants",
     "//ios/chrome/browser/ui/content_suggestions/set_up_list",
+    "//ios/chrome/browser/ui/content_suggestions/set_up_list:utils",
     "//ios/chrome/browser/ui/ntp:constants",
     "//ios/chrome/browser/ui/start_surface:feature_flags",
     "//ios/chrome/browser/ui/toolbar/public",
diff --git a/ios/chrome/browser/ui/content_suggestions/cells/BUILD.gn b/ios/chrome/browser/ui/content_suggestions/cells/BUILD.gn
index 7df65f1..d26ca9a9 100644
--- a/ios/chrome/browser/ui/content_suggestions/cells/BUILD.gn
+++ b/ios/chrome/browser/ui/content_suggestions/cells/BUILD.gn
@@ -28,6 +28,8 @@
     "content_suggestions_tile_view.mm",
     "magic_stack_module_container.h",
     "magic_stack_module_container.mm",
+    "multi_row_module.h",
+    "multi_row_module.mm",
     "query_suggestion_view.h",
     "query_suggestion_view.mm",
     "suggested_content.h",
diff --git a/ios/chrome/browser/ui/content_suggestions/cells/magic_stack_module_container.mm b/ios/chrome/browser/ui/content_suggestions/cells/magic_stack_module_container.mm
index 11d05a72..f20b145 100644
--- a/ios/chrome/browser/ui/content_suggestions/cells/magic_stack_module_container.mm
+++ b/ios/chrome/browser/ui/content_suggestions/cells/magic_stack_module_container.mm
@@ -66,6 +66,7 @@
     case ContentSuggestionsModuleType::kSetUpListSync:
     case ContentSuggestionsModuleType::kSetUpListDefaultBrowser:
     case ContentSuggestionsModuleType::kSetUpListAutofill:
+    case ContentSuggestionsModuleType::kCompactedSetUpList:
       return l10n_util::GetNSString(IDS_IOS_SET_UP_LIST_TITLE);
     default:
       NOTREACHED();
diff --git a/ios/chrome/browser/ui/content_suggestions/cells/multi_row_module.h b/ios/chrome/browser/ui/content_suggestions/cells/multi_row_module.h
new file mode 100644
index 0000000..38c3681
--- /dev/null
+++ b/ios/chrome/browser/ui/content_suggestions/cells/multi_row_module.h
@@ -0,0 +1,22 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef IOS_CHROME_BROWSER_UI_CONTENT_SUGGESTIONS_CELLS_MULTI_ROW_MODULE_H_
+#define IOS_CHROME_BROWSER_UI_CONTENT_SUGGESTIONS_CELLS_MULTI_ROW_MODULE_H_
+
+#import "ios/chrome/browser/ui/content_suggestions/cells/magic_stack_module_container.h"
+
+// Module implementation intended to display multiple rows of content elements.
+@interface MultiRowModule : MagicStackModuleContainer
+
+// Initializes and configures this view to contain each element in `views` in a
+// row and configure the module for `type`. Note that there is a height limit
+// defined in MagicStackModuleContainer. It is the job of the implementor to
+// ensure `views` are laid out with that in mind.
+- (instancetype)initWithViews:(NSArray<UIView*>*)views
+                         type:(ContentSuggestionsModuleType)type;
+
+@end
+
+#endif  // IOS_CHROME_BROWSER_UI_CONTENT_SUGGESTIONS_CELLS_MULTI_ROW_MODULE_H_
diff --git a/ios/chrome/browser/ui/content_suggestions/cells/multi_row_module.mm b/ios/chrome/browser/ui/content_suggestions/cells/multi_row_module.mm
new file mode 100644
index 0000000..b15d2ee1
--- /dev/null
+++ b/ios/chrome/browser/ui/content_suggestions/cells/multi_row_module.mm
@@ -0,0 +1,114 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import "ios/chrome/browser/ui/content_suggestions/cells/multi_row_module.h"
+
+#import "ios/chrome/common/ui/colors/semantic_color_names.h"
+#import "ios/chrome/common/ui/util/constraints_ui_util.h"
+#import "ios/chrome/common/ui/util/ui_util.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+namespace {
+
+// Vertical spacing between the title and the content.
+const CGFloat kContentVerticalSpacing = 12.0f;
+
+// The horizontal inset for the content within this container.
+const CGFloat kContentHorizontalInset = 16.0f;
+
+// The top inset for the content within this container.
+const CGFloat kContentTopInset = 14.0f;
+
+// The bottom inset for the content within this container.
+const CGFloat kContentBottomInset = 10.0f;
+
+const CGFloat kSeparatorHeight = 0.5;
+
+}  // namespace
+
+@implementation MultiRowModule {
+  NSLayoutConstraint* _contentViewWidthAnchor;
+}
+
+- (instancetype)initWithViews:(NSArray<UIView*>*)views
+                         type:(ContentSuggestionsModuleType)type {
+  self = [super initWithType:type];
+  if (self) {
+    UILabel* title = [[UILabel alloc] init];
+    title.text = [MagicStackModuleContainer titleStringForModule:type];
+    title.font = [UIFont preferredFontForTextStyle:UIFontTextStyleFootnote];
+    title.textColor = [UIColor colorNamed:kTextPrimaryColor];
+    title.accessibilityTraits |= UIAccessibilityTraitHeader;
+    title.accessibilityIdentifier =
+        [MagicStackModuleContainer titleStringForModule:type];
+
+    UIStackView* rowsStackView = [[UIStackView alloc] init];
+    rowsStackView.spacing = AlignValueToPixel(8.5);
+    rowsStackView.axis = UILayoutConstraintAxisVertical;
+    rowsStackView.translatesAutoresizingMaskIntoConstraints = NO;
+    rowsStackView.alignment = UIStackViewAlignmentLeading;
+    rowsStackView.distribution = UIStackViewDistributionFill;
+    NSUInteger index = 0;
+    for (UIView* view in views) {
+      [rowsStackView addArrangedSubview:view];
+      if (index < [views count] - 1) {
+        UIView* separator = [[UIView alloc] init];
+        separator.backgroundColor = [UIColor colorNamed:kSeparatorColor];
+        [rowsStackView addArrangedSubview:separator];
+        [NSLayoutConstraint activateConstraints:@[
+          [separator.heightAnchor
+              constraintEqualToConstant:AlignValueToPixel(kSeparatorHeight)],
+          [separator.widthAnchor
+              constraintEqualToConstant:[self contentViewWidth]],
+        ]];
+      }
+      index++;
+    }
+
+    _contentViewWidthAnchor = [rowsStackView.widthAnchor
+        constraintEqualToConstant:[self contentViewWidth]];
+    [NSLayoutConstraint activateConstraints:@[ _contentViewWidthAnchor ]];
+
+    UIStackView* containerStackView = [[UIStackView alloc] init];
+    containerStackView.spacing = kContentVerticalSpacing;
+    containerStackView.axis = UILayoutConstraintAxisVertical;
+    containerStackView.translatesAutoresizingMaskIntoConstraints = NO;
+    containerStackView.alignment = UIStackViewAlignmentLeading;
+    containerStackView.distribution = UIStackViewDistributionFill;
+    [containerStackView addArrangedSubview:title];
+    [containerStackView addArrangedSubview:rowsStackView];
+
+    [self addSubview:containerStackView];
+    NSDirectionalEdgeInsets contentInsets = NSDirectionalEdgeInsetsMake(
+        kContentTopInset, kContentHorizontalInset, kContentBottomInset, 0);
+    AddSameConstraintsWithInsets(containerStackView, self, contentInsets);
+  }
+  return self;
+}
+
+#pragma mark - UITraitEnvironment
+
+- (void)traitCollectionDidChange:(UITraitCollection*)previousTraitCollection {
+  [super traitCollectionDidChange:previousTraitCollection];
+  if (previousTraitCollection.horizontalSizeClass !=
+      self.traitCollection.horizontalSizeClass) {
+    _contentViewWidthAnchor.constant = [self contentViewWidth];
+  }
+}
+
+#pragma mark - Helpers
+
+// Returns the expected width of the contentView subview.
+- (CGFloat)contentViewWidth {
+  // Give content the same width as the StackView, which is inset from this
+  // container view.
+  return [MagicStackModuleContainer
+             moduleWidthForHorizontalTraitCollection:self.traitCollection] -
+         kContentHorizontalInset;
+}
+
+@end
diff --git a/ios/chrome/browser/ui/content_suggestions/content_suggestions_constants.h b/ios/chrome/browser/ui/content_suggestions/content_suggestions_constants.h
index f9ab736..ea60bb9 100644
--- a/ios/chrome/browser/ui/content_suggestions/content_suggestions_constants.h
+++ b/ios/chrome/browser/ui/content_suggestions/content_suggestions_constants.h
@@ -17,6 +17,7 @@
   kSetUpListSync,
   kSetUpListDefaultBrowser,
   kSetUpListAutofill,
+  kCompactedSetUpList,
 };
 
 // Represents the content suggestions collection view.
diff --git a/ios/chrome/browser/ui/content_suggestions/content_suggestions_mediator.mm b/ios/chrome/browser/ui/content_suggestions/content_suggestions_mediator.mm
index e352b2c..981a00f 100644
--- a/ios/chrome/browser/ui/content_suggestions/content_suggestions_mediator.mm
+++ b/ios/chrome/browser/ui/content_suggestions/content_suggestions_mediator.mm
@@ -779,9 +779,14 @@
 - (NSArray<NSNumber*>*)magicStackOrder {
   NSMutableArray* magicStackModules = [NSMutableArray array];
   if ([self shouldShowSetUpList]) {
-    for (SetUpListItem* model in self.setUpList.items) {
+    if (set_up_list_utils::ShouldShowCompactedSetUpListModule()) {
       [magicStackModules
-          addObject:@(int(SetUpListModuleTypeForSetUpListType(model.type)))];
+          addObject:@(int(ContentSuggestionsModuleType::kCompactedSetUpList))];
+    } else {
+      for (SetUpListItem* model in self.setUpList.items) {
+        [magicStackModules
+            addObject:@(int(SetUpListModuleTypeForSetUpListType(model.type)))];
+      }
     }
   }
   if (ShouldPutMostVisitedSitesInMagicStack()) {
@@ -821,6 +826,13 @@
                                            complete:model.complete];
     [items addObject:item];
   }
+  // For the compacted Set Up List Module in the Magic Stack, there will only be
+  // two items shown.
+  if (IsMagicStackEnabled() &&
+      set_up_list_utils::ShouldShowCompactedSetUpListModule() &&
+      [items count] > 2) {
+    return [items subarrayWithRange:NSMakeRange(0, 2)];
+  }
   return items;
 }
 
diff --git a/ios/chrome/browser/ui/content_suggestions/content_suggestions_view_controller.mm b/ios/chrome/browser/ui/content_suggestions/content_suggestions_view_controller.mm
index b44d6eb..dde5e5df 100644
--- a/ios/chrome/browser/ui/content_suggestions/content_suggestions_view_controller.mm
+++ b/ios/chrome/browser/ui/content_suggestions/content_suggestions_view_controller.mm
@@ -24,6 +24,7 @@
 #import "ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_shortcut_tile_view.h"
 #import "ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_tile_layout_util.h"
 #import "ios/chrome/browser/ui/content_suggestions/cells/magic_stack_module_container.h"
+#import "ios/chrome/browser/ui/content_suggestions/cells/multi_row_module.h"
 #import "ios/chrome/browser/ui/content_suggestions/cells/query_suggestion_view.h"
 #import "ios/chrome/browser/ui/content_suggestions/content_suggestions_collection_utils.h"
 #import "ios/chrome/browser/ui/content_suggestions/content_suggestions_commands.h"
@@ -36,6 +37,7 @@
 #import "ios/chrome/browser/ui/content_suggestions/set_up_list/set_up_list_item_view.h"
 #import "ios/chrome/browser/ui/content_suggestions/set_up_list/set_up_list_item_view_data.h"
 #import "ios/chrome/browser/ui/content_suggestions/set_up_list/set_up_list_view.h"
+#import "ios/chrome/browser/ui/content_suggestions/set_up_list/utils.h"
 #import "ios/chrome/browser/ui/ntp/new_tab_page_header_constants.h"
 #import "ios/chrome/browser/ui/start_surface/start_surface_features.h"
 #import "ios/chrome/browser/ui/toolbar/public/toolbar_utils.h"
@@ -128,6 +130,7 @@
   SetUpListItemView* _setUpListSyncItemView;
   SetUpListItemView* _setUpListDefaultBrowserItemView;
   SetUpListItemView* _setUpListAutofillItemView;
+  NSMutableArray<SetUpListItemView*>* _compactedSetUpListViews;
 }
 
 - (instancetype)init {
@@ -424,32 +427,53 @@
     index++;
   }
   if (IsMagicStackEnabled()) {
+    BOOL shouldShowCompactedSetUpListModule =
+        set_up_list_utils::ShouldShowCompactedSetUpListModule();
+    if (shouldShowCompactedSetUpListModule) {
+      _compactedSetUpListViews = [NSMutableArray array];
+    }
     for (SetUpListItemViewData* data in items) {
+      data.compactLayout = shouldShowCompactedSetUpListModule;
       SetUpListItemView* view = [[SetUpListItemView alloc] initWithData:data];
       view.tapDelegate = self;
       ContentSuggestionsModuleType type =
           SetUpListModuleTypeForSetUpListType(data.type);
-      switch (type) {
-        case ContentSuggestionsModuleType::kSetUpListSync:
-          _setUpListSyncItemView = view;
-          break;
-        case ContentSuggestionsModuleType::kSetUpListDefaultBrowser:
-          _setUpListDefaultBrowserItemView = view;
-          break;
-        case ContentSuggestionsModuleType::kSetUpListAutofill:
-          _setUpListAutofillItemView = view;
-          break;
-        default:
-          break;
+      if (shouldShowCompactedSetUpListModule) {
+        [_compactedSetUpListViews addObject:view];
+      } else {
+        switch (type) {
+          case ContentSuggestionsModuleType::kSetUpListSync:
+            _setUpListSyncItemView = view;
+            break;
+          case ContentSuggestionsModuleType::kSetUpListDefaultBrowser:
+            _setUpListDefaultBrowserItemView = view;
+            break;
+          case ContentSuggestionsModuleType::kSetUpListAutofill:
+            _setUpListAutofillItemView = view;
+            break;
+          default:
+            break;
+        }
       }
       // Only add it to the Magic Stack here if it is after the inital
       // construction of the Magic Stack.
       if (_magicStack) {
-        ActionListModule* setUpListModule =
-            [[ActionListModule alloc] initWithContentView:view type:type];
-        [_magicStack
-            insertArrangedSubview:setUpListModule
-                          atIndex:[self indexForMagicStackModule:type]];
+        if (shouldShowCompactedSetUpListModule) {
+          MultiRowModule* setUpListCompactedModule = [[MultiRowModule alloc]
+              initWithViews:_compactedSetUpListViews
+                       type:ContentSuggestionsModuleType::kCompactedSetUpList];
+          [_magicStack
+              insertArrangedSubview:setUpListCompactedModule
+                            atIndex:[self indexForMagicStackModule:
+                                              ContentSuggestionsModuleType::
+                                                  kCompactedSetUpList]];
+        } else {
+          ActionListModule* setUpListModule =
+              [[ActionListModule alloc] initWithContentView:view type:type];
+          [_magicStack
+              insertArrangedSubview:setUpListModule
+                            atIndex:[self indexForMagicStackModule:type]];
+        }
       }
     }
 
@@ -831,6 +855,13 @@
         [_magicStack addArrangedSubview:setUpListAutofillModule];
         break;
       }
+      case ContentSuggestionsModuleType::kCompactedSetUpList: {
+        MultiRowModule* setUpListCompactedModule = [[MultiRowModule alloc]
+            initWithViews:_compactedSetUpListViews
+                     type:ContentSuggestionsModuleType::kCompactedSetUpList];
+        [_magicStack addArrangedSubview:setUpListCompactedModule];
+        break;
+      }
       default:
         break;
     }
diff --git a/ios/chrome/browser/ui/content_suggestions/set_up_list/set_up_list_item_icon.h b/ios/chrome/browser/ui/content_suggestions/set_up_list/set_up_list_item_icon.h
index 6c3879b8..b130f4e 100644
--- a/ios/chrome/browser/ui/content_suggestions/set_up_list/set_up_list_item_icon.h
+++ b/ios/chrome/browser/ui/content_suggestions/set_up_list/set_up_list_item_icon.h
@@ -15,9 +15,11 @@
 // A view which contains an icon for a Set Up List item.
 @interface SetUpListItemIcon : UIView
 
-// Instantiates a `SetUpListItemItemIcon` for the given `type` and with the
-// given `complete` state.
-- (instancetype)initWithType:(SetUpListItemType)type complete:(BOOL)complete;
+// Instantiates a `SetUpListItemItemIcon` for the given `type` with the
+// given `complete` state and whether to configure with a `compactLayout`.
+- (instancetype)initWithType:(SetUpListItemType)type
+                    complete:(BOOL)complete
+               compactLayout:(BOOL)compactLayout;
 
 // Plays the "sparkle" animation with the given `duration`, after the given
 // `delay`.
diff --git a/ios/chrome/browser/ui/content_suggestions/set_up_list/set_up_list_item_icon.mm b/ios/chrome/browser/ui/content_suggestions/set_up_list/set_up_list_item_icon.mm
index 9266f23..5ed2735d 100644
--- a/ios/chrome/browser/ui/content_suggestions/set_up_list/set_up_list_item_icon.mm
+++ b/ios/chrome/browser/ui/content_suggestions/set_up_list/set_up_list_item_icon.mm
@@ -19,6 +19,7 @@
 
 // Constants related to icon sizing.
 constexpr CGFloat kIconSize = 36;
+constexpr CGFloat kCompactIconSize = 26;
 constexpr CGFloat kSymbolPointSize = 18;
 constexpr CGFloat kSparkleSize = 72;
 constexpr CGFloat kSparkleOffset = (kSparkleSize - kIconSize) / 2;
@@ -32,7 +33,9 @@
 
 // Returns a UIImageView for the given SF Symbol, and with a color named
 // `colorName`.
-UIImageView* IconForSymbol(NSString* symbol, NSString* colorName = nil) {
+UIImageView* IconForSymbol(NSString* symbol,
+                           BOOL compact_layout,
+                           NSString* colorName = nil) {
   UIImageSymbolConfiguration* config = [UIImageSymbolConfiguration
       configurationWithWeight:UIImageSymbolWeightLight];
   UIImage* image = DefaultSymbolWithConfiguration(symbol, config);
@@ -41,26 +44,28 @@
     icon.tintColor = [UIColor colorNamed:colorName];
   }
   icon.translatesAutoresizingMaskIntoConstraints = NO;
+  CGFloat icon_width = compact_layout ? kCompactIconSize : kIconSize;
   [NSLayoutConstraint activateConstraints:@[
-    [icon.widthAnchor constraintEqualToConstant:kIconSize],
+    [icon.widthAnchor constraintEqualToConstant:icon_width],
     [icon.heightAnchor constraintEqualToAnchor:icon.widthAnchor],
   ]];
   return icon;
 }
 
-UIImageView* DefaultBrowserIcon() {
+UIImageView* DefaultBrowserIcon(BOOL compact_layout) {
 #if BUILDFLAG(IOS_USE_BRANDED_SYMBOLS)
   UIImage* image = MakeSymbolMulticolor(
       CustomSymbolWithPointSize(kChromeSymbol, kSymbolPointSize));
   UIImageView* icon = [[UIImageView alloc] initWithImage:image];
   icon.translatesAutoresizingMaskIntoConstraints = NO;
+  CGFloat icon_width = compact_layout ? kCompactIconSize : kIconSize;
   [NSLayoutConstraint activateConstraints:@[
-    [icon.widthAnchor constraintEqualToConstant:kIconSize],
+    [icon.widthAnchor constraintEqualToConstant:icon_width],
     [icon.heightAnchor constraintEqualToAnchor:icon.widthAnchor],
   ]];
   return icon;
 #else
-  return IconForSymbol(kDefaultBrowserSymbol);
+  return IconForSymbol(kDefaultBrowserSymbol, compact_layout);
 #endif
 }
 
@@ -68,10 +73,19 @@
 // circle. The circle's color will be the color named `circleColorName`.
 // Note: this was necessary because there was no symbol exactly matching
 // what was needed for the Autofill icon.
-UIImageView* IconInCircle(NSString* symbol, NSString* circle_color_name) {
+UIImageView* IconInCircle(NSString* symbol,
+                          BOOL compact_layout,
+                          NSString* circle_color_name) {
   UIImageView* circle_view =
-      IconForSymbol(kCircleFillSymbol, circle_color_name);
-  UIImage* symbol_image = DefaultSymbolWithPointSize(symbol, kSymbolPointSize);
+      IconForSymbol(kCircleFillSymbol, compact_layout, circle_color_name);
+  UIImageConfiguration* compactImageConfiguration = [UIImageSymbolConfiguration
+      configurationWithPointSize:kSymbolPointSize
+                          weight:UIImageSymbolWeightLight
+                           scale:UIImageSymbolScaleSmall];
+  UIImage* symbol_image =
+      compact_layout
+          ? DefaultSymbolWithConfiguration(symbol, compactImageConfiguration)
+          : DefaultSymbolWithPointSize(symbol, kSymbolPointSize);
   CHECK(symbol_image);
 
   UIImageView* symbol_view = [[UIImageView alloc] initWithImage:symbol_image];
@@ -90,13 +104,18 @@
   UIImageView* _typeIcon;
   UIImageView* _checkmark;
   UIImageView* _sparkle;
+  // YES if this view should configure itself with a compacted layout.
+  BOOL _compactLayout;
 }
 
-- (instancetype)initWithType:(SetUpListItemType)type complete:(BOOL)complete {
+- (instancetype)initWithType:(SetUpListItemType)type
+                    complete:(BOOL)complete
+               compactLayout:(BOOL)compactLayout {
   self = [super init];
   if (self) {
     _type = type;
     _complete = complete;
+    _compactLayout = compactLayout;
   }
   return self;
 }
@@ -154,7 +173,8 @@
   }
 
   _typeIcon = [self createTypeIcon];
-  _checkmark = IconForSymbol(kCheckmarkCircleFillSymbol, kBlue500Color);
+  _checkmark =
+      IconForSymbol(kCheckmarkCircleFillSymbol, _compactLayout, kBlue500Color);
   _sparkle = [self createSparkle];
   [self addSubview:_typeIcon];
   [self addSubview:_checkmark];
@@ -173,11 +193,12 @@
 - (UIImageView*)createTypeIcon {
   switch (_type) {
     case SetUpListItemType::kSignInSync:
-      return IconForSymbol(kSyncCircleSymbol, kGreen500Color);
+      return IconForSymbol(kSyncCircleSymbol, _compactLayout, kGreen500Color);
     case SetUpListItemType::kDefaultBrowser:
-      return DefaultBrowserIcon();
+      return DefaultBrowserIcon(_compactLayout);
     case SetUpListItemType::kAutofill:
-      return IconInCircle(kEllipsisRectangleSymbol, kBlue600Color);
+      return IconInCircle(kEllipsisRectangleSymbol, _compactLayout,
+                          kBlue600Color);
     case SetUpListItemType::kFollow:
       // TODO(crbug.com/1428070): Add a Follow item to the Set Up List.
       NOTREACHED();
diff --git a/ios/chrome/browser/ui/content_suggestions/set_up_list/set_up_list_item_view.mm b/ios/chrome/browser/ui/content_suggestions/set_up_list/set_up_list_item_view.mm
index 3a4de47..ead8cd0 100644
--- a/ios/chrome/browser/ui/content_suggestions/set_up_list/set_up_list_item_view.mm
+++ b/ios/chrome/browser/ui/content_suggestions/set_up_list/set_up_list_item_view.mm
@@ -30,6 +30,7 @@
 
 // The spacing between the title and description labels.
 constexpr CGFloat kTextSpacing = 5;
+constexpr CGFloat kCompactTextSpacing = 0;
 
 // The duration of the icon / label crossfade animation.
 constexpr base::TimeDelta kAnimationDuration = base::Seconds(0.5);
@@ -63,6 +64,7 @@
   CrossfadeLabel* _description;
   UIStackView* _contentStack;
   UITapGestureRecognizer* _tapGestureRecognizer;
+  BOOL _compactLayout;
 }
 
 - (instancetype)initWithData:(SetUpListItemViewData*)data {
@@ -70,6 +72,7 @@
   if (self) {
     _type = data.type;
     _complete = data.complete;
+    _compactLayout = data.compactLayout;
   }
   return self;
 }
@@ -146,7 +149,9 @@
     self.accessibilityTraits += UIAccessibilityTraitNotEnabled;
   }
 
-  _icon = [[SetUpListItemIcon alloc] initWithType:_type complete:_complete];
+  _icon = [[SetUpListItemIcon alloc] initWithType:_type
+                                         complete:_complete
+                                    compactLayout:_compactLayout];
   _title = [self createTitle];
   _description = [self createDescription];
 
@@ -155,7 +160,7 @@
       [[UIStackView alloc] initWithArrangedSubviews:@[ _title, _description ]];
   textStack.axis = UILayoutConstraintAxisVertical;
   textStack.translatesAutoresizingMaskIntoConstraints = NO;
-  textStack.spacing = kTextSpacing;
+  textStack.spacing = _compactLayout ? kCompactTextSpacing : kTextSpacing;
 
   // Add a horizontal stack to contain the icon(s) and the text stack.
   _contentStack =
@@ -178,7 +183,10 @@
   CrossfadeLabel* label = [[CrossfadeLabel alloc] init];
   label.text = [self titleText];
   label.translatesAutoresizingMaskIntoConstraints = NO;
-  label.font = [UIFont preferredFontForTextStyle:UIFontTextStyleSubheadline];
+  label.font =
+      _compactLayout
+          ? [UIFont preferredFontForTextStyle:UIFontTextStyleFootnote]
+          : [UIFont preferredFontForTextStyle:UIFontTextStyleSubheadline];
   if (_complete) {
     label.textColor = [UIColor colorNamed:kTextQuaternaryColor];
     label.attributedText = Strikethrough(label.text);
@@ -196,7 +204,9 @@
   label.numberOfLines = 0;
   label.lineBreakMode = NSLineBreakByWordWrapping;
   label.translatesAutoresizingMaskIntoConstraints = NO;
-  label.font = [UIFont preferredFontForTextStyle:UIFontTextStyleFootnote];
+  label.font = _compactLayout
+                   ? [UIFont preferredFontForTextStyle:UIFontTextStyleCaption2]
+                   : [UIFont preferredFontForTextStyle:UIFontTextStyleFootnote];
   if (_complete) {
     label.textColor = [UIColor colorNamed:kTextQuaternaryColor];
     label.attributedText = Strikethrough(label.text);
@@ -226,13 +236,23 @@
 - (NSString*)descriptionText {
   switch (_type) {
     case SetUpListItemType::kSignInSync:
-      return l10n_util::GetNSString(
-          IDS_IOS_SET_UP_LIST_SIGN_IN_SYNC_DESCRIPTION);
+      return _compactLayout
+                 ? l10n_util::GetNSString(
+                       IDS_IOS_SET_UP_LIST_SIGN_IN_SYNC_SHORT_DESCRIPTION)
+                 : l10n_util::GetNSString(
+                       IDS_IOS_SET_UP_LIST_SIGN_IN_SYNC_DESCRIPTION);
     case SetUpListItemType::kDefaultBrowser:
-      return l10n_util::GetNSString(
-          IDS_IOS_SET_UP_LIST_DEFAULT_BROWSER_DESCRIPTION);
+      return _compactLayout
+                 ? l10n_util::GetNSString(
+                       IDS_IOS_SET_UP_LIST_DEFAULT_BROWSER_SHORT_DESCRIPTION)
+                 : l10n_util::GetNSString(
+                       IDS_IOS_SET_UP_LIST_DEFAULT_BROWSER_DESCRIPTION);
     case SetUpListItemType::kAutofill:
-      return l10n_util::GetNSString(IDS_IOS_SET_UP_LIST_AUTOFILL_DESCRIPTION);
+      return _compactLayout
+                 ? l10n_util::GetNSString(
+                       IDS_IOS_SET_UP_LIST_AUTOFILL_SHORT_DESCRIPTION)
+                 : l10n_util::GetNSString(
+                       IDS_IOS_SET_UP_LIST_AUTOFILL_DESCRIPTION);
     case SetUpListItemType::kFollow:
       // TODO(crbug.com/1428070): Add a Follow item to the Set Up List.
       NOTREACHED();
diff --git a/ios/chrome/browser/ui/content_suggestions/set_up_list/set_up_list_item_view_data.h b/ios/chrome/browser/ui/content_suggestions/set_up_list/set_up_list_item_view_data.h
index 3440e009..da0b528 100644
--- a/ios/chrome/browser/ui/content_suggestions/set_up_list/set_up_list_item_view_data.h
+++ b/ios/chrome/browser/ui/content_suggestions/set_up_list/set_up_list_item_view_data.h
@@ -21,6 +21,9 @@
 // Indicates whether this item is complete.
 @property(nonatomic, readonly) BOOL complete;
 
+// YES if this view should configure itself with a compacted layout.
+@property(nonatomic, assign) BOOL compactLayout;
+
 @end
 
 #endif  // IOS_CHROME_BROWSER_UI_CONTENT_SUGGESTIONS_SET_UP_LIST_SET_UP_LIST_ITEM_VIEW_DATA_H_
diff --git a/ios/chrome/browser/ui/content_suggestions/set_up_list/utils.h b/ios/chrome/browser/ui/content_suggestions/set_up_list/utils.h
index 8a09177e72..6ab61c85 100644
--- a/ios/chrome/browser/ui/content_suggestions/set_up_list/utils.h
+++ b/ios/chrome/browser/ui/content_suggestions/set_up_list/utils.h
@@ -14,6 +14,10 @@
 // a local state pref.
 bool IsSetUpListActive(PrefService* local_state);
 
+// true if the Set Up List should be shown in a compacted layout in the Magic
+// Stack.
+bool ShouldShowCompactedSetUpListModule();
+
 }  // namespace set_up_list_utils
 
 #endif  // IOS_CHROME_BROWSER_UI_CONTENT_SUGGESTIONS_SET_UP_LIST_UTILS_H_
diff --git a/ios/chrome/browser/ui/content_suggestions/set_up_list/utils.mm b/ios/chrome/browser/ui/content_suggestions/set_up_list/utils.mm
index 7fb3a316..83074af 100644
--- a/ios/chrome/browser/ui/content_suggestions/set_up_list/utils.mm
+++ b/ios/chrome/browser/ui/content_suggestions/set_up_list/utils.mm
@@ -33,4 +33,13 @@
   return true;
 }
 
+bool ShouldShowCompactedSetUpListModule() {
+  absl::optional<base::Time> firstRunTime = GetFirstRunTime();
+  base::Time expiry_time = firstRunTime.value() + base::Days(3);
+  if (base::Time::Now() > expiry_time) {
+    return true;
+  }
+  return false;
+}
+
 }  // namespace set_up_list_utils
diff --git a/media/audio/BUILD.gn b/media/audio/BUILD.gn
index 52aa99a..a828562c 100644
--- a/media/audio/BUILD.gn
+++ b/media/audio/BUILD.gn
@@ -248,6 +248,8 @@
     sources += [
       "android/aaudio_output.cc",
       "android/aaudio_output.h",
+      "android/aaudio_stream_wrapper.cc",
+      "android/aaudio_stream_wrapper.h",
       "android/audio_manager_android.cc",
       "android/audio_manager_android.h",
       "android/audio_track_output_stream.cc",
diff --git a/media/audio/android/aaudio_output.cc b/media/audio/android/aaudio_output.cc
index 269aa135..6266a6c 100644
--- a/media/audio/android/aaudio_output.cc
+++ b/media/audio/android/aaudio_output.cc
@@ -17,198 +17,48 @@
 
 namespace media {
 
-// Used to circumvent issues where the AAudio thread callbacks continue
-// after AAudioStream_requestStop() completes. See crbug.com/1183255.
-class LOCKABLE AAudioDestructionHelper {
- public:
-  explicit AAudioDestructionHelper(AAudioOutputStream* stream)
-      : output_stream_(stream) {}
-
-  ~AAudioDestructionHelper() {
-    DCHECK(is_closing_);
-    if (aaudio_stream_)
-      AAudioStream_close(aaudio_stream_);
-  }
-
-  AAudioOutputStream* GetAndLockStream() EXCLUSIVE_LOCK_FUNCTION() {
-    lock_.Acquire();
-    return is_closing_ ? nullptr : output_stream_.get();
-  }
-
-  void UnlockStream() UNLOCK_FUNCTION() { lock_.Release(); }
-
-  void DeferStreamClosure(AAudioStream* stream) {
-    base::AutoLock al(lock_);
-    DCHECK(!is_closing_);
-
-    is_closing_ = true;
-    aaudio_stream_ = stream;
-  }
-
- private:
-  base::Lock lock_;
-  raw_ptr<AAudioOutputStream> output_stream_ GUARDED_BY(lock_) = nullptr;
-  raw_ptr<AAudioStream> aaudio_stream_ GUARDED_BY(lock_) = nullptr;
-  bool is_closing_ GUARDED_BY(lock_) = false;
-};
-
-static aaudio_data_callback_result_t OnAudioDataRequestedCallback(
-    AAudioStream* stream,
-    void* user_data,
-    void* audio_data,
-    int32_t num_frames) {
-  AAudioDestructionHelper* destruction_helper =
-      reinterpret_cast<AAudioDestructionHelper*>(user_data);
-
-  AAudioOutputStream* output_stream = destruction_helper->GetAndLockStream();
-
-  aaudio_data_callback_result_t result = AAUDIO_CALLBACK_RESULT_STOP;
-  if (output_stream)
-    result = output_stream->OnAudioDataRequested(audio_data, num_frames);
-
-  destruction_helper->UnlockStream();
-
-  return result;
-}
-
-static void OnStreamErrorCallback(AAudioStream* stream,
-                                  void* user_data,
-                                  aaudio_result_t error) {
-  AAudioDestructionHelper* destruction_helper =
-      reinterpret_cast<AAudioDestructionHelper*>(user_data);
-
-  AAudioOutputStream* output_stream = destruction_helper->GetAndLockStream();
-
-  if (output_stream)
-    output_stream->OnStreamError(error);
-
-  destruction_helper->UnlockStream();
-}
-
 AAudioOutputStream::AAudioOutputStream(AudioManagerAndroid* manager,
                                        const AudioParameters& params,
                                        aaudio_usage_t usage)
     : audio_manager_(manager),
       params_(params),
-      usage_(usage),
-      performance_mode_(AAUDIO_PERFORMANCE_MODE_NONE),
-      ns_per_frame_(base::Time::kNanosecondsPerSecond /
-                    static_cast<double>(params.sample_rate())),
-      destruction_helper_(std::make_unique<AAudioDestructionHelper>(this)) {
-  DCHECK(manager);
-  DCHECK(params.IsValid());
-
-  if (AudioManagerAndroid::SupportsPerformanceModeForOutput()) {
-    switch (params.latency_tag()) {
-      case AudioLatency::LATENCY_EXACT_MS:
-      case AudioLatency::LATENCY_INTERACTIVE:
-      case AudioLatency::LATENCY_RTC:
-        performance_mode_ = AAUDIO_PERFORMANCE_MODE_LOW_LATENCY;
-        break;
-      case AudioLatency::LATENCY_PLAYBACK:
-        performance_mode_ = AAUDIO_PERFORMANCE_MODE_POWER_SAVING;
-        break;
-      default:
-        performance_mode_ = AAUDIO_PERFORMANCE_MODE_NONE;
-    }
-  }
-
-  TRACE_EVENT2("audio", "AAudioOutputStream::AAudioOutputStream",
-               "AAUDIO_PERFORMANCE_MODE_LOW_LATENCY",
-               performance_mode_ == AAUDIO_PERFORMANCE_MODE_LOW_LATENCY
-                   ? "true" : "false",
-               "frames_per_buffer", params.frames_per_buffer());
+      stream_wrapper_(this,
+                      AAudioStreamWrapper::StreamType::kOutput,
+                      params,
+                      usage) {
+  CHECK(manager);
+  CHECK(params_.IsValid());
 }
 
 AAudioOutputStream::~AAudioOutputStream() {
-  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
-
-  if (base::android::SdkVersion::SDK_VERSION_S >=
-      base::android::BuildInfo::GetInstance()->sdk_int()) {
-    // On Android S+, |destruction_helper_| can be destroyed as part of the
-    // normal class teardown.
-    return;
-  }
-
-  // In R and earlier, it is possible for callbacks to still be running even
-  // after calling AAudioStream_close(). The code below is a mitigation to work
-  // around this issue. See crbug.com/1183255.
-
-  // Keep |destruction_helper_| alive longer than |this|, so the |user_data|
-  // bound to the callback stays valid, until the callbacks stop.
-  base::SequencedTaskRunner::GetCurrentDefault()->PostDelayedTask(
-      FROM_HERE, base::DoNothingWithBoundArgs(std::move(destruction_helper_)),
-      base::Seconds(1));
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 }
 
 void AAudioOutputStream::Flush() {}
 
 bool AAudioOutputStream::Open() {
-  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 
-  AAudioStreamBuilder* builder;
-  auto result = AAudio_createStreamBuilder(&builder);
-  if (AAUDIO_OK != result)
+  if (!stream_wrapper_.Open()) {
     return false;
-
-  // Parameters
-  AAudioStreamBuilder_setDirection(builder, AAUDIO_DIRECTION_OUTPUT);
-  AAudioStreamBuilder_setSampleRate(builder, params_.sample_rate());
-  AAudioStreamBuilder_setChannelCount(builder, params_.channels());
-  AAudioStreamBuilder_setFormat(builder, AAUDIO_FORMAT_PCM_FLOAT);
-  AAudioStreamBuilder_setUsage(builder, usage_);
-  AAudioStreamBuilder_setPerformanceMode(builder, performance_mode_);
-  AAudioStreamBuilder_setFramesPerDataCallback(builder,
-                                               params_.frames_per_buffer());
-
-  // Callbacks
-  AAudioStreamBuilder_setDataCallback(builder, OnAudioDataRequestedCallback,
-                                      destruction_helper_.get());
-  AAudioStreamBuilder_setErrorCallback(builder, OnStreamErrorCallback,
-                                       destruction_helper_.get());
-
-  result = AAudioStreamBuilder_openStream(builder, &aaudio_stream_);
-
-  AAudioStreamBuilder_delete(builder);
-
-  if (AAUDIO_OK != result)
-    return false;
-
-  // After opening the stream, sets the effective buffer size to 3X the burst
-  // size to prevent glitching if the burst is small (e.g. < 128). On some
-  // devices you can get by with 1X or 2X, but 3X is safer.
-  int32_t framesPerBurst = AAudioStream_getFramesPerBurst(aaudio_stream_);
-  int32_t sizeRequested = framesPerBurst * (framesPerBurst < 128 ? 3 : 2);
-  AAudioStream_setBufferSizeInFrames(aaudio_stream_, sizeRequested);
+  }
 
   audio_bus_ = AudioBus::Create(params_);
 
-  TRACE_EVENT2("audio", "AAudioOutputStream::Open",
-               "params_", params_.AsHumanReadableString(),
-               "requested BufferSizeInFrames", sizeRequested);
-
   return true;
 }
 
 void AAudioOutputStream::Close() {
-  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 
-  Stop();
-
-  // |destruction_helper_->GetStreamAndLock()| will return nullptr after this.
-  destruction_helper_->DeferStreamClosure(aaudio_stream_);
-
-  // We shouldn't be acessing |aaudio_stream_| after it's stopped.
-  aaudio_stream_ = nullptr;
+  stream_wrapper_.Close();
 
   // Note: This must be last, it will delete |this|.
   audio_manager_->ReleaseOutputStream(this);
 }
 
 void AAudioOutputStream::Start(AudioSourceCallback* callback) {
-  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
-  DCHECK(aaudio_stream_);
-
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   {
     base::AutoLock al(lock_);
 
@@ -218,17 +68,16 @@
       return;
     }
 
-    DCHECK(!callback_);
+    CHECK(!callback_);
     callback_ = callback;
   }
 
-  auto result = AAudioStream_requestStart(aaudio_stream_);
-  if (result != AAUDIO_OK) {
-    DLOG(ERROR) << "Failed to start audio stream, result: "
-                << AAudio_convertResultToText(result);
+  if (stream_wrapper_.Start()) {
+    // Successfully started `stream_wrapper_`.
+    return;
+  }
 
-    // Lock is required in case a previous asynchronous requestStop() still has
-    // not completed by the time we reach this point.
+  {
     base::AutoLock al(lock_);
     callback_->OnError(AudioSourceCallback::ErrorType::kUnknown);
     callback_ = nullptr;
@@ -236,79 +85,40 @@
 }
 
 void AAudioOutputStream::Stop() {
-  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 
+  AudioSourceCallback* temp_error_callback;
   {
     base::AutoLock al(lock_);
-    if (!callback_ || !aaudio_stream_)
+    if (!callback_) {
       return;
-  }
-
-  // Note: This call may be asynchronous, so we must clear |callback_| under
-  // lock below to ensure no further calls occur after Stop(). Since it may
-  // not always be asynchronous, we don't hold |lock_| while we call stop.
-  auto result = AAudioStream_requestStop(aaudio_stream_);
-
-  {
-    base::AutoLock al(lock_);
-    if (result != AAUDIO_OK) {
-      DLOG(ERROR) << "Failed to stop audio stream, result: "
-                  << AAudio_convertResultToText(result);
-      callback_->OnError(AudioSourceCallback::ErrorType::kUnknown);
     }
 
+    // Save a copy of copy of the callback for error reporting.
+    temp_error_callback = callback_;
+
+    // OnAudioDataRequested should no longer provide data from this point on.
     callback_ = nullptr;
   }
 
-  // Wait for AAUDIO_STREAM_STATE_STOPPED, but do not explicitly check for the
-  // success of this wait.
-  aaudio_stream_state_t current_state = AAUDIO_STREAM_STATE_STOPPING;
-  aaudio_stream_state_t next_state = AAUDIO_STREAM_STATE_UNINITIALIZED;
-  static const int64_t kTimeoutNanoseconds = 1e8;
-  result = AAudioStream_waitForStateChange(aaudio_stream_, current_state,
-                                           &next_state, kTimeoutNanoseconds);
-}
-
-base::TimeDelta AAudioOutputStream::GetDelay(base::TimeTicks delay_timestamp) {
-  // Get the time that a known audio frame was presented for playing.
-  int64_t existing_frame_index;
-  int64_t existing_frame_pts;
-  auto result =
-      AAudioStream_getTimestamp(aaudio_stream_, CLOCK_MONOTONIC,
-                                &existing_frame_index, &existing_frame_pts);
-
-  if (result != AAUDIO_OK) {
-    DLOG(ERROR) << "Failed to get audio latency, result: "
-                << AAudio_convertResultToText(result);
-    return base::TimeDelta();
+  if (!stream_wrapper_.Stop()) {
+    temp_error_callback->OnError(AudioSourceCallback::ErrorType::kUnknown);
   }
-
-  // Calculate the number of frames between our known frame and the write index.
-  const int64_t frame_index_delta =
-      AAudioStream_getFramesWritten(aaudio_stream_) - existing_frame_index;
-
-  // Calculate the time which the next frame will be presented.
-  const base::TimeDelta next_frame_pts =
-      base::Nanoseconds(existing_frame_pts + frame_index_delta * ns_per_frame_);
-
-  // Calculate the latency between write time and presentation time. At startup
-  // we may end up with negative values here.
-  return std::max(base::TimeDelta(),
-                  next_frame_pts - (delay_timestamp - base::TimeTicks()));
 }
 
-aaudio_data_callback_result_t AAudioOutputStream::OnAudioDataRequested(
-    void* audio_data,
-    int32_t num_frames) {
-  // TODO(tguilbert): This can be downgraded to a DCHECK after we've launched.
+bool AAudioOutputStream::OnAudioDataRequested(void* audio_data,
+                                              int32_t num_frames) {
   CHECK_EQ(num_frames, audio_bus_->frames());
 
   base::AutoLock al(lock_);
-  if (!callback_)
-    return AAUDIO_CALLBACK_RESULT_STOP;
+  if (!callback_) {
+    // Stop() might have already been called, but there can still be pending
+    // data callbacks in flight.
+    return false;
+  }
 
   const base::TimeTicks delay_timestamp = base::TimeTicks::Now();
-  const base::TimeDelta delay = GetDelay(delay_timestamp);
+  const base::TimeDelta delay = stream_wrapper_.GetOutputDelay(delay_timestamp);
 
   const int frames_filled =
       callback_->OnMoreData(delay, delay_timestamp, {}, audio_bus_.get());
@@ -316,50 +126,64 @@
   audio_bus_->Scale(muted_ ? 0.0 : volume_);
   audio_bus_->ToInterleaved<Float32SampleTypeTraits>(
       frames_filled, reinterpret_cast<float*>(audio_data));
-  return AAUDIO_CALLBACK_RESULT_CONTINUE;
+
+  return true;
 }
 
-void AAudioOutputStream::OnStreamError(aaudio_result_t error) {
+void AAudioOutputStream::OnDeviceChange() {
   base::AutoLock al(lock_);
 
-  if (error == AAUDIO_ERROR_DISCONNECTED)
-    device_changed_ = true;
+  device_changed_ = true;
 
-  if (!callback_)
-    return;
-
-  if (device_changed_) {
-    callback_->OnError(AudioSourceCallback::ErrorType::kDeviceChange);
+  if (!callback_) {
+    // Report the device change in Start() instead.
     return;
   }
 
-  // TODO(dalecurtis): Consider sending a translated |error| code.
+  callback_->OnError(AudioSourceCallback::ErrorType::kDeviceChange);
+}
+
+void AAudioOutputStream::OnError() {
+  base::AutoLock al(lock_);
+
+  if (!callback_) {
+    return;
+  }
+
+  if (device_changed_) {
+    // We should have already reported a device change error, either in
+    // OnDeviceChange() or in Start(). In both cases, `this` should be closed
+    // and deleted soon, so silently ignore additional error reporting.
+    return;
+  }
+
   callback_->OnError(AudioSourceCallback::ErrorType::kUnknown);
 }
 
 void AAudioOutputStream::SetVolume(double volume) {
-  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 
   double volume_override = 0;
-  if (audio_manager_->HasOutputVolumeOverride(&volume_override))
+  if (audio_manager_->HasOutputVolumeOverride(&volume_override)) {
     volume = volume_override;
+  }
 
-  if (volume < 0.0 || volume > 1.0)
+  if (volume < 0.0 || volume > 1.0) {
     return;
+  }
 
   base::AutoLock al(lock_);
   volume_ = volume;
 }
 
 void AAudioOutputStream::GetVolume(double* volume) {
-  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   base::AutoLock al(lock_);
   *volume = volume_;
 }
 
 void AAudioOutputStream::SetMute(bool muted) {
-  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
-
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   base::AutoLock al(lock_);
   muted_ = muted;
 }
diff --git a/media/audio/android/aaudio_output.h b/media/audio/android/aaudio_output.h
index 4473578..8ceef90 100644
--- a/media/audio/android/aaudio_output.h
+++ b/media/audio/android/aaudio_output.h
@@ -9,18 +9,20 @@
 
 #include "base/memory/raw_ptr.h"
 #include "base/memory/raw_ptr_exclusion.h"
+#include "base/sequence_checker.h"
 #include "base/synchronization/lock.h"
 #include "base/thread_annotations.h"
-#include "base/threading/thread_checker.h"
+#include "media/audio/android/aaudio_stream_wrapper.h"
 #include "media/audio/android/muteable_audio_output_stream.h"
 #include "media/base/audio_parameters.h"
 
 namespace media {
 
-class AAudioDestructionHelper;
 class AudioManagerAndroid;
 
-class AAudioOutputStream : public MuteableAudioOutputStream {
+// Class which uses the AAudio library to playback output.
+class AAudioOutputStream : public MuteableAudioOutputStream,
+                           public AAudioStreamWrapper::DataCallback {
  public:
   AAudioOutputStream(AudioManagerAndroid* manager,
                      const AudioParameters& params,
@@ -41,37 +43,20 @@
   void GetVolume(double* volume) override;
   void SetMute(bool muted) override;
 
-  // Public callbacks.
-  aaudio_data_callback_result_t OnAudioDataRequested(void* audio_data,
-                                                     int32_t num_frames);
-  void OnStreamError(aaudio_result_t error);
+  // AAudioStreamWrapper::DataCallback implementation.
+  bool OnAudioDataRequested(void* audio_data, int32_t num_frames) override;
+  void OnError() override;
+  void OnDeviceChange() override;
 
  private:
-  // Returns the amount of unplayed audio relative to |delay_timestamp|. See the
-  // definition for AudioOutputStream::AudioSourceCallback::OnMoreData() for
-  // more information on these terms.
-  base::TimeDelta GetDelay(base::TimeTicks delay_timestamp);
-
-  THREAD_CHECKER(thread_checker_);
+  SEQUENCE_CHECKER(sequence_checker_);
 
   const raw_ptr<AudioManagerAndroid> audio_manager_;
   const AudioParameters params_;
 
-  aaudio_usage_t usage_;
-  aaudio_performance_mode_t performance_mode_;
-
-  // Constant used for calculating latency. Amount of nanoseconds per frame.
-  const double ns_per_frame_;
-
   std::unique_ptr<AudioBus> audio_bus_;
 
-  // This field is not a raw_ptr<> because it was filtered by the rewriter for:
-  // #addr-of
-  RAW_PTR_EXCLUSION AAudioStream* aaudio_stream_ = nullptr;
-
-  // Bound to the audio data callback. Outlives |this| in case the callbacks
-  // continue after |this| is destroyed. See crbug.com/1183255.
-  std::unique_ptr<AAudioDestructionHelper> destruction_helper_;
+  AAudioStreamWrapper stream_wrapper_;
 
   // Lock protects all members below which may be read concurrently from the
   // audio manager thread and the OS provided audio thread.
diff --git a/media/audio/android/aaudio_stream_wrapper.cc b/media/audio/android/aaudio_stream_wrapper.cc
new file mode 100644
index 0000000..de65fa2a
--- /dev/null
+++ b/media/audio/android/aaudio_stream_wrapper.cc
@@ -0,0 +1,304 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "media/audio/android/aaudio_stream_wrapper.h"
+
+#include "base/android/build_info.h"
+#include "base/logging.h"
+#include "base/memory/raw_ptr.h"
+#include "base/task/sequenced_task_runner.h"
+#include "base/thread_annotations.h"
+#include "base/trace_event/trace_event.h"
+#include "media/audio/android/aaudio_stubs.h"
+
+namespace media {
+
+// Used to circumvent issues where the AAudio thread callbacks continue
+// after AAudioStream_requestStop() completes. See crbug.com/1183255.
+class LOCKABLE AAudioDestructionHelper {
+ public:
+  explicit AAudioDestructionHelper(AAudioStreamWrapper* wrapper)
+      : wrapper_(wrapper) {}
+
+  ~AAudioDestructionHelper() {
+    CHECK(is_closing_);
+    if (aaudio_stream_) {
+      AAudioStream_close(aaudio_stream_);
+    }
+  }
+
+  AAudioStreamWrapper* GetAndLockWrapper() EXCLUSIVE_LOCK_FUNCTION() {
+    lock_.Acquire();
+    return is_closing_ ? nullptr : wrapper_.get();
+  }
+
+  void UnlockWrapper() UNLOCK_FUNCTION() { lock_.Release(); }
+
+  void DeferStreamClosure(AAudioStream* stream) {
+    base::AutoLock al(lock_);
+    CHECK(!is_closing_);
+
+    is_closing_ = true;
+    aaudio_stream_ = stream;
+  }
+
+ private:
+  base::Lock lock_;
+  const raw_ptr<AAudioStreamWrapper> wrapper_ GUARDED_BY(lock_) = nullptr;
+  raw_ptr<AAudioStream> aaudio_stream_ GUARDED_BY(lock_) = nullptr;
+  bool is_closing_ GUARDED_BY(lock_) = false;
+};
+
+static aaudio_data_callback_result_t OnAudioDataRequestedCallback(
+    AAudioStream* stream,
+    void* user_data,
+    void* audio_data,
+    int32_t num_frames) {
+  AAudioDestructionHelper* destruction_helper =
+      reinterpret_cast<AAudioDestructionHelper*>(user_data);
+
+  AAudioStreamWrapper* wrapper = destruction_helper->GetAndLockWrapper();
+
+  aaudio_data_callback_result_t result = AAUDIO_CALLBACK_RESULT_STOP;
+  if (wrapper) {
+    result = wrapper->OnAudioDataRequested(audio_data, num_frames);
+  }
+
+  destruction_helper->UnlockWrapper();
+
+  return result;
+}
+
+static void OnStreamErrorCallback(AAudioStream* stream,
+                                  void* user_data,
+                                  aaudio_result_t error) {
+  AAudioDestructionHelper* destruction_helper =
+      reinterpret_cast<AAudioDestructionHelper*>(user_data);
+
+  AAudioStreamWrapper* wrapper = destruction_helper->GetAndLockWrapper();
+
+  if (wrapper) {
+    wrapper->OnStreamError(error);
+  }
+
+  destruction_helper->UnlockWrapper();
+}
+
+AAudioStreamWrapper::AAudioStreamWrapper(DataCallback* callback,
+                                         StreamType stream_type,
+                                         const AudioParameters& params,
+                                         aaudio_usage_t usage)
+    : params_(params),
+      stream_type_(stream_type),
+      usage_(usage),
+      callback_(callback),
+      ns_per_frame_(base::Time::kNanosecondsPerSecond /
+                    static_cast<double>(params.sample_rate())),
+      destruction_helper_(std::make_unique<AAudioDestructionHelper>(this)) {
+  CHECK(params.IsValid());
+  CHECK(callback_);
+
+  switch (params.latency_tag()) {
+    case AudioLatency::LATENCY_EXACT_MS:
+    case AudioLatency::LATENCY_INTERACTIVE:
+    case AudioLatency::LATENCY_RTC:
+      performance_mode_ = AAUDIO_PERFORMANCE_MODE_LOW_LATENCY;
+      break;
+    case AudioLatency::LATENCY_PLAYBACK:
+      performance_mode_ = AAUDIO_PERFORMANCE_MODE_POWER_SAVING;
+      break;
+    default:
+      performance_mode_ = AAUDIO_PERFORMANCE_MODE_NONE;
+  }
+
+  TRACE_EVENT2("audio", "AAudioStreamWrapper::AAudioStreamWrapper",
+               "AAUDIO_PERFORMANCE_MODE_LOW_LATENCY",
+               performance_mode_ == AAUDIO_PERFORMANCE_MODE_LOW_LATENCY
+                   ? "true"
+                   : "false",
+               "frames_per_buffer", params_.frames_per_buffer());
+}
+
+AAudioStreamWrapper::~AAudioStreamWrapper() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+  if (!is_closed_) {
+    Close();
+  }
+
+  CHECK(!aaudio_stream_);
+
+  if (base::android::SdkVersion::SDK_VERSION_S >=
+      base::android::BuildInfo::GetInstance()->sdk_int()) {
+    // On Android S+, |destruction_helper_| can be destroyed as part of the
+    // normal class teardown.
+    return;
+  }
+
+  // In R and earlier, it is possible for callbacks to still be running even
+  // after calling AAudioStream_close(). The code below is a mitigation to work
+  // around this issue. See crbug.com/1183255.
+
+  // Keep |destruction_helper_| alive longer than |this|, so the |user_data|
+  // bound to the callback stays valid, until the callbacks stop.
+  base::SequencedTaskRunner::GetCurrentDefault()->PostDelayedTask(
+      FROM_HERE, base::DoNothingWithBoundArgs(std::move(destruction_helper_)),
+      base::Seconds(1));
+}
+
+bool AAudioStreamWrapper::Open() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  CHECK(!is_closed_);
+
+  AAudioStreamBuilder* builder;
+  auto result = AAudio_createStreamBuilder(&builder);
+  if (AAUDIO_OK != result) {
+    return false;
+  }
+
+  // Parameters
+  AAudioStreamBuilder_setDirection(
+      builder, (stream_type_ == StreamType::kInput ? AAUDIO_DIRECTION_INPUT
+                                                   : AAUDIO_DIRECTION_OUTPUT));
+  AAudioStreamBuilder_setSampleRate(builder, params_.sample_rate());
+  AAudioStreamBuilder_setChannelCount(builder, params_.channels());
+  AAudioStreamBuilder_setFormat(builder, AAUDIO_FORMAT_PCM_FLOAT);
+  AAudioStreamBuilder_setUsage(builder, usage_);
+  AAudioStreamBuilder_setPerformanceMode(builder, performance_mode_);
+  AAudioStreamBuilder_setFramesPerDataCallback(builder,
+                                               params_.frames_per_buffer());
+
+  // Callbacks
+  AAudioStreamBuilder_setDataCallback(builder, OnAudioDataRequestedCallback,
+                                      destruction_helper_.get());
+  AAudioStreamBuilder_setErrorCallback(builder, OnStreamErrorCallback,
+                                       destruction_helper_.get());
+
+  result = AAudioStreamBuilder_openStream(builder, &aaudio_stream_);
+
+  AAudioStreamBuilder_delete(builder);
+
+  if (AAUDIO_OK != result) {
+    CHECK(!aaudio_stream_);
+    return false;
+  }
+
+  // After opening the stream, sets the effective buffer size to 3X the burst
+  // size to prevent glitching if the burst is small (e.g. < 128). On some
+  // devices you can get by with 1X or 2X, but 3X is safer.
+  int32_t frames_per_burst = AAudioStream_getFramesPerBurst(aaudio_stream_);
+  int32_t size_requested = frames_per_burst * (frames_per_burst < 128 ? 3 : 2);
+  AAudioStream_setBufferSizeInFrames(aaudio_stream_, size_requested);
+
+  TRACE_EVENT2("audio", "AAudioStreamWrapper::Open", "params",
+               params_.AsHumanReadableString(), "requested buffer size",
+               size_requested);
+
+  return true;
+}
+
+void AAudioStreamWrapper::Close() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  CHECK(!is_closed_);
+
+  Stop();
+
+  // |destruction_helper_->GetStreamAndLock()| will return nullptr after this.
+  destruction_helper_->DeferStreamClosure(aaudio_stream_);
+
+  // We shouldn't be accessing |aaudio_stream_| after it's stopped.
+  aaudio_stream_ = nullptr;
+
+  is_closed_ = true;
+}
+
+bool AAudioStreamWrapper::Start() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  CHECK(aaudio_stream_);
+  CHECK(!is_closed_);
+
+  auto result = AAudioStream_requestStart(aaudio_stream_);
+  if (result != AAUDIO_OK) {
+    DLOG(ERROR) << "Failed to start audio stream, result: "
+                << AAudio_convertResultToText(result);
+  }
+
+  return result == AAUDIO_OK;
+}
+
+bool AAudioStreamWrapper::Stop() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  CHECK(!is_closed_);
+
+  if (!aaudio_stream_) {
+    return true;
+  }
+
+  // Note: This call may or may not be asynchronous, depending on the Android
+  // version.
+  auto result = AAudioStream_requestStop(aaudio_stream_);
+
+  if (result != AAUDIO_OK) {
+    DLOG(ERROR) << "Failed to stop audio stream, result: "
+                << AAudio_convertResultToText(result);
+    return false;
+  }
+
+  // Wait for AAUDIO_STREAM_STATE_STOPPED, but do not explicitly check for the
+  // success of this wait.
+  aaudio_stream_state_t current_state = AAUDIO_STREAM_STATE_STOPPING;
+  aaudio_stream_state_t next_state = AAUDIO_STREAM_STATE_UNINITIALIZED;
+  static const int64_t kTimeoutNanoseconds = 1e8;
+  result = AAudioStream_waitForStateChange(aaudio_stream_, current_state,
+                                           &next_state, kTimeoutNanoseconds);
+
+  return true;
+}
+
+base::TimeDelta AAudioStreamWrapper::GetOutputDelay(
+    base::TimeTicks delay_timestamp) {
+  // Get the time that a known audio frame was presented for playing.
+  int64_t existing_frame_index;
+  int64_t existing_frame_pts;
+  auto result =
+      AAudioStream_getTimestamp(aaudio_stream_, CLOCK_MONOTONIC,
+                                &existing_frame_index, &existing_frame_pts);
+
+  if (result != AAUDIO_OK) {
+    DLOG(ERROR) << "Failed to get audio latency, result: "
+                << AAudio_convertResultToText(result);
+    return base::TimeDelta();
+  }
+
+  // Calculate the number of frames between our known frame and the write index.
+  const int64_t frame_index_delta =
+      AAudioStream_getFramesWritten(aaudio_stream_) - existing_frame_index;
+
+  // Calculate the time which the next frame will be presented.
+  const base::TimeDelta next_frame_pts =
+      base::Nanoseconds(existing_frame_pts + frame_index_delta * ns_per_frame_);
+
+  // Calculate the latency between write time and presentation time. At startup
+  // we may end up with negative values here.
+  return std::max(base::TimeDelta(),
+                  next_frame_pts - (delay_timestamp - base::TimeTicks()));
+}
+
+aaudio_data_callback_result_t AAudioStreamWrapper::OnAudioDataRequested(
+    void* audio_data,
+    int32_t num_frames) {
+  return callback_->OnAudioDataRequested(audio_data, num_frames)
+             ? AAUDIO_CALLBACK_RESULT_CONTINUE
+             : AAUDIO_CALLBACK_RESULT_STOP;
+}
+
+void AAudioStreamWrapper::OnStreamError(aaudio_result_t error) {
+  if (error == AAUDIO_ERROR_DISCONNECTED) {
+    callback_->OnDeviceChange();
+  } else {
+    callback_->OnError();
+  }
+}
+
+}  // namespace media
diff --git a/media/audio/android/aaudio_stream_wrapper.h b/media/audio/android/aaudio_stream_wrapper.h
new file mode 100644
index 0000000..2c37933
--- /dev/null
+++ b/media/audio/android/aaudio_stream_wrapper.h
@@ -0,0 +1,96 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef MEDIA_AUDIO_ANDROID_AAUDIO_STREAM_WRAPPER_H_
+#define MEDIA_AUDIO_ANDROID_AAUDIO_STREAM_WRAPPER_H_
+
+#include <aaudio/AAudio.h>
+
+#include "base/memory/raw_ptr.h"
+#include "base/memory/raw_ptr_exclusion.h"
+#include "base/sequence_checker.h"
+#include "base/synchronization/lock.h"
+#include "media/base/audio_bus.h"
+#include "media/base/audio_parameters.h"
+
+namespace media {
+
+class AAudioDestructionHelper;
+
+// Small wrapper around AAudioStream which handles its lifetime.
+class AAudioStreamWrapper {
+ public:
+  enum class StreamType {
+    kInput,
+    kOutput,
+  };
+
+  // Interface to report errors or provide/request audio data.
+  // Called on AAudio's realtime audio thread.
+  // Do not perform blocking operations from these callback, or call
+  // Open()/Start()/Stop()/Close() without jumping to a different thread first.
+  class DataCallback {
+   public:
+    virtual ~DataCallback() = default;
+
+    virtual bool OnAudioDataRequested(void* audio_data, int32_t num_frames) = 0;
+    virtual void OnError() = 0;
+    virtual void OnDeviceChange() = 0;
+  };
+
+  AAudioStreamWrapper(DataCallback* callback,
+                      StreamType stream_type,
+                      const AudioParameters& params,
+                      aaudio_usage_t usage);
+
+  AAudioStreamWrapper(const AAudioStreamWrapper&) = delete;
+  AAudioStreamWrapper& operator=(const AAudioStreamWrapper&) = delete;
+
+  ~AAudioStreamWrapper();
+
+  // Manage the underlying stream's lifetime.
+  // Returns whether the operation was successful.
+  bool Open();
+  bool Start();
+  bool Stop();
+  void Close();  // No other calls should be made after this one.
+
+  // Called on AAudio's realtime thread. Forwards calls to `callback_`.
+  aaudio_data_callback_result_t OnAudioDataRequested(void* audio_data,
+                                                     int32_t num_frames);
+  void OnStreamError(aaudio_result_t error);
+
+  // Returns the amount of unplayed audio relative to |delay_timestamp|.
+  base::TimeDelta GetOutputDelay(base::TimeTicks delay_timestamp);
+
+ private:
+  SEQUENCE_CHECKER(sequence_checker_);
+
+  const AudioParameters params_;
+
+  // Whether this class is using an input or an output stream.
+  StreamType stream_type_;
+
+  aaudio_usage_t usage_;
+  aaudio_performance_mode_t performance_mode_;
+
+  const raw_ptr<DataCallback> callback_;
+
+  bool is_closed_ = false;
+
+  // This field is not a raw_ptr<> because it was filtered by the rewriter for:
+  // #addr-of
+  RAW_PTR_EXCLUSION AAudioStream* aaudio_stream_ = nullptr;
+
+  // Constant used for calculating latency. Amount of nanoseconds per frame.
+  const double ns_per_frame_;
+
+  // Bound to the audio data callback. Outlives |this| in case the callbacks
+  // continue after |this| is destroyed. See crbug.com/1183255.
+  std::unique_ptr<AAudioDestructionHelper> destruction_helper_;
+};
+
+}  // namespace media
+
+#endif  // MEDIA_AUDIO_ANDROID_AAUDIO_STREAM_WRAPPER_H_
diff --git a/media/formats/mp4/writable_box_definitions.cc b/media/formats/mp4/writable_box_definitions.cc
index d9145795..3bd05991 100644
--- a/media/formats/mp4/writable_box_definitions.cc
+++ b/media/formats/mp4/writable_box_definitions.cc
@@ -20,4 +20,10 @@
 DataReference::DataReference(const DataReference&) = default;
 DataReference& DataReference::operator=(const DataReference&) = default;
 
+SampleDescription::SampleDescription() = default;
+SampleDescription::~SampleDescription() = default;
+SampleDescription::SampleDescription(const SampleDescription&) = default;
+SampleDescription& SampleDescription::operator=(const SampleDescription&) =
+    default;
+
 }  // namespace media::mp4::writable_boxes
diff --git a/media/formats/mp4/writable_box_definitions.h b/media/formats/mp4/writable_box_definitions.h
index 57d5a92..3273117 100644
--- a/media/formats/mp4/writable_box_definitions.h
+++ b/media/formats/mp4/writable_box_definitions.h
@@ -5,11 +5,14 @@
 #ifndef MEDIA_FORMATS_MP4_WRITABLE_BOX_DEFINITIONS_H_
 #define MEDIA_FORMATS_MP4_WRITABLE_BOX_DEFINITIONS_H_
 
+#include <string>
 #include <vector>
 
 #include "base/time/time.h"
 #include "media/base/media_export.h"
+#include "media/formats/mp4/box_definitions.h"
 #include "media/formats/mp4/fourccs.h"
+#include "media/media_buildflags.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 #include "ui/gfx/geometry/size.h"
 
@@ -31,10 +34,49 @@
   uint32_t flags : 24;
 };
 
+// Pixel Aspect Ratio Box (`pasp`) box.
+struct MEDIA_EXPORT PixelAspectRatioBox : Box {
+  // It has relative width and height of a pixel.
+  // We use default value of 1 for both of these values.
+};
+
+#if BUILDFLAG(USE_PROPRIETARY_CODECS)
+// AVC DecoderConfiguration Record (`avcc`) box.
+struct MEDIA_EXPORT AVCDecoderConfiguration : Box {
+  // Refer AVCDecoderConfigurationRecord of box_definitions.h
+  // because it provides Serialize method and the format
+  // is hard to be correct.
+  AVCDecoderConfigurationRecord avc_config_record;
+};
+
+// VisualSampleEtnry (`avc1`) box.
+struct MEDIA_EXPORT VisualSampleEntry : Box {
+  gfx::Size coded_size;
+  // It is formatted in a fixed 32-byte field, with the first
+  // byte set to the number of bytes to be displayed, followed
+  // by that number of bytes of displayable data, and then padding
+  // to complete 32 bytes total (including the size byte).
+  // The field may be set to 0.
+
+  // It will have browser brand name.
+  std::string compressor_name;  // char compressor_name[32];
+  AVCDecoderConfiguration avc_decoder_configuration;
+  PixelAspectRatioBox pixel_aspect_ratio;
+};
+#endif  // BUILDFLAG(USE_PROPRIETARY_CODECS)
+
 // Media sample table (`stsd`) box.
 struct MEDIA_EXPORT SampleDescription : FullBox {
+  SampleDescription();
+  ~SampleDescription();
+  SampleDescription(const SampleDescription&);
+  SampleDescription& operator=(const SampleDescription&);
+
   uint32_t entry_count;
-  // TODO: add optional `avc1` or `mp4a` box.
+
+#if BUILDFLAG(USE_PROPRIETARY_CODECS)
+  absl::optional<VisualSampleEntry> visual_sample_entry;
+#endif
 };
 
 // `stco`, `stsz`, `stts`, `stsc`' are mandatory boxes.
diff --git a/media/gpu/test/video_decode_accelerator_tests.cc b/media/gpu/test/video_decode_accelerator_tests.cc
index 7b11cb2..fbc15f5c 100644
--- a/media/gpu/test/video_decode_accelerator_tests.cc
+++ b/media/gpu/test/video_decode_accelerator_tests.cc
@@ -304,8 +304,18 @@
 #else
       media::VideoDecoderType::kUnknown;
 #endif
-  ASSERT_TRUE(VideoDecoderPipeline::GetSupportedConfigs(
-      decoder_type, gpu::GpuDriverBugWorkarounds()));
+  const auto supported_configs = VideoDecoderPipeline::GetSupportedConfigs(
+      decoder_type, gpu::GpuDriverBugWorkarounds());
+  ASSERT_FALSE(supported_configs->empty());
+
+  const bool contains_h264 =
+      std::find_if(supported_configs->begin(), supported_configs->end(),
+                   [](SupportedVideoDecoderConfig config) {
+                     return config.profile_min >= H264PROFILE_MIN &&
+                            config.profile_max <= H264PROFILE_MAX;
+                   }) != supported_configs->end();
+  // Every hardware video decoder in ChromeOS supports some kind of H.264.
+  EXPECT_TRUE(contains_h264);
 }
 #endif  // BUILDFLAG(USE_CHROMEOS_MEDIA_ACCELERATION)
 
diff --git a/media/gpu/v4l2/BUILD.gn b/media/gpu/v4l2/BUILD.gn
index 2531f24..e7b73820 100644
--- a/media/gpu/v4l2/BUILD.gn
+++ b/media/gpu/v4l2/BUILD.gn
@@ -203,7 +203,7 @@
 
 test("v4l2_unittest") {
   testonly = true
-  sources = [ "v4l2_unittest.cc" ]
+  sources = [ "test/v4l2_unittest.cc" ]
 
   deps = [
     ":v4l2",
diff --git a/media/gpu/v4l2/legacy/v4l2_slice_video_decode_accelerator.cc b/media/gpu/v4l2/legacy/v4l2_slice_video_decode_accelerator.cc
index 6cf3c78..7ffdb5d7 100644
--- a/media/gpu/v4l2/legacy/v4l2_slice_video_decode_accelerator.cc
+++ b/media/gpu/v4l2/legacy/v4l2_slice_video_decode_accelerator.cc
@@ -498,8 +498,9 @@
 
   size_t input_size;
   gfx::Size max_resolution, min_resolution;
-  device_->GetSupportedResolution(input_format_fourcc_, &min_resolution,
-                                  &max_resolution);
+  GetSupportedResolution(base::BindRepeating(&V4L2Device::Ioctl, device_),
+                         input_format_fourcc_, &min_resolution,
+                         &max_resolution);
   if (max_resolution.width() > 1920 && max_resolution.height() > 1088)
     input_size = kInputBufferMaxSizeFor4k;
   else
diff --git a/media/gpu/v4l2/legacy/v4l2_video_decode_accelerator.cc b/media/gpu/v4l2/legacy/v4l2_video_decode_accelerator.cc
index 12b092d..6bd9513 100644
--- a/media/gpu/v4l2/legacy/v4l2_video_decode_accelerator.cc
+++ b/media/gpu/v4l2/legacy/v4l2_video_decode_accelerator.cc
@@ -2242,8 +2242,9 @@
 
   size_t input_size;
   gfx::Size max_resolution, min_resolution;
-  device_->GetSupportedResolution(input_format_fourcc_, &min_resolution,
-                                  &max_resolution);
+  GetSupportedResolution(base::BindRepeating(&V4L2Device::Ioctl, device_),
+                         input_format_fourcc_, &min_resolution,
+                         &max_resolution);
   if (max_resolution.width() > 1920 && max_resolution.height() > 1088)
     input_size = kInputBufferMaxSizeFor4k;
   else
diff --git a/media/gpu/v4l2/v4l2_unittest.cc b/media/gpu/v4l2/test/v4l2_unittest.cc
similarity index 99%
rename from media/gpu/v4l2/v4l2_unittest.cc
rename to media/gpu/v4l2/test/v4l2_unittest.cc
index 8a949ae..aacbec2 100644
--- a/media/gpu/v4l2/v4l2_unittest.cc
+++ b/media/gpu/v4l2/test/v4l2_unittest.cc
@@ -1,4 +1,4 @@
-// Copyright 2022 The Chromium Authors
+// Copyright 2023 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -41,8 +41,9 @@
 
 // Converts v4l2 format to gbm format
 uint32_t ToGBMFormat(uint32_t v4l2_format) {
-  if (v4l2_format == V4L2_PIX_FMT_NV12 || v4l2_format == V4L2_PIX_FMT_NV12M)
+  if (v4l2_format == V4L2_PIX_FMT_NV12 || v4l2_format == V4L2_PIX_FMT_NV12M) {
     return DRM_FORMAT_NV12;
+  }
   return DRM_FORMAT_INVALID;
 }
 
diff --git a/media/gpu/v4l2/v4l2_device.cc b/media/gpu/v4l2/v4l2_device.cc
index d5afd09d..e0cb96b 100644
--- a/media/gpu/v4l2/v4l2_device.cc
+++ b/media/gpu/v4l2/v4l2_device.cc
@@ -2115,34 +2115,6 @@
   return {};
 }
 
-void V4L2Device::GetSupportedResolution(uint32_t pixelformat,
-                                        gfx::Size* min_resolution,
-                                        gfx::Size* max_resolution) {
-  constexpr gfx::Size kDefaultMaxCodedSize(1920, 1088);
-  *max_resolution = kDefaultMaxCodedSize;
-  constexpr gfx::Size kDefaultMinCodedSize(16, 16);
-  *min_resolution = kDefaultMinCodedSize;
-
-  v4l2_frmsizeenum frame_size;
-  memset(&frame_size, 0, sizeof(frame_size));
-  frame_size.pixel_format = pixelformat;
-  if (Ioctl(VIDIOC_ENUM_FRAMESIZES, &frame_size) == 0) {
-    if (frame_size.type == V4L2_FRMSIZE_TYPE_STEPWISE) {
-      max_resolution->SetSize(frame_size.stepwise.max_width,
-                              frame_size.stepwise.max_height);
-      min_resolution->SetSize(frame_size.stepwise.min_width,
-                              frame_size.stepwise.min_height);
-    } else {
-#if BUILDFLAG(IS_CHROMEOS)
-      // All of Chrome-supported implementations support STEPWISE only.
-      CHECK_EQ(frame_size.type, V4L2_FRMSIZE_TYPE_STEPWISE);
-#endif
-    }
-  } else {
-    DLOGF(INFO) << "VIDIOC_ENUM_FRAMESIZES failed, using default values";
-  }
-}
-
 VideoEncodeAccelerator::SupportedRateControlMode
 V4L2Device::GetSupportedRateControlMode() {
   auto rate_control_mode = VideoEncodeAccelerator::kNoMode;
@@ -2285,7 +2257,8 @@
     }
 
     VideoDecodeAccelerator::SupportedProfile profile;
-    GetSupportedResolution(pixelformat, &profile.min_resolution,
+    GetSupportedResolution(base::BindRepeating(&V4L2Device::Ioctl, this),
+                           pixelformat, &profile.min_resolution,
                            &profile.max_resolution);
 
     const auto video_codec_profiles = EnumerateSupportedProfilesForV4L2Codec(
@@ -2324,7 +2297,8 @@
       continue;
     }
     gfx::Size min_resolution;
-    GetSupportedResolution(pixelformat, &min_resolution,
+    GetSupportedResolution(base::BindRepeating(&V4L2Device::Ioctl, this),
+                           pixelformat, &min_resolution,
                            &profile.max_resolution);
     const auto video_codec_profiles = EnumerateSupportedProfilesForV4L2Codec(
         base::BindRepeating(&V4L2Device::Ioctl, this), pixelformat);
diff --git a/media/gpu/v4l2/v4l2_device.h b/media/gpu/v4l2/v4l2_device.h
index 811223e..3614379 100644
--- a/media/gpu/v4l2/v4l2_device.h
+++ b/media/gpu/v4l2/v4l2_device.h
@@ -785,12 +785,6 @@
   // Returns the preferred V4L2 input formats for |type| or empty if none.
   std::vector<uint32_t> PreferredInputFormat(Type type) const;
 
-  // Get minimum and maximum resolution for fourcc |pixelformat| and store to
-  // |min_resolution| and |max_resolution|.
-  void GetSupportedResolution(uint32_t pixelformat,
-                              gfx::Size* min_resolution,
-                              gfx::Size* max_resolution);
-
   // Get the supported bitrate control modes. This function should be called
   // when V4L2Device opens an encoder driver node.
   VideoEncodeAccelerator::SupportedRateControlMode
diff --git a/media/gpu/v4l2/v4l2_stateful_video_decoder.cc b/media/gpu/v4l2/v4l2_stateful_video_decoder.cc
index e2d6af4..d1c0dc06 100644
--- a/media/gpu/v4l2/v4l2_stateful_video_decoder.cc
+++ b/media/gpu/v4l2/v4l2_stateful_video_decoder.cc
@@ -4,9 +4,22 @@
 
 #include "media/gpu/v4l2/v4l2_stateful_video_decoder.h"
 
+#include <fcntl.h>
+#include <sys/ioctl.h>
+
+#include "base/files/file_util.h"
 #include "base/memory/ptr_util.h"
+#include "base/posix/eintr_wrapper.h"
 #include "media/base/media_log.h"
 #include "media/gpu/macros.h"
+#include "media/gpu/v4l2/v4l2_utils.h"
+#include "ui/gfx/geometry/size.h"
+
+namespace {
+int HandledIoctl(int fd, int request, void* arg) {
+  return HANDLE_EINTR(ioctl(fd, request, arg));
+}
+}  // namespace
 
 namespace media {
 
@@ -25,8 +38,60 @@
 // static
 absl::optional<SupportedVideoDecoderConfigs>
 V4L2StatefulVideoDecoder::GetSupportedConfigs() {
-  NOTIMPLEMENTED();
-  return absl::nullopt;
+  SupportedVideoDecoderConfigs supported_media_configs;
+
+  constexpr char kVideoDeviceDriverPath[] = "/dev/video-dec0";
+  CHECK(base::PathExists(base::FilePath(kVideoDeviceDriverPath)));
+
+  base::ScopedFD device_fd(HANDLE_EINTR(
+      open(kVideoDeviceDriverPath, O_RDWR | O_NONBLOCK | O_CLOEXEC)));
+  if (!device_fd.is_valid()) {
+    return absl::nullopt;
+  }
+
+  std::vector<uint32_t> v4l2_codecs = EnumerateSupportedPixFmts(
+      base::BindRepeating(&HandledIoctl, device_fd.get()),
+      V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE);
+
+  // V4L2 stateful formats (don't end up with _SLICE or _FRAME) supported.
+  constexpr std::array<uint32_t, 4> kSupportedInputCodecs = {
+    V4L2_PIX_FMT_H264,
+#if BUILDFLAG(ENABLE_HEVC_PARSER_AND_HW_DECODER)
+    V4L2_PIX_FMT_HEVC,
+#endif  // BUILDFLAG(ENABLE_HEVC_PARSER_AND_HW_DECODER)
+    V4L2_PIX_FMT_VP8,
+    V4L2_PIX_FMT_VP9,
+  };
+  std::erase_if(v4l2_codecs, [kSupportedInputCodecs](uint32_t v4l2_codec) {
+    return !base::Contains(kSupportedInputCodecs, v4l2_codec);
+  });
+
+  for (const uint32_t v4l2_codec : v4l2_codecs) {
+    const std::vector<VideoCodecProfile> media_codec_profiles =
+        EnumerateSupportedProfilesForV4L2Codec(
+            base::BindRepeating(&HandledIoctl, device_fd.get()), v4l2_codec);
+
+    gfx::Size min_coded_size;
+    gfx::Size max_coded_size;
+    GetSupportedResolution(base::BindRepeating(&HandledIoctl, device_fd.get()),
+                           v4l2_codec, &min_coded_size, &max_coded_size);
+
+    for (const auto& profile : media_codec_profiles) {
+      supported_media_configs.emplace_back(SupportedVideoDecoderConfig(
+          profile, profile, min_coded_size, max_coded_size,
+          /*allow_encrypted=*/false, /*require_encrypted=*/false));
+    }
+  }
+
+#if DCHECK_IS_ON()
+  for (const auto& config : supported_media_configs) {
+    DVLOGF(3) << "Enumerated " << GetProfileName(config.profile_min) << " ("
+              << config.coded_size_min.ToString() << "-"
+              << config.coded_size_max.ToString() << ")";
+  }
+#endif
+
+  return supported_media_configs;
 }
 
 void V4L2StatefulVideoDecoder::Initialize(const VideoDecoderConfig& config,
diff --git a/media/gpu/v4l2/v4l2_utils.cc b/media/gpu/v4l2/v4l2_utils.cc
index 5a501e2..4d5fa74 100644
--- a/media/gpu/v4l2/v4l2_utils.cc
+++ b/media/gpu/v4l2/v4l2_utils.cc
@@ -316,4 +316,34 @@
   return pix_fmts;
 }
 
+void GetSupportedResolution(
+    const base::RepeatingCallback<int(int, void*)>& ioctl_cb,
+    uint32_t pixelformat,
+    gfx::Size* min_resolution,
+    gfx::Size* max_resolution) {
+  constexpr gfx::Size kDefaultMaxCodedSize(1920, 1088);
+  *max_resolution = kDefaultMaxCodedSize;
+  constexpr gfx::Size kDefaultMinCodedSize(16, 16);
+  *min_resolution = kDefaultMinCodedSize;
+
+  v4l2_frmsizeenum frame_size;
+  memset(&frame_size, 0, sizeof(frame_size));
+  frame_size.pixel_format = pixelformat;
+  if (ioctl_cb.Run(VIDIOC_ENUM_FRAMESIZES, &frame_size) == kIoctlOk) {
+    if (frame_size.type == V4L2_FRMSIZE_TYPE_STEPWISE) {
+      max_resolution->SetSize(frame_size.stepwise.max_width,
+                              frame_size.stepwise.max_height);
+      min_resolution->SetSize(frame_size.stepwise.min_width,
+                              frame_size.stepwise.min_height);
+    } else {
+#if BUILDFLAG(IS_CHROMEOS)
+      // All of Chrome-supported implementations support STEPWISE only.
+      CHECK_EQ(frame_size.type, V4L2_FRMSIZE_TYPE_STEPWISE);
+#endif
+    }
+  } else {
+    DLOGF(INFO) << "VIDIOC_ENUM_FRAMESIZES failed, using default values";
+  }
+}
+
 }  // namespace media
diff --git a/media/gpu/v4l2/v4l2_utils.h b/media/gpu/v4l2/v4l2_utils.h
index 9fa8125f..6c624bd7 100644
--- a/media/gpu/v4l2/v4l2_utils.h
+++ b/media/gpu/v4l2/v4l2_utils.h
@@ -12,6 +12,9 @@
 #include "base/functional/callback.h"
 #include "media/base/video_codecs.h"
 
+namespace gfx {
+class Size;
+}
 namespace media {
 
 // Returns a human readable description of |memory|.
@@ -49,6 +52,14 @@
     base::RepeatingCallback<int(int, void*)> ioctl_cb,
     v4l2_buf_type buf_type);
 
+// Gets minimum and maximum resolution for fourcc |pixelformat|. If the driver
+// doesn't support enumeration, default values are returned instead.
+void GetSupportedResolution(
+    const base::RepeatingCallback<int(int, void*)>& ioctl_cb,
+    uint32_t pixelformat,
+    gfx::Size* min_resolution,
+    gfx::Size* max_resolution);
+
 }  // namespace media
 
 #endif  // MEDIA_GPU_V4L2_V4L2_UTILS_H_
diff --git a/media/gpu/v4l2/v4l2_video_decoder.cc b/media/gpu/v4l2/v4l2_video_decoder.cc
index 76b8a851..bab779b 100644
--- a/media/gpu/v4l2/v4l2_video_decoder.cc
+++ b/media/gpu/v4l2/v4l2_video_decoder.cc
@@ -397,7 +397,8 @@
 
   // Determine the input buffer size.
   gfx::Size max_size, min_size;
-  device_->GetSupportedResolution(fourcc, &min_size, &max_size);
+  GetSupportedResolution(base::BindRepeating(&V4L2Device::Ioctl, device_),
+                         fourcc, &min_size, &max_size);
   size_t input_size = max_size.GetArea() > k1080pArea
                           ? kInputBufferMaxSizeFor4k
                           : kInputBufferMaxSizeFor1080p;
diff --git a/media/gpu/v4l2/v4l2_video_encode_accelerator.cc b/media/gpu/v4l2/v4l2_video_encode_accelerator.cc
index 1d6d87d..b8486cf5 100644
--- a/media/gpu/v4l2/v4l2_video_encode_accelerator.cc
+++ b/media/gpu/v4l2/v4l2_video_encode_accelerator.cc
@@ -41,6 +41,7 @@
 #include "media/gpu/chromeos/platform_video_frame_utils.h"
 #include "media/gpu/gpu_video_encode_accelerator_helpers.h"
 #include "media/gpu/macros.h"
+#include "media/gpu/v4l2/v4l2_utils.h"
 #include "media/video/h264_level_limits.h"
 #include "media/video/h264_parser.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
@@ -228,8 +229,9 @@
 
   gfx::Size min_resolution;
   gfx::Size max_resolution;
-  device_->GetSupportedResolution(output_format_fourcc_, &min_resolution,
-                                  &max_resolution);
+  GetSupportedResolution(base::BindRepeating(&V4L2Device::Ioctl, device_),
+                         output_format_fourcc_, &min_resolution,
+                         &max_resolution);
   if (config.input_visible_size.width() < min_resolution.width() ||
       config.input_visible_size.height() < min_resolution.height() ||
       config.input_visible_size.width() > max_resolution.width() ||
diff --git a/media/muxers/mp4_movie_box_writer.cc b/media/muxers/mp4_movie_box_writer.cc
index ba08b88b..b10646c 100644
--- a/media/muxers/mp4_movie_box_writer.cc
+++ b/media/muxers/mp4_movie_box_writer.cc
@@ -586,6 +586,12 @@
     const mp4::writable_boxes::SampleDescription& box)
     : Mp4BoxWriter(context), box_(box) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+#if BUILDFLAG(USE_PROPRIETARY_CODECS)
+  if (box_.visual_sample_entry.has_value()) {
+    AddChildBox(std::make_unique<Mp4MovieVisualSampleEntryBoxWriter>(
+        context, box_.visual_sample_entry.value()));
+  }
+#endif
 }
 
 Mp4MovieSampleDescriptionBoxWriter::~Mp4MovieSampleDescriptionBoxWriter() =
@@ -598,7 +604,110 @@
 
   writer.WriteU32(box_.entry_count);
 
+  WriteChildren(writer);
+
   writer.EndBox();
 }
 
+#if BUILDFLAG(USE_PROPRIETARY_CODECS)
+// Mp4MovieVisualSampleEntryBoxWriter (`avc1`) class.
+Mp4MovieVisualSampleEntryBoxWriter::Mp4MovieVisualSampleEntryBoxWriter(
+    const Mp4MuxerContext& context,
+    const mp4::writable_boxes::VisualSampleEntry& box)
+    : Mp4BoxWriter(context), box_(box) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  AddChildBox(std::make_unique<Mp4MovieAVCDecoderConfigurationBoxWriter>(
+      context, box_.avc_decoder_configuration));
+  AddChildBox(std::make_unique<Mp4MoviePixelAspectRatioBoxBoxWriter>(context));
+}
+
+Mp4MovieVisualSampleEntryBoxWriter::~Mp4MovieVisualSampleEntryBoxWriter() =
+    default;
+
+void Mp4MovieVisualSampleEntryBoxWriter::Write(BoxByteStream& writer) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+  writer.StartBox(mp4::FOURCC_AVC1);
+
+  writer.WriteU32(0);  // reserved.
+  writer.WriteU16(0);  // reserved.
+  writer.WriteU16(1);  // data_reference_index in `dref` box, 1 is start index.
+  writer.WriteU16(0);  // predefined1.
+  writer.WriteU16(1);  // reserved2.
+  writer.WriteU32(0);  // pre_defined2[0].
+  writer.WriteU32(0);  // pre_defined2[1].
+  writer.WriteU32(0);  // pre_defined2[2].
+  writer.WriteU16(box_.coded_size.width());
+  writer.WriteU16(box_.coded_size.height());
+  writer.WriteU32(0x00480000);  // horizontal resolution, 72 dpi.
+  writer.WriteU32(0x00480000);  // vertical resolution, 72 dpi.
+  writer.WriteU32(0);           // reserved3.
+  writer.WriteU16(1);           // frame_count.
+
+  // compressor_name.
+  constexpr size_t kMaxCompressorNameSize = 30;
+
+  std::string compressor_name = box_.compressor_name;
+  uint8_t compressor_name_size =
+      std::min(compressor_name.size(), kMaxCompressorNameSize);
+  writer.WriteU8(compressor_name_size);
+  compressor_name.resize(kMaxCompressorNameSize);
+  compressor_name.push_back(0);
+  writer.WriteString(compressor_name);  // It will write 31 chars.
+
+  writer.WriteU16(0x0018);  // depth.
+  writer.WriteU16(0xFFFF);  // pre_defined, -1.
+
+  WriteChildren(writer);
+
+  writer.EndBox();
+}
+
+// Mp4MovieAVCDecoderConfigurationBoxWriter (`avcC`) class.
+Mp4MovieAVCDecoderConfigurationBoxWriter::
+    Mp4MovieAVCDecoderConfigurationBoxWriter(
+        const Mp4MuxerContext& context,
+        const mp4::writable_boxes::AVCDecoderConfiguration& box)
+    : Mp4BoxWriter(context), box_(box) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+}
+
+Mp4MovieAVCDecoderConfigurationBoxWriter::
+    ~Mp4MovieAVCDecoderConfigurationBoxWriter() = default;
+
+void Mp4MovieAVCDecoderConfigurationBoxWriter::Write(BoxByteStream& writer) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+  writer.StartBox(mp4::FOURCC_AVCC);
+
+  std::vector<uint8_t> write_data;
+  CHECK(box_.avc_config_record.Serialize(write_data));
+
+  writer.WriteBytes(write_data.data(), write_data.size());
+
+  writer.EndBox();
+}
+
+// Mp4MoviePixelAspectRatioBoxBoxWriter (`pasp`) class.
+Mp4MoviePixelAspectRatioBoxBoxWriter::Mp4MoviePixelAspectRatioBoxBoxWriter(
+    const Mp4MuxerContext& context)
+    : Mp4BoxWriter(context) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+}
+
+Mp4MoviePixelAspectRatioBoxBoxWriter::~Mp4MoviePixelAspectRatioBoxBoxWriter() =
+    default;
+
+void Mp4MoviePixelAspectRatioBoxBoxWriter::Write(BoxByteStream& writer) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+  writer.StartBox(mp4::FOURCC_PASP);
+
+  writer.WriteU32(1);  // Horizontal spacing.
+  writer.WriteU32(1);  // Vertical spacing.
+
+  writer.EndBox();
+}
+#endif
+
 }  // namespace media
diff --git a/media/muxers/mp4_movie_box_writer.h b/media/muxers/mp4_movie_box_writer.h
index 9e27249..f4bd804 100644
--- a/media/muxers/mp4_movie_box_writer.h
+++ b/media/muxers/mp4_movie_box_writer.h
@@ -78,6 +78,13 @@
 DECLARE_MP4_BOX_WRITER_CLASS_NO_DATA(Mp4MovieDecodingTimeToSampleBoxWriter);
 DECLARE_MP4_BOX_WRITER_CLASS_NO_DATA(Mp4MovieSampleSizeBoxWriter);
 DECLARE_MP4_BOX_WRITER_CLASS_NO_DATA(Mp4MovieSampleChunkOffsetBoxWriter);
+#if BUILDFLAG(USE_PROPRIETARY_CODECS)
+DECLARE_MP4_BOX_WRITER_CLASS(Mp4MovieVisualSampleEntryBoxWriter,
+                             mp4::writable_boxes::VisualSampleEntry);
+DECLARE_MP4_BOX_WRITER_CLASS(Mp4MovieAVCDecoderConfigurationBoxWriter,
+                             mp4::writable_boxes::AVCDecoderConfiguration);
+#endif
+DECLARE_MP4_BOX_WRITER_CLASS_NO_DATA(Mp4MoviePixelAspectRatioBoxBoxWriter);
 }  // namespace media
 
 #endif  // MEDIA_MUXERS_MP4_MOVIE_BOX_WRITER_H_
diff --git a/media/muxers/mp4_muxer_box_writer_unittest.cc b/media/muxers/mp4_muxer_box_writer_unittest.cc
index 84d3957..d16280bf 100644
--- a/media/muxers/mp4_muxer_box_writer_unittest.cc
+++ b/media/muxers/mp4_muxer_box_writer_unittest.cc
@@ -9,8 +9,11 @@
 #include "base/big_endian.h"
 #include "base/containers/span.h"
 #include "base/functional/bind.h"
+#include "base/memory/scoped_refptr.h"
 #include "base/run_loop.h"
 #include "base/test/task_environment.h"
+#include "media/base/subsample_entry.h"
+#include "media/formats/mp4/bitstream_converter.h"
 #include "media/formats/mp4/box_definitions.h"
 #include "media/formats/mp4/box_reader.h"
 #include "media/formats/mp4/writable_box_definitions.h"
@@ -42,6 +45,22 @@
 constexpr uint32_t kEntryCountLength = 4u;
 constexpr uint32_t kSampleSizeAndCount = 8u;
 
+#if BUILDFLAG(USE_PROPRIETARY_CODECS)
+constexpr uint8_t kProfileIndicationNoChroma = 77;
+constexpr uint8_t kProfileIndication = 122;
+constexpr uint8_t kProfileCompatibility = 100;
+constexpr uint8_t kLevelIndication = 64;
+constexpr uint8_t kNALUUnitLength = 4;
+constexpr uint8_t kSPS[] = {0x67, 0x64, 0x00, 0x0C, 0xAC, 0xD9, 0x41,
+                            0x41, 0xFB, 0x01, 0x10, 0x00, 0x00, 0x03,
+                            0x00, 0x10, 0x00, 0x00, 0x00, 0x03, 0x01,
+                            0xE0, 0xF1, 0x42, 0x99, 0x60};
+constexpr uint8_t kPPS[] = {0x68, 0xEE, 0xE3, 0xCB, 0x22, 0xC0};
+constexpr uint8_t kChromaFormat = 0x3;
+constexpr uint8_t kLumaMinus8 = 0x4;
+constexpr uint8_t kChromaMinus8 = 0x4;
+#endif
+
 uint64_t ConvertTo1904TimeInSeconds(base::Time time) {
   base::Time time1904;
   CHECK(base::Time::FromUTCString("1904-01-01 00:00:00 UTC", &time1904));
@@ -493,4 +512,115 @@
   EXPECT_EQ(mp4::FOURCC_STSD, static_cast<mp4::FourCC>(fourcc));
 }
 
+#if BUILDFLAG(USE_PROPRIETARY_CODECS)
+TEST_F(Mp4MuxerBoxWriterTest, Mp4MovieVisualSampleEntry) {
+  // Tests `avc1` and its children box writer.
+  std::vector<uint8_t> written_data;
+  CreateContext(written_data);
+
+  mp4::writable_boxes::SampleDescription sample_description;
+
+  mp4::writable_boxes::VisualSampleEntry visual_sample_entry;
+  visual_sample_entry.coded_size = gfx::Size(kWidth, kHeight);
+  visual_sample_entry.compressor_name = "Chromium AVC Coding";
+
+  mp4::writable_boxes::AVCDecoderConfiguration avc = {};
+  avc.avc_config_record.version = 1;
+  avc.avc_config_record.profile_indication = kProfileIndicationNoChroma;
+  avc.avc_config_record.profile_compatibility = kProfileCompatibility;
+  avc.avc_config_record.avc_level = kLevelIndication;
+  avc.avc_config_record.length_size = kNALUUnitLength;
+
+  std::vector<uint8_t> sps(std::begin(kSPS), std::end(kSPS));
+  avc.avc_config_record.sps_list.emplace_back(sps);
+
+  std::vector<uint8_t> pps(std::begin(kPPS), std::end(kPPS));
+  avc.avc_config_record.pps_list.emplace_back(pps);
+
+  visual_sample_entry.avc_decoder_configuration = std::move(avc);
+
+  sample_description.visual_sample_entry = std::move(visual_sample_entry);
+
+  Mp4MovieSampleDescriptionBoxWriter box_writer(*context(), sample_description);
+  FlushAndWait(&box_writer);
+
+  // MediaInformation will have multiple sample boxes even though they
+  // not added exclusively.
+  std::unique_ptr<mp4::BoxReader> box_reader(
+      mp4::BoxReader::ReadConcatentatedBoxes(written_data.data(),
+                                             written_data.size(), nullptr));
+
+  EXPECT_TRUE(box_reader->ScanChildren());
+
+  mp4::SampleDescription reader_sample_description;
+  reader_sample_description.type = mp4::kVideo;
+
+  EXPECT_TRUE(box_reader->ReadChild(&reader_sample_description));
+  EXPECT_EQ(1u, reader_sample_description.video_entries.size());
+
+  const auto& video_sample_entry = reader_sample_description.video_entries[0];
+  EXPECT_TRUE(video_sample_entry.IsFormatValid());
+  EXPECT_EQ(1, video_sample_entry.data_reference_index);
+  EXPECT_EQ(static_cast<uint16_t>(kWidth), video_sample_entry.width);
+  EXPECT_EQ(static_cast<uint16_t>(kHeight), video_sample_entry.height);
+  EXPECT_EQ(VideoCodecProfile::H264PROFILE_MAIN,
+            video_sample_entry.video_codec_profile);
+}
+
+TEST_F(Mp4MuxerBoxWriterTest, Mp4MovieAVCDecoderConfigurationRecord) {
+  // Tests `avc1` and its children box writer.
+  std::vector<uint8_t> written_data;
+  CreateContext(written_data);
+
+  mp4::writable_boxes::AVCDecoderConfiguration avc = {};
+  avc.avc_config_record.version = 1;
+  avc.avc_config_record.profile_indication = kProfileIndication;
+  avc.avc_config_record.profile_compatibility = kProfileCompatibility;
+  avc.avc_config_record.avc_level = kLevelIndication;
+  avc.avc_config_record.length_size = kNALUUnitLength;
+
+  std::vector<uint8_t> sps(std::begin(kSPS), std::end(kSPS));
+  avc.avc_config_record.sps_list.emplace_back(sps);
+
+  std::vector<uint8_t> pps(std::begin(kPPS), std::end(kPPS));
+  avc.avc_config_record.pps_list.emplace_back(pps);
+
+  avc.avc_config_record.chroma_format = kChromaFormat;
+  avc.avc_config_record.bit_depth_luma_minus8 = kLumaMinus8;
+  avc.avc_config_record.bit_depth_chroma_minus8 = kChromaMinus8;
+
+  Mp4MovieAVCDecoderConfigurationBoxWriter box_writer(*context(), avc);
+  FlushAndWait(&box_writer);
+
+  // MediaInformation will have multiple sample boxes even though they
+  // not added exclusively.
+  std::unique_ptr<mp4::BoxReader> box_reader(
+      mp4::BoxReader::ReadConcatentatedBoxes(written_data.data(),
+                                             written_data.size(), nullptr));
+
+  EXPECT_TRUE(box_reader->ScanChildren());
+
+  mp4::AVCDecoderConfigurationRecord avc_config_reader;
+  EXPECT_TRUE(box_reader->ReadChild(&avc_config_reader));
+
+  EXPECT_EQ(kProfileIndication, avc_config_reader.profile_indication);
+  EXPECT_EQ(kProfileCompatibility, avc_config_reader.profile_compatibility);
+  EXPECT_EQ(kLevelIndication, avc_config_reader.avc_level);
+  EXPECT_EQ(kNALUUnitLength, avc_config_reader.length_size);
+
+  EXPECT_EQ(1u, avc_config_reader.sps_list.size());
+  EXPECT_EQ(1u, avc_config_reader.pps_list.size());
+  std::vector<uint8_t> sps1 = avc_config_reader.sps_list[0];
+  std::vector<uint8_t> pps1 = avc_config_reader.pps_list[0];
+  EXPECT_EQ(std::vector<uint8_t>(std::begin(kSPS), std::end(kSPS)), sps1);
+  EXPECT_EQ(std::vector<uint8_t>(std::begin(kPPS), std::end(kPPS)), pps1);
+
+  EXPECT_EQ((kChromaFormat & 0x3), avc_config_reader.chroma_format);
+  EXPECT_EQ((kLumaMinus8 & 0x7), avc_config_reader.bit_depth_luma_minus8);
+  EXPECT_EQ((kChromaMinus8 & 0x7), avc_config_reader.bit_depth_chroma_minus8);
+  EXPECT_EQ(0u, avc_config_reader.sps_ext_list.size());
+}
+
+#endif
+
 }  // namespace media
diff --git a/net/cert/x509_cert_types.cc b/net/cert/x509_cert_types.cc
index e2d6c8a..d857ae4 100644
--- a/net/cert/x509_cert_types.cc
+++ b/net/cert/x509_cert_types.cc
@@ -13,6 +13,8 @@
 
 CertPrincipal::CertPrincipal(const std::string& name) : common_name(name) {}
 
+CertPrincipal::CertPrincipal(CertPrincipal&&) = default;
+
 CertPrincipal::~CertPrincipal() = default;
 
 bool CertPrincipal::ParseDistinguishedName(
diff --git a/net/cert/x509_cert_types.h b/net/cert/x509_cert_types.h
index 2dd6d3c9..e18b8dab 100644
--- a/net/cert/x509_cert_types.h
+++ b/net/cert/x509_cert_types.h
@@ -17,6 +17,7 @@
 struct NET_EXPORT CertPrincipal {
   CertPrincipal();
   explicit CertPrincipal(const std::string& name);
+  CertPrincipal(CertPrincipal&&);
   ~CertPrincipal();
 
   // Configures handling of PrintableString values in the DistinguishedName. Do
diff --git a/net/cert/x509_certificate.cc b/net/cert/x509_certificate.cc
index dc406a0..78d6285 100644
--- a/net/cert/x509_certificate.cc
+++ b/net/cert/x509_certificate.cc
@@ -133,12 +133,8 @@
 scoped_refptr<X509Certificate> X509Certificate::CreateFromBuffer(
     bssl::UniquePtr<CRYPTO_BUFFER> cert_buffer,
     std::vector<bssl::UniquePtr<CRYPTO_BUFFER>> intermediates) {
-  DCHECK(cert_buffer);
-  auto cert = base::WrapRefCounted(
-      new X509Certificate(std::move(cert_buffer), std::move(intermediates)));
-  if (!cert->cert_buffer())
-    return nullptr;  // Initialize() failed.
-  return cert;
+  return CreateFromBufferUnsafeOptions(std::move(cert_buffer),
+                                       std::move(intermediates), {});
 }
 
 // static
@@ -147,11 +143,12 @@
     std::vector<bssl::UniquePtr<CRYPTO_BUFFER>> intermediates,
     UnsafeCreateOptions options) {
   DCHECK(cert_buffer);
-  auto cert = base::WrapRefCounted(new X509Certificate(
-      std::move(cert_buffer), std::move(intermediates), options));
-  if (!cert->cert_buffer())
-    return nullptr;  // Initialize() failed.
-  return cert;
+  ParsedFields parsed;
+  if (!parsed.Initialize(cert_buffer.get(), options)) {
+    return nullptr;
+  }
+  return base::WrapRefCounted(new X509Certificate(
+      std::move(parsed), std::move(cert_buffer), std::move(intermediates)));
 }
 
 // static
@@ -706,32 +703,28 @@
 }
 
 X509Certificate::X509Certificate(
+    ParsedFields parsed,
     bssl::UniquePtr<CRYPTO_BUFFER> cert_buffer,
     std::vector<bssl::UniquePtr<CRYPTO_BUFFER>> intermediates)
-    : X509Certificate(std::move(cert_buffer), std::move(intermediates), {}) {}
-
-X509Certificate::X509Certificate(
-    bssl::UniquePtr<CRYPTO_BUFFER> cert_buffer,
-    std::vector<bssl::UniquePtr<CRYPTO_BUFFER>> intermediates,
-    UnsafeCreateOptions options)
-    : cert_buffer_(std::move(cert_buffer)),
-      intermediate_ca_certs_(std::move(intermediates)) {
-  // Platform-specific initialization.
-  if (!Initialize(options) && cert_buffer_) {
-    // Signal initialization failure by clearing cert_buffer_.
-    cert_buffer_.reset();
-  }
-}
+    : parsed_(std::move(parsed)),
+      cert_buffer_(std::move(cert_buffer)),
+      intermediate_ca_certs_(std::move(intermediates)) {}
 
 X509Certificate::~X509Certificate() = default;
 
-bool X509Certificate::Initialize(UnsafeCreateOptions options) {
+X509Certificate::ParsedFields::ParsedFields() = default;
+X509Certificate::ParsedFields::ParsedFields(ParsedFields&&) = default;
+X509Certificate::ParsedFields::~ParsedFields() = default;
+
+bool X509Certificate::ParsedFields::Initialize(
+    const CRYPTO_BUFFER* cert_buffer,
+    X509Certificate::UnsafeCreateOptions options) {
   der::Input tbs_certificate_tlv;
   der::Input signature_algorithm_tlv;
   der::BitString signature_value;
 
-  if (!ParseCertificate(der::Input(CRYPTO_BUFFER_data(cert_buffer_.get()),
-                                   CRYPTO_BUFFER_len(cert_buffer_.get())),
+  if (!ParseCertificate(der::Input(CRYPTO_BUFFER_data(cert_buffer),
+                                   CRYPTO_BUFFER_len(cert_buffer)),
                         &tbs_certificate_tlv, &signature_algorithm_tlv,
                         &signature_value, nullptr)) {
     return false;
diff --git a/net/cert/x509_certificate.h b/net/cert/x509_certificate.h
index 447683c..afd51d7 100644
--- a/net/cert/x509_certificate.h
+++ b/net/cert/x509_certificate.h
@@ -153,23 +153,23 @@
   void Persist(base::Pickle* pickle) const;
 
   // The serial number, DER encoded, possibly including a leading 00 byte.
-  const std::string& serial_number() const { return serial_number_; }
+  const std::string& serial_number() const { return parsed_.serial_number_; }
 
   // The subject of the certificate.  For HTTPS server certificates, this
   // represents the web server.  The common name of the subject should match
   // the host name of the web server.
-  const CertPrincipal& subject() const { return subject_; }
+  const CertPrincipal& subject() const { return parsed_.subject_; }
 
   // The issuer of the certificate.
-  const CertPrincipal& issuer() const { return issuer_; }
+  const CertPrincipal& issuer() const { return parsed_.issuer_; }
 
   // Time period during which the certificate is valid.  More precisely, this
   // certificate is invalid before the |valid_start| date and invalid after
   // the |valid_expiry| date.
   // If we were unable to parse either date from the certificate (or if the cert
   // lacks either date), the date will be null (i.e., is_null() will be true).
-  const base::Time& valid_start() const { return valid_start_; }
-  const base::Time& valid_expiry() const { return valid_expiry_; }
+  const base::Time& valid_start() const { return parsed_.valid_start_; }
+  const base::Time& valid_expiry() const { return parsed_.valid_expiry_; }
 
   // Gets the subjectAltName extension field from the certificate, if any.
   // For future extension; currently this only returns those name types that
@@ -267,19 +267,39 @@
   FRIEND_TEST_ALL_PREFIXES(X509CertificateNameVerifyTest, VerifyHostname);
   FRIEND_TEST_ALL_PREFIXES(X509CertificateTest, SerialNumbers);
 
+  class ParsedFields {
+   public:
+    ParsedFields();
+    ParsedFields(ParsedFields&&);
+    ~ParsedFields();
+
+    bool Initialize(const CRYPTO_BUFFER* cert_buffer,
+                    UnsafeCreateOptions options);
+
+    // The subject of the certificate.
+    CertPrincipal subject_;
+
+    // The issuer of the certificate.
+    CertPrincipal issuer_;
+
+    // This certificate is not valid before |valid_start_|
+    base::Time valid_start_;
+
+    // This certificate is not valid after |valid_expiry_|
+    base::Time valid_expiry_;
+
+    // The serial number of this certificate, DER encoded.
+    std::string serial_number_;
+  };
+
   // Construct an X509Certificate from a CRYPTO_BUFFER containing the
   // DER-encoded representation.
-  X509Certificate(bssl::UniquePtr<CRYPTO_BUFFER> cert_buffer,
+  X509Certificate(ParsedFields parsed,
+                  bssl::UniquePtr<CRYPTO_BUFFER> cert_buffer,
                   std::vector<bssl::UniquePtr<CRYPTO_BUFFER>> intermediates);
-  X509Certificate(bssl::UniquePtr<CRYPTO_BUFFER> cert_buffer,
-                  std::vector<bssl::UniquePtr<CRYPTO_BUFFER>> intermediates,
-                  UnsafeCreateOptions options);
 
   ~X509Certificate();
 
-  // Common object initialization code.  Called by the constructors only.
-  bool Initialize(UnsafeCreateOptions options);
-
   // Verifies that |hostname| matches one of the certificate names or IP
   // addresses supplied, based on TLS name matching rules - specifically,
   // following http://tools.ietf.org/html/rfc6125.
@@ -292,27 +312,15 @@
                              const std::vector<std::string>& cert_san_dns_names,
                              const std::vector<std::string>& cert_san_ip_addrs);
 
-  // The subject of the certificate.
-  CertPrincipal subject_;
-
-  // The issuer of the certificate.
-  CertPrincipal issuer_;
-
-  // This certificate is not valid before |valid_start_|
-  base::Time valid_start_;
-
-  // This certificate is not valid after |valid_expiry_|
-  base::Time valid_expiry_;
-
-  // The serial number of this certificate, DER encoded.
-  std::string serial_number_;
+  // Fields that were parsed from |cert_buffer_|.
+  const ParsedFields parsed_;
 
   // A handle to the DER encoded certificate data.
-  bssl::UniquePtr<CRYPTO_BUFFER> cert_buffer_;
+  const bssl::UniquePtr<CRYPTO_BUFFER> cert_buffer_;
 
   // Untrusted intermediate certificates associated with this certificate
   // that may be needed for chain building.
-  std::vector<bssl::UniquePtr<CRYPTO_BUFFER>> intermediate_ca_certs_;
+  const std::vector<bssl::UniquePtr<CRYPTO_BUFFER>> intermediate_ca_certs_;
 };
 
 }  // namespace net
diff --git a/net/extras/sqlite/sqlite_persistent_shared_dictionary_store.cc b/net/extras/sqlite/sqlite_persistent_shared_dictionary_store.cc
index b3477384..29aad33 100644
--- a/net/extras/sqlite/sqlite_persistent_shared_dictionary_store.cc
+++ b/net/extras/sqlite/sqlite_persistent_shared_dictionary_store.cc
@@ -123,6 +123,21 @@
                                              static_cast<uint64_t>(token_low));
 }
 
+template <typename ResultType>
+base::OnceCallback<void(ResultType)> WrapCallbackWithWeakPtrCheck(
+    base::WeakPtr<SQLitePersistentSharedDictionaryStore> weak_ptr,
+    base::OnceCallback<void(ResultType)> callback) {
+  return base::BindOnce(
+      [](base::WeakPtr<SQLitePersistentSharedDictionaryStore> weak_ptr,
+         base::OnceCallback<void(ResultType)> callback, ResultType result) {
+        if (!weak_ptr) {
+          return;
+        }
+        std::move(callback).Run(std::move(result));
+      },
+      std::move(weak_ptr), std::move(callback));
+}
+
 }  // namespace
 
 class SQLitePersistentSharedDictionaryStore::Backend
@@ -143,18 +158,34 @@
   Backend(const Backend&) = delete;
   Backend& operator=(const Backend&) = delete;
 
-  void GetTotalDictionarySize(
-      base::OnceCallback<void(base::expected<uint64_t, Error>)> callback);
-  void RegisterDictionary(
-      const SharedDictionaryStorageIsolationKey& isolation_key,
-      SharedDictionaryInfo dictionary_info,
-      base::OnceCallback<void(RegisterDictionaryResultOrError)> callback);
-  void GetDictionaries(
-      const SharedDictionaryStorageIsolationKey& isolation_key,
-      base::OnceCallback<void(DictionaryListOrError)> callback);
-  void GetAllDictionaries(
-      base::OnceCallback<void(DictionaryMapOrError)> callback);
-  void ClearAllDictionaries(base::OnceCallback<void(Error)> callback);
+#define DEFINE_CROSS_SEQUENCE_CALL_METHOD(Name)                              \
+  template <typename ResultType, typename... Args>                           \
+  void Name(base::OnceCallback<void(ResultType)> callback, Args&&... args) { \
+    CHECK(client_task_runner()->RunsTasksInCurrentSequence());               \
+    PostBackgroundTask(                                                      \
+        FROM_HERE,                                                           \
+        base::BindOnce(                                                      \
+            [](scoped_refptr<Backend> backend,                               \
+               base::OnceCallback<void(ResultType)> callback,                \
+               Args&&... args) {                                             \
+              backend->PostClientTask(                                       \
+                  FROM_HERE,                                                 \
+                  base::BindOnce(                                            \
+                      std::move(callback),                                   \
+                      backend->Name##Impl(std::forward<Args>(args)...)));    \
+            },                                                               \
+            scoped_refptr<Backend>(this), std::move(callback),               \
+            std::forward<Args>(args)...));                                   \
+  }
+
+  // The following methods call *Impl() method in the background task runner,
+  // and call the passed `callback` with the result in the client task runner.
+  DEFINE_CROSS_SEQUENCE_CALL_METHOD(GetTotalDictionarySize)
+  DEFINE_CROSS_SEQUENCE_CALL_METHOD(RegisterDictionary)
+  DEFINE_CROSS_SEQUENCE_CALL_METHOD(GetDictionaries)
+  DEFINE_CROSS_SEQUENCE_CALL_METHOD(GetAllDictionaries)
+  DEFINE_CROSS_SEQUENCE_CALL_METHOD(ClearAllDictionaries)
+#undef DEFINE_CROSS_SEQUENCE_CALL_METHOD
 
  private:
   ~Backend() override = default;
@@ -219,20 +250,6 @@
   // for updating `last_used_time`.
 }
 
-void SQLitePersistentSharedDictionaryStore::Backend::GetTotalDictionarySize(
-    base::OnceCallback<void(base::expected<uint64_t, Error>)> callback) {
-  if (!background_task_runner()->RunsTasksInCurrentSequence()) {
-    CHECK(client_task_runner()->RunsTasksInCurrentSequence());
-    PostBackgroundTask(FROM_HERE,
-                       base::BindOnce(&Backend::GetTotalDictionarySize, this,
-                                      std::move(callback)));
-    return;
-  }
-
-  PostClientTask(FROM_HERE, base::BindOnce(std::move(callback),
-                                           GetTotalDictionarySizeImpl()));
-}
-
 base::expected<uint64_t, SQLitePersistentSharedDictionaryStore::Error>
 SQLitePersistentSharedDictionaryStore::Backend::GetTotalDictionarySizeImpl() {
   CHECK(background_task_runner()->RunsTasksInCurrentSequence());
@@ -251,25 +268,6 @@
   return base::ok(static_cast<uint64_t>(unsigned_total_dictionary_size));
 }
 
-void SQLitePersistentSharedDictionaryStore::Backend::RegisterDictionary(
-    const SharedDictionaryStorageIsolationKey& isolation_key,
-    SharedDictionaryInfo dictionary_info,
-    base::OnceCallback<void(RegisterDictionaryResultOrError)> callback) {
-  CHECK(!dictionary_info.primary_key_in_database().has_value());
-  if (!background_task_runner()->RunsTasksInCurrentSequence()) {
-    CHECK(client_task_runner()->RunsTasksInCurrentSequence());
-    PostBackgroundTask(
-        FROM_HERE,
-        base::BindOnce(&Backend::RegisterDictionary, this, isolation_key,
-                       std::move(dictionary_info), std::move(callback)));
-    return;
-  }
-  PostClientTask(
-      FROM_HERE,
-      base::BindOnce(std::move(callback),
-                     RegisterDictionaryImpl(isolation_key, dictionary_info)));
-}
-
 SQLitePersistentSharedDictionaryStore::RegisterDictionaryResultOrError
 SQLitePersistentSharedDictionaryStore::Backend::RegisterDictionaryImpl(
     const SharedDictionaryStorageIsolationKey& isolation_key,
@@ -358,20 +356,6 @@
                                .total_dictionary_size = total_dictionary_size});
 }
 
-void SQLitePersistentSharedDictionaryStore::Backend::GetDictionaries(
-    const SharedDictionaryStorageIsolationKey& isolation_key,
-    base::OnceCallback<void(DictionaryListOrError)> callback) {
-  if (!background_task_runner()->RunsTasksInCurrentSequence()) {
-    CHECK(client_task_runner()->RunsTasksInCurrentSequence());
-    PostBackgroundTask(FROM_HERE,
-                       base::BindOnce(&Backend::GetDictionaries, this,
-                                      isolation_key, std::move(callback)));
-    return;
-  }
-  PostClientTask(FROM_HERE, base::BindOnce(std::move(callback),
-                                           GetDictionariesImpl(isolation_key)));
-}
-
 SQLitePersistentSharedDictionaryStore::DictionaryListOrError
 SQLitePersistentSharedDictionaryStore::Backend::GetDictionariesImpl(
     const SharedDictionaryStorageIsolationKey& isolation_key) {
@@ -436,18 +420,6 @@
   return base::ok(std::move(result));
 }
 
-void SQLitePersistentSharedDictionaryStore::Backend::GetAllDictionaries(
-    base::OnceCallback<void(DictionaryMapOrError)> callback) {
-  if (!background_task_runner()->RunsTasksInCurrentSequence()) {
-    CHECK(client_task_runner()->RunsTasksInCurrentSequence());
-    PostBackgroundTask(FROM_HERE, base::BindOnce(&Backend::GetAllDictionaries,
-                                                 this, std::move(callback)));
-    return;
-  }
-  PostClientTask(FROM_HERE,
-                 base::BindOnce(std::move(callback), GetAllDictionariesImpl()));
-}
-
 SQLitePersistentSharedDictionaryStore::DictionaryMapOrError
 SQLitePersistentSharedDictionaryStore::Backend::GetAllDictionariesImpl() {
   CHECK(background_task_runner()->RunsTasksInCurrentSequence());
@@ -521,18 +493,6 @@
   return base::ok(std::move(result));
 }
 
-void SQLitePersistentSharedDictionaryStore::Backend::ClearAllDictionaries(
-    base::OnceCallback<void(Error)> callback) {
-  if (!background_task_runner()->RunsTasksInCurrentSequence()) {
-    CHECK(client_task_runner()->RunsTasksInCurrentSequence());
-    PostBackgroundTask(FROM_HERE, base::BindOnce(&Backend::ClearAllDictionaries,
-                                                 this, std::move(callback)));
-    return;
-  }
-  PostClientTask(FROM_HERE, base::BindOnce(std::move(callback),
-                                           ClearAllDictionariesImpl()));
-}
-
 SQLitePersistentSharedDictionaryStore::Error
 SQLitePersistentSharedDictionaryStore::Backend::ClearAllDictionariesImpl() {
   CHECK(background_task_runner()->RunsTasksInCurrentSequence());
@@ -644,16 +604,8 @@
 void SQLitePersistentSharedDictionaryStore::GetTotalDictionarySize(
     base::OnceCallback<void(base::expected<uint64_t, Error>)> callback) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  backend_->GetTotalDictionarySize(base::BindOnce(
-      [](base::WeakPtr<SQLitePersistentSharedDictionaryStore> weak_ptr,
-         base::OnceCallback<void(base::expected<uint64_t, Error>)> callback,
-         base::expected<uint64_t, Error> result) {
-        if (!weak_ptr) {
-          return;
-        }
-        std::move(callback).Run(std::move(result));
-      },
-      GetWeakPtr(), std::move(callback)));
+  backend_->GetTotalDictionarySize(
+      WrapCallbackWithWeakPtrCheck(GetWeakPtr(), std::move(callback)));
 }
 
 void SQLitePersistentSharedDictionaryStore::RegisterDictionary(
@@ -662,17 +614,8 @@
     base::OnceCallback<void(RegisterDictionaryResultOrError)> callback) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   backend_->RegisterDictionary(
-      isolation_key, std::move(dictionary_info),
-      base::BindOnce(
-          [](base::WeakPtr<SQLitePersistentSharedDictionaryStore> weak_ptr,
-             base::OnceCallback<void(RegisterDictionaryResultOrError)> callback,
-             RegisterDictionaryResultOrError result) {
-            if (!weak_ptr) {
-              return;
-            }
-            std::move(callback).Run(std::move(result));
-          },
-          GetWeakPtr(), std::move(callback)));
+      WrapCallbackWithWeakPtrCheck(GetWeakPtr(), std::move(callback)),
+      isolation_key, std::move(dictionary_info));
 }
 
 void SQLitePersistentSharedDictionaryStore::GetDictionaries(
@@ -680,46 +623,22 @@
     base::OnceCallback<void(DictionaryListOrError)> callback) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   backend_->GetDictionaries(
-      isolation_key,
-      base::BindOnce(
-          [](base::WeakPtr<SQLitePersistentSharedDictionaryStore> weak_ptr,
-             base::OnceCallback<void(DictionaryListOrError)> callback,
-             DictionaryListOrError result) {
-            if (!weak_ptr) {
-              return;
-            }
-            std::move(callback).Run(std::move(result));
-          },
-          GetWeakPtr(), std::move(callback)));
+      WrapCallbackWithWeakPtrCheck(GetWeakPtr(), std::move(callback)),
+      isolation_key);
 }
 
 void SQLitePersistentSharedDictionaryStore::GetAllDictionaries(
     base::OnceCallback<void(DictionaryMapOrError)> callback) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  backend_->GetAllDictionaries(base::BindOnce(
-      [](base::WeakPtr<SQLitePersistentSharedDictionaryStore> weak_ptr,
-         base::OnceCallback<void(DictionaryMapOrError)> callback,
-         DictionaryMapOrError result) {
-        if (!weak_ptr) {
-          return;
-        }
-        std::move(callback).Run(std::move(result));
-      },
-      GetWeakPtr(), std::move(callback)));
+  backend_->GetAllDictionaries(
+      WrapCallbackWithWeakPtrCheck(GetWeakPtr(), std::move(callback)));
 }
 
 void SQLitePersistentSharedDictionaryStore::ClearAllDictionaries(
     base::OnceCallback<void(Error)> callback) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  backend_->ClearAllDictionaries(base::BindOnce(
-      [](base::WeakPtr<SQLitePersistentSharedDictionaryStore> weak_ptr,
-         base::OnceCallback<void(Error)> callback, Error error) {
-        if (!weak_ptr) {
-          return;
-        }
-        std::move(callback).Run(error);
-      },
-      GetWeakPtr(), std::move(callback)));
+  backend_->ClearAllDictionaries(
+      WrapCallbackWithWeakPtrCheck(GetWeakPtr(), std::move(callback)));
 }
 
 base::WeakPtr<SQLitePersistentSharedDictionaryStore>
diff --git a/pdf/pdfium/pdfium_page.cc b/pdf/pdfium/pdfium_page.cc
index 190366f..0a210a8 100644
--- a/pdf/pdfium/pdfium_page.cc
+++ b/pdf/pdfium/pdfium_page.cc
@@ -487,9 +487,6 @@
           ? GetFloatCharRectInPixels(page, text_page, start_char_index)
           : gfx::RectF();
 
-  // Pdfium trims more than 1 consecutive spaces to 1 space.
-  DCHECK_LE(actual_start_char_index - start_char_index, 1);
-
   int char_index = actual_start_char_index;
 
   // Set text run's style info from the first character of the text run.
diff --git a/remoting/host/crash/crash_directory_watcher.cc b/remoting/host/crash/crash_directory_watcher.cc
index 8da4c2b..d9dce64 100644
--- a/remoting/host/crash/crash_directory_watcher.cc
+++ b/remoting/host/crash/crash_directory_watcher.cc
@@ -121,9 +121,7 @@
   // json file to a new directory when they are ready to be uploaded and we
   // don't want to enumerate any files which have already been moved.
   base::FileEnumerator metadata_file_enumerator(
-      path, /*recursive=*/false,
-      base::FileEnumerator::FileType::FILES |
-          base::FileEnumerator::FileType::NAMES_ONLY,
+      path, /*recursive=*/false, base::FileEnumerator::FileType::FILES,
       FILE_PATH_LITERAL("*.json"));
 
   std::vector<std::string> crash_guids;
diff --git a/testing/buildbot/chrome.gpu.fyi.json b/testing/buildbot/chrome.gpu.fyi.json
index e21c9585..f85e4fb2 100644
--- a/testing/buildbot/chrome.gpu.fyi.json
+++ b/testing/buildbot/chrome.gpu.fyi.json
@@ -119,7 +119,6 @@
           "--extra-browser-args=--log-level=0 --js-flags=--expose-gc --use-gl=angle --use-angle=gles --use-cmd-decoder=passthrough --force_high_performance_gpu",
           "--webgl-conformance-version=2.0.1",
           "--read-abbreviated-json-results-from=../../content/test/data/gpu/webgl2_conformance_tests_output.json",
-          "--test-filter=conformance/rendering/*::conformance2/rendering/*",
           "--jobs=1",
           "--deploy-lacros",
           "--remote=variable_chromeos_device_hostname"
@@ -164,7 +163,6 @@
           "--stable-jobs",
           "--extra-browser-args=--log-level=0 --js-flags=--expose-gc --use-gl=angle --use-angle=gles --use-cmd-decoder=passthrough --force_high_performance_gpu",
           "--read-abbreviated-json-results-from=../../content/test/data/gpu/webgl_conformance_tests_output.json",
-          "--test-filter=conformance/rendering/*",
           "--jobs=1",
           "--deploy-lacros",
           "--remote=variable_chromeos_device_hostname"
@@ -319,7 +317,6 @@
           "--extra-browser-args=--log-level=0 --js-flags=--expose-gc --use-gl=angle --use-angle=gles --use-cmd-decoder=passthrough --force_high_performance_gpu",
           "--webgl-conformance-version=2.0.1",
           "--read-abbreviated-json-results-from=../../content/test/data/gpu/webgl2_conformance_tests_output.json",
-          "--test-filter=conformance/rendering/*::conformance2/rendering/*",
           "--jobs=1",
           "--deploy-lacros",
           "--remote=variable_chromeos_device_hostname"
@@ -364,7 +361,6 @@
           "--stable-jobs",
           "--extra-browser-args=--log-level=0 --js-flags=--expose-gc --use-gl=angle --use-angle=gles --use-cmd-decoder=passthrough --force_high_performance_gpu",
           "--read-abbreviated-json-results-from=../../content/test/data/gpu/webgl_conformance_tests_output.json",
-          "--test-filter=conformance/rendering/*",
           "--jobs=1",
           "--deploy-lacros",
           "--remote=variable_chromeos_device_hostname"
diff --git a/testing/buildbot/filters/pixel_tests.filter b/testing/buildbot/filters/pixel_tests.filter
index cfb7364f..edc48c9 100644
--- a/testing/buildbot/filters/pixel_tests.filter
+++ b/testing/buildbot/filters/pixel_tests.filter
@@ -35,6 +35,7 @@
 GlobalErrorBubbleTest.*
 HatsBubbleTest.*
 *HighEfficiencyFaviconTreatmentTest.*
+*HighEfficiencyMemorySavingsReportingImprovementsTest.*
 HomeButtonUiTest.*
 HungRendererDialogViewBrowserTest.*
 ImportLockDialogViewBrowserTest.*
diff --git a/testing/buildbot/test_suite_exceptions.pyl b/testing/buildbot/test_suite_exceptions.pyl
index 491988f3..02c3ab0 100644
--- a/testing/buildbot/test_suite_exceptions.pyl
+++ b/testing/buildbot/test_suite_exceptions.pyl
@@ -4147,6 +4147,22 @@
       },
     },
   },
+  'webgl2_conformance_gles_passthrough_tests Ash ToT': {
+    'modifications': {
+      # Not enough capacity.
+      'Lacros FYI Release (jacuzzi)': {
+        'swarming': {
+          'shards': 1,
+        },
+      },
+      # Not enough capacity.
+      'Lacros FYI Release (octopus)': {
+        'swarming': {
+          'shards': 1,
+        },
+      },
+    },
+  },
   'webgl2_conformance_metal_passthrough_tests': {
     'remove_from': [
       # crbug.com/1270755
@@ -4237,6 +4253,22 @@
       },
     },
   },
+  'webgl_conformance_gles_passthrough_tests Ash ToT': {
+    'modifications': {
+      # Not enough capacity.
+      'Lacros FYI Release (jacuzzi)': {
+        'swarming': {
+          'shards': 1,
+        },
+      },
+      # Not enough capacity.
+      'Lacros FYI Release (octopus)': {
+        'swarming': {
+          'shards': 1,
+        },
+      },
+    },
+  },
   'webgl_conformance_metal_passthrough_tests': {
     'remove_from': [
       # crbug.com/1270755
diff --git a/testing/buildbot/test_suites.pyl b/testing/buildbot/test_suites.pyl
index 2e3509f..82f2a42e 100644
--- a/testing/buildbot/test_suites.pyl
+++ b/testing/buildbot/test_suites.pyl
@@ -3462,32 +3462,6 @@
       },
     },
 
-    'gpu_webgl2_conformance_gles_passthrough_representative_telemetry_tests': {
-      'webgl2_conformance_gles_passthrough_tests': {
-        'telemetry_test_name': 'webgl2_conformance',
-        'args': [
-          '--webgl-conformance-version=2.0.1',
-          # The current working directory when run via isolate is
-          # out/Debug or out/Release. Reference this file relatively to
-          # it.
-          '--read-abbreviated-json-results-from=../../content/test/data/gpu/webgl2_conformance_tests_output.json',
-          # On dual-GPU devices we want the high-performance GPU to be active
-          '--extra-browser-args=--use-gl=angle --use-angle=gles --use-cmd-decoder=passthrough --force_high_performance_gpu',
-          '--test-filter=conformance/rendering/*::conformance2/rendering/*',
-          '$$MAGIC_SUBSTITUTION_GPUParallelJobs',
-        ],
-        'chromeos_args': [
-          '$$MAGIC_SUBSTITUTION_ChromeOSTelemetryRemote',
-        ],
-        'mixins': [
-          'has_native_resultdb_integration',
-        ],
-        'swarming': {
-          'shards': 1,
-        },
-      },
-    },
-
     'gpu_webgl2_conformance_gles_passthrough_telemetry_tests': {
       'webgl2_conformance_gles_passthrough_tests': {
         'telemetry_test_name': 'webgl2_conformance',
@@ -3653,31 +3627,6 @@
       },
     },
 
-    'gpu_webgl_conformance_gles_passthrough_representative_telemetry_tests': {
-      'webgl_conformance_gles_passthrough': {
-        'telemetry_test_name': 'webgl1_conformance',
-        'args': [
-          # On dual-GPU devices we want the high-performance GPU to be active
-          '--extra-browser-args=--use-gl=angle --use-angle=gles --use-cmd-decoder=passthrough --force_high_performance_gpu',
-          # The current working directory when run via isolate is
-          # out/Debug or out/Release. Reference this file relatively to
-          # it.
-          '--read-abbreviated-json-results-from=../../content/test/data/gpu/webgl_conformance_tests_output.json',
-          '--test-filter=conformance/rendering/*',
-          '$$MAGIC_SUBSTITUTION_GPUParallelJobs',
-        ],
-        'chromeos_args': [
-          '$$MAGIC_SUBSTITUTION_ChromeOSTelemetryRemote',
-        ],
-        'mixins': [
-          'has_native_resultdb_integration',
-        ],
-        'swarming': {
-          'shards': 1,
-        },
-      },
-    },
-
     'gpu_webgl_conformance_gles_passthrough_telemetry_tests': {
       'webgl_conformance_gles_passthrough': {
         'telemetry_test_name': 'webgl1_conformance',
@@ -3693,6 +3642,9 @@
         'android_args': [
           '$$MAGIC_SUBSTITUTION_GPUTelemetryNoRootForUnrootedDevices',
         ],
+        'chromeos_args': [
+          '$$MAGIC_SUBSTITUTION_ChromeOSTelemetryRemote',
+        ],
         'lacros_args': [
           '--extra-browser-args=--enable-features=UseOzonePlatform --ozone-platform=wayland',
           '--xvfb',
@@ -6894,12 +6846,12 @@
           'LACROS_ASH_TOT',
         ],
       },
-      'gpu_webgl_conformance_gles_passthrough_representative_telemetry_tests': {
+      'gpu_webgl_conformance_gles_passthrough_telemetry_tests': {
         'variants': [
           'LACROS_ASH_TOT',
         ],
       },
-      'gpu_webgl2_conformance_gles_passthrough_representative_telemetry_tests': {
+      'gpu_webgl2_conformance_gles_passthrough_telemetry_tests': {
         'variants': [
           'LACROS_ASH_TOT',
           ],
diff --git a/testing/variations/fieldtrial_testing_config.json b/testing/variations/fieldtrial_testing_config.json
index dc78d30..910c098 100644
--- a/testing/variations/fieldtrial_testing_config.json
+++ b/testing/variations/fieldtrial_testing_config.json
@@ -121,6 +121,21 @@
             ]
         }
     ],
+    "AllowQt": [
+        {
+            "platforms": [
+                "linux"
+            ],
+            "experiments": [
+                {
+                    "name": "Enabled",
+                    "enable_features": [
+                        "AllowQt"
+                    ]
+                }
+            ]
+        }
+    ],
     "AllowSpeechSynthesisIntoBFCache": [
         {
             "platforms": [
@@ -5043,6 +5058,22 @@
             ]
         }
     ],
+    "EnableGetDebugdLogsInParallel": [
+        {
+            "platforms": [
+                "chromeos",
+                "chromeos_lacros"
+            ],
+            "experiments": [
+                {
+                    "name": "Enabled_20230519",
+                    "enable_features": [
+                        "EnableGetDebugdLogsInParallel"
+                    ]
+                }
+            ]
+        }
+    ],
     "EnableMSAAOnNewIntelGPUs": [
         {
             "platforms": [
@@ -10516,6 +10547,26 @@
             ]
         }
     ],
+    "PriceTrackingChipStudy": [
+        {
+            "platforms": [
+                "linux",
+                "mac",
+                "windows"
+            ],
+            "experiments": [
+                {
+                    "name": "EnabledWithChipDelay_20230428",
+                    "params": {
+                        "price-tracking-chip-experiment-variation": "1"
+                    },
+                    "enable_features": [
+                        "CommercePriceTrackingChipExperiment"
+                    ]
+                }
+            ]
+        }
+    ],
     "PriceTrackingPageActionIconLabelFeatureIPH": [
         {
             "platforms": [
diff --git a/third_party/blink/public/devtools_protocol/browser_protocol.pdl b/third_party/blink/public/devtools_protocol/browser_protocol.pdl
index 0523cf85..39b6563 100644
--- a/third_party/blink/public/devtools_protocol/browser_protocol.pdl
+++ b/third_party/blink/public/devtools_protocol/browser_protocol.pdl
@@ -2047,6 +2047,7 @@
       StyleSheetId styleSheetId
 
 experimental domain CacheStorage
+  depends on Storage
 
   # Unique identifier of the Cache object.
   type CacheId extends string
@@ -2090,6 +2091,8 @@
       string securityOrigin
       # Storage key of the cache.
       string storageKey
+      # Storage bucket of the cache.
+      optional Storage.StorageBucket storageBucket
       # The name of the cache.
       string cacheName
 
@@ -2121,11 +2124,13 @@
   # Requests cache names.
   command requestCacheNames
     parameters
-      # At least and at most one of securityOrigin, storageKey must be specified.
+      # At least and at most one of securityOrigin, storageKey, storageBucket must be specified.
       # Security origin.
       optional string securityOrigin
       # Storage key.
       optional string storageKey
+      # Storage bucket. If not specified, it uses the default bucket.
+      optional Storage.StorageBucket storageBucket
     returns
       # Caches for the security origin.
       array of Cache caches
@@ -9432,6 +9437,8 @@
       string origin
       # Storage key to update.
       string storageKey
+      # Storage bucket to update.
+      string bucketId
       # Name of cache in origin.
       string cacheName
 
@@ -9442,6 +9449,8 @@
       string origin
       # Storage key to update.
       string storageKey
+      # Storage bucket to update.
+      string bucketId
 
   # The origin's IndexedDB object store has been modified.
   event indexedDBContentUpdated
diff --git a/third_party/blink/public/mojom/buckets/bucket_manager_host.mojom b/third_party/blink/public/mojom/buckets/bucket_manager_host.mojom
index 6b1abaf1..da7cb43 100644
--- a/third_party/blink/public/mojom/buckets/bucket_manager_host.mojom
+++ b/third_party/blink/public/mojom/buckets/bucket_manager_host.mojom
@@ -104,8 +104,7 @@
       => (pending_remote<BucketHost>? remote, BucketError error);
 
   // Gets bucket without creating or updating it.
-  GetBucketForDevtools(string name)
-      => (pending_remote<BucketHost>? remote, BucketError error);
+  GetBucketForDevtools(string name, pending_receiver<BucketHost> receiver);
 
   // Returns a list of stored bucket names in alphabetical order.
   Keys() => (array<string> buckets, bool success);
diff --git a/third_party/blink/renderer/bindings/core/v8/scheduled_action.cc b/third_party/blink/renderer/bindings/core/v8/scheduled_action.cc
index a7e159e..bb100bcf 100644
--- a/third_party/blink/renderer/bindings/core/v8/scheduled_action.cc
+++ b/third_party/blink/renderer/bindings/core/v8/scheduled_action.cc
@@ -51,7 +51,7 @@
 namespace blink {
 
 ScheduledAction::ScheduledAction(ScriptState* script_state,
-                                 ExecutionContext* target,
+                                 ExecutionContext& target,
                                  V8Function* handler,
                                  const HeapVector<ScriptValue>& arguments)
     : script_state_(
@@ -59,7 +59,7 @@
   if (script_state->World().IsWorkerWorld() ||
       BindingSecurity::ShouldAllowAccessTo(
           EnteredDOMWindow(script_state->GetIsolate()),
-          To<LocalDOMWindow>(target))) {
+          To<LocalDOMWindow>(&target))) {
     function_ = handler;
     arguments_ = arguments;
     auto* tracker = ThreadScheduler::Current()->GetTaskAttributionTracker();
@@ -73,14 +73,14 @@
 }
 
 ScheduledAction::ScheduledAction(ScriptState* script_state,
-                                 ExecutionContext* target,
+                                 ExecutionContext& target,
                                  const String& handler)
     : script_state_(
           MakeGarbageCollected<ScriptStateProtectingContext>(script_state)) {
   if (script_state->World().IsWorkerWorld() ||
       BindingSecurity::ShouldAllowAccessTo(
           EnteredDOMWindow(script_state->GetIsolate()),
-          To<LocalDOMWindow>(target))) {
+          To<LocalDOMWindow>(&target))) {
     code_ = handler;
     auto* tracker = ThreadScheduler::Current()->GetTaskAttributionTracker();
     if (tracker && script_state->World().IsMainWorld()) {
diff --git a/third_party/blink/renderer/bindings/core/v8/scheduled_action.h b/third_party/blink/renderer/bindings/core/v8/scheduled_action.h
index 44a2c24..00e15815 100644
--- a/third_party/blink/renderer/bindings/core/v8/scheduled_action.h
+++ b/third_party/blink/renderer/bindings/core/v8/scheduled_action.h
@@ -53,11 +53,11 @@
 
  public:
   ScheduledAction(ScriptState*,
-                  ExecutionContext* target,
+                  ExecutionContext& target,
                   V8Function* handler,
                   const HeapVector<ScriptValue>& arguments);
   ScheduledAction(ScriptState*,
-                  ExecutionContext* target,
+                  ExecutionContext& target,
                   const String& handler);
 
   ScheduledAction(const ScheduledAction&) = delete;
diff --git a/third_party/blink/renderer/bindings/core/v8/v8_intersection_observer_delegate.h b/third_party/blink/renderer/bindings/core/v8/v8_intersection_observer_delegate.h
index ab7b097..b3fac9f 100644
--- a/third_party/blink/renderer/bindings/core/v8/v8_intersection_observer_delegate.h
+++ b/third_party/blink/renderer/bindings/core/v8/v8_intersection_observer_delegate.h
@@ -38,6 +38,12 @@
     return IntersectionObserver::kPostTaskToDeliver;
   }
 
+  // The IntersectionObserver spec requires that at least one observation be
+  // recorded after observe() is called, even if the target is detached.
+  bool NeedsInitialObservationWithDetachedTarget() const override {
+    return true;
+  }
+
   void Deliver(const HeapVector<Member<IntersectionObserverEntry>>&,
                IntersectionObserver&) override;
 
diff --git a/third_party/blink/renderer/core/editing/caret_display_item_client.cc b/third_party/blink/renderer/core/editing/caret_display_item_client.cc
index 918bb6d3..bf8e8f6 100644
--- a/third_party/blink/renderer/core/editing/caret_display_item_client.cc
+++ b/third_party/blink/renderer/core/editing/caret_display_item_client.cc
@@ -272,7 +272,7 @@
                                                     display_item_type))
       return;
     recorder.emplace(context, *this, display_item_type,
-                     ToEnclosingRect(drawing_rect));
+                     ToPixelSnappedRect(drawing_rect));
   }
 
   gfx::Rect paint_rect = ToPixelSnappedRect(drawing_rect);
diff --git a/third_party/blink/renderer/core/execution_context/execution_context.cc b/third_party/blink/renderer/core/execution_context/execution_context.cc
index e7941e1..a95f240 100644
--- a/third_party/blink/renderer/core/execution_context/execution_context.cc
+++ b/third_party/blink/renderer/core/execution_context/execution_context.cc
@@ -548,7 +548,6 @@
   visitor->Trace(public_url_manager_);
   visitor->Trace(pending_exceptions_);
   visitor->Trace(csp_delegate_);
-  visitor->Trace(timers_);
   visitor->Trace(origin_trial_context_);
   visitor->Trace(content_security_policy_);
   visitor->Trace(runtime_feature_state_override_context_);
diff --git a/third_party/blink/renderer/core/execution_context/execution_context.h b/third_party/blink/renderer/core/execution_context/execution_context.h
index 55a7d64..54d84eb4 100644
--- a/third_party/blink/renderer/core/execution_context/execution_context.h
+++ b/third_party/blink/renderer/core/execution_context/execution_context.h
@@ -46,7 +46,6 @@
 #include "third_party/blink/renderer/bindings/core/v8/sanitize_script_errors.h"
 #include "third_party/blink/renderer/core/core_export.h"
 #include "third_party/blink/renderer/core/execution_context/security_context.h"
-#include "third_party/blink/renderer/core/frame/dom_timer_coordinator.h"
 #include "third_party/blink/renderer/core/frame/web_feature_forward.h"
 #include "third_party/blink/renderer/platform/heap/collection_support/heap_vector.h"
 #include "third_party/blink/renderer/platform/heap_observer_set.h"
@@ -221,15 +220,6 @@
 
   virtual HttpsState GetHttpsState() const = 0;
 
-  // Gets the DOMTimerCoordinator which maintains the "active timer
-  // list" of tasks created by setTimeout and setInterval. The
-  // DOMTimerCoordinator is owned by the ExecutionContext and should
-  // not be used after the ExecutionContext is destroyed.
-  DOMTimerCoordinator* Timers() {
-    DCHECK(!IsWorkletGlobalScope());
-    return &timers_;
-  }
-
   virtual ResourceFetcher* Fetcher() = 0;
 
   SecurityContext& GetSecurityContext() { return security_context_; }
@@ -528,8 +518,6 @@
 
   const Member<ContentSecurityPolicyDelegate> csp_delegate_;
 
-  DOMTimerCoordinator timers_;
-
   // Counter that keeps track of how many window interaction calls are allowed
   // for this ExecutionContext. Callers are expected to call
   // |allowWindowInteraction()| and |consumeWindowInteraction()| in order to
diff --git a/third_party/blink/renderer/core/frame/build.gni b/third_party/blink/renderer/core/frame/build.gni
index 61558b6..a4f4c26e 100644
--- a/third_party/blink/renderer/core/frame/build.gni
+++ b/third_party/blink/renderer/core/frame/build.gni
@@ -55,8 +55,6 @@
   "document_policy_violation_report_body.h",
   "dom_timer.cc",
   "dom_timer.h",
-  "dom_timer_coordinator.cc",
-  "dom_timer_coordinator.h",
   "dom_visual_viewport.cc",
   "dom_visual_viewport.h",
   "dom_window.cc",
diff --git a/third_party/blink/renderer/core/frame/dom_timer.cc b/third_party/blink/renderer/core/frame/dom_timer.cc
index 72cd8fc..47d505f 100644
--- a/third_party/blink/renderer/core/frame/dom_timer.cc
+++ b/third_party/blink/renderer/core/frame/dom_timer.cc
@@ -51,38 +51,116 @@
 constexpr base::TimeDelta kMinimumInterval = base::Milliseconds(4);
 constexpr base::TimeDelta kMaxHighResolutionInterval = base::Milliseconds(32);
 
+// Maintains a set of DOMTimers for a given ExecutionContext. Assigns IDs to
+// timers; these IDs are the ones returned to web authors from setTimeout or
+// setInterval. It also tracks recursive creation or iterative scheduling of
+// timers, which is used as a signal for throttling repetitive timers.
+class DOMTimerCoordinator : public GarbageCollected<DOMTimerCoordinator>,
+                            public Supplement<ExecutionContext> {
+ public:
+  constexpr static const char kSupplementName[] = "DOMTimerCoordinator";
+
+  static DOMTimerCoordinator& From(ExecutionContext& context) {
+    CHECK(!context.IsWorkletGlobalScope());
+    auto* coordinator =
+        Supplement<ExecutionContext>::From<DOMTimerCoordinator>(context);
+    if (!coordinator) {
+      coordinator = MakeGarbageCollected<DOMTimerCoordinator>(context);
+      Supplement<ExecutionContext>::ProvideTo(context, coordinator);
+    }
+    return *coordinator;
+  }
+
+  explicit DOMTimerCoordinator(ExecutionContext& context)
+      : Supplement<ExecutionContext>(context) {}
+
+  int Install(DOMTimer* timer) {
+    int timeout_id = NextID();
+    timers_.insert(timeout_id, timer);
+    return timeout_id;
+  }
+
+  // Removes and disposes the timer with the specified ID, if any. This may
+  // destroy the timer.
+  DOMTimer* RemoveTimeoutByID(int timeout_id) {
+    if (timeout_id <= 0) {
+      return nullptr;
+    }
+    DOMTimer* removed_timer = timers_.Take(timeout_id);
+    if (removed_timer) {
+      removed_timer->Stop();
+    }
+    return removed_timer;
+  }
+
+  // Timers created during the execution of other timers, and
+  // repeating timers, are throttled. Timer nesting level tracks the
+  // number of linked timers or repetitions of a timer. See
+  // https://html.spec.whatwg.org/C/#timers
+  int TimerNestingLevel() { return timer_nesting_level_; }
+
+  // Sets the timer nesting level. Set when a timer executes so that
+  // any timers created while the timer is executing will incur a
+  // deeper timer nesting level, see DOMTimer::DOMTimer.
+  void SetTimerNestingLevel(int level) { timer_nesting_level_ = level; }
+
+  void Trace(Visitor* visitor) const final {
+    visitor->Trace(timers_);
+    Supplement<ExecutionContext>::Trace(visitor);
+  }
+
+ private:
+  int NextID() {
+    while (true) {
+      ++circular_sequential_id_;
+
+      if (circular_sequential_id_ <= 0) {
+        circular_sequential_id_ = 1;
+      }
+
+      if (!timers_.Contains(circular_sequential_id_)) {
+        return circular_sequential_id_;
+      }
+    }
+  }
+
+  HeapHashMap<int, Member<DOMTimer>> timers_;
+  int circular_sequential_id_ = 0;
+  int timer_nesting_level_ = 0;
+};
+
 }  // namespace
 
-int DOMTimer::Install(ExecutionContext* context,
+int DOMTimer::Install(ExecutionContext& context,
                       ScheduledAction* action,
                       base::TimeDelta timeout,
                       bool single_shot) {
-  int timeout_id = context->Timers()->InstallNewTimeout(context, action,
-                                                        timeout, single_shot);
-  return timeout_id;
+  return MakeGarbageCollected<DOMTimer>(context, action, timeout, single_shot)
+      ->timeout_id_;
 }
 
-void DOMTimer::RemoveByID(ExecutionContext* context, int timeout_id) {
-  DOMTimer* timer = context->Timers()->RemoveTimeoutByID(timeout_id);
+void DOMTimer::RemoveByID(ExecutionContext& context, int timeout_id) {
   DEVTOOLS_TIMELINE_TRACE_EVENT_INSTANT(
-      "TimerRemove", inspector_timer_remove_event::Data, context, timeout_id);
+      "TimerRemove", inspector_timer_remove_event::Data, &context, timeout_id);
   // Eagerly unregister as ExecutionContext observer.
-  if (timer)
+  if (DOMTimer* timer =
+          DOMTimerCoordinator::From(context).RemoveTimeoutByID(timeout_id)) {
+    // Eagerly unregister as ExecutionContext observer.
     timer->SetExecutionContext(nullptr);
+  }
 }
 
-DOMTimer::DOMTimer(ExecutionContext* context,
+DOMTimer::DOMTimer(ExecutionContext& context,
                    ScheduledAction* action,
                    base::TimeDelta timeout,
-                   bool single_shot,
-                   int timeout_id)
-    : ExecutionContextLifecycleObserver(context),
+                   bool single_shot)
+    : ExecutionContextLifecycleObserver(&context),
       TimerBase(nullptr),
-      timeout_id_(timeout_id),
+      timeout_id_(DOMTimerCoordinator::From(context).Install(this)),
       // Step 9:
-      nesting_level_(context->Timers()->TimerNestingLevel()),
+      nesting_level_(DOMTimerCoordinator::From(context).TimerNestingLevel()),
       action_(action) {
-  DCHECK_GT(timeout_id, 0);
+  DCHECK_GT(timeout_id_, 0);
 
   // Step 10:
   if (timeout.is_negative())
@@ -120,7 +198,7 @@
   } else {
     task_type = TaskType::kJavascriptTimerDelayedLowNesting;
   }
-  MoveToNewTaskRunner(context->GetTaskRunner(task_type));
+  MoveToNewTaskRunner(context.GetTaskRunner(task_type));
 
   // Clamping up to 1ms for historical reasons crbug.com/402694.
   // Removing clamp for single_shot behind a feature flag.
@@ -133,11 +211,11 @@
     StartRepeating(timeout, FROM_HERE, precise);
 
   DEVTOOLS_TIMELINE_TRACE_EVENT_INSTANT(
-      "TimerInstall", inspector_timer_install_event::Data, context, timeout_id,
-      timeout, single_shot);
+      "TimerInstall", inspector_timer_install_event::Data, &context,
+      timeout_id_, timeout, single_shot);
   const char* name = single_shot ? "setTimeout" : "setInterval";
-  async_task_context_.Schedule(context, name);
-  probe::BreakableLocation(context, name);
+  async_task_context_.Schedule(&context, name);
+  probe::BreakableLocation(&context, name);
 }
 
 DOMTimer::~DOMTimer() = default;
@@ -171,7 +249,7 @@
 void DOMTimer::Fired() {
   ExecutionContext* context = GetExecutionContext();
   DCHECK(context);
-  context->Timers()->SetTimerNestingLevel(nesting_level_);
+  DOMTimerCoordinator::From(*context).SetTimerNestingLevel(nesting_level_);
   DCHECK(!context->IsContextPaused());
   // Only the first execution of a multi-shot timer should get an affirmative
   // user gesture indicator.
@@ -223,7 +301,7 @@
     // No access to member variables after this point, it can delete the timer.
     action_->Execute(context);
 
-    context->Timers()->SetTimerNestingLevel(0);
+    DOMTimerCoordinator::From(*context).SetTimerNestingLevel(0);
 
     return;
   }
@@ -231,7 +309,7 @@
   // Unregister the timer from ExecutionContext before executing the action
   // for one-shot timers.
   ScheduledAction* action = action_.Release();
-  context->Timers()->RemoveTimeoutByID(timeout_id_);
+  DOMTimerCoordinator::From(*context).RemoveTimeoutByID(timeout_id_);
 
   action->Execute(context);
 
@@ -243,7 +321,7 @@
   if (!execution_context)
     return;
 
-  execution_context->Timers()->SetTimerNestingLevel(0);
+  DOMTimerCoordinator::From(*execution_context).SetTimerNestingLevel(0);
   // Eagerly unregister as ExecutionContext observer.
   SetExecutionContext(nullptr);
 }
diff --git a/third_party/blink/renderer/core/frame/dom_timer.h b/third_party/blink/renderer/core/frame/dom_timer.h
index 5864b27..176c0544 100644
--- a/third_party/blink/renderer/core/frame/dom_timer.h
+++ b/third_party/blink/renderer/core/frame/dom_timer.h
@@ -50,17 +50,16 @@
  public:
   // Creates a new timer owned by the ExecutionContext, starts it and returns
   // its ID.
-  static int Install(ExecutionContext*,
+  static int Install(ExecutionContext&,
                      ScheduledAction*,
                      base::TimeDelta timeout,
                      bool single_shot);
-  static void RemoveByID(ExecutionContext*, int timeout_id);
+  static void RemoveByID(ExecutionContext&, int timeout_id);
 
-  DOMTimer(ExecutionContext*,
+  DOMTimer(ExecutionContext&,
            ScheduledAction*,
            base::TimeDelta timeout,
-           bool single_shot,
-           int timeout_id);
+           bool single_shot);
   ~DOMTimer() override;
 
   // ExecutionContextLifecycleObserver
diff --git a/third_party/blink/renderer/core/frame/dom_timer_coordinator.cc b/third_party/blink/renderer/core/frame/dom_timer_coordinator.cc
deleted file mode 100644
index d8abb272..0000000
--- a/third_party/blink/renderer/core/frame/dom_timer_coordinator.cc
+++ /dev/null
@@ -1,54 +0,0 @@
-// Copyright 2015 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "third_party/blink/renderer/core/frame/dom_timer_coordinator.h"
-
-#include <algorithm>
-#include <memory>
-#include "third_party/blink/renderer/core/execution_context/execution_context.h"
-#include "third_party/blink/renderer/core/frame/dom_timer.h"
-
-namespace blink {
-
-int DOMTimerCoordinator::InstallNewTimeout(ExecutionContext* context,
-                                           ScheduledAction* action,
-                                           base::TimeDelta timeout,
-                                           bool single_shot) {
-  // FIXME: DOMTimers depends heavily on ExecutionContext. Decouple them.
-  DCHECK_EQ(context->Timers(), this);
-  int timeout_id = NextID();
-  timers_.insert(timeout_id,
-                 MakeGarbageCollected<DOMTimer>(context, action, timeout,
-                                                single_shot, timeout_id));
-  return timeout_id;
-}
-
-DOMTimer* DOMTimerCoordinator::RemoveTimeoutByID(int timeout_id) {
-  if (timeout_id <= 0)
-    return nullptr;
-
-  DOMTimer* removed_timer = timers_.Take(timeout_id);
-  if (removed_timer)
-    removed_timer->Stop();
-
-  return removed_timer;
-}
-
-void DOMTimerCoordinator::Trace(Visitor* visitor) const {
-  visitor->Trace(timers_);
-}
-
-int DOMTimerCoordinator::NextID() {
-  while (true) {
-    ++circular_sequential_id_;
-
-    if (circular_sequential_id_ <= 0)
-      circular_sequential_id_ = 1;
-
-    if (!timers_.Contains(circular_sequential_id_))
-      return circular_sequential_id_;
-  }
-}
-
-}  // namespace blink
diff --git a/third_party/blink/renderer/core/frame/dom_timer_coordinator.h b/third_party/blink/renderer/core/frame/dom_timer_coordinator.h
deleted file mode 100644
index b42db3d5..0000000
--- a/third_party/blink/renderer/core/frame/dom_timer_coordinator.h
+++ /dev/null
@@ -1,66 +0,0 @@
-// Copyright 2015 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef THIRD_PARTY_BLINK_RENDERER_CORE_FRAME_DOM_TIMER_COORDINATOR_H_
-#define THIRD_PARTY_BLINK_RENDERER_CORE_FRAME_DOM_TIMER_COORDINATOR_H_
-
-#include "base/task/single_thread_task_runner.h"
-#include "third_party/blink/renderer/platform/heap/collection_support/heap_hash_map.h"
-#include "third_party/blink/renderer/platform/heap/garbage_collected.h"
-
-namespace blink {
-
-class DOMTimer;
-class ExecutionContext;
-class ScheduledAction;
-
-// Maintains a set of DOMTimers for a given page or
-// worker. DOMTimerCoordinator assigns IDs to timers; these IDs are
-// the ones returned to web authors from setTimeout or setInterval. It
-// also tracks recursive creation or iterative scheduling of timers,
-// which is used as a signal for throttling repetitive timers.
-class DOMTimerCoordinator {
-  DISALLOW_NEW();
-
- public:
-  DOMTimerCoordinator() = default;
-  DOMTimerCoordinator(const DOMTimerCoordinator&) = delete;
-  DOMTimerCoordinator& operator=(const DOMTimerCoordinator&) = delete;
-
-  // Creates and installs a new timer. Returns the assigned ID.
-  int InstallNewTimeout(ExecutionContext*,
-                        ScheduledAction*,
-                        base::TimeDelta timeout,
-                        bool single_shot);
-
-  // Removes and disposes the timer with the specified ID, if any. This may
-  // destroy the timer.
-  DOMTimer* RemoveTimeoutByID(int id);
-
-  // Timers created during the execution of other timers, and
-  // repeating timers, are throttled. Timer nesting level tracks the
-  // number of linked timers or repetitions of a timer. See
-  // https://html.spec.whatwg.org/C/#timers
-  int TimerNestingLevel() { return timer_nesting_level_; }
-
-  // Sets the timer nesting level. Set when a timer executes so that
-  // any timers created while the timer is executing will incur a
-  // deeper timer nesting level, see DOMTimer::DOMTimer.
-  void SetTimerNestingLevel(int level) { timer_nesting_level_ = level; }
-
-  void Trace(Visitor*) const;  // Oilpan.
-
- private:
-  int NextID();
-
-  using TimeoutMap = HeapHashMap<int, Member<DOMTimer>>;
-  TimeoutMap timers_;
-
-  int circular_sequential_id_ = 0;
-  int timer_nesting_level_ = 0;
-};
-
-}  // namespace blink
-
-#endif  // THIRD_PARTY_BLINK_RENDERER_CORE_FRAME_DOM_TIMER_COORDINATOR_H_
diff --git a/third_party/blink/renderer/core/frame/window_or_worker_global_scope.cc b/third_party/blink/renderer/core/frame/window_or_worker_global_scope.cc
index 15d75d61..372d89f 100644
--- a/third_party/blink/renderer/core/frame/window_or_worker_global_scope.cc
+++ b/third_party/blink/renderer/core/frame/window_or_worker_global_scope.cc
@@ -38,7 +38,6 @@
 #include "third_party/blink/renderer/bindings/core/v8/serialization/unpacked_serialized_script_value.h"
 #include "third_party/blink/renderer/bindings/core/v8/v8_gc_for_context_dispose.h"
 #include "third_party/blink/renderer/bindings/core/v8/v8_script_runner.h"
-#include "third_party/blink/renderer/core/dom/events/event_target.h"
 #include "third_party/blink/renderer/core/execution_context/execution_context.h"
 #include "third_party/blink/renderer/core/frame/csp/content_security_policy.h"
 #include "third_party/blink/renderer/core/frame/dom_timer.h"
@@ -56,7 +55,7 @@
 
 namespace blink {
 
-static bool IsAllowed(ExecutionContext* execution_context,
+static bool IsAllowed(ExecutionContext& execution_context,
                       bool is_eval,
                       const String& source) {
   if (auto* window = DynamicTo<LocalDOMWindow>(execution_context)) {
@@ -75,9 +74,8 @@
     }
     return true;
   }
-  if (execution_context->IsWorkerGlobalScope()) {
-    WorkerGlobalScope* worker_global_scope =
-        static_cast<WorkerGlobalScope*>(execution_context);
+  if (auto* worker_global_scope =
+          DynamicTo<WorkerGlobalScope>(execution_context)) {
     if (!worker_global_scope->ScriptController())
       return false;
     ContentSecurityPolicy* policy =
@@ -95,7 +93,7 @@
 }
 
 void WindowOrWorkerGlobalScope::reportError(ScriptState* script_state,
-                                            EventTarget& event_target,
+                                            ExecutionContext&,
                                             const ScriptValue& e) {
   if (!script_state->ContextIsValid()) {
     return;
@@ -104,7 +102,7 @@
   V8ScriptRunner::ReportException(script_state->GetIsolate(), e.V8Value());
 }
 
-String WindowOrWorkerGlobalScope::btoa(EventTarget&,
+String WindowOrWorkerGlobalScope::btoa(ExecutionContext&,
                                        const String& string_to_encode,
                                        ExceptionState& exception_state) {
   if (string_to_encode.IsNull())
@@ -122,7 +120,7 @@
       base::as_bytes(base::make_span(string_to_encode.Latin1())));
 }
 
-String WindowOrWorkerGlobalScope::atob(EventTarget&,
+String WindowOrWorkerGlobalScope::atob(ExecutionContext&,
                                        const String& encoded_string,
                                        ExceptionState& exception_state) {
   if (encoded_string.IsNull())
@@ -148,80 +146,74 @@
 
 int WindowOrWorkerGlobalScope::setTimeout(
     ScriptState* script_state,
-    EventTarget& event_target,
+    ExecutionContext& context,
     V8Function* handler,
     int timeout,
     const HeapVector<ScriptValue>& arguments) {
-  ExecutionContext* execution_context = event_target.GetExecutionContext();
-  if (!IsAllowed(execution_context, false, g_empty_string))
+  if (!IsAllowed(context, false, g_empty_string)) {
     return 0;
-  auto* action = MakeGarbageCollected<ScheduledAction>(
-      script_state, execution_context, handler, arguments);
-  return DOMTimer::Install(execution_context, action,
-                           base::Milliseconds(timeout), true);
+  }
+  auto* action = MakeGarbageCollected<ScheduledAction>(script_state, context,
+                                                       handler, arguments);
+  return DOMTimer::Install(context, action, base::Milliseconds(timeout), true);
 }
 
 int WindowOrWorkerGlobalScope::setTimeout(ScriptState* script_state,
-                                          EventTarget& event_target,
+                                          ExecutionContext& context,
                                           const String& handler,
                                           int timeout,
                                           const HeapVector<ScriptValue>&) {
-  ExecutionContext* execution_context = event_target.GetExecutionContext();
-  if (!IsAllowed(execution_context, true, handler))
+  if (!IsAllowed(context, true, handler)) {
     return 0;
+  }
   // Don't allow setting timeouts to run empty functions.  Was historically a
   // performance issue.
   if (handler.empty())
     return 0;
-  auto* action = MakeGarbageCollected<ScheduledAction>(
-      script_state, execution_context, handler);
-  return DOMTimer::Install(execution_context, action,
-                           base::Milliseconds(timeout), true);
+  auto* action =
+      MakeGarbageCollected<ScheduledAction>(script_state, context, handler);
+  return DOMTimer::Install(context, action, base::Milliseconds(timeout), true);
 }
 
 int WindowOrWorkerGlobalScope::setInterval(
     ScriptState* script_state,
-    EventTarget& event_target,
+    ExecutionContext& context,
     V8Function* handler,
     int timeout,
     const HeapVector<ScriptValue>& arguments) {
-  ExecutionContext* execution_context = event_target.GetExecutionContext();
-  if (!IsAllowed(execution_context, false, g_empty_string))
+  if (!IsAllowed(context, false, g_empty_string)) {
     return 0;
-  auto* action = MakeGarbageCollected<ScheduledAction>(
-      script_state, execution_context, handler, arguments);
-  return DOMTimer::Install(execution_context, action,
-                           base::Milliseconds(timeout), false);
+  }
+  auto* action = MakeGarbageCollected<ScheduledAction>(script_state, context,
+                                                       handler, arguments);
+  return DOMTimer::Install(context, action, base::Milliseconds(timeout), false);
 }
 
 int WindowOrWorkerGlobalScope::setInterval(ScriptState* script_state,
-                                           EventTarget& event_target,
+                                           ExecutionContext& context,
                                            const String& handler,
                                            int timeout,
                                            const HeapVector<ScriptValue>&) {
-  ExecutionContext* execution_context = event_target.GetExecutionContext();
-  if (!IsAllowed(execution_context, true, handler))
+  if (!IsAllowed(context, true, handler)) {
     return 0;
+  }
   // Don't allow setting timeouts to run empty functions.  Was historically a
   // performance issue.
   if (handler.empty())
     return 0;
-  auto* action = MakeGarbageCollected<ScheduledAction>(
-      script_state, execution_context, handler);
-  return DOMTimer::Install(execution_context, action,
-                           base::Milliseconds(timeout), false);
+  auto* action =
+      MakeGarbageCollected<ScheduledAction>(script_state, context, handler);
+  return DOMTimer::Install(context, action, base::Milliseconds(timeout), false);
 }
 
-void WindowOrWorkerGlobalScope::clearTimeout(EventTarget& event_target,
+void WindowOrWorkerGlobalScope::clearTimeout(ExecutionContext& context,
                                              int timeout_id) {
-  if (ExecutionContext* context = event_target.GetExecutionContext())
-    DOMTimer::RemoveByID(context, timeout_id);
+  DOMTimer::RemoveByID(context, timeout_id);
 }
 
-void WindowOrWorkerGlobalScope::clearInterval(EventTarget& event_target,
+void WindowOrWorkerGlobalScope::clearInterval(ExecutionContext& context,
                                               int timeout_id) {
-  if (ExecutionContext* context = event_target.GetExecutionContext())
-    DOMTimer::RemoveByID(context, timeout_id);
+  DOMTimer::RemoveByID(context, timeout_id);
 }
 
 bool WindowOrWorkerGlobalScope::crossOriginIsolated(
@@ -248,7 +240,7 @@
 
 ScriptValue WindowOrWorkerGlobalScope::structuredClone(
     ScriptState* script_state,
-    EventTarget& event_target,
+    ExecutionContext&,
     const ScriptValue& message,
     const StructuredSerializeOptions* options,
     ExceptionState& exception_state) {
diff --git a/third_party/blink/renderer/core/frame/window_or_worker_global_scope.h b/third_party/blink/renderer/core/frame/window_or_worker_global_scope.h
index 6bc706a..b2254bd 100644
--- a/third_party/blink/renderer/core/frame/window_or_worker_global_scope.h
+++ b/third_party/blink/renderer/core/frame/window_or_worker_global_scope.h
@@ -42,7 +42,6 @@
 
 namespace blink {
 
-class EventTarget;
 class ExceptionState;
 class StructuredSerializeOptions;
 class ScriptState;
@@ -53,43 +52,43 @@
   STATIC_ONLY(WindowOrWorkerGlobalScope);
 
  public:
-  static void reportError(ScriptState*, EventTarget&, const ScriptValue&);
+  static void reportError(ScriptState*, ExecutionContext&, const ScriptValue&);
 
-  static String btoa(EventTarget&,
+  static String btoa(ExecutionContext&,
                      const String& string_to_encode,
                      ExceptionState&);
-  static String atob(EventTarget&,
+  static String atob(ExecutionContext&,
                      const String& encoded_string,
                      ExceptionState&);
 
   static int setTimeout(ScriptState*,
-                        EventTarget&,
+                        ExecutionContext&,
                         V8Function* handler,
                         int timeout,
                         const HeapVector<ScriptValue>& arguments);
   static int setTimeout(ScriptState*,
-                        EventTarget&,
+                        ExecutionContext&,
                         const String& handler,
                         int timeout,
                         const HeapVector<ScriptValue>&);
   static int setInterval(ScriptState*,
-                         EventTarget&,
+                         ExecutionContext&,
                          V8Function* handler,
                          int timeout,
                          const HeapVector<ScriptValue>&);
   static int setInterval(ScriptState*,
-                         EventTarget&,
+                         ExecutionContext&,
                          const String& handler,
                          int timeout,
                          const HeapVector<ScriptValue>&);
-  static void clearTimeout(EventTarget&, int timeout_id);
-  static void clearInterval(EventTarget&, int timeout_id);
+  static void clearTimeout(ExecutionContext&, int timeout_id);
+  static void clearInterval(ExecutionContext&, int timeout_id);
 
   static bool crossOriginIsolated(const ExecutionContext&);
   static String crossOriginEmbedderPolicy(const ExecutionContext&);
 
   static ScriptValue structuredClone(ScriptState*,
-                                     EventTarget&,
+                                     ExecutionContext&,
                                      const ScriptValue& message,
                                      const StructuredSerializeOptions*,
                                      ExceptionState&);
diff --git a/third_party/blink/renderer/core/html/html_image_element.cc b/third_party/blink/renderer/core/html/html_image_element.cc
index d5414175..e71d17e 100644
--- a/third_party/blink/renderer/core/html/html_image_element.cc
+++ b/third_party/blink/renderer/core/html/html_image_element.cc
@@ -115,8 +115,7 @@
       is_ad_related_(false),
       is_lcp_element_(false),
       is_changed_shortly_after_mouseover_(false),
-      has_sizes_attribute_in_img_or_sibling_(false),
-      is_lazy_loaded_(false) {}
+      has_sizes_attribute_in_img_or_sibling_(false) {}
 
 HTMLImageElement::~HTMLImageElement() = default;
 
@@ -346,8 +345,6 @@
     if (loading == LoadingAttributeValue::kEager ||
         (loading == LoadingAttributeValue::kAuto)) {
       GetImageLoader().LoadDeferredImage();
-    } else {
-      is_lazy_loaded_ = true;
     }
   } else if (name == html_names::kFetchpriorityAttr &&
              RuntimeEnabledFeatures::PriorityHintsEnabled(
@@ -416,6 +413,11 @@
   return MIMETypeRegistry::IsSupportedImagePrefixedMIMEType(trimmed_type);
 }
 
+bool HTMLImageElement::HasLazyLoadingAttribute() const {
+  return GetLoadingAttributeValue(FastGetAttribute(html_names::kLoadingAttr)) ==
+         LoadingAttributeValue::kLazy;
+}
+
 // http://picture.responsiveimages.org/#update-source-set
 ImageCandidate HTMLImageElement::FindBestFitImageFromPictureParent() {
   DCHECK(IsMainThread());
@@ -933,7 +935,7 @@
 }
 
 void HTMLImageElement::SetAutoSizesUsecounter() {
-  if (is_lazy_loaded_ && listener_) {
+  if (listener_ && HasLazyLoadingAttribute()) {
     UseCounter::Count(
         GetDocument(),
         has_sizes_attribute_in_img_or_sibling_
diff --git a/third_party/blink/renderer/core/html/html_image_element.h b/third_party/blink/renderer/core/html/html_image_element.h
index 2e95d586..942edb54 100644
--- a/third_party/blink/renderer/core/html/html_image_element.h
+++ b/third_party/blink/renderer/core/html/html_image_element.h
@@ -190,7 +190,10 @@
   static bool SupportedImageType(const String& type,
                                  const HashSet<String>* disabled_image_types);
 
-  bool is_lazy_loaded() const { return is_lazy_loaded_; }
+  // True if the `loading` attribute is present and the value is lazy. Note that
+  // additional conditions can prevent lazy loading even when this is true, such
+  // as script being disabled (see: `LazyImageHelper::ShouldDeferImageLoad`).
+  bool HasLazyLoadingAttribute() const;
 
  protected:
   // Controls how an image element appears in the layout. See:
@@ -280,7 +283,6 @@
   bool is_lcp_element_ : 1;
   bool is_changed_shortly_after_mouseover_ : 1;
   bool has_sizes_attribute_in_img_or_sibling_ : 1;
-  bool is_lazy_loaded_ : 1;
 
   std::unique_ptr<LazyLoadImageObserver::VisibleLoadTimeMetrics>
       visible_load_time_metrics_;
diff --git a/third_party/blink/renderer/core/html/lazy_load_image_observer.cc b/third_party/blink/renderer/core/html/lazy_load_image_observer.cc
index 4b4f57b..9f9aedf2 100644
--- a/third_party/blink/renderer/core/html/lazy_load_image_observer.cc
+++ b/third_party/blink/renderer/core/html/lazy_load_image_observer.cc
@@ -200,7 +200,7 @@
     // If the loading_attr is 'lazy' explicitly, we'd better to wait for
     // intersection.
     if (!entry->isIntersecting() && image_element &&
-        !EqualIgnoringASCIICase(image_element->FastGetAttribute(html_names::kLoadingAttr), "lazy")) {
+        !image_element->HasLazyLoadingAttribute()) {
       // Fully load the invisible image elements. The elements can be invisible
       // by style such as display:none, visibility: hidden, or hidden via
       // attribute, etc. Style might also not be calculated if the ancestors
@@ -243,7 +243,15 @@
         {}, {std::numeric_limits<float>::min()}, root_document,
         WTF::BindRepeating(&LazyLoadImageObserver::OnVisibilityChanged,
                            WrapWeakPersistent(this)),
-        LocalFrameUkmAggregator::kLazyLoadIntersectionObserver);
+        LocalFrameUkmAggregator::kLazyLoadIntersectionObserver,
+        IntersectionObserver::kDeliverDuringPostLifecycleSteps,
+        IntersectionObserver::kFractionOfTarget,
+        /* delay */ 0,
+        /* track_visibility */ false,
+        /* always_report_root_bounds */ false,
+        IntersectionObserver::kApplyMarginToRoot,
+        /* use_overflow_clip_edge */ false,
+        /* needs_initial_observation_with_detached_target */ false);
   }
   visibility_metrics_observer_->observe(image_element);
 }
diff --git a/third_party/blink/renderer/core/html/lazy_load_image_observer_test.cc b/third_party/blink/renderer/core/html/lazy_load_image_observer_test.cc
index 23818244..5dc3aabd 100644
--- a/third_party/blink/renderer/core/html/lazy_load_image_observer_test.cc
+++ b/third_party/blink/renderer/core/html/lazy_load_image_observer_test.cc
@@ -685,6 +685,109 @@
       "Blink.VisibleLoadTime.LazyLoadImages.BelowTheFold2.4G", 0);
 }
 
+// A fully above-the-fold cached image should not report below-the-fold metrics.
+TEST_F(LazyLoadImagesTest, AboveTheFoldCachedImageMetrics) {
+  HistogramTester histogram_tester;
+  SimRequest main_resource("https://example.com/", "text/html");
+  SimSubresourceRequest image_resource("https://example.com/image.png",
+                                       "image/png");
+  LoadURL("https://example.com/");
+  // Load a page with a non-lazy image that loads immediately, inserting the
+  // image into the cache.
+  main_resource.Complete(R"HTML(
+        <!doctype html>
+        <img id='image' src='https://example.com/image.png'/>
+        <div id='container'></div>
+      )HTML");
+  image_resource.Complete(TestImage());
+  Compositor().BeginFrame();
+  test::RunPendingTasks();
+
+  auto* image = To<HTMLImageElement>(GetDocument().getElementById("image"));
+  EXPECT_TRUE(image->CachedImage()->IsLoaded());
+
+  // Insert a lazy loaded image with a src that is already cached.
+  auto* container = GetDocument().getElementById("container");
+  container->setInnerHTML(R"HTML(
+    <img src='https://example.com/image.png' loading='lazy' id='lazy'/>
+  )HTML");
+
+  // The lazy image should have completed loading.
+  auto* lazy_image = To<HTMLImageElement>(GetDocument().getElementById("lazy"));
+  EXPECT_TRUE(lazy_image->CachedImage()->IsLoaded());
+
+  // We should have a load time, but not yet `is_initially_intersecting`.
+  auto& visible_load_time = lazy_image->EnsureVisibleLoadTimeMetrics();
+  EXPECT_FALSE(visible_load_time.time_when_first_load_finished.is_null());
+  EXPECT_FALSE(visible_load_time.has_initial_intersection_been_set);
+
+  Compositor().BeginFrame();
+  test::RunPendingTasks();
+
+  // After a frame, the visibility metrics observer should fire and correctly
+  // set `is_initially_intersecting`.
+  EXPECT_TRUE(visible_load_time.has_initial_intersection_been_set);
+  EXPECT_TRUE(visible_load_time.is_initially_intersecting);
+
+  // Nothing was below the fold so no BelowTheFold metrics should be reported.
+  histogram_tester.ExpectTotalCount(
+      "Blink.VisibleBeforeLoaded.LazyLoadImages.BelowTheFold2", 0);
+  histogram_tester.ExpectTotalCount(
+      "Blink.VisibleLoadTime.LazyLoadImages.BelowTheFold2.4G", 0);
+}
+
+// An image that loads immediately due to being cached should not report
+// Blink.VisibleBeforeLoaded.LazyLoadImages metrics.
+TEST_F(LazyLoadImagesTest, CachedImageVisibleBeforeLoadedMetrics) {
+  HistogramTester histogram_tester;
+  SimRequest main_resource("https://a.com/", "text/html");
+  SimSubresourceRequest image_resource("https://a.com/image.png", "image/png");
+  LoadURL("https://a.com/");
+
+  // Load a page with a non-lazy image that loads immediately, inserting the
+  // image into the cache.
+  main_resource.Complete(
+      String::Format(R"HTML(
+        <!doctype html>
+        <div id='spacer' style='height: %dpx;'></div>
+        <div id='container'></div>
+        <!-- This non-lazy image will load immediately. -->
+        <img src='https://a.com/image.png' />
+      )HTML",
+                     kViewportHeight + kLoadingDistanceThreshold - 50));
+  image_resource.Complete(TestImage());
+  Compositor().BeginFrame();
+  test::RunPendingTasks();
+
+  // Insert a lazy loaded image with a src that is already cached.
+  auto* container = GetDocument().getElementById("container");
+  container->setInnerHTML("<img src='https://a.com/image.png' loading='lazy'>");
+
+  Compositor().BeginFrame();
+  test::RunPendingTasks();
+
+  // VisibleBeforeLoaded should not be recorded since the image is not visible.
+  histogram_tester.ExpectTotalCount(
+      "Blink.VisibleBeforeLoaded.LazyLoadImages.AboveTheFold2", 0);
+  histogram_tester.ExpectTotalCount(
+      "Blink.VisibleBeforeLoaded.LazyLoadImages.BelowTheFold2", 0);
+
+  // Scroll down so that the image is in the viewport.
+  GetDocument().View()->LayoutViewport()->SetScrollOffset(
+      ScrollOffset(0, kViewportHeight + kLoadingDistanceThreshold + 50),
+      mojom::blink::ScrollType::kProgrammatic);
+
+  Compositor().BeginFrame();
+  test::RunPendingTasks();
+
+  // The image is now visible but loaded before being visible, so no
+  // VisibleBeforeLoaded metrics should have been recorded.
+  histogram_tester.ExpectTotalCount(
+      "Blink.VisibleBeforeLoaded.LazyLoadImages.AboveTheFold2", 0);
+  histogram_tester.ExpectTotalCount(
+      "Blink.VisibleBeforeLoaded.LazyLoadImages.BelowTheFold2", 0);
+}
+
 TEST_F(LazyLoadImagesTest, AboveTheFoldImageVisibleBeforeLoaded) {
   HistogramTester histogram_tester;
 
diff --git a/third_party/blink/renderer/core/intersection_observer/intersection_observer.cc b/third_party/blink/renderer/core/intersection_observer/intersection_observer.cc
index 9abc009..952a6169 100644
--- a/third_party/blink/renderer/core/intersection_observer/intersection_observer.cc
+++ b/third_party/blink/renderer/core/intersection_observer/intersection_observer.cc
@@ -47,11 +47,14 @@
       ExecutionContext* context,
       IntersectionObserver::EventCallback callback,
       LocalFrameUkmAggregator::MetricId ukm_metric_id,
-      IntersectionObserver::DeliveryBehavior delivery_behavior)
+      IntersectionObserver::DeliveryBehavior delivery_behavior,
+      bool needs_initial_observation_with_detached_target)
       : context_(context),
         callback_(std::move(callback)),
         ukm_metric_id_(ukm_metric_id),
-        delivery_behavior_(delivery_behavior) {}
+        delivery_behavior_(delivery_behavior),
+        needs_initial_observation_with_detached_target_(
+            needs_initial_observation_with_detached_target) {}
   IntersectionObserverDelegateImpl(const IntersectionObserverDelegateImpl&) =
       delete;
   IntersectionObserverDelegateImpl& operator=(
@@ -65,6 +68,10 @@
     return delivery_behavior_;
   }
 
+  bool NeedsInitialObservationWithDetachedTarget() const override {
+    return needs_initial_observation_with_detached_target_;
+  }
+
   void Deliver(const HeapVector<Member<IntersectionObserverEntry>>& entries,
                IntersectionObserver& observer) override {
     callback_.Run(entries);
@@ -82,6 +89,7 @@
   IntersectionObserver::EventCallback callback_;
   LocalFrameUkmAggregator::MetricId ukm_metric_id_;
   IntersectionObserver::DeliveryBehavior delivery_behavior_;
+  bool needs_initial_observation_with_detached_target_;
 };
 
 void ParseMargin(String margin_parameter,
@@ -251,11 +259,12 @@
     bool always_report_root_bounds,
     MarginTarget margin_target,
     bool use_overflow_clip_edge,
+    bool needs_initial_observation_with_detached_target,
     ExceptionState& exception_state) {
   IntersectionObserverDelegateImpl* intersection_observer_delegate =
       MakeGarbageCollected<IntersectionObserverDelegateImpl>(
           document->GetExecutionContext(), std::move(callback), ukm_metric_id,
-          behavior);
+          behavior, needs_initial_observation_with_detached_target);
   return MakeGarbageCollected<IntersectionObserver>(
       *intersection_observer_delegate, nullptr, margin, thresholds, semantics,
       delay, track_visibility, always_report_root_bounds, margin_target,
@@ -360,14 +369,12 @@
         .EnsureIntersectionObserverController()
         .AddTrackedObservation(*observation);
     if (LocalFrameView* frame_view = target->GetDocument().View()) {
-      // The IntersectionObsever spec requires that at least one observation
+      // The IntersectionObserver spec requires that at least one observation
       // be recorded after observe() is called, even if the frame is throttled.
       frame_view->SetIntersectionObservationState(LocalFrameView::kRequired);
       frame_view->ScheduleAnimation();
     }
-  } else {
-    // The IntersectionObsever spec requires that at least one observation
-    // be recorded after observe() is called, even if the target is detached.
+  } else if (delegate_->NeedsInitialObservationWithDetachedTarget()) {
     absl::optional<base::TimeTicks> monotonic_time;
     observation->ComputeIntersection(
         IntersectionObservation::kImplicitRootObserversNeedUpdate |
diff --git a/third_party/blink/renderer/core/intersection_observer/intersection_observer.h b/third_party/blink/renderer/core/intersection_observer/intersection_observer.h
index 8444c50e..cb38e991 100644
--- a/third_party/blink/renderer/core/intersection_observer/intersection_observer.h
+++ b/third_party/blink/renderer/core/intersection_observer/intersection_observer.h
@@ -117,6 +117,7 @@
       bool always_report_root_bounds = false,
       MarginTarget margin_target = kApplyMarginToRoot,
       bool use_overflow_clip_edge = false,
+      bool needs_initial_observation_with_detached_target = true,
       ExceptionState& = ASSERT_NO_EXCEPTION);
 
   explicit IntersectionObserver(IntersectionObserverDelegate&,
diff --git a/third_party/blink/renderer/core/intersection_observer/intersection_observer_delegate.h b/third_party/blink/renderer/core/intersection_observer/intersection_observer_delegate.h
index a1ba9a0..063297bb 100644
--- a/third_party/blink/renderer/core/intersection_observer/intersection_observer_delegate.h
+++ b/third_party/blink/renderer/core/intersection_observer/intersection_observer_delegate.h
@@ -26,6 +26,10 @@
   virtual IntersectionObserver::DeliveryBehavior GetDeliveryBehavior()
       const = 0;
 
+  // The IntersectionObserver spec requires that at least one observation be
+  // recorded after observe() is called, even if the target is detached.
+  virtual bool NeedsInitialObservationWithDetachedTarget() const = 0;
+
   virtual void Deliver(const HeapVector<Member<IntersectionObserverEntry>>&,
                        IntersectionObserver&) = 0;
   virtual ExecutionContext* GetExecutionContext() const = 0;
diff --git a/third_party/blink/renderer/core/layout/ng/grid/ng_grid_item.cc b/third_party/blink/renderer/core/layout/ng/grid/ng_grid_item.cc
index 9e5fb9bd..2daa1cf 100644
--- a/third_party/blink/renderer/core/layout/ng/grid/ng_grid_item.cc
+++ b/third_party/blink/renderer/core/layout/ng/grid/ng_grid_item.cc
@@ -198,11 +198,10 @@
   // The `false, true, false, true` parameters get the converter to calculate
   // whether the subgrids and its root grid are opposite direction in all cases.
   const LogicalToLogical<bool> direction_converter(
-      root_grid_style.GetWritingDirection(), style.GetWritingDirection(),
-      /* inline_start */ false,
-      /* inline_end */ true,
-      /* block_start */ false,
-      /* block_end */ true);
+      style.GetWritingDirection(), root_grid_style.GetWritingDirection(),
+      /* inline_start */ false, /* inline_end */ true,
+      /* block_start */ false, /* block_end */ true);
+
   is_opposite_direction_in_root_grid_columns =
       direction_converter.InlineStart();
   is_opposite_direction_in_root_grid_rows = direction_converter.BlockStart();
diff --git a/third_party/blink/renderer/core/layout/ng/grid/ng_grid_item.h b/third_party/blink/renderer/core/layout/ng/grid/ng_grid_item.h
index c510b70..ab6bf3d8 100644
--- a/third_party/blink/renderer/core/layout/ng/grid/ng_grid_item.h
+++ b/third_party/blink/renderer/core/layout/ng/grid/ng_grid_item.h
@@ -93,13 +93,6 @@
                : block_axis_alignment == AxisEdge::kLastBaseline;
   }
 
-  bool IsOppositeDirectionInRootGrid(
-      GridTrackSizingDirection root_track_direction) const {
-    return (root_track_direction == kForColumns)
-               ? is_opposite_direction_in_root_grid_columns
-               : is_opposite_direction_in_root_grid_rows;
-  }
-
   // For this item and track direction, computes the pair of indices |begin| and
   // |end| such that the item spans every set from the respective collection's
   // |sets_| with an index in the range [begin, end).
@@ -166,6 +159,13 @@
                                             : is_considered_for_row_sizing;
   }
 
+  bool IsOppositeDirectionInRootGrid(
+      GridTrackSizingDirection track_direction) const {
+    return (track_direction == kForColumns)
+               ? is_opposite_direction_in_root_grid_columns
+               : is_opposite_direction_in_root_grid_rows;
+  }
+
   bool MustConsiderGridItemsForSizing(
       GridTrackSizingDirection track_direction) const {
     return (track_direction == kForColumns)
diff --git a/third_party/blink/renderer/core/layout/ng/grid/ng_grid_layout_algorithm.cc b/third_party/blink/renderer/core/layout/ng/grid/ng_grid_layout_algorithm.cc
index 370223c..a75c456 100644
--- a/third_party/blink/renderer/core/layout/ng/grid/ng_grid_layout_algorithm.cc
+++ b/third_party/blink/renderer/core/layout/ng/grid/ng_grid_layout_algorithm.cc
@@ -1584,7 +1584,9 @@
           GutterSize(track_direction, parent_track_collection.GutterSize()),
           ComputeMarginsForSelf(ConstraintSpace(), Style()),
           BorderScrollbarPadding(), track_direction,
-          subgrid_data->IsOppositeDirectionInRootGrid(track_direction)));
+          is_for_columns_in_parent
+              ? subgrid_data->is_opposite_direction_in_root_grid_columns
+              : subgrid_data->is_opposite_direction_in_root_grid_rows));
 }
 
 void NGGridLayoutAlgorithm::InitializeTrackCollection(
@@ -2594,6 +2596,7 @@
 
       auto start_extra_margin = subgrid_track_collection.StartExtraMargin();
       auto end_extra_margin = subgrid_track_collection.EndExtraMargin();
+
       if (grid_item.IsOppositeDirectionInRootGrid(track_direction)) {
         std::swap(start_extra_margin, end_extra_margin);
       }
diff --git a/third_party/blink/renderer/core/layout/ng/grid/ng_grid_node.cc b/third_party/blink/renderer/core/layout/ng/grid/ng_grid_node.cc
index 3b47013f..93a77d0f1 100644
--- a/third_party/blink/renderer/core/layout/ng/grid/ng_grid_node.cc
+++ b/third_party/blink/renderer/core/layout/ng/grid/ng_grid_node.cc
@@ -83,20 +83,16 @@
       grid_items.SortByOrderProperty();
   }
 
-  const auto& grid_style = Style();
-  const bool grid_is_parallel_with_root_grid = IsParallelWritingMode(
-      root_grid_style.GetWritingMode(), grid_style.GetWritingMode());
-
 #if DCHECK_IS_ON()
   if (cached_placement_data) {
-    NGGridPlacement grid_placement(grid_style, placement_data);
+    NGGridPlacement grid_placement(Style(), placement_data);
     DCHECK(*cached_placement_data ==
            grid_placement.RunAutoPlacementAlgorithm(grid_items));
   }
 #endif
 
   if (!cached_placement_data) {
-    NGGridPlacement grid_placement(grid_style, placement_data);
+    NGGridPlacement grid_placement(Style(), placement_data);
     layout_grid->SetCachedPlacementData(
         grid_placement.RunAutoPlacementAlgorithm(grid_items));
     cached_placement_data = &layout_grid->CachedPlacementData();
@@ -106,11 +102,6 @@
   auto* resolved_position = cached_placement_data->grid_item_positions.begin();
   for (auto& grid_item : grid_items) {
     grid_item.resolved_position = *(resolved_position++);
-
-    if (!grid_is_parallel_with_root_grid) {
-      std::swap(grid_item.resolved_position.columns,
-                grid_item.resolved_position.rows);
-    }
   }
   return grid_items;
 }
@@ -134,13 +125,37 @@
         current_item.must_consider_grid_items_for_column_sizing,
         current_item.must_consider_grid_items_for_row_sizing);
 
-    const wtf_size_t column_start_line = current_item.StartLine(kForColumns);
-    const wtf_size_t row_start_line = current_item.StartLine(kForRows);
+    auto TranslateSubgriddedItem =
+        [&current_item](GridSpan& subgridded_item_span,
+                        GridTrackSizingDirection track_direction) {
+          if (current_item.MustConsiderGridItemsForSizing(track_direction)) {
+            // If a subgrid is in an opposite writing direction to the root
+            // grid, we should "reverse" the subgridded item's span.
+            if (current_item.IsOppositeDirectionInRootGrid(track_direction)) {
+              const wtf_size_t subgrid_span_size =
+                  current_item.SpanSize(track_direction);
+
+              DCHECK_LE(subgridded_item_span.EndLine(), subgrid_span_size);
+
+              subgridded_item_span = GridSpan::TranslatedDefiniteGridSpan(
+                  subgrid_span_size - subgridded_item_span.EndLine(),
+                  subgrid_span_size - subgridded_item_span.StartLine());
+            }
+            subgridded_item_span.Translate(
+                current_item.StartLine(track_direction));
+          }
+        };
 
     for (auto& subgridded_item : subgridded_items) {
-      subgridded_item.resolved_position.columns.Translate(column_start_line);
-      subgridded_item.resolved_position.rows.Translate(row_start_line);
       subgridded_item.is_subgridded_to_parent_grid = true;
+      auto& item_position = subgridded_item.resolved_position;
+
+      if (!current_item.is_parallel_with_root_grid) {
+        std::swap(item_position.columns, item_position.rows);
+      }
+
+      TranslateSubgriddedItem(item_position.columns, kForColumns);
+      TranslateSubgriddedItem(item_position.rows, kForRows);
     }
     grid_items->Append(&subgridded_items);
   }
diff --git a/third_party/blink/renderer/core/loader/lazy_image_helper.cc b/third_party/blink/renderer/core/loader/lazy_image_helper.cc
index da32c40..f976a7d 100644
--- a/third_party/blink/renderer/core/loader/lazy_image_helper.cc
+++ b/third_party/blink/renderer/core/loader/lazy_image_helper.cc
@@ -95,7 +95,10 @@
 
 void LazyImageHelper::RecordMetricsOnLoadFinished(
     HTMLImageElement* image_element) {
-  if (!image_element->is_lazy_loaded()) {
+  // TODO(pdr): We should only report metrics for images that were actually lazy
+  // loaded, and checking the attribute alone is not sufficient. See:
+  // `LazyImageHelper::ShouldDeferImageLoad`.
+  if (!image_element->HasLazyLoadingAttribute()) {
     return;
   }
 
diff --git a/third_party/blink/renderer/core/testing/intersection_observer_test_helper.h b/third_party/blink/renderer/core/testing/intersection_observer_test_helper.h
index bd147a7..40aeff09 100644
--- a/third_party/blink/renderer/core/testing/intersection_observer_test_helper.h
+++ b/third_party/blink/renderer/core/testing/intersection_observer_test_helper.h
@@ -33,6 +33,9 @@
     call_count_++;
     entries_.AppendVector(entries);
   }
+  bool NeedsInitialObservationWithDetachedTarget() const override {
+    return true;
+  }
   ExecutionContext* GetExecutionContext() const override {
     return document_->GetExecutionContext();
   }
diff --git a/third_party/blink/renderer/core/workers/worker_global_scope.cc b/third_party/blink/renderer/core/workers/worker_global_scope.cc
index 635b86e..483a9815 100644
--- a/third_party/blink/renderer/core/workers/worker_global_scope.cc
+++ b/third_party/blink/renderer/core/workers/worker_global_scope.cc
@@ -46,7 +46,6 @@
 #include "third_party/blink/renderer/core/events/error_event.h"
 #include "third_party/blink/renderer/core/events/message_event.h"
 #include "third_party/blink/renderer/core/execution_context/agent.h"
-#include "third_party/blink/renderer/core/frame/dom_timer_coordinator.h"
 #include "third_party/blink/renderer/core/frame/font_matching_metrics.h"
 #include "third_party/blink/renderer/core/frame/user_activation.h"
 #include "third_party/blink/renderer/core/inspector/console_message.h"
diff --git a/third_party/blink/renderer/modules/buckets/storage_bucket_manager.cc b/third_party/blink/renderer/modules/buckets/storage_bucket_manager.cc
index b7c53ec..3167cf1 100644
--- a/third_party/blink/renderer/modules/buckets/storage_bucket_manager.cc
+++ b/third_party/blink/renderer/modules/buckets/storage_bucket_manager.cc
@@ -265,42 +265,22 @@
   resolver->Resolve();
 }
 
-void StorageBucketManager::GetBucketForDevtools(
+StorageBucket* StorageBucketManager::GetBucketForDevtools(
     ScriptState* script_state,
-    const String& name,
-    base::OnceCallback<void(StorageBucket*)> callback) {
+    const String& name) {
   ExecutionContext* context = ExecutionContext::From(script_state);
   if (!context->GetSecurityOrigin()->CanAccessStorageBuckets()) {
-    std::move(callback).Run(nullptr);
-    return;
+    return nullptr;
   }
 
+  mojo::PendingRemote<mojom::blink::BucketHost> bucket_remote;
+
   GetBucketManager(script_state)
-      ->GetBucketForDevtools(
-          name,
-          WTF::BindOnce(&StorageBucketManager::DidGetBucketForDevtools,
-                        WrapPersistent(this), WrapPersistent(script_state),
-                        name, std::move(callback)));
-}
+      ->GetBucketForDevtools(name,
+                             bucket_remote.InitWithNewPipeAndPassReceiver());
 
-void StorageBucketManager::DidGetBucketForDevtools(
-    ScriptState* script_state,
-    const String& name,
-    base::OnceCallback<void(StorageBucket*)> callback,
-    mojo::PendingRemote<mojom::blink::BucketHost> bucket_remote,
-    mojom::blink::BucketError) {
-  if (!script_state->ContextIsValid()) {
-    std::move(callback).Run(nullptr);
-    return;
-  }
-  ScriptState::Scope scope(script_state);
-
-  if (!bucket_remote) {
-    std::move(callback).Run(nullptr);
-    return;
-  }
-  std::move(callback).Run(MakeGarbageCollected<StorageBucket>(
-      navigator_base_, name, std::move(bucket_remote)));
+  return MakeGarbageCollected<StorageBucket>(navigator_base_, name,
+                                             std::move(bucket_remote));
 }
 
 void StorageBucketManager::Trace(Visitor* visitor) const {
diff --git a/third_party/blink/renderer/modules/buckets/storage_bucket_manager.h b/third_party/blink/renderer/modules/buckets/storage_bucket_manager.h
index d1f21d82..4407e24 100644
--- a/third_party/blink/renderer/modules/buckets/storage_bucket_manager.h
+++ b/third_party/blink/renderer/modules/buckets/storage_bucket_manager.h
@@ -49,18 +49,10 @@
   void Trace(Visitor*) const override;
 
   // These are not exposed to the web applications and only used by DevTools.
-  void GetBucketForDevtools(ScriptState* script_state,
-                            const String& name,
-                            base::OnceCallback<void(StorageBucket*)> callback);
+  StorageBucket* GetBucketForDevtools(ScriptState* script_state,
+                                      const String& name);
 
  private:
-  void DidGetBucketForDevtools(
-      ScriptState* script_state,
-      const String& name,
-      base::OnceCallback<void(StorageBucket*)> callback,
-      mojo::PendingRemote<mojom::blink::BucketHost> bucket_remote,
-      mojom::blink::BucketError);
-
   mojom::blink::BucketManagerHost* GetBucketManager(ScriptState* script_state);
 
   void DidOpen(ScriptPromiseResolver* resolver,
diff --git a/third_party/blink/renderer/modules/cache_storage/DEPS b/third_party/blink/renderer/modules/cache_storage/DEPS
index c4508ff..eca1cc7 100644
--- a/third_party/blink/renderer/modules/cache_storage/DEPS
+++ b/third_party/blink/renderer/modules/cache_storage/DEPS
@@ -1,6 +1,8 @@
 include_rules = [
     "+mojo/public/cpp/bindings",
     "-third_party/blink/renderer/modules",
+    "+third_party/blink/renderer/modules/buckets/storage_bucket.h",
+    "+third_party/blink/renderer/modules/buckets/storage_bucket_manager.h",
     "+third_party/blink/renderer/modules/cache_storage",
     "+third_party/blink/renderer/modules/event_modules.h",
     "+third_party/blink/renderer/modules/event_target_modules.h",
diff --git a/third_party/blink/renderer/modules/cache_storage/cache_storage.cc b/third_party/blink/renderer/modules/cache_storage/cache_storage.cc
index 7f88eb3..9b2b5f19 100644
--- a/third_party/blink/renderer/modules/cache_storage/cache_storage.cc
+++ b/third_party/blink/renderer/modules/cache_storage/cache_storage.cc
@@ -53,8 +53,9 @@
 
     MultiCacheQueryOptionsPtr output = MultiCacheQueryOptions::New();
     output->query_options = std::move(query_options);
-    if (input->hasCacheName())
+    if (input->hasCacheName()) {
       output->cache_name = input->cacheName();
+    }
     return output;
   }
 };
@@ -395,8 +396,9 @@
     case V8RequestInfo::ContentType::kUSVString:
       request_object = Request::Create(script_state, request->GetAsUSVString(),
                                        exception_state);
-      if (exception_state.HadException())
+      if (exception_state.HadException()) {
         return ScriptPromise();
+      }
       break;
   }
   return MatchImpl(script_state, request_object, options, exception_state);
@@ -552,8 +554,9 @@
     // optimization.
     mojo::PendingRemote<mojom::blink::CacheStorage> info =
         service_worker->TakeCacheStorage();
-    if (info)
+    if (info) {
       cache_storage_remote_.Bind(std::move(info), task_runner);
+    }
   }
 
   // Otherwise wait for MaybeInit() to bind a new mojo connection.
@@ -581,12 +584,14 @@
 }
 
 void CacheStorage::MaybeInit() {
-  if (cache_storage_remote_.is_bound())
+  if (cache_storage_remote_.is_bound()) {
     return;
+  }
 
   auto* context = GetExecutionContext();
-  if (!context || context->IsContextDestroyed())
+  if (!context || context->IsContextDestroyed()) {
     return;
+  }
 
   scoped_refptr<base::SingleThreadTaskRunner> task_runner =
       context->GetTaskRunner(blink::TaskType::kMiscPlatformAPI);
@@ -595,4 +600,10 @@
       cache_storage_remote_.BindNewPipeAndPassReceiver(task_runner));
 }
 
+mojom::blink::CacheStorage* CacheStorage::GetRemoteForDevtools(
+    base::OnceClosure disconnect_handler) {
+  cache_storage_remote_.set_disconnect_handler(std::move(disconnect_handler));
+  return cache_storage_remote_.get();
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/modules/cache_storage/cache_storage.h b/third_party/blink/renderer/modules/cache_storage/cache_storage.h
index 94faa66..6ff2c84e 100644
--- a/third_party/blink/renderer/modules/cache_storage/cache_storage.h
+++ b/third_party/blink/renderer/modules/cache_storage/cache_storage.h
@@ -56,6 +56,9 @@
   bool HasPendingActivity() const override;
   void Trace(Visitor*) const override;
 
+  mojom::blink::CacheStorage* GetRemoteForDevtools(
+      base::OnceClosure disconnect_handler);
+
  private:
   void MaybeInit();
 
diff --git a/third_party/blink/renderer/modules/cache_storage/inspector_cache_storage_agent.cc b/third_party/blink/renderer/modules/cache_storage/inspector_cache_storage_agent.cc
index 1664f352..1718a25 100644
--- a/third_party/blink/renderer/modules/cache_storage/inspector_cache_storage_agent.cc
+++ b/third_party/blink/renderer/modules/cache_storage/inspector_cache_storage_agent.cc
@@ -24,8 +24,12 @@
 #include "third_party/blink/renderer/core/fileapi/file_reader_loader.h"
 #include "third_party/blink/renderer/core/frame/local_dom_window.h"
 #include "third_party/blink/renderer/core/frame/local_frame.h"
+#include "third_party/blink/renderer/core/frame/navigator.h"
 #include "third_party/blink/renderer/core/inspector/inspected_frames.h"
 #include "third_party/blink/renderer/core/typed_arrays/dom_array_buffer.h"
+#include "third_party/blink/renderer/modules/buckets/storage_bucket.h"
+#include "third_party/blink/renderer/modules/buckets/storage_bucket_manager.h"
+#include "third_party/blink/renderer/modules/cache_storage/cache_storage.h"
 #include "third_party/blink/renderer/platform/blob/blob_data.h"
 #include "third_party/blink/renderer/platform/heap/garbage_collected.h"
 #include "third_party/blink/renderer/platform/loader/fetch/resource_request.h"
@@ -67,10 +71,16 @@
 
 namespace {
 
-String BuildCacheId(const String& storage_key, const String& cache_name) {
+String BuildCacheId(const String& storage_key,
+                    const absl::optional<String>& storage_bucket_name,
+                    const String& cache_name) {
   DCHECK(storage_key.find('|') == WTF::kNotFound);
   StringBuilder id;
   id.Append(storage_key);
+  if (storage_bucket_name.has_value()) {
+    id.Append('|');
+    id.Append(storage_bucket_name.value());
+  }
   id.Append('|');
   id.Append(cache_name);
   return id.ToString();
@@ -78,12 +88,21 @@
 
 ProtocolResponse ParseCacheId(const String& id,
                               String* storage_key,
+                              absl::optional<String>* storage_bucket_name,
                               String* cache_name) {
-  wtf_size_t pipe = id.find('|');
-  if (pipe == WTF::kNotFound)
+  Vector<String> id_parts;
+  id.Split('|', true, id_parts);
+  if (id_parts.size() == 2) {
+    *storage_key = id_parts[0];
+    *storage_bucket_name = absl::nullopt;
+    *cache_name = id_parts[1];
+  } else if (id_parts.size() == 3) {
+    *storage_key = id_parts[0];
+    *storage_bucket_name = id_parts[1];
+    *cache_name = id_parts[2];
+  } else {
     return ProtocolResponse::ServerError("Invalid cache id");
-  *storage_key = id.Substring(0, pipe);
-  *cache_name = id.Substring(pipe + 1);
+  }
 
   absl::optional<StorageKey> key =
       StorageKey::Deserialize(StringUTF8Adaptor(*storage_key).AsStringPiece());
@@ -100,61 +119,6 @@
   return ProtocolResponse::Success();
 }
 
-ProtocolResponse GetExecutionContext(InspectedFrames* frames,
-                                     const String& storage_key,
-                                     ExecutionContext** context) {
-  LocalFrame* frame = frames->FrameWithStorageKey(storage_key);
-  if (!frame) {
-    return ProtocolResponse::InvalidParams(
-        "No frame found for given storage key");
-  }
-
-  *context = frame->DomWindow();
-
-  return ProtocolResponse::Success();
-}
-
-ProtocolResponse AssertCacheStorage(
-    const String& storage_key,
-    InspectedFrames* frames,
-    InspectorCacheStorageAgent::CachesMap* caches,
-    mojom::blink::CacheStorage** result) {
-  ExecutionContext* context = nullptr;
-  ProtocolResponse response =
-      GetExecutionContext(frames, storage_key, &context);
-  if (!response.IsSuccess())
-    return response;
-
-  auto it = caches->find(storage_key);
-
-  if (it == caches->end()) {
-    mojo::Remote<mojom::blink::CacheStorage> cache_storage_remote;
-    context->GetBrowserInterfaceBroker().GetInterface(
-        cache_storage_remote.BindNewPipeAndPassReceiver(
-            frames->Root()->GetTaskRunner(TaskType::kFileReading)));
-    *result = cache_storage_remote.get();
-    caches->Set(storage_key, std::move(cache_storage_remote));
-  } else {
-    *result = it->value.get();
-  }
-
-  return ProtocolResponse::Success();
-}
-
-ProtocolResponse AssertCacheStorageAndNameForId(
-    const String& cache_id,
-    InspectedFrames* frames,
-    String* cache_name,
-    InspectorCacheStorageAgent::CachesMap* caches,
-    mojom::blink::CacheStorage** result) {
-  String storage_key;
-  ProtocolResponse response = ParseCacheId(cache_id, &storage_key, cache_name);
-
-  if (!response.IsSuccess())
-    return response;
-  return AssertCacheStorage(storage_key, frames, caches, result);
-}
-
 const char* CacheStorageErrorString(mojom::blink::CacheStorageError error) {
   switch (error) {
     case mojom::blink::CacheStorageError::kErrorNotImplemented:
@@ -223,6 +187,51 @@
   HTTPHeaderMap response_headers;
 };
 
+// A RefCounted wrapper for a Devtools callback so that it can be referenced in
+// multiple locations.
+template <typename RequestCallback>
+class RequestCallbackWrapper
+    : public RefCounted<RequestCallbackWrapper<RequestCallback>> {
+ public:
+  static scoped_refptr<RequestCallbackWrapper<RequestCallback>> Wrap(
+      std::unique_ptr<RequestCallback> request_callback) {
+    return AdoptRef(new RequestCallbackWrapper<RequestCallback>(
+        std::move(request_callback)));
+  }
+
+  template <typename... Args>
+  void SendSuccess(Args... args) {
+    if (request_callback_) {
+      request_callback_->sendSuccess(std::forward<Args>(args)...);
+      request_callback_.reset();
+    }
+  }
+
+  void SendFailure(ProtocolResponse response) {
+    if (request_callback_) {
+      request_callback_->sendFailure(response);
+      request_callback_.reset();
+    }
+  }
+
+  base::OnceCallback<void(ProtocolResponse)> GetFailureCallback() {
+    return WTF::BindOnce(
+        [](scoped_refptr<RequestCallbackWrapper<RequestCallback>>
+               callback_wrapper,
+           ProtocolResponse response) {
+          callback_wrapper->SendFailure(response);
+        },
+        WrapRefCounted(this));
+  }
+
+ private:
+  explicit RequestCallbackWrapper(
+      std::unique_ptr<RequestCallback> request_callback)
+      : request_callback_(std::move(request_callback)) {}
+
+  std::unique_ptr<RequestCallback> request_callback_;
+};
+
 class ResponsesAccumulator : public RefCounted<ResponsesAccumulator> {
  public:
   ResponsesAccumulator(
@@ -230,11 +239,12 @@
       const DataRequestParams& params,
       mojo::PendingAssociatedRemote<mojom::blink::CacheStorageCache>
           cache_pending_remote,
-      std::unique_ptr<RequestEntriesCallback> callback)
+      scoped_refptr<RequestCallbackWrapper<RequestEntriesCallback>>
+          callback_wrapper)
       : params_(params),
         num_responses_left_(num_responses),
         cache_remote_(std::move(cache_pending_remote)),
-        callback_(std::move(callback)) {}
+        callback_wrapper_(std::move(callback_wrapper)) {}
 
   ResponsesAccumulator(const ResponsesAccumulator&) = delete;
   ResponsesAccumulator& operator=(const ResponsesAccumulator&) = delete;
@@ -252,14 +262,15 @@
       for (auto& request : old_requests) {
         String urlPath(request->url.GetPath());
         if (!urlPath.Contains(params_.path_filter,
-                              WTF::kTextCaseUnicodeInsensitive))
+                              WTF::kTextCaseUnicodeInsensitive)) {
           continue;
+        }
         requests.push_back(std::move(request));
       }
     }
     wtf_size_t requestSize = requests.size();
     if (!requestSize) {
-      callback_->sendSuccess(std::make_unique<Array<DataEntry>>(), 0);
+      callback_wrapper_->SendSuccess(std::make_unique<Array<DataEntry>>(), 0);
       return;
     }
 
@@ -323,8 +334,9 @@
                                                  AtomicString(header.value));
     }
 
-    if (--num_responses_left_ != 0)
+    if (--num_responses_left_ != 0) {
       return;
+    }
 
     std::sort(responses_.begin(), responses_.end(),
               [](const RequestResponse& a, const RequestResponse& b) {
@@ -332,8 +344,9 @@
                                                     b.request_url);
               });
     size_t returned_entries_count = responses_.size();
-    if (params_.skip_count > 0)
+    if (params_.skip_count > 0) {
       responses_.EraseAt(0, params_.skip_count);
+    }
     if (params_.page_size != -1 &&
         static_cast<size_t>(params_.page_size) < responses_.size()) {
       responses_.EraseAt(params_.page_size,
@@ -358,11 +371,11 @@
       array->emplace_back(std::move(entry));
     }
 
-    callback_->sendSuccess(std::move(array), returned_entries_count);
+    callback_wrapper_->SendSuccess(std::move(array), returned_entries_count);
   }
 
   void SendFailure(const mojom::blink::CacheStorageError& error) {
-    callback_->sendFailure(ProtocolResponse::ServerError(
+    callback_wrapper_->SendFailure(ProtocolResponse::ServerError(
         String::Format("Error requesting responses for cache %s : %s",
                        params_.cache_name.Latin1().c_str(),
                        CacheStorageErrorString(error))
@@ -386,7 +399,8 @@
   int num_responses_left_;
   Vector<RequestResponse> responses_;
   mojo::AssociatedRemote<mojom::blink::CacheStorageCache> cache_remote_;
-  std::unique_ptr<RequestEntriesCallback> callback_;
+  scoped_refptr<RequestCallbackWrapper<RequestEntriesCallback>>
+      callback_wrapper_;
 };
 
 class GetCacheKeysForRequestData {
@@ -397,8 +411,9 @@
       const DataRequestParams& params,
       mojo::PendingAssociatedRemote<mojom::blink::CacheStorageCache>
           cache_pending_remote,
-      std::unique_ptr<RequestEntriesCallback> callback)
-      : params_(params), callback_(std::move(callback)) {
+      scoped_refptr<RequestCallbackWrapper<RequestEntriesCallback>>
+          callback_wrapper)
+      : params_(params), callback_wrapper_(std::move(callback_wrapper)) {
     cache_remote_.Bind(std::move(cache_pending_remote));
   }
 
@@ -418,23 +433,23 @@
                std::unique_ptr<GetCacheKeysForRequestData> self,
                mojom::blink::CacheKeysResultPtr result) {
               if (result->is_status()) {
-                self->callback_->sendFailure(ProtocolResponse::ServerError(
-                    String::Format(
-                        "Error requesting requests for cache %s: %s",
-                        params.cache_name.Latin1().c_str(),
-                        CacheStorageErrorString(result->get_status()))
-                        .Utf8()));
+                self->callback_wrapper_->SendFailure(
+                    ProtocolResponse::ServerError(
+                        String::Format(
+                            "Error requesting requests for cache %s: %s",
+                            params.cache_name.Latin1().c_str(),
+                            CacheStorageErrorString(result->get_status()))
+                            .Utf8()));
               } else {
                 if (result->get_keys().empty()) {
                   auto array = std::make_unique<protocol::Array<DataEntry>>();
-                  self->callback_->sendSuccess(std::move(array), 0);
+                  self->callback_wrapper_->SendSuccess(std::move(array), 0);
                   return;
                 }
                 scoped_refptr<ResponsesAccumulator> accumulator =
                     base::AdoptRef(new ResponsesAccumulator(
                         result->get_keys().size(), params,
-                        self->cache_remote_.Unbind(),
-                        std::move(self->callback_)));
+                        self->cache_remote_.Unbind(), self->callback_wrapper_));
                 accumulator->Dispatch(std::move(result->get_keys()));
               }
             },
@@ -444,18 +459,21 @@
  private:
   DataRequestParams params_;
   mojo::AssociatedRemote<mojom::blink::CacheStorageCache> cache_remote_;
-  std::unique_ptr<RequestEntriesCallback> callback_;
+  scoped_refptr<RequestCallbackWrapper<RequestEntriesCallback>>
+      callback_wrapper_;
 };
 
 class CachedResponseFileReaderLoaderClient final
     : public GarbageCollected<CachedResponseFileReaderLoaderClient>,
       public FileReaderClient {
  public:
-  static void Load(scoped_refptr<base::SingleThreadTaskRunner> task_runner,
-                   scoped_refptr<BlobDataHandle> blob,
-                   std::unique_ptr<RequestCachedResponseCallback> callback) {
+  static void Load(
+      scoped_refptr<base::SingleThreadTaskRunner> task_runner,
+      scoped_refptr<BlobDataHandle> blob,
+      scoped_refptr<RequestCallbackWrapper<RequestCachedResponseCallback>>
+          callback_wrapper) {
     MakeGarbageCollected<CachedResponseFileReaderLoaderClient>(
-        std::move(task_runner), std::move(blob), std::move(callback));
+        std::move(task_runner), std::move(blob), callback_wrapper);
   }
 
   CachedResponseFileReaderLoaderClient(
@@ -472,12 +490,12 @@
         CachedResponse::create()
             .setBody(protocol::Binary::fromSharedBuffer(data_))
             .build();
-    callback_->sendSuccess(std::move(response));
+    callback_wrapper_->SendSuccess(std::move(response));
     dispose();
   }
 
   void DidFail(FileErrorCode error) override {
-    callback_->sendFailure(ProtocolResponse::ServerError(
+    callback_wrapper_->SendFailure(ProtocolResponse::ServerError(
         String::Format("Unable to read the cached response, error code: %d",
                        static_cast<int>(error))
             .Utf8()));
@@ -498,10 +516,11 @@
   CachedResponseFileReaderLoaderClient(
       scoped_refptr<base::SingleThreadTaskRunner> task_runner,
       scoped_refptr<BlobDataHandle>&& blob,
-      std::unique_ptr<RequestCachedResponseCallback>&& callback)
+      scoped_refptr<RequestCallbackWrapper<RequestCachedResponseCallback>>
+          callback_wrapper)
       : loader_(MakeGarbageCollected<FileReaderLoader>(this,
                                                        std::move(task_runner))),
-        callback_(std::move(callback)),
+        callback_wrapper_(callback_wrapper),
         data_(SharedBuffer::Create()),
         keep_alive_(this) {
     loader_->Start(std::move(blob));
@@ -516,7 +535,8 @@
   }
 
   Member<FileReaderLoader> loader_;
-  std::unique_ptr<RequestCachedResponseCallback> callback_;
+  scoped_refptr<RequestCallbackWrapper<RequestCachedResponseCallback>>
+      callback_wrapper_;
   scoped_refptr<SharedBuffer> data_;
   SelfKeepAlive<CachedResponseFileReaderLoaderClient> keep_alive_;
 };
@@ -533,23 +553,99 @@
   InspectorBaseAgent::Trace(visitor);
 }
 
+// Gets the cache storage remote associated with the storage key and bucket
+// name. If the storage bucket name is not specified, it will get the default
+// bucket for the storage key.
+base::expected<mojom::blink::CacheStorage*, protocol::Response>
+InspectorCacheStorageAgent::GetCacheStorageRemote(
+    const String& storage_key,
+    const absl::optional<String>& storage_bucket_name,
+    base::OnceCallback<void(ProtocolResponse)> on_failure_callback) {
+  LocalFrame* frame = frames_->FrameWithStorageKey(storage_key);
+  if (!frame) {
+    return base::unexpected(ProtocolResponse::InvalidParams(
+        "No frame found for given storage key"));
+  }
+
+  if (storage_bucket_name.has_value()) {
+    // Handle a non-default bucket.
+    ScriptState* script_state = ToScriptStateForMainWorld(frame);
+    if (!script_state) {
+      return base::unexpected(ProtocolResponse::InternalError());
+    }
+
+    Navigator* navigator = frame->DomWindow()->navigator();
+    StorageBucketManager* storage_bucket_manager =
+        StorageBucketManager::storageBuckets(*navigator);
+    StorageBucket* storage_bucket =
+        storage_bucket_manager->GetBucketForDevtools(
+            script_state, storage_bucket_name.value());
+    if (storage_bucket) {
+      DummyExceptionStateForTesting exception_state;
+      return storage_bucket->caches(exception_state)
+          ->GetRemoteForDevtools(WTF::BindOnce(
+              std::move(on_failure_callback),
+              ProtocolResponse::ServerError("Couldn't retreive caches")));
+    }
+    return base::unexpected(
+        ProtocolResponse::ServerError("Couldn't retrieve bucket"));
+  }
+
+  // Handle the default bucket.
+  auto it = caches_.find(storage_key);
+
+  if (it == caches_.end()) {
+    ExecutionContext* context = frame->DomWindow();
+    mojo::Remote<mojom::blink::CacheStorage> cache_storage_remote;
+    context->GetBrowserInterfaceBroker().GetInterface(
+        cache_storage_remote.BindNewPipeAndPassReceiver(
+            frames_->Root()->GetTaskRunner(TaskType::kFileReading)));
+    auto new_iter = caches_.Set(storage_key, std::move(cache_storage_remote));
+    return new_iter.stored_value->value.get();
+  }
+
+  return it->value.get();
+}
+
+base::expected<mojom::blink::CacheStorage*, protocol::Response>
+InspectorCacheStorageAgent::GetCacheStorageRemoteForId(
+    const String& cache_id,
+    String& cache_name,
+    base::OnceCallback<void(ProtocolResponse)> on_failure_callback) {
+  String storage_key;
+  absl::optional<String> storage_bucket_name;
+  ProtocolResponse response =
+      ParseCacheId(cache_id, &storage_key, &storage_bucket_name, &cache_name);
+
+  if (!response.IsSuccess()) {
+    return base::unexpected(response);
+  }
+  return GetCacheStorageRemote(storage_key, storage_bucket_name,
+                               std::move(on_failure_callback));
+}
+
 void InspectorCacheStorageAgent::requestCacheNames(
     protocol::Maybe<String> maybe_security_origin,
     protocol::Maybe<String> maybe_storage_key,
+    protocol::Maybe<protocol::Storage::StorageBucket> maybe_storage_bucket,
     std::unique_ptr<RequestCacheNamesCallback> callback) {
   int64_t trace_id = blink::cache_storage::CreateTraceId();
   TRACE_EVENT_WITH_FLOW0("CacheStorage",
                          "InspectorCacheStorageAgent::requestCacheNames",
                          TRACE_ID_GLOBAL(trace_id), TRACE_EVENT_FLAG_FLOW_OUT);
-  if (maybe_security_origin.isJust() == maybe_storage_key.isJust()) {
+  if (maybe_security_origin.isJust() + maybe_storage_key.isJust() +
+          maybe_storage_bucket.isJust() !=
+      1) {
     callback->sendFailure(ProtocolResponse::InvalidParams(
         "At least and at most one of security_origin, "
-        "storage_key must be specified"));
+        "storage_key, storage_bucket must be specified"));
     return;
   }
   String storage_key, security_origin;
-  if (maybe_storage_key.isJust()) {
-    storage_key = maybe_storage_key.fromJust();
+  if (maybe_storage_key.isJust() || maybe_storage_bucket.isJust()) {
+    storage_key = maybe_storage_key.isJust()
+                      ? maybe_storage_key.fromJust()
+                      : maybe_storage_bucket.fromJust()->getStorageKey();
     absl::optional<StorageKey> key =
         StorageKey::Deserialize(StringUTF8Adaptor(storage_key).AsStringPiece());
     if (!key.has_value()) {
@@ -585,34 +681,57 @@
         StorageKey::CreateFirstParty(sec_origin->ToUrlOrigin()).Serialize());
   }
 
-  mojom::blink::CacheStorage* cache_storage = nullptr;
+  absl::optional<WTF::String> bucket_name;
+  if (maybe_storage_bucket.isJust() &&
+      maybe_storage_bucket.fromJust()->hasName()) {
+    bucket_name = maybe_storage_bucket.fromJust()->getName("");
+  }
 
-  ProtocolResponse response =
-      AssertCacheStorage(storage_key, frames_, &caches_, &cache_storage);
-  if (!response.IsSuccess()) {
-    callback->sendFailure(response);
+  auto callback_wrapper =
+      RequestCallbackWrapper<RequestCacheNamesCallback>::Wrap(
+          std::move(callback));
+
+  auto cache_storage = GetCacheStorageRemote(
+      storage_key, bucket_name, callback_wrapper->GetFailureCallback());
+  if (!cache_storage.has_value()) {
+    callback_wrapper->SendFailure(cache_storage.error());
     return;
   }
 
-  cache_storage->Keys(
-      trace_id, WTF::BindOnce(
-                    [](String security_origin, String storage_key,
-                       std::unique_ptr<RequestCacheNamesCallback> callback,
-                       const Vector<String>& caches) {
-                      auto array =
-                          std::make_unique<protocol::Array<ProtocolCache>>();
-                      for (auto& cache : caches) {
-                        array->emplace_back(
-                            ProtocolCache::create()
-                                .setSecurityOrigin(security_origin)
-                                .setStorageKey(storage_key)
-                                .setCacheName(cache)
-                                .setCacheId(BuildCacheId(storage_key, cache))
-                                .build());
-                      }
-                      callback->sendSuccess(std::move(array));
-                    },
-                    security_origin, storage_key, std::move(callback)));
+  cache_storage.value()->Keys(
+      trace_id,
+      WTF::BindOnce(
+          [](String security_origin, String storage_key,
+             absl::optional<WTF::String> bucket_name,
+             protocol::Maybe<protocol::Storage::StorageBucket>
+                 maybe_storage_bucket,
+             int64_t trace_id,
+             scoped_refptr<RequestCallbackWrapper<RequestCacheNamesCallback>>
+                 callback,
+             const Vector<String>& cache_names) {
+            auto array = std::make_unique<protocol::Array<ProtocolCache>>();
+            for (auto& cache_name : cache_names) {
+              std::unique_ptr<ProtocolCache> protocol_cache =
+                  ProtocolCache::create()
+                      .setSecurityOrigin(security_origin)
+                      .setStorageKey(storage_key)
+                      .setCacheName(cache_name)
+                      .setCacheId(
+                          BuildCacheId(storage_key, bucket_name, cache_name))
+                      .build();
+
+              if (maybe_storage_bucket.isJust()) {
+                protocol_cache->setStorageBucket(
+                    maybe_storage_bucket.fromJust()->Clone());
+              }
+
+              array->emplace_back(std::move(protocol_cache));
+            }
+            callback->SendSuccess(std::move(array));
+          },
+          security_origin, storage_key, bucket_name,
+          std::move(maybe_storage_bucket), trace_id,
+          std::move(callback_wrapper)));
 }
 
 void InspectorCacheStorageAgent::requestEntries(
@@ -626,40 +745,44 @@
                          "InspectorCacheStorageAgent::requestEntries",
                          TRACE_ID_GLOBAL(trace_id), TRACE_EVENT_FLAG_FLOW_OUT);
 
+  auto callback_wrapper =
+      RequestCallbackWrapper<RequestEntriesCallback>::Wrap(std::move(callback));
+
   String cache_name;
-  mojom::blink::CacheStorage* cache_storage = nullptr;
-  ProtocolResponse response = AssertCacheStorageAndNameForId(
-      cache_id, frames_, &cache_name, &caches_, &cache_storage);
-  if (!response.IsSuccess()) {
-    callback->sendFailure(response);
+  auto cache_storage = GetCacheStorageRemoteForId(
+      cache_id, cache_name, callback_wrapper->GetFailureCallback());
+  if (!cache_storage.has_value()) {
+    callback_wrapper->SendFailure(cache_storage.error());
     return;
   }
+
   DataRequestParams params;
-  params.cache_name = cache_name;
   params.page_size = page_size.fromMaybe(-1);
   params.skip_count = skip_count.fromMaybe(0);
   params.path_filter = path_filter.fromMaybe("");
+  params.cache_name = cache_name;
 
-  cache_storage->Open(
+  cache_storage.value()->Open(
       cache_name, trace_id,
       WTF::BindOnce(
-          [](DataRequestParams params,
-             std::unique_ptr<RequestEntriesCallback> callback,
-             mojom::blink::OpenResultPtr result) {
+          [](scoped_refptr<RequestCallbackWrapper<RequestEntriesCallback>>
+                 callback_wrapper,
+             DataRequestParams params, mojom::blink::OpenResultPtr result) {
             if (result->is_status()) {
-              callback->sendFailure(ProtocolResponse::ServerError(
+              callback_wrapper->SendFailure(ProtocolResponse::ServerError(
                   String::Format("Error requesting cache %s: %s",
                                  params.cache_name.Latin1().c_str(),
                                  CacheStorageErrorString(result->get_status()))
                       .Utf8()));
             } else {
               auto request = std::make_unique<GetCacheKeysForRequestData>(
-                  params, std::move(result->get_cache()), std::move(callback));
+                  params, std::move(result->get_cache()),
+                  std::move(callback_wrapper));
               auto* request_ptr = request.get();
               request_ptr->Dispatch(std::move(request));
             }
           },
-          params, std::move(callback)));
+          std::move(callback_wrapper), params));
 }
 
 void InspectorCacheStorageAgent::deleteCache(
@@ -670,29 +793,32 @@
                          "InspectorCacheStorageAgent::deleteCache",
                          TRACE_ID_GLOBAL(trace_id), TRACE_EVENT_FLAG_FLOW_OUT);
 
+  auto callback_wrapper =
+      RequestCallbackWrapper<DeleteCacheCallback>::Wrap(std::move(callback));
+
   String cache_name;
-  mojom::blink::CacheStorage* cache_storage = nullptr;
-  ProtocolResponse response = AssertCacheStorageAndNameForId(
-      cache_id, frames_, &cache_name, &caches_, &cache_storage);
-  if (!response.IsSuccess()) {
-    callback->sendFailure(response);
+  auto cache_storage = GetCacheStorageRemoteForId(
+      cache_id, cache_name, callback_wrapper->GetFailureCallback());
+  if (!cache_storage.has_value()) {
+    callback_wrapper->SendFailure(cache_storage.error());
     return;
   }
-  cache_storage->Delete(
+  cache_storage.value()->Delete(
       cache_name, trace_id,
       WTF::BindOnce(
-          [](std::unique_ptr<DeleteCacheCallback> callback,
+          [](scoped_refptr<RequestCallbackWrapper<DeleteCacheCallback>>
+                 callback_wrapper,
              mojom::blink::CacheStorageError error) {
             if (error == mojom::blink::CacheStorageError::kSuccess) {
-              callback->sendSuccess();
+              callback_wrapper->SendSuccess();
             } else {
-              callback->sendFailure(ProtocolResponse::ServerError(
+              callback_wrapper->SendFailure(ProtocolResponse::ServerError(
                   String::Format("Error requesting cache names: %s",
                                  CacheStorageErrorString(error))
                       .Utf8()));
             }
           },
-          std::move(callback)));
+          std::move(callback_wrapper)));
 }
 
 void InspectorCacheStorageAgent::deleteEntry(
@@ -704,22 +830,25 @@
                          "InspectorCacheStorageAgent::deleteEntry",
                          TRACE_ID_GLOBAL(trace_id), TRACE_EVENT_FLAG_FLOW_OUT);
 
+  auto callback_wrapper =
+      RequestCallbackWrapper<DeleteEntryCallback>::Wrap(std::move(callback));
+
   String cache_name;
-  mojom::blink::CacheStorage* cache_storage = nullptr;
-  ProtocolResponse response = AssertCacheStorageAndNameForId(
-      cache_id, frames_, &cache_name, &caches_, &cache_storage);
-  if (!response.IsSuccess()) {
-    callback->sendFailure(response);
+  auto cache_storage = GetCacheStorageRemoteForId(
+      cache_id, cache_name, callback_wrapper->GetFailureCallback());
+  if (!cache_storage.has_value()) {
+    callback_wrapper->SendFailure(cache_storage.error());
     return;
   }
-  cache_storage->Open(
+  cache_storage.value()->Open(
       cache_name, trace_id,
       WTF::BindOnce(
-          [](String cache_name, String request, int64_t trace_id,
-             std::unique_ptr<DeleteEntryCallback> callback,
-             mojom::blink::OpenResultPtr result) {
+          [](String request, int64_t trace_id,
+             scoped_refptr<RequestCallbackWrapper<DeleteEntryCallback>>
+                 callback_wrapper,
+             String cache_name, mojom::blink::OpenResultPtr result) {
             if (result->is_status()) {
-              callback->sendFailure(ProtocolResponse::ServerError(
+              callback_wrapper->SendFailure(ProtocolResponse::ServerError(
                   String::Format("Error requesting cache %s: %s",
                                  cache_name.Latin1().c_str(),
                                  CacheStorageErrorString(result->get_status()))
@@ -742,23 +871,25 @@
                   WTF::BindOnce(
                       [](mojo::AssociatedRemote<mojom::blink::CacheStorageCache>
                              cache_remote,
-                         std::unique_ptr<DeleteEntryCallback> callback,
+                         scoped_refptr<RequestCallbackWrapper<
+                             DeleteEntryCallback>> callback_wrapper,
                          mojom::blink::CacheStorageVerboseErrorPtr error) {
                         if (error->value !=
                             mojom::blink::CacheStorageError::kSuccess) {
-                          callback->sendFailure(ProtocolResponse::ServerError(
-                              String::Format(
-                                  "Error deleting cache entry: %s",
-                                  CacheStorageErrorString(error->value))
-                                  .Utf8()));
+                          callback_wrapper->SendFailure(
+                              ProtocolResponse::ServerError(
+                                  String::Format(
+                                      "Error deleting cache entry: %s",
+                                      CacheStorageErrorString(error->value))
+                                      .Utf8()));
                         } else {
-                          callback->sendSuccess();
+                          callback_wrapper->SendSuccess();
                         }
                       },
-                      std::move(cache_remote), std::move(callback)));
+                      std::move(cache_remote), std::move(callback_wrapper)));
             }
           },
-          cache_name, request, trace_id, std::move(callback)));
+          request, trace_id, std::move(callback_wrapper), cache_name));
 }
 
 void InspectorCacheStorageAgent::requestCachedResponse(
@@ -772,14 +903,18 @@
                          "InspectorCacheStorageAgent::requestCachedResponse",
                          TRACE_ID_GLOBAL(trace_id), TRACE_EVENT_FLAG_FLOW_OUT);
 
+  auto callback_wrapper =
+      RequestCallbackWrapper<RequestCachedResponseCallback>::Wrap(
+          std::move(callback));
+
   String cache_name;
-  mojom::blink::CacheStorage* cache_storage = nullptr;
-  ProtocolResponse response = AssertCacheStorageAndNameForId(
-      cache_id, frames_, &cache_name, &caches_, &cache_storage);
-  if (!response.IsSuccess()) {
-    callback->sendFailure(response);
+  auto cache_storage = GetCacheStorageRemoteForId(
+      cache_id, cache_name, callback_wrapper->GetFailureCallback());
+  if (!cache_storage.has_value()) {
+    callback_wrapper->SendFailure(cache_storage.error());
     return;
   }
+
   auto request = mojom::blink::FetchAPIRequest::New();
   request->url = KURL(request_url);
   request->method = String("GET");
@@ -793,16 +928,17 @@
   multi_query_options->query_options = mojom::blink::CacheQueryOptions::New();
   multi_query_options->cache_name = cache_name;
 
-  cache_storage->Match(
+  cache_storage.value()->Match(
       std::move(request), std::move(multi_query_options),
-      /*in_related_fetch_event=*/false, /*in_range_fetch_event=*/false,
-      trace_id,
+      /*in_related_fetch_event=*/false,
+      /*in_range_fetch_event=*/false, trace_id,
       WTF::BindOnce(
-          [](std::unique_ptr<RequestCachedResponseCallback> callback,
+          [](scoped_refptr<RequestCallbackWrapper<
+                 RequestCachedResponseCallback>> callback_wrapper,
              scoped_refptr<base::SingleThreadTaskRunner> task_runner,
              mojom::blink::MatchResultPtr result) {
             if (result->is_status()) {
-              callback->sendFailure(ProtocolResponse::ServerError(
+              callback_wrapper->SendFailure(ProtocolResponse::ServerError(
                   String::Format("Unable to read cached response: %s",
                                  CacheStorageErrorString(result->get_status()))
                       .Utf8()));
@@ -810,16 +946,16 @@
               std::unique_ptr<protocol::DictionaryValue> headers =
                   protocol::DictionaryValue::create();
               if (!result->get_response()->blob) {
-                callback->sendSuccess(CachedResponse::create()
-                                          .setBody(protocol::Binary())
-                                          .build());
+                callback_wrapper->SendSuccess(CachedResponse::create()
+                                                  .setBody(protocol::Binary())
+                                                  .build());
                 return;
               }
               CachedResponseFileReaderLoaderClient::Load(
                   std::move(task_runner),
-                  std::move(result->get_response()->blob), std::move(callback));
+                  std::move(result->get_response()->blob), callback_wrapper);
             }
           },
-          std::move(callback), std::move(task_runner)));
+          std::move(callback_wrapper), std::move(task_runner)));
 }
 }  // namespace blink
diff --git a/third_party/blink/renderer/modules/cache_storage/inspector_cache_storage_agent.h b/third_party/blink/renderer/modules/cache_storage/inspector_cache_storage_agent.h
index d746c84..6d10a34 100644
--- a/third_party/blink/renderer/modules/cache_storage/inspector_cache_storage_agent.h
+++ b/third_party/blink/renderer/modules/cache_storage/inspector_cache_storage_agent.h
@@ -7,6 +7,7 @@
 
 #include <memory>
 
+#include "base/types/expected.h"
 #include "mojo/public/cpp/bindings/remote.h"
 #include "third_party/blink/public/mojom/cache_storage/cache_storage.mojom-blink.h"
 #include "third_party/blink/renderer/core/inspector/inspector_base_agent.h"
@@ -33,9 +34,11 @@
   ~InspectorCacheStorageAgent() override;
   void Trace(Visitor*) const override;
 
-  void requestCacheNames(protocol::Maybe<String> maybe_security_origin,
-                         protocol::Maybe<String> maybe_storage_key,
-                         std::unique_ptr<RequestCacheNamesCallback>) override;
+  void requestCacheNames(
+      protocol::Maybe<String> maybe_security_origin,
+      protocol::Maybe<String> maybe_storage_key,
+      protocol::Maybe<protocol::Storage::StorageBucket> maybe_storage_bucket,
+      std::unique_ptr<RequestCacheNamesCallback>) override;
   void requestEntries(const String& cache_id,
                       protocol::Maybe<int> skip_count,
                       protocol::Maybe<int> page_size,
@@ -54,6 +57,16 @@
       std::unique_ptr<RequestCachedResponseCallback>) override;
 
  private:
+  base::expected<mojom::blink::CacheStorage*, protocol::Response>
+  GetCacheStorageRemote(
+      const String& storage_key,
+      const absl::optional<String>& storage_bucket_name,
+      base::OnceCallback<void(protocol::Response)> on_failure_callback);
+  base::expected<mojom::blink::CacheStorage*, protocol::Response>
+  GetCacheStorageRemoteForId(
+      const String& cache_id,
+      String& cache_name,
+      base::OnceCallback<void(protocol::Response)> on_failure_callback);
   Member<InspectedFrames> frames_;
 
   GC_PLUGIN_IGNORE("https://crbug.com/1381979")
diff --git a/third_party/blink/renderer/modules/indexeddb/inspector_indexed_db_agent.cc b/third_party/blink/renderer/modules/indexeddb/inspector_indexed_db_agent.cc
index 241ce16c..b9a4360 100644
--- a/third_party/blink/renderer/modules/indexeddb/inspector_indexed_db_agent.cc
+++ b/third_party/blink/renderer/modules/indexeddb/inspector_indexed_db_agent.cc
@@ -196,14 +196,10 @@
     Navigator* navigator = dom_window->navigator();
     StorageBucketManager* storage_bucket_manager =
         StorageBucketManager::storageBuckets(*navigator);
-    storage_bucket_manager->GetBucketForDevtools(
-        script_state, storage_bucket_name,
-        WTF::BindOnce(&ExecutableWithIdbFactory::GotBucketIDBFactory,
-                      WrapPersistent(this)));
-  }
-
-  void GotBucketIDBFactory(StorageBucket* storage_bucket) {
-    if (storage_bucket != nullptr) {
+    StorageBucket* storage_bucket =
+        storage_bucket_manager->GetBucketForDevtools(script_state,
+                                                     storage_bucket_name);
+    if (storage_bucket) {
       OnSuccess(storage_bucket->indexedDB());
     } else {
       OnFailure(protocol::Response::ServerError("Couldn't retrieve bucket"));
diff --git a/third_party/blink/renderer/platform/graphics/gpu/xr_frame_transport.cc b/third_party/blink/renderer/platform/graphics/gpu/xr_frame_transport.cc
index 2715da38e..ecae7e3 100644
--- a/third_party/blink/renderer/platform/graphics/gpu/xr_frame_transport.cc
+++ b/third_party/blink/renderer/platform/graphics/gpu/xr_frame_transport.cc
@@ -214,6 +214,7 @@
 }
 
 void XRFrameTransport::WaitForPreviousTransfer() {
+  DVLOG(3) << __func__ << " Start";
   TRACE_EVENT0("gpu", "waitForPreviousTransferToFinish");
   while (waiting_for_previous_frame_transfer_) {
     if (!submit_frame_client_receiver_.WaitForIncomingCall()) {
@@ -221,6 +222,7 @@
       break;
     }
   }
+  DVLOG(3) << __func__ << " Stop";
 }
 
 void XRFrameTransport::OnSubmitFrameRendered() {
@@ -229,6 +231,7 @@
 }
 
 base::TimeDelta XRFrameTransport::WaitForPreviousRenderToFinish() {
+  DVLOG(3) << __func__ << " Start";
   TRACE_EVENT0("gpu", "waitForPreviousRenderToFinish");
   base::TimeTicks start = base::TimeTicks::Now();
   while (waiting_for_previous_frame_render_) {
@@ -237,6 +240,7 @@
       break;
     }
   }
+  DVLOG(3) << __func__ << " Stop";
   return base::TimeTicks::Now() - start;
 }
 
@@ -247,6 +251,7 @@
 }
 
 base::TimeDelta XRFrameTransport::WaitForGpuFenceReceived() {
+  DVLOG(3) << __func__ << " Start";
   TRACE_EVENT0("gpu", "WaitForGpuFenceReceived");
   base::TimeTicks start = base::TimeTicks::Now();
   while (waiting_for_previous_frame_fence_) {
@@ -255,6 +260,7 @@
       break;
     }
   }
+  DVLOG(3) << __func__ << " Stop";
   return base::TimeTicks::Now() - start;
 }
 
diff --git a/third_party/blink/renderer/platform/text/writing_mode_utils.h b/third_party/blink/renderer/platform/text/writing_mode_utils.h
index c6ec3d83..89ff05fc 100644
--- a/third_party/blink/renderer/platform/text/writing_mode_utils.h
+++ b/third_party/blink/renderer/platform/text/writing_mode_utils.h
@@ -159,14 +159,14 @@
   STACK_ALLOCATED();
 
  public:
-  LogicalToLogical(WritingDirectionMode parent_writing_direction,
-                   WritingDirectionMode child_writing_direction,
+  LogicalToLogical(WritingDirectionMode from_writing_direction,
+                   WritingDirectionMode to_writing_direction,
                    Value inline_start,
                    Value inline_end,
                    Value block_start,
                    Value block_end)
-      : LogicalToLogical(child_writing_direction,
-                         LogicalToPhysical<Value>(parent_writing_direction,
+      : LogicalToLogical(to_writing_direction,
+                         LogicalToPhysical<Value>(from_writing_direction,
                                                   inline_start,
                                                   inline_end,
                                                   block_start,
@@ -177,9 +177,9 @@
   Value BlockStart() const { return logical_.BlockStart(); }
 
  private:
-  LogicalToLogical(WritingDirectionMode child_writing_direction,
+  LogicalToLogical(WritingDirectionMode to_writing_direction,
                    LogicalToPhysical<Value> physical)
-      : logical_(child_writing_direction,
+      : logical_(to_writing_direction,
                  physical.Top(),
                  physical.Right(),
                  physical.Bottom(),
diff --git a/third_party/blink/web_tests/TestExpectations b/third_party/blink/web_tests/TestExpectations
index 877b14e..d321aa7e 100644
--- a/third_party/blink/web_tests/TestExpectations
+++ b/third_party/blink/web_tests/TestExpectations
@@ -3542,12 +3542,8 @@
 
 # [css-subgrid]
 crbug.com/618969 external/wpt/css/css-grid/subgrid/baseline-001.html [ Failure ]
-crbug.com/618969 external/wpt/css/css-grid/subgrid/grid-gap-007.html [ Failure ]
 crbug.com/618969 external/wpt/css/css-grid/subgrid/line-names-007.html [ Failure ]
-crbug.com/618969 external/wpt/css/css-grid/subgrid/orthogonal-writing-mode-001.html [ Failure ]
 crbug.com/618969 external/wpt/css/css-grid/subgrid/orthogonal-writing-mode-002.html [ Failure ]
-crbug.com/618969 external/wpt/css/css-grid/subgrid/orthogonal-writing-mode-003.html [ Failure ]
-crbug.com/618969 external/wpt/css/css-grid/subgrid/orthogonal-writing-mode-004.html [ Failure ]
 crbug.com/618969 external/wpt/css/css-grid/subgrid/subgrid-baseline-001.html [ Failure ]
 crbug.com/618969 external/wpt/css/css-grid/subgrid/subgrid-baseline-002.html [ Failure ]
 
diff --git a/third_party/blink/web_tests/http/tests/inspector-protocol/storage/cache-storage-request-cache-names-expected.txt b/third_party/blink/web_tests/http/tests/inspector-protocol/storage/cache-storage-request-cache-names-expected.txt
index 0157eaf..5ec1a24 100644
--- a/third_party/blink/web_tests/http/tests/inspector-protocol/storage/cache-storage-request-cache-names-expected.txt
+++ b/third_party/blink/web_tests/http/tests/inspector-protocol/storage/cache-storage-request-cache-names-expected.txt
@@ -1,16 +1,38 @@
-Tests that requesting cache names for origin works correctly
+Tests that requesting cache names for storage buckets works correctly
 
 Open cache, add item
 cache item added successfully
 
-Request cache names for origin
+Request cache names for 'default' storage bucket
 [
     [0] : {
-        cacheId : http://127.0.0.1:8000/|test-cache
+        cacheId : <string>
         cacheName : test-cache
         securityOrigin : http://127.0.0.1:8000
+        storageBucket : {
+            storageKey : <string>
+        }
         storageKey : <string>
     }
 ]
 security origin differs from storage key: true
 
+Open cache, add item
+cache item added successfully
+
+Request cache names for 'test-bucket' storage bucket
+[
+    [0] : {
+        cacheId : <string>
+        cacheName : test-cache
+        securityOrigin : http://127.0.0.1:8000
+        storageBucket : {
+            name : test-bucket
+            storageKey : <string>
+        }
+        storageKey : <string>
+    }
+]
+security origin differs from storage key: true
+
+
diff --git a/third_party/blink/web_tests/http/tests/inspector-protocol/storage/cache-storage-request-cache-names.js b/third_party/blink/web_tests/http/tests/inspector-protocol/storage/cache-storage-request-cache-names.js
index 1569c66..2cc37de 100644
--- a/third_party/blink/web_tests/http/tests/inspector-protocol/storage/cache-storage-request-cache-names.js
+++ b/third_party/blink/web_tests/http/tests/inspector-protocol/storage/cache-storage-request-cache-names.js
@@ -1,34 +1,56 @@
-(async function(testRunner) {
-  const {dp, session} = await testRunner.startBlank(
-      `Tests that requesting cache names for origin works correctly\n`);
+(async function (testRunner) {
+  async function testBucket(bucketName) {
+    const bucket = bucketName === undefined ?
+      'self' :
+      `(await navigator.storageBuckets.open('${bucketName}'))`;
+
+    const stabilizeNames = [
+      ...TestRunner.stabilizeNames, 'storageKey', 'storageBucketId', 'cacheId'
+    ];
+
+    const frameId = (await dp.Page.getResourceTree()).result.frameTree.frame.id;
+    const storageKey =
+      (await dp.Storage.getStorageKeyForFrame({ frameId })).result.storageKey;
+    const bucketPromise = (async () => {
+      dp.Storage.setStorageBucketTracking({ storageKey, enable: true });
+      const { params: { bucketInfo: { bucket } } } =
+        await dp.Storage.onceStorageBucketCreatedOrUpdated(
+          e => e.params.bucketInfo.bucket.name === bucketName);
+      return bucket;
+    })();
+
+    await dp.Storage.trackCacheStorageForStorageKey({ storageKey });
+
+    testRunner.log(`Open cache, add item`);
+    // Create cache and add an item.
+    const addResult = await session.evaluateAsync(`
+      (async function() {
+        try {
+          const cache = await ${bucket}.caches.open("test-cache");
+          await cache.add('/inspector-protocol/resources/empty.html');
+          return 'cache item added successfully';
+        } catch (err) {
+          return err;
+        }
+      })()`);
+
+    testRunner.log(addResult);
+
+    const storageBucket = await bucketPromise;
+
+    testRunner.log(`\nRequest cache names for '${bucketName ?? 'default'}' storage bucket`);
+    const requestCacheNamesPromise =
+      dp.CacheStorage.requestCacheNames({ storageBucket });
+    const caches = (await requestCacheNamesPromise).result.caches;
+    testRunner.log(caches, '', stabilizeNames);
+    testRunner.log(`security origin differs from storage key: ${caches[0].securityOrigin !== caches[0].storageKey}\n`);
+  }
+  const { dp, session } = await testRunner.startBlank(
+    `Tests that requesting cache names for storage buckets works correctly\n`);
   await dp.Page.enable();
 
-  const stabilizeNames = [...TestRunner.stabilizeNames, 'storageKey'];
-
-  const origin = (await dp.Page.getResourceTree()).result.frameTree.frame.securityOrigin;
-  await dp.Storage.trackCacheStorageForOrigin({origin});
-
-  testRunner.log(`Open cache, add item`);
-  // Create cache and add an item.
-  const addPromise = session.evaluateAsync(`
-    new Promise(async resolve => {
-      try {
-        const cache = await caches.open("test-cache");
-        await cache.add('/inspector-protocol/resources/empty.html');
-        resolve('cache item added successfully');
-      } catch (err) {
-        resolve(err);
-      }
-    })
-  `);
-
-  testRunner.log(await addPromise);
-
-  testRunner.log('\nRequest cache names for origin');
-  const requestCacheNamesPromise = dp.CacheStorage.requestCacheNames({securityOrigin: origin});
-  const caches = (await requestCacheNamesPromise).result.caches;
-  testRunner.log(caches, '', stabilizeNames);
-  testRunner.log(`security origin differs from storage key: ${caches[0].securityOrigin !== caches[0].storageKey}`);
+  await testBucket();
+  await testBucket('test-bucket');
 
   testRunner.completeTest();
 })
diff --git a/third_party/blink/web_tests/http/tests/inspector-protocol/storage/cache-storage-storage-key-request-cache-names-expected.txt b/third_party/blink/web_tests/http/tests/inspector-protocol/storage/cache-storage-storage-key-request-cache-names-expected.txt
index ca84eed..a69f70a 100644
--- a/third_party/blink/web_tests/http/tests/inspector-protocol/storage/cache-storage-storage-key-request-cache-names-expected.txt
+++ b/third_party/blink/web_tests/http/tests/inspector-protocol/storage/cache-storage-storage-key-request-cache-names-expected.txt
@@ -1,8 +1,15 @@
-Tests that requesting cache names for storage key works correctly
+Tests that requesting cache names for storage buckets works correctly
 
 Open cache, add item
 cache item added successfully
 
-Request cache names for storage key
+Request cache names for 'default' storage bucket
 1 cache with name: test-cache
 
+Open cache, add item
+cache item added successfully
+
+Request cache names for 'test-bucket' storage bucket
+1 cache with name: test-cache
+
+
diff --git a/third_party/blink/web_tests/http/tests/inspector-protocol/storage/cache-storage-storage-key-request-cache-names.js b/third_party/blink/web_tests/http/tests/inspector-protocol/storage/cache-storage-storage-key-request-cache-names.js
index 423a845..bd410d7 100644
--- a/third_party/blink/web_tests/http/tests/inspector-protocol/storage/cache-storage-storage-key-request-cache-names.js
+++ b/third_party/blink/web_tests/http/tests/inspector-protocol/storage/cache-storage-storage-key-request-cache-names.js
@@ -1,34 +1,54 @@
-(async function(testRunner) {
-  const {dp, session} = await testRunner.startBlank(
-      `Tests that requesting cache names for storage key works correctly\n`);
+(async function (testRunner) {
+  async function testBucket(bucketName) {
+    const bucket = bucketName === undefined ?
+      'self' :
+      `(await navigator.storageBuckets.open('${bucketName}'))`;
+
+    const frameId = (await dp.Page.getResourceTree()).result.frameTree.frame.id;
+    const storageKey = (await dp.Storage.getStorageKeyForFrame({
+      frameId: frameId
+    })).result.storageKey;
+    const bucketPromise = (async () => {
+      dp.Storage.setStorageBucketTracking({ storageKey, enable: true });
+      const { params: { bucketInfo: { bucket } } } =
+        await dp.Storage.onceStorageBucketCreatedOrUpdated(
+          e => e.params.bucketInfo.bucket.name === bucketName);
+      return bucket;
+    })();
+
+    await dp.Storage.trackCacheStorageForStorageKey({ storageKey });
+
+    testRunner.log(`Open cache, add item`);
+    // Create cache and add an item.
+    const addResult = await session.evaluateAsync(`
+      (async function() {
+        try {
+          const cache = await ${bucket}.caches.open("test-cache");
+          await cache.add('/inspector-protocol/resources/empty.html');
+          return 'cache item added successfully';
+        } catch (err) {
+          return err;
+        }
+      })()`);
+
+    testRunner.log(addResult);
+
+    const storageBucket = await bucketPromise;
+
+    testRunner.log(`\nRequest cache names for '${bucketName ?? 'default'}' storage bucket`);
+    const requestCacheNamesPromise =
+      dp.CacheStorage.requestCacheNames({ storageBucket });
+    const caches = (await requestCacheNamesPromise).result.caches;
+    testRunner.log(
+      `${caches.length} cache with name: ${caches[0].cacheName}\n`);
+  }
+
+  const { dp, session } = await testRunner.startBlank(
+    `Tests that requesting cache names for storage buckets works correctly\n`);
   await dp.Page.enable();
 
-  const frameId = (await dp.Page.getResourceTree()).result.frameTree.frame.id;
-  const storageKey = (await dp.Storage.getStorageKeyForFrame({
-                       frameId: frameId
-                     })).result.storageKey;
-  await dp.Storage.trackCacheStorageForStorageKey({storageKey});
-
-  testRunner.log(`Open cache, add item`);
-  // Create cache and add an item.
-  const addPromise = session.evaluateAsync(`
-    new Promise(async resolve => {
-      try {
-        const cache = await caches.open("test-cache");
-        await cache.add('/inspector-protocol/resources/empty.html');
-        resolve('cache item added successfully');
-      } catch (err) {
-        resolve(err);
-      }
-    })
-  `);
-
-  testRunner.log(await addPromise);
-
-  testRunner.log('\nRequest cache names for storage key');
-  const requestCacheNamesPromise = dp.CacheStorage.requestCacheNames({storageKey});
-  const caches = (await requestCacheNamesPromise).result.caches;
-  testRunner.log(`${caches.length} cache with name: ${caches[0].cacheName}`);
+  await testBucket();
+  await testBucket('test-bucket');
 
   testRunner.completeTest();
 })
diff --git a/third_party/blink/web_tests/media/video-source-none-supported.html b/third_party/blink/web_tests/media/video-source-none-supported.html
index d43487680..4b5e65a 100644
--- a/third_party/blink/web_tests/media/video-source-none-supported.html
+++ b/third_party/blink/web_tests/media/video-source-none-supported.html
@@ -15,15 +15,16 @@
     var sourceList = document.querySelectorAll("source");
     for (var source of sourceList) {
         source.onerror = t.step_func(function(event) {
-            errorCount++;
-            if (errorCount < 3) {
-                // Because the error event is fired asynchronously the network state
-                // can be either NETWORK_LOADING or NETWORK_NO_SOURCE, depending on
-                // whether or not any pending "source" element is available.
-                assert_greater_than(video.networkState, HTMLMediaElement.NETWORK_IDLE);
-            } else {
-                assert_equals(video.networkState, HTMLMediaElement.NETWORK_NO_SOURCE);
-                t.done();
+            // Because the error event is fired asynchronously the network state
+            // can be either NETWORK_LOADING or NETWORK_NO_SOURCE, depending on
+            // whether or not any pending "source" element is available.
+            assert_greater_than(video.networkState, HTMLMediaElement.NETWORK_IDLE);
+            if (++errorCount == 3) {
+                // Allow time for asynchronous event processing.
+                setTimeout(_ => {
+                    assert_equals(video.networkState, HTMLMediaElement.NETWORK_NO_SOURCE);
+                    t.done();
+                }, 0);
             }
         });
     }
diff --git a/third_party/freetype/README.chromium b/third_party/freetype/README.chromium
index 75f3d48..073800b 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-13-0-140-g115e92754
-Revision: 115e927540dba128980dd734dadeb06aa7b0f4d8
+Version: VER-2-13-0-141-g2342a03a9
+Revision: 2342a03a9d3e58a82e698fc4074dc2e5f95c4e26
 CPEPrefix: cpe:/a:freetype:freetype:2.12.1
 License: Custom license "inspired by the BSD, Artistic, and IJG (Independent
          JPEG Group) licenses"
diff --git a/third_party/nearby/BUILD.gn b/third_party/nearby/BUILD.gn
index 0d1e75e..a59883d 100644
--- a/third_party/nearby/BUILD.gn
+++ b/third_party/nearby/BUILD.gn
@@ -6,6 +6,7 @@
 if (is_chromeos_device) {
   import("//build/config/chromeos/rules.gni")
 }
+import("//build/config/rust.gni")
 
 # Nearby Config
 config("nearby_include_config") {
@@ -25,6 +26,10 @@
   defines = [ "NEARBY_CHROMIUM=1" ]
 }
 
+config("nearby_rust_defines") {
+  defines = [ "USE_RUST_LDT" ]
+}
+
 ###############################################################################
 # src/connections
 ###############################################################################
@@ -887,6 +892,10 @@
     "//third_party/abseil-cpp:absl",
     "//third_party/boringssl:boringssl",
   ]
+  if (enable_rust && enable_rust_boringssl) {
+    public_deps += [ "//third_party/beto-core:ldt_np_adv_ffi" ]
+    public_configs += [ ":nearby_rust_defines" ]
+  }
   configs -= [ "//build/config/compiler:chromium_code" ]
   configs += [ "//build/config/compiler:no_chromium_code" ]
 }
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
index 7efbe3f..a80f4dd 100644
--- a/tools/metrics/histograms/enums.xml
+++ b/tools/metrics/histograms/enums.xml
@@ -4710,6 +4710,20 @@
   <int value="12" label="shortSupportMessage"/>
   <int value="13" label="statusReportingSettings"/>
   <int value="14" label="workAccountAppWhitelist"/>
+  <int value="15" label="apkCacheEnabled"/>
+  <int value="16" label="debuggingFeaturesDisabled"/>
+  <int value="17" label="cameraDisabled"/>
+  <int value="18" label="printingDisabled"/>
+  <int value="19" label="screenCaptureDisabled"/>
+  <int value="20" label="shareLocationDisabled"/>
+  <int value="21" label="unmuteMicrophoneDisabled"/>
+  <int value="22" label="setWallpaperDisabled"/>
+  <int value="23" label="vpnConfigDisabled"/>
+  <int value="24" label="privateKeySelectionEnabled"/>
+  <int value="25" label="choosePrivateKeyRules"/>
+  <int value="26" label="credentialsConfigDisabled"/>
+  <int value="27" label="caCerts"/>
+  <int value="28" label="requiredKeyPairs"/>
 </enum>
 
 <enum name="ArcProvisioningCheckInError">
@@ -59682,6 +59696,7 @@
   <int value="-1573468162" label="ClientStorageAccessContextAuditing:enabled"/>
   <int value="-1572374515"
       label="SafeSitesFilterBehaviorPolicyAndroid:disabled"/>
+  <int value="-1572246329" label="TabDragDropAndroid:enabled"/>
   <int value="-1572010356" label="enable-privet-v3"/>
   <int value="-1571841513" label="enable-devtools-experiments"/>
   <int value="-1571525676" label="FilesSWA:disabled"/>
@@ -65611,6 +65626,7 @@
   <int value="1594993731" label="BluetoothRevamp:enabled"/>
   <int value="1595208893" label="AudioWorkletRealtimeThread:enabled"/>
   <int value="1595796800" label="CameraAppLowStorageWarning:disabled"/>
+  <int value="1595840275" label="TabDragDropAndroid:disabled"/>
   <int value="1596615083"
       label="AutofillFillCreditCardAsPerFormatString:disabled"/>
   <int value="1596689942" label="OmniboxRevertModelBeforeClosingPopup:enabled"/>
@@ -104749,40 +104765,6 @@
   <int value="9" label="The page has already been translated"/>
 </enum>
 
-<enum name="TranslateBubbleUiEvent">
-  <int value="1" label="[DEPRECATED] Switch to Options page"/>
-  <int value="2" label="[DEPRECATED] Leave Options page"/>
-  <int value="3" label="[DEPRECATED] Advanced Link clicked"/>
-  <int value="4" label="Always Translate checked"/>
-  <int value="5" label="Always Translate unchecked"/>
-  <int value="6" label="[DEPRECATED] Nope menu clicked"/>
-  <int value="7" label="Never Translate Language menu clicked"/>
-  <int value="8" label="Never Translate This Site menu clicked"/>
-  <int value="9" label="Triggered translation by clicking target language tab"/>
-  <int value="10" label="Done button clicked"/>
-  <int value="11" label="[DEPRECATED] Cancel button clicked"/>
-  <int value="12" label="Close button clicked"/>
-  <int value="13" label="Try Again button clicked"/>
-  <int value="14" label="Reverted translation by clicking source language tab"/>
-  <int value="15" label="[DEPRECATED] Language Settings link clicked"/>
-  <int value="16" label="Language selected in source language combobox"/>
-  <int value="17" label="Language selected in target language combobox"/>
-  <int value="18" label="[DEPRECATED] Page Action icon activated"/>
-  <int value="19" label="[DEPRECATED] Page Action icon deactivated"/>
-  <int value="20" label="Bubble was shown"/>
-  <int value="21"
-      label="[DEPRECATED] Bubble not shown: window is no longer valid"/>
-  <int value="22" label="Bubble not shown: window is minimized"/>
-  <int value="23" label="[DEPRECATED] Bubble not shown: window is not active"/>
-  <int value="24"
-      label="[DEPRECATED] Bubble not shown: web contents not active"/>
-  <int value="25" label="Bubble not shown: editable field is active"/>
-  <int value="26"
-      label="Page is Not in {Source Language} item, or Choose Another
-             Language item, in options menu clicked"/>
-  <int value="27" label="[DEPRECATED] Advanced menu button clicked"/>
-</enum>
-
 <enum name="TranslateCompactUIEvent">
   <int value="0" label="Infobar impression"/>
   <int value="1" label="Translation accepted by clicking on tab language"/>
diff --git a/tools/metrics/histograms/metadata/others/histograms.xml b/tools/metrics/histograms/metadata/others/histograms.xml
index e62a21c..edbf12e2 100644
--- a/tools/metrics/histograms/metadata/others/histograms.xml
+++ b/tools/metrics/histograms/metadata/others/histograms.xml
@@ -10908,8 +10908,8 @@
   <owner>chrome-counter-abuse-alerts@google.com</owner>
   <summary>
     Result of URL-classification API calls from Chrome via
-    RemoteSafeBrowsingApiHandler. Logged after each URL is judged safe/not-safe,
-    or hits a deadline. The INTERNAL_ERROR cases are further classified under
+    RemoteSafetyNetApiHandler. Logged after each URL is judged safe/not-safe, or
+    hits a deadline. The INTERNAL_ERROR cases are further classified under
     SB2.RemoteCall.InternalErrorStatusCode.
   </summary>
 </histogram>
diff --git a/tools/metrics/histograms/metadata/translate/histograms.xml b/tools/metrics/histograms/metadata/translate/histograms.xml
index a89db72..04ecf732 100644
--- a/tools/metrics/histograms/metadata/translate/histograms.xml
+++ b/tools/metrics/histograms/metadata/translate/histograms.xml
@@ -96,13 +96,6 @@
   </summary>
 </histogram>
 
-<histogram name="Translate.BubbleUiEvent" enum="TranslateBubbleUiEvent"
-    expires_after="2023-10-01">
-  <owner>groby@google.com</owner>
-  <owner>chrome-language@google.com</owner>
-  <summary>Tracks UI events related to the Full Page Translate bubble.</summary>
-</histogram>
-
 <histogram name="Translate.CaptureText" units="ms" expires_after="2023-01-15">
   <owner>sclittle@google.com</owner>
   <owner>megjablon@google.com</owner>
@@ -344,16 +337,6 @@
   </summary>
 </histogram>
 
-<histogram name="Translate.ModifyTargetLang" units="units"
-    expires_after="2023-10-08">
-  <owner>megjablon@google.com</owner>
-  <owner>chrome-language@google.com</owner>
-  <summary>
-    The number of times the target language in the translate infobar has been
-    changed.
-  </summary>
-</histogram>
-
 <histogram name="Translate.NeverTranslateLang" units="units"
     expires_after="2023-10-08">
   <owner>megjablon@google.com</owner>
@@ -692,16 +675,6 @@
   </summary>
 </histogram>
 
-<histogram name="Translate.RevertTranslation" units="units"
-    expires_after="2023-10-08">
-  <owner>megjablon@google.com</owner>
-  <owner>chrome-language@google.com</owner>
-  <summary>
-    The number of times the show original button was clicked in the translate
-    infobar.
-  </summary>
-</histogram>
-
 <histogram name="Translate.ShowErrorUI" enum="TranslateError"
     expires_after="2023-10-01">
   <owner>megjablon@google.com</owner>
@@ -713,26 +686,6 @@
   </summary>
 </histogram>
 
-<histogram name="Translate.SourceLanguage" enum="LocaleCodeISO639"
-    expires_after="2023-10-01">
-  <owner>megjablon@google.com</owner>
-  <owner>chrome-language@google.com</owner>
-  <summary>
-    The number of requests sent to the Translate server, grouped by source
-    language.
-  </summary>
-</histogram>
-
-<histogram name="Translate.TargetLanguage" enum="LocaleCodeISO639"
-    expires_after="2023-10-08">
-  <owner>megjablon@google.com</owner>
-  <owner>chrome-language@google.com</owner>
-  <summary>
-    The number of requests sent to the Translate server, grouped by target
-    language.
-  </summary>
-</histogram>
-
 <histogram name="Translate.Translate" enum="BooleanTranslate"
     expires_after="2023-10-01">
   <owner>megjablon@google.com</owner>
diff --git a/tools/perf/core/perfetto_binary_roller/binary_deps.json b/tools/perf/core/perfetto_binary_roller/binary_deps.json
index 2fcf66d..e1e68021 100644
--- a/tools/perf/core/perfetto_binary_roller/binary_deps.json
+++ b/tools/perf/core/perfetto_binary_roller/binary_deps.json
@@ -5,24 +5,24 @@
             "full_remote_path": "perfetto-luci-artifacts/v34.0/linux-arm64/trace_processor_shell"
         },
         "win": {
-            "hash": "f3fe9085b28c42e81608ceb77c7864933bb94fee",
-            "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/win/a9c0e2b0a61932cc706f57d504ea289d308e2af8/trace_processor_shell.exe"
+            "hash": "2b03595a2a238eb8cc9e2c852d4c2e950414f8c8",
+            "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/win/87d4934194957d4d3073209cd450c197843fe25a/trace_processor_shell.exe"
         },
         "linux_arm": {
             "hash": "336a42cb9ec3c417e13a97816271fec10cdf67e5",
             "full_remote_path": "perfetto-luci-artifacts/v34.0/linux-arm/trace_processor_shell"
         },
         "mac": {
-            "hash": "e054da3e3e8d81d546e94a81de951c3dafbe042c",
-            "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/mac/a9c0e2b0a61932cc706f57d504ea289d308e2af8/trace_processor_shell"
+            "hash": "2999ff57f16546167297a64dc76e5873c20c94e1",
+            "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/mac/87d4934194957d4d3073209cd450c197843fe25a/trace_processor_shell"
         },
         "mac_arm64": {
             "hash": "c32364e05e22cdf82ee0866aedd11c0e2050809c",
             "full_remote_path": "perfetto-luci-artifacts/v34.0/mac-arm64/trace_processor_shell"
         },
         "linux": {
-            "hash": "a4be7357cb5e8c55032ef305f9b1950c1d8d8c13",
-            "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/linux/a9c0e2b0a61932cc706f57d504ea289d308e2af8/trace_processor_shell"
+            "hash": "7097b3ce2a8c523258b092ef3c5bc4adb40186a4",
+            "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/linux/87d4934194957d4d3073209cd450c197843fe25a/trace_processor_shell"
         }
     },
     "power_profile.sql": {
diff --git a/ui/color/color_id.h b/ui/color/color_id.h
index bec6ee84..112fcce 100644
--- a/ui/color/color_id.h
+++ b/ui/color/color_id.h
@@ -402,8 +402,14 @@
   E_CPONLY(kColorSuggestionChipIcon) \
   E_CPONLY(kColorTextfieldBackground) \
   E_CPONLY(kColorTextfieldBackgroundDisabled) \
+  E_CPONLY(kColorTextfieldFilledBackground) \
+  E_CPONLY(kColorTextfieldFilledForegroundInvalid) \
+  E_CPONLY(kColorTextfieldFilledUnderline) \
+  E_CPONLY(kColorTextfieldFilledUnderlineFocused) \
   E_CPONLY(kColorTextfieldForeground) \
   E_CPONLY(kColorTextfieldForegroundDisabled) \
+  E_CPONLY(kColorTextfieldForegroundIcon) \
+  E_CPONLY(kColorTextfieldForegroundLabel) \
   E_CPONLY(kColorTextfieldForegroundPlaceholderInvalid) \
   E_CPONLY(kColorTextfieldForegroundPlaceholder) \
   E_CPONLY(kColorTextfieldSelectionBackground) \
diff --git a/ui/color/material_ui_color_mixer.cc b/ui/color/material_ui_color_mixer.cc
index 654f0f2..2cc1c354 100644
--- a/ui/color/material_ui_color_mixer.cc
+++ b/ui/color/material_ui_color_mixer.cc
@@ -82,14 +82,22 @@
   mixer[kColorSliderTrackMinimal] = {kColorSysOnSecondary};
   mixer[kColorSuggestionChipBorder] = {kColorSysTonalOutline};
   mixer[kColorSuggestionChipIcon] = {kColorSysPrimary};
+  // TODO(colehorvitz): Rename textfield color IDs to specify which
+  // textfield variation they are used for ('filled' or 'stroked').
   mixer[kColorTextfieldBackground] = {kColorSysSurface};
   mixer[kColorTextfieldBackgroundDisabled] = {GetResultingPaintColor(
       {kColorSysStateDisabledContainer}, {kColorTextfieldBackground})};
+  mixer[kColorTextfieldFilledUnderline] = {kColorSysOutline};
+  mixer[kColorTextfieldFilledUnderlineFocused] = {kColorSysPrimary};
+  mixer[kColorTextfieldFilledBackground] = {kColorSysSurfaceVariant};
+  mixer[kColorTextfieldFilledForegroundInvalid] = {kColorSysError};
   mixer[kColorTextfieldForeground] = {kColorSysOnSurface};
   mixer[kColorTextfieldForegroundPlaceholderInvalid] = {
       BlendForMinContrast(kColorSysError, kColorTextfieldBackground)};
   mixer[kColorTextfieldForegroundDisabled] = {kColorSysStateDisabled};
+  mixer[kColorTextfieldForegroundLabel] = {kColorSysOnSurfaceSubtle};
   mixer[kColorTextfieldForegroundPlaceholder] = {kColorSysOnSurfaceSubtle};
+  mixer[kColorTextfieldForegroundIcon] = {kColorSysOnSurfaceSubtle};
   mixer[kColorTextfieldOutline] = {kColorSysNeutralOutline};
   mixer[kColorTextfieldDisabledOutline] = {SK_ColorTRANSPARENT};
   mixer[kColorTextfieldInvalidOutline] = {
diff --git a/ui/linux/linux_ui_factory.cc b/ui/linux/linux_ui_factory.cc
index 95d7223..0f29a8b 100644
--- a/ui/linux/linux_ui_factory.cc
+++ b/ui/linux/linux_ui_factory.cc
@@ -55,6 +55,9 @@
 }
 
 std::unique_ptr<LinuxUiAndTheme> CreateQtUi() {
+  if (!base::FeatureList::IsEnabled(kAllowQt)) {
+    return nullptr;
+  }
 #if BUILDFLAG(USE_QT)
   auto qt_ui = qt::CreateQtUi(GetGtkUi());
   if (qt_ui->Initialize()) {
@@ -124,6 +127,8 @@
 
 }  // namespace
 
+BASE_FEATURE(kAllowQt, "AllowQt", base::FEATURE_DISABLED_BY_DEFAULT);
+
 LinuxUi* GetDefaultLinuxUi() {
   auto* linux_ui = GetDefaultLinuxUiAndTheme();
 #if !BUILDFLAG(IS_CASTOS)
diff --git a/ui/linux/linux_ui_factory.h b/ui/linux/linux_ui_factory.h
index 7620b92..5d4f4f4 100644
--- a/ui/linux/linux_ui_factory.h
+++ b/ui/linux/linux_ui_factory.h
@@ -14,6 +14,9 @@
 class LinuxUiTheme;
 enum class SystemTheme : int;
 
+// TODO(https://crbug.com/1317782): Remove in M110.
+COMPONENT_EXPORT(LINUX_UI_FACTORY) BASE_DECLARE_FEATURE(kAllowQt);
+
 // Returns a LinuxUi for the default toolkit.  May create a LinuxUi instance if
 // one does not exist.  May return nullptr if no toolkits are available.
 COMPONENT_EXPORT(LINUX_UI_FACTORY)
diff --git a/ui/webui/resources/cr_elements/cr_input/cr_input.html b/ui/webui/resources/cr_elements/cr_input/cr_input.html
index f74ae49..7957153 100644
--- a/ui/webui/resources/cr_elements/cr_input/cr_input.html
+++ b/ui/webui/resources/cr_elements/cr_input/cr_input.html
@@ -31,6 +31,11 @@
         pointer-events: none;
       }
 
+      :host-context([chrome-refresh-2023]):host([disabled])
+          :is(#label, #error, #input-container) {
+        opacity: 1;
+      }
+
       /* Margin between <input> and <cr-button> in the 'suffix' slot */
       :host ::slotted(cr-button[slot=suffix]) {
         margin-inline-start: var(--cr-button-edge-spacing) !important;
@@ -45,6 +50,10 @@
         letter-spacing: var(--cr-input-letter-spacing);
       }
 
+      :host-context([chrome-refresh-2023]) #input {
+        border-bottom: none;
+      }
+
       :host-context([chrome-refresh-2023]) #input-container {
         border: var(--cr-input-border, none);
       }
@@ -90,12 +99,18 @@
         white-space: var(--cr-input-error-white-space);
       }
 
+      :host-context([chrome-refresh-2023]) #error {
+        font-size: 11px;
+        line-height: 16px;
+        margin: 4px 10px;
+      }
+
       :host([invalid]) #error {
         visibility: visible;
       }
 
       #row-container,
-      #inner-input-container {
+      #inner-input-content {
         align-items: center;
         display: flex;
         /* This will spread the input field and the suffix apart only if the
@@ -104,6 +119,11 @@
         position: relative;
       }
 
+      :host-context([chrome-refresh-2023]) #inner-input-content {
+        /* Ensures content sits above the hover layer */
+        z-index: 1;
+      }
+
       #input[type='search']::-webkit-search-cancel-button {
         display: none;
       }
@@ -123,24 +143,28 @@
     <div id="row-container" part="row-container">
       <div id="input-container">
         <div id="inner-input-container">
-          <slot name="inline-prefix"></slot>
-          <!-- Only attributes that are named inconsistently between html and js
-              need to use attr$="", such as |readonly| vs .readOnly. -->
-          <input id="input" disabled="[[disabled]]" autofocus="[[autofocus]]"
-              value="{{value::input}}" tabindex$="[[inputTabindex]]"
-              type="[[type]]"
-              readonly$="[[readonly]]" maxlength$="[[maxlength]]"
-              pattern$="[[pattern]]" required="[[required]]"
-              minlength$="[[minlength]]" inputmode$="[[inputmode]]"
-              aria-description$="[[ariaDescription]]"
-              aria-label$="[[getAriaLabel_(ariaLabel, label, placeholder)]]"
-              aria-invalid$="[[getAriaInvalid_(invalid)]]"
-              max="[[max]]" min="[[min]]" on-focus="onInputFocus_"
-              on-blur="onInputBlur_" on-change="onInputChange_"
-              part="input"
-              autocomplete="off">
-          <slot name="inline-suffix"></slot>
+          <div id="hover-layer"></div>
+          <div id="inner-input-content">
+            <slot name="inline-prefix"></slot>
+            <!-- Only attributes that are named inconsistently between html and js
+                need to use attr$="", such as |readonly| vs .readOnly. -->
+            <input id="input" disabled="[[disabled]]" autofocus="[[autofocus]]"
+                value="{{value::input}}" tabindex$="[[inputTabindex]]"
+                type="[[type]]"
+                readonly$="[[readonly]]" maxlength$="[[maxlength]]"
+                pattern$="[[pattern]]" required="[[required]]"
+                minlength$="[[minlength]]" inputmode$="[[inputmode]]"
+                aria-description$="[[ariaDescription]]"
+                aria-label$="[[getAriaLabel_(ariaLabel, label, placeholder)]]"
+                aria-invalid$="[[getAriaInvalid_(invalid)]]"
+                max="[[max]]" min="[[min]]" on-focus="onInputFocus_"
+                on-blur="onInputBlur_" on-change="onInputChange_"
+                part="input"
+                autocomplete="off">
+            <slot name="inline-suffix"></slot>
+          </div>
         </div>
+        <div id="underline-base"></div>
         <div id="underline"></div>
       </div>
       <slot name="suffix"></slot>
diff --git a/ui/webui/resources/cr_elements/cr_input/cr_input_style.css b/ui/webui/resources/cr_elements/cr_input/cr_input_style.css
index 33e67a5bf..69f29e3 100644
--- a/ui/webui/resources/cr_elements/cr_input/cr_input_style.css
+++ b/ui/webui/resources/cr_elements/cr_input/cr_input_style.css
@@ -18,6 +18,29 @@
         outline: none;
       }
 
+      :host-context([chrome-refresh-2023]):host {
+        --cr-input-background-color: var(--color-textfield-filled-background,
+            var(--cr-fallback-color-surface-variant));
+        --cr-input-border-bottom: 1px solid
+            var(--color-textfield-filled-underline,
+                var(--cr-fallback-color-outline));
+        --cr-input-border-radius: 8px 8px 0 0;
+        --cr-input-error-color: var(--color-textfield-filled-error,
+            var(--cr-fallback-color-error));
+        --cr-input-focus-color: var(--color-textfield-filled-underline-focused,
+            var(--cr-fallback-color-primary));
+        --cr-input-hover-background-color: var(--cr-hover-background-color);
+        --cr-input-padding-bottom: 10px;
+        --cr-input-padding-top: 10px;
+        --cr-input-placeholder-color:
+            var(--color-textfield-foreground-placeholder,
+                var(--cr-fallback-on-surface-subtle));
+      }
+
+      :host-context([chrome-refresh-2023]):host([readonly]) {
+        --cr-input-border-radius: 8px 8px;
+      }
+
       @media (prefers-color-scheme: dark) {
         :host {
           --cr-input-background-color: rgba(0, 0, 0, .3);
@@ -26,10 +49,17 @@
         }
       }
 
-      :host([focused_]:not([readonly]):not([invalid])) #label {
+      :host-context(html:not([chrome-refresh-2023])):host([focused_]:not([readonly]):not([invalid]))
+          #label {
         color: var(--cr-input-focus-color);
       }
 
+      :host-context([chrome-refresh-2023]) #label {
+        color: var(--color-textfield-foreground-label,
+            var(--cr-fallback-color-on-surface-subtle));
+        font-size: 11px;
+      }
+
       /* Input styling below. */
       #input-container {
         border-radius: var(--cr-input-border-radius, 4px);
@@ -44,6 +74,36 @@
         padding: 0;
       }
 
+      :host-context([chrome-refresh-2023]) #inner-input-content ::slotted(*) {
+        --cr-icon-button-fill-color: var(--color-textfield-foreground-icon,
+            var(--cr-fallback-color-on-surface-subtle));
+        --cr-icon-color: var(--color-textfield-foreground-icon,
+            var(--cr-fallback-color-on-surface-subtle));
+      }
+
+      :host-context([chrome-refresh-2023]):host([invalid])
+          #inner-input-content ::slotted(*) {
+        --cr-icon-color: var(--cr-input-error-color);
+        --cr-icon-button-fill-color: var(--cr-input-error-color);
+      }
+
+      #hover-layer {
+        display: none;
+      }
+
+      :host-context([chrome-refresh-2023]) #hover-layer {
+        background-color: var(--cr-input-hover-background-color);
+        inset: 0;
+        pointer-events: none;
+        position: absolute;
+        z-index: 0;
+      }
+
+      :host-context([chrome-refresh-2023]):host(:not([readonly]))
+          #input-container:hover #hover-layer {
+        display: block;
+      }
+
       #input {
         -webkit-appearance: none;
         /* Transparent, #inner-input-container will apply background. */
@@ -77,6 +137,11 @@
         width: 100%;
       }
 
+      :host-context([chrome-refresh-2023]) #input {
+        font-size: 12px;
+        line-height: 16px;
+      }
+
       /* Underline styling below. */
       #underline {
         border-bottom: 2px solid var(--cr-input-focus-color);
@@ -101,3 +166,39 @@
         transition: opacity 120ms ease-in, width 180ms ease-out;
         width: 100%;
       }
+
+      #underline-base {
+        display: none;
+      }
+
+      :host-context([chrome-refresh-2023]):host([readonly]) #underline {
+        display: none;
+      }
+
+      :host-context([chrome-refresh-2023]):host(:not([readonly]))
+          #underline-base {
+        border-bottom: var(--cr-input-border-bottom);
+        bottom: 0;
+        display: block;
+        left: 0;
+        position: absolute;
+        right: 0;
+      }
+
+      :host-context([chrome-refresh-2023]):host([disabled]) {
+        color: var(--color-textfield-foreground-disabled,
+              rgba(var(--cr-fallback-color-on-surface-rgb),
+                  var(--cr-disabled-opacity)));
+        --cr-input-border-bottom: 1px solid currentColor;
+        --cr-input-placeholder-color: currentColor;
+        --cr-input-color: currentColor;
+        --cr-input-background-color: var(--color-textfield-background-disabled,
+            rgba(var(--cr-fallback-color-on-surface-rgb), .12));
+      }
+
+      :host-context([chrome-refresh-2023]):host([disabled])
+          #inner-input-content ::slotted(*) {
+        --cr-icon-color: currentColor;
+        --cr-icon-button-fill-color: currentColor;
+      }
+
diff --git a/ui/webui/resources/cr_elements/cr_shared_vars.css b/ui/webui/resources/cr_elements/cr_shared_vars.css
index 70ce36bc..34f88ac 100644
--- a/ui/webui/resources/cr_elements/cr_shared_vars.css
+++ b/ui/webui/resources/cr_elements/cr_shared_vars.css
@@ -275,6 +275,8 @@
 
   --cr-fallback-color-tonal-outline: rgb(168, 199, 250);
 
+  --cr-fallback-color-error: rgb(179, 38, 30);
+
   --cr-fallback-color-state-on-subtle-rgb: 31, 31, 31;
   --cr-fallback-color-state-hover-on-subtle: rgba(
       var(--cr-fallback-color-state-on-subtle-rgb), .06);
@@ -321,6 +323,7 @@
     --cr-fallback-color-tonal-container: rgb(0, 74, 119);
     --cr-fallback-color-on-tonal-container: rgb(194, 231, 255);
     --cr-fallback-color-tonal-outline: rgb(0, 99, 155);
+    --cr-fallback-color-error: rgb(242, 184, 181);
     --cr-fallback-color-state-on-subtle-rgb: 253, 252, 251;
     --cr-fallback-color-state-hover-on-subtle: rgba(
         var(--cr-fallback-color-state-on-subtle-rgb), .10);
diff --git a/ui/webui/resources/tools/build_webui.gni b/ui/webui/resources/tools/build_webui.gni
index 1386f55..f9dbb88 100644
--- a/ui/webui/resources/tools/build_webui.gni
+++ b/ui/webui/resources/tools/build_webui.gni
@@ -273,11 +273,16 @@
   }
 
   if (defined(invoker.mojo_files_deps)) {
+    mojo_base_path = "."
+    if (defined(invoker.mojo_base_path)) {
+      mojo_base_path = invoker.mojo_base_path
+    }
+
     copy("copy_mojo") {
       visibility = [ ":build_ts" ]
       deps = invoker.mojo_files_deps
       sources = invoker.mojo_files
-      outputs = [ "${preprocess_dir}/{{source_file_part}}" ]
+      outputs = [ "${preprocess_dir}/${mojo_base_path}/{{source_file_part}}" ]
     }
   }
 
diff --git a/ui/webui/resources/tools/bundle_js.gni b/ui/webui/resources/tools/bundle_js.gni
index 56dc603..982aba5 100644
--- a/ui/webui/resources/tools/bundle_js.gni
+++ b/ui/webui/resources/tools/bundle_js.gni
@@ -23,15 +23,23 @@
     input_count = 0
     foreach(f, invoker.js_module_in_files) {
       assert(get_path_info(f, "extension") == "js")
+      bundle_path = get_path_info(f, "dir")
       input_count = input_count + 1
-      out_file = string_replace(f, ".js", ".rollup.js")
-      outputs += [ "$target_gen_dir/$out_file" ]
+      out_file = string_replace(get_path_info(f, "file"), ".js", ".rollup.js")
+      outputs += [
+        "$target_gen_dir/$bundle_path/$out_file",
+        "$target_gen_dir/$bundle_path/${out_file}.map",
+      ]
       if (input_count == 2) {
-        folder = get_path_info(f, "dir")
-        outputs += [ "$target_gen_dir/$folder/shared.rollup.js" ]
+        outputs += [
+          "$target_gen_dir/$bundle_path/shared.rollup.js",
+          "$target_gen_dir/$bundle_path/shared.rollup.js.map",
+        ]
       }
     }
 
+    outputs += [ "$target_gen_dir/$bundle_path/rollup.config.mjs" ]
+
     # Note that we have to manually pass the sources to our script if the
     # script needs them as inputs.
     args = [
diff --git a/ui/webui/resources/tools/bundle_js.py b/ui/webui/resources/tools/bundle_js.py
index b575cbe..5f09553 100755
--- a/ui/webui/resources/tools/bundle_js.py
+++ b/ui/webui/resources/tools/bundle_js.py
@@ -12,7 +12,6 @@
 import re
 import shutil
 import sys
-import tempfile
 
 _HERE_PATH = os.path.dirname(__file__)
 _SRC_PATH = os.path.normpath(os.path.join(_HERE_PATH, '..', '..', '..', '..'))
@@ -39,18 +38,18 @@
   _BASE_EXCLUDES.append("//" + excluded_file)
 
 
-def _request_list_path(out_path, target_name):
+def _request_list_path(out_folder, target_name):
   # Using |target_name| as a prefix which is guaranteed to be unique within the
   # same folder, to avoid problems when multiple bundle_js() targets in the
   # same BUILD.gn file exist.
-  return os.path.join(out_path, target_name + '_requestlist.txt')
+  return os.path.join(out_folder, target_name + '_requestlist.txt')
 
 
-def _get_dep_path(dep, host_url, in_path):
+def _get_dep_path(dep, host_url, out_folder):
   if dep.startswith(host_url):
-    return dep.replace(host_url, os.path.relpath(in_path, _CWD))
+    return dep.replace(host_url, os.path.relpath(out_folder, _CWD))
   elif not (dep.startswith('chrome://') or dep.startswith('//')):
-    return os.path.relpath(in_path, _CWD) + '/' + dep
+    return os.path.relpath(out_folder, _CWD) + '/' + dep
   return dep
 
 
@@ -66,8 +65,9 @@
 
   # Add a slash in front of every dependency that is not a chrome:// URL, so
   # that we can map it to the correct source file path below.
-  request_list = map(lambda dep: _get_dep_path(dep, args.host_url, in_path),
-                     request_list)
+  request_list = map(
+      lambda dep: _get_dep_path(dep, args.host_url, args.out_folder),
+      request_list)
 
   deps = map(os.path.normpath, request_list)
 
@@ -79,8 +79,8 @@
 # pass it information about the location of the directories and files to
 # exclude from the bundle.
 # Arguments:
-# tmp_out_dir: The root directory for the output (i.e. corresponding to
-#              host_url at runtime).
+# out_dir: The root directory for the output (i.e. corresponding to
+#          host_url at runtime).
 # path_to_plugin: Path to the rollup plugin.
 # in_path: Root directory for the input files.
 # bundle_path: Path to the output files from the root output directory.
@@ -90,10 +90,9 @@
 # external_paths: Path mappings for import paths that are outside of
 #                 |in_path|. For example:
 #                 chrome://resources/|gen/ui/webui/resources/tsc
-def _generate_rollup_config(tmp_out_dir, path_to_plugin, in_path, bundle_path,
+def _generate_rollup_config(out_dir, path_to_plugin, in_path, bundle_path,
                             host_url, excludes, external_paths):
-  rollup_config_file = os.path.join(tmp_out_dir, bundle_path,
-                                    'rollup.config.mjs')
+  rollup_config_file = os.path.join(out_dir, bundle_path, 'rollup.config.mjs')
   config_content = r'''
     import plugin from '{plugin_path}';
     export default ({{
@@ -116,7 +115,8 @@
 # Create the manifest file from the sourcemap generated by rollup and return the
 # list of bundles.
 def _generate_manifest_file(out_dir, in_path, bundle_path, manifest_out_path):
-  generated_sourcemaps = glob.glob('%s/*.map' % out_dir)
+  generated_sourcemaps = glob.glob('%s/*.map' %
+                                   os.path.join(out_dir, bundle_path))
   manifest = {}
   output_filenames = []
   for sourcemap_file in generated_sourcemaps:
@@ -126,16 +126,13 @@
         raise Exception('rollup could not construct source map')
       sources = sourcemap['sources']
       replaced_sources = []
-      # Normalize everything to be relative to the input directory. This is
+      # Normalize everything to be relative to the output directory. This is
       # where the conversion to a dependency file expects it to be.
-      output_to_input = os.path.relpath(in_path, out_dir) + "/"
-      bundle_to_input = os.path.relpath(in_path,
-                                        os.path.join(in_path, bundle_path))
+      bundle_to_output = os.path.relpath(out_dir,
+                                         os.path.join(out_dir, bundle_path))
       for source in sources:
-        if output_to_input in source:
-          replaced_sources.append(source.replace(output_to_input, "", 1))
-        elif bundle_to_input != ".":
-          replaced_sources.append(source.replace(bundle_to_input + "/", "", 1))
+        if bundle_to_output != ".":
+          replaced_sources.append(source.replace(bundle_to_output + "/", "", 1))
         else:
           replaced_sources.append(source)
       filename = sourcemap_file[:-len('.map')]
@@ -150,18 +147,18 @@
   return output_filenames
 
 
-def _bundle(tmp_out_dir, in_path, out_path, manifest_out_path, args, excludes,
+def _bundle(out_folder, in_path, manifest_out_path, args, excludes,
             external_paths):
   bundle_path = os.path.dirname(args.js_module_in_files[0])
-  out_dir = tmp_out_dir if not bundle_path else os.path.join(
-      tmp_out_dir, bundle_path)
+  out_dir = out_folder if not bundle_path else os.path.join(
+      out_folder, bundle_path)
   if not os.path.exists(out_dir):
     os.makedirs(out_dir)
 
   path_to_plugin = os.path.join(
-      os.path.relpath(_HERE_PATH, os.path.join(tmp_out_dir, bundle_path)),
+      os.path.relpath(_HERE_PATH, os.path.join(out_folder, bundle_path)),
       'rollup_plugin.mjs')
-  rollup_config_file = _generate_rollup_config(tmp_out_dir, path_to_plugin,
+  rollup_config_file = _generate_rollup_config(out_folder, path_to_plugin,
                                                in_path, bundle_path,
                                                args.host_url, excludes,
                                                external_paths)
@@ -180,7 +177,7 @@
     bundle_name = '%s.rollup.js' % js_file[:-len('.js')]
     assert os.path.dirname(js_file) == bundle_path, \
            'All input files must be in the same directory.'
-    bundled_paths.append(os.path.join(tmp_out_dir, bundle_name))
+    bundled_paths.append(os.path.join(out_folder, bundle_name))
     bundle_names.append(bundle_name)
 
   # This indicates that rollup is expected to generate a shared chunk file as
@@ -207,7 +204,7 @@
   ])
 
   # Create the manifest file from the sourcemaps generated by rollup.
-  generated_paths = _generate_manifest_file(out_dir, in_path, bundle_path,
+  generated_paths = _generate_manifest_file(out_folder, in_path, bundle_path,
                                             manifest_out_path)
   assert len(generated_paths) == len(bundled_paths), \
          'unexpected number of bundles - %s - generated by rollup' % \
@@ -227,7 +224,6 @@
   in_path = os.path.normpath(os.path.join(_CWD, in_folder)).replace('\\', '/')
   out_path = os.path.join(_CWD, args.out_folder).replace('\\', '/')
   manifest_out_path = _request_list_path(out_path, args.target_name)
-  tmp_out_dir = tempfile.mkdtemp(dir=out_path).replace('\\', '/')
 
   excludes = _BASE_EXCLUDES + [
       # This file is dynamically created by C++. Should always be imported with
@@ -242,18 +238,9 @@
         ' Only .js files can appear in |excludes|.'
 
   external_paths = args.external_paths or []
-  js_module_out_files = []
 
-  try:
-    js_module_out_files = _bundle(tmp_out_dir, in_path, out_path,
-                                  manifest_out_path, args, excludes,
-                                  external_paths)
-    for index, js_out_file in enumerate(js_module_out_files):
-      shutil.copy(
-          os.path.join(tmp_out_dir, js_out_file),
-          os.path.join(out_path, js_out_file))
-  finally:
-    shutil.rmtree(tmp_out_dir)
+  js_module_out_files = _bundle(out_path, in_path, manifest_out_path, args,
+                                excludes, external_paths)
   return {
       'manifest_out_path': manifest_out_path,
       'js_module_out_files': js_module_out_files,
diff --git a/ui/webui/resources/tools/bundle_js_test.py b/ui/webui/resources/tools/bundle_js_test.py
index ec598b7..bc71634 100755
--- a/ui/webui/resources/tools/bundle_js_test.py
+++ b/ui/webui/resources/tools/bundle_js_test.py
@@ -202,10 +202,10 @@
         os.path.normpath('element_in_dir/element_in_dir.js'), depfile_d)
     self.assertIn(
         os.path.normpath(
-            '../gen/ui/webui/resources/tsc/js/scheme_relative_resource.js'),
+            'gen/ui/webui/resources/tsc/js/scheme_relative_resource.js'),
         depfile_d)
     self.assertIn(
-        os.path.normpath('../gen/ui/webui/resources/tsc/js/fake_resource.js'),
+        os.path.normpath('gen/ui/webui/resources/tsc/js/fake_resource.js'),
         depfile_d)
 
   def testMultiBundleBundle(self):
@@ -283,8 +283,7 @@
 
     depfile_d = self._read_out_file('depfile.d')
     self.assertIn('element.js', depfile_d)
-    # Relative path from the src of the root module to the external root dir
-    relpath = os.path.relpath(custom_dir, self._tmp_src_dir)
+    relpath = os.path.relpath(custom_dir, _CWD)
     self.assertIn(
         os.path.normpath(
             os.path.join(relpath, 'external_dir', 'external_element.js')),
@@ -408,8 +407,7 @@
 
     depfile_d = self._read_out_file('depfile.d')
     self.assertIn('element.js', depfile_d)
-    # Relative path from the src of the root module to the external root dir
-    relpath = os.path.relpath(custom_dir_foo, self._tmp_src_dir)
+    relpath = os.path.relpath(custom_dir_foo, _CWD)
     self.assertIn(
         os.path.normpath(
             os.path.join(relpath, 'external_dir', 'external_element.js')),
@@ -418,13 +416,29 @@
         os.path.normpath(
             os.path.join(relpath, 'external_dir', 'sub_dir',
                          'external_element_dep.js')), depfile_d)
-    # Relative path from the src of the root module to the secondary dependency
-    # root dir.
-    relpath_bar = os.path.relpath(custom_dir_bar, self._tmp_src_dir)
+    relpath_bar = os.path.relpath(custom_dir_bar, _CWD)
     self.assertIn(
         os.path.normpath(os.path.join(relpath_bar, 'another_element.js')),
         depfile_d)
 
+  def testBundleWithBundleSubpath(self):
+    self._write_files_to_src_dir()
+    self._write_file_to_src_dir(
+        'subfolder/ui.js', '''
+import '../element.js';
+import '../element_in_dir/element_in_dir.js';
+''')
+    args = [
+        '--host',
+        'fake-host',
+        '--js_module_in_files',
+        'subfolder/ui.js',
+    ]
+    self._run_bundle(args)
+
+    self._check_output_js(os.path.normpath('subfolder/ui.rollup.js'))
+    self._check_output_depfile(False)
+
 
 if __name__ == '__main__':
   unittest.main()
diff --git a/weblayer/browser/java/org/chromium/weblayer_private/TranslateCompactInfoBar.java b/weblayer/browser/java/org/chromium/weblayer_private/TranslateCompactInfoBar.java
index 67a9526..dd42909e 100644
--- a/weblayer/browser/java/org/chromium/weblayer_private/TranslateCompactInfoBar.java
+++ b/weblayer/browser/java/org/chromium/weblayer_private/TranslateCompactInfoBar.java
@@ -59,9 +59,6 @@
     private long mNativeTranslateInfoBarPtr;
     private TranslateTabLayout mTabLayout;
 
-    // Metric to track the total number of translations in a page, including reverts to original.
-    private int mTotalTranslationCount;
-
     // Histogram names for logging metrics.
     private static final String INFOBAR_HISTOGRAM = "Translate.CompactInfobar.Event";